@reticlehq/server 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (778) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge-security.d.ts +0 -1
  3. package/dist/bridge-security.js +0 -1
  4. package/dist/bridge.d.ts +0 -1
  5. package/dist/bridge.js +60 -10
  6. package/dist/bridge.test-harness.d.ts +0 -1
  7. package/dist/bridge.test-harness.js +0 -1
  8. package/dist/capsule/capsule-store.d.ts +2 -3
  9. package/dist/capsule/capsule-store.js +0 -1
  10. package/dist/capsule/capsule.d.ts +0 -1
  11. package/dist/capsule/capsule.js +0 -1
  12. package/dist/capsule/causal-summary.d.ts +21 -1
  13. package/dist/capsule/causal-summary.js +20 -2
  14. package/dist/capsule/divergence.d.ts +0 -1
  15. package/dist/capsule/divergence.js +0 -1
  16. package/dist/capsule/minimize.d.ts +0 -1
  17. package/dist/capsule/minimize.js +0 -1
  18. package/dist/capsule/predicate-to-links.d.ts +0 -1
  19. package/dist/capsule/predicate-to-links.js +0 -1
  20. package/dist/cli-doctor.d.ts +6 -0
  21. package/dist/cli-doctor.js +60 -0
  22. package/dist/cli-flow-commands.d.ts +0 -1
  23. package/dist/cli-flow-commands.js +0 -1
  24. package/dist/cli-launch.d.ts +0 -1
  25. package/dist/cli-launch.js +7 -3
  26. package/dist/cli-parse.d.ts +34 -2
  27. package/dist/cli-parse.js +107 -1
  28. package/dist/cli-port.d.ts +0 -1
  29. package/dist/cli-port.js +0 -1
  30. package/dist/cli-verify.d.ts +0 -1
  31. package/dist/cli-verify.js +1 -2
  32. package/dist/cli.d.ts +0 -1
  33. package/dist/cli.js +79 -67
  34. package/dist/cloud/cloud-config.d.ts +0 -1
  35. package/dist/cloud/cloud-config.js +0 -1
  36. package/dist/cloud/cloud-sync.d.ts +4 -5
  37. package/dist/cloud/cloud-sync.js +0 -1
  38. package/dist/cloud-cli.d.ts +0 -1
  39. package/dist/cloud-cli.js +9 -2
  40. package/dist/crawl/crawl-tools.d.ts +0 -1
  41. package/dist/crawl/crawl-tools.js +1 -2
  42. package/dist/crawl/crawl.d.ts +17 -3
  43. package/dist/crawl/crawl.js +43 -4
  44. package/dist/daemon-resilience.d.ts +6 -1
  45. package/dist/daemon-resilience.js +63 -1
  46. package/dist/daemon.d.ts +23 -3
  47. package/dist/daemon.js +36 -24
  48. package/dist/domain/domain-model.d.ts +0 -1
  49. package/dist/domain/domain-model.js +1 -2
  50. package/dist/domain/domain-tools.d.ts +0 -7
  51. package/dist/domain/domain-tools.js +21 -4
  52. package/dist/domain/flow-risk.d.ts +0 -1
  53. package/dist/domain/flow-risk.js +0 -1
  54. package/dist/ee/audit-log.d.ts +0 -1
  55. package/dist/ee/audit-log.js +0 -1
  56. package/dist/events/body-failures.d.ts +4 -0
  57. package/dist/events/body-failures.js +113 -0
  58. package/dist/events/contradictions.d.ts +53 -0
  59. package/dist/events/contradictions.js +282 -0
  60. package/dist/events/echo-mismatch.d.ts +4 -0
  61. package/dist/events/echo-mismatch.js +138 -0
  62. package/dist/events/event-filters.d.ts +17 -3
  63. package/dist/events/event-filters.js +13 -3
  64. package/dist/events/json-salvage.d.ts +32 -0
  65. package/dist/events/json-salvage.js +96 -0
  66. package/dist/events/predicate-eval.d.ts +2 -1
  67. package/dist/events/predicate-eval.js +56 -9
  68. package/dist/events/predicate.d.ts +0 -1
  69. package/dist/events/predicate.js +56 -2
  70. package/dist/events/reaction.d.ts +0 -1
  71. package/dist/events/reaction.js +0 -1
  72. package/dist/events/reconcile.d.ts +39 -0
  73. package/dist/events/reconcile.js +211 -0
  74. package/dist/events/ring-buffer.d.ts +0 -1
  75. package/dist/events/ring-buffer.js +0 -1
  76. package/dist/events/stale-response.d.ts +4 -0
  77. package/dist/events/stale-response.js +139 -0
  78. package/dist/events/unit-mismatch.d.ts +16 -0
  79. package/dist/events/unit-mismatch.js +167 -0
  80. package/dist/flows/affected-tools.d.ts +18 -0
  81. package/dist/flows/affected-tools.js +52 -0
  82. package/dist/flows/affected.d.ts +0 -1
  83. package/dist/flows/affected.js +0 -1
  84. package/dist/flows/annotate-tools.d.ts +0 -1
  85. package/dist/flows/annotate-tools.js +21 -3
  86. package/dist/flows/annotate.d.ts +0 -1
  87. package/dist/flows/annotate.js +0 -1
  88. package/dist/flows/annotation-store.d.ts +0 -1
  89. package/dist/flows/annotation-store.js +0 -1
  90. package/dist/flows/assertion-integrity.d.ts +0 -1
  91. package/dist/flows/assertion-integrity.js +0 -1
  92. package/dist/flows/assertion-tiers-store.d.ts +0 -1
  93. package/dist/flows/assertion-tiers-store.js +0 -1
  94. package/dist/flows/buddy-status.d.ts +0 -1
  95. package/dist/flows/buddy-status.js +0 -1
  96. package/dist/flows/coverage.d.ts +0 -1
  97. package/dist/flows/coverage.js +0 -1
  98. package/dist/flows/decision.d.ts +0 -1
  99. package/dist/flows/decision.js +0 -1
  100. package/dist/flows/flake-store.d.ts +0 -1
  101. package/dist/flows/flake-store.js +0 -1
  102. package/dist/flows/flake.d.ts +0 -1
  103. package/dist/flows/flake.js +0 -1
  104. package/dist/flows/flow-classify.d.ts +0 -1
  105. package/dist/flows/flow-classify.js +0 -1
  106. package/dist/flows/flow-replay-run.d.ts +0 -1
  107. package/dist/flows/flow-replay-run.js +2 -3
  108. package/dist/flows/flow-replay.d.ts +20 -4
  109. package/dist/flows/flow-replay.js +9 -43
  110. package/dist/flows/flow-report.d.ts +0 -1
  111. package/dist/flows/flow-report.js +0 -1
  112. package/dist/flows/flow-scope.d.ts +0 -1
  113. package/dist/flows/flow-scope.js +0 -1
  114. package/dist/flows/flow-sources.d.ts +0 -1
  115. package/dist/flows/flow-sources.js +0 -1
  116. package/dist/flows/flow-step-runners.d.ts +23 -0
  117. package/dist/flows/flow-step-runners.js +102 -0
  118. package/dist/flows/flow-success.d.ts +0 -1
  119. package/dist/flows/flow-success.js +0 -1
  120. package/dist/flows/flow-tools.d.ts +0 -1
  121. package/dist/flows/flow-tools.js +30 -8
  122. package/dist/flows/flows.d.ts +6 -2
  123. package/dist/flows/flows.js +15 -2
  124. package/dist/flows/gate.d.ts +0 -1
  125. package/dist/flows/gate.js +0 -1
  126. package/dist/flows/git-changed.d.ts +0 -1
  127. package/dist/flows/git-changed.js +0 -1
  128. package/dist/flows/heal-run.d.ts +0 -1
  129. package/dist/flows/heal-run.js +0 -1
  130. package/dist/flows/heal.d.ts +0 -1
  131. package/dist/flows/heal.js +0 -1
  132. package/dist/flows/parallel-suite.d.ts +0 -1
  133. package/dist/flows/parallel-suite.js +0 -1
  134. package/dist/flows/recordings.d.ts +0 -1
  135. package/dist/flows/recordings.js +0 -1
  136. package/dist/flows/replay.d.ts +0 -1
  137. package/dist/flows/replay.js +71 -4
  138. package/dist/flows/server-verify.d.ts +0 -1
  139. package/dist/flows/server-verify.js +0 -1
  140. package/dist/flows/verify-change-tools.d.ts +2 -0
  141. package/dist/flows/verify-change-tools.js +187 -0
  142. package/dist/flows/watch-batcher.d.ts +0 -1
  143. package/dist/flows/watch-batcher.js +0 -1
  144. package/dist/honesty/accepted-write.d.ts +14 -0
  145. package/dist/honesty/accepted-write.js +17 -0
  146. package/dist/honesty/blind-spots.d.ts +18 -1
  147. package/dist/honesty/blind-spots.js +47 -5
  148. package/dist/honesty/honesty.d.ts +0 -1
  149. package/dist/honesty/honesty.js +0 -1
  150. package/dist/honesty/uncaptured-bodies.d.ts +19 -0
  151. package/dist/honesty/uncaptured-bodies.js +30 -0
  152. package/dist/honesty/unread-outcome.d.ts +2 -0
  153. package/dist/honesty/unread-outcome.js +50 -0
  154. package/dist/honesty/verified.d.ts +40 -0
  155. package/dist/honesty/verified.js +86 -0
  156. package/dist/http-server.d.ts +0 -1
  157. package/dist/http-server.js +5 -1
  158. package/dist/hunt/hunt-report.d.ts +50 -0
  159. package/dist/hunt/hunt-report.js +84 -0
  160. package/dist/idle-shutdown.d.ts +0 -1
  161. package/dist/idle-shutdown.js +0 -1
  162. package/dist/index.d.ts +0 -1
  163. package/dist/index.js +0 -1
  164. package/dist/init/agent-rules.d.ts +0 -1
  165. package/dist/init/agent-rules.js +4 -2
  166. package/dist/init/cursor.d.ts +0 -1
  167. package/dist/init/cursor.js +0 -1
  168. package/dist/init/desktop-doctor.d.ts +40 -0
  169. package/dist/init/desktop-doctor.js +198 -0
  170. package/dist/init/detect.d.ts +0 -1
  171. package/dist/init/detect.js +0 -1
  172. package/dist/init/mcp.d.ts +0 -1
  173. package/dist/init/mcp.js +0 -1
  174. package/dist/init/node-io.d.ts +0 -1
  175. package/dist/init/node-io.js +0 -1
  176. package/dist/init/plan.d.ts +4 -1
  177. package/dist/init/plan.js +23 -5
  178. package/dist/init/project-id.d.ts +0 -1
  179. package/dist/init/project-id.js +0 -1
  180. package/dist/init/run.d.ts +0 -1
  181. package/dist/init/run.js +31 -3
  182. package/dist/init/snippets.d.ts +5 -1
  183. package/dist/init/snippets.js +5 -1
  184. package/dist/init/vite-config.d.ts +0 -1
  185. package/dist/init/vite-config.js +0 -1
  186. package/dist/input/network-detail.d.ts +0 -1
  187. package/dist/input/network-detail.js +0 -1
  188. package/dist/input/network-mock-tools.d.ts +0 -1
  189. package/dist/input/network-mock-tools.js +3 -4
  190. package/dist/input/network-mock.d.ts +0 -1
  191. package/dist/input/network-mock.js +0 -1
  192. package/dist/input/real-input.d.ts +14 -1
  193. package/dist/input/real-input.js +45 -3
  194. package/dist/input/scroll-find.d.ts +0 -1
  195. package/dist/input/scroll-find.js +0 -1
  196. package/dist/input/scroll-tools.d.ts +0 -1
  197. package/dist/input/scroll-tools.js +0 -1
  198. package/dist/input/viewport-tools.d.ts +0 -1
  199. package/dist/input/viewport-tools.js +3 -4
  200. package/dist/journal/ambient-store.d.ts +0 -1
  201. package/dist/journal/ambient-store.js +0 -1
  202. package/dist/journal/ambient.d.ts +0 -1
  203. package/dist/journal/ambient.js +0 -1
  204. package/dist/journal/attach-journal.d.ts +0 -1
  205. package/dist/journal/attach-journal.js +0 -1
  206. package/dist/journal/deviation-report.d.ts +0 -1
  207. package/dist/journal/deviation-report.js +0 -1
  208. package/dist/journal/deviation-service.d.ts +0 -1
  209. package/dist/journal/deviation-service.js +0 -1
  210. package/dist/journal/envelope-store.d.ts +0 -1
  211. package/dist/journal/envelope-store.js +0 -1
  212. package/dist/journal/envelope.d.ts +8 -9
  213. package/dist/journal/envelope.js +0 -1
  214. package/dist/journal/journal-query.d.ts +0 -1
  215. package/dist/journal/journal-query.js +0 -1
  216. package/dist/journal/journal-recorder.d.ts +0 -1
  217. package/dist/journal/journal-recorder.js +0 -1
  218. package/dist/journal/retention.d.ts +0 -1
  219. package/dist/journal/retention.js +0 -1
  220. package/dist/journal/rollups.d.ts +0 -1
  221. package/dist/journal/rollups.js +0 -1
  222. package/dist/journal/session-end.d.ts +0 -1
  223. package/dist/journal/session-end.js +0 -1
  224. package/dist/journal/session-journal.d.ts +0 -1
  225. package/dist/journal/session-journal.js +0 -1
  226. package/dist/license/license.d.ts +0 -1
  227. package/dist/license/license.js +0 -1
  228. package/dist/log.d.ts +0 -1
  229. package/dist/log.js +0 -1
  230. package/dist/mcp-proxy.d.ts +0 -1
  231. package/dist/mcp-proxy.js +0 -1
  232. package/dist/mcp.d.ts +15 -1
  233. package/dist/mcp.js +145 -17
  234. package/dist/oracles/flow-instrument-gaps.d.ts +0 -1
  235. package/dist/oracles/flow-instrument-gaps.js +0 -1
  236. package/dist/oracles/propose-consequences.d.ts +0 -1
  237. package/dist/oracles/propose-consequences.js +0 -1
  238. package/dist/oracles/self-instrument.d.ts +0 -1
  239. package/dist/oracles/self-instrument.js +0 -1
  240. package/dist/pairing-token.d.ts +0 -1
  241. package/dist/pairing-token.js +0 -1
  242. package/dist/phenomena/phenomena.d.ts +0 -1
  243. package/dist/phenomena/phenomena.js +0 -1
  244. package/dist/pool/browser-pool.d.ts +0 -1
  245. package/dist/pool/browser-pool.js +0 -1
  246. package/dist/pool/lease-reaper.d.ts +0 -1
  247. package/dist/pool/lease-reaper.js +0 -1
  248. package/dist/pool/playwright-launcher.d.ts +0 -1
  249. package/dist/pool/playwright-launcher.js +10 -2
  250. package/dist/project/baselines.d.ts +0 -1
  251. package/dist/project/baselines.js +0 -1
  252. package/dist/project/file-lock.d.ts +2 -1
  253. package/dist/project/file-lock.js +12 -2
  254. package/dist/project/fs-port.d.ts +0 -1
  255. package/dist/project/fs-port.js +0 -1
  256. package/dist/project/project-store.d.ts +0 -1
  257. package/dist/project/project-store.js +0 -1
  258. package/dist/project/project-tools.d.ts +0 -1
  259. package/dist/project/project-tools.js +29 -4
  260. package/dist/project/reticle-dir.d.ts +0 -1
  261. package/dist/project/reticle-dir.js +0 -1
  262. package/dist/runs/build-verification-run.d.ts +0 -1
  263. package/dist/runs/build-verification-run.js +0 -1
  264. package/dist/runs/profile-redact.d.ts +0 -1
  265. package/dist/runs/profile-redact.js +0 -1
  266. package/dist/runs/render-report.d.ts +0 -1
  267. package/dist/runs/render-report.js +0 -1
  268. package/dist/runs/repair-prompt.d.ts +0 -1
  269. package/dist/runs/repair-prompt.js +0 -1
  270. package/dist/runs/replay-mapping.d.ts +0 -1
  271. package/dist/runs/replay-mapping.js +0 -1
  272. package/dist/runs/reticle-runner.d.ts +0 -1
  273. package/dist/runs/reticle-runner.js +4 -2
  274. package/dist/runs/risk-classify.d.ts +0 -1
  275. package/dist/runs/risk-classify.js +0 -1
  276. package/dist/runs/run-diff.d.ts +0 -1
  277. package/dist/runs/run-diff.js +0 -1
  278. package/dist/runs/run-store.d.ts +0 -1
  279. package/dist/runs/run-store.js +0 -1
  280. package/dist/runs/run-tools.d.ts +0 -1
  281. package/dist/runs/run-tools.js +0 -1
  282. package/dist/runs/runner-port.d.ts +0 -1
  283. package/dist/runs/runner-port.js +0 -1
  284. package/dist/runs/verification-sync.d.ts +0 -1
  285. package/dist/runs/verification-sync.js +0 -1
  286. package/dist/runs/verify-http.d.ts +0 -1
  287. package/dist/runs/verify-http.js +0 -1
  288. package/dist/runs/verify-server.d.ts +0 -1
  289. package/dist/runs/verify-server.js +0 -1
  290. package/dist/server-version.d.ts +0 -1
  291. package/dist/server-version.js +0 -1
  292. package/dist/session/command-timeout.d.ts +35 -0
  293. package/dist/session/command-timeout.js +41 -0
  294. package/dist/session/control-envelope.d.ts +2 -3
  295. package/dist/session/control-envelope.js +2 -3
  296. package/dist/session/last-act.d.ts +31 -0
  297. package/dist/session/last-act.js +24 -0
  298. package/dist/session/live-control-tools.d.ts +0 -1
  299. package/dist/session/live-control-tools.js +3 -4
  300. package/dist/session/live-control.d.ts +0 -1
  301. package/dist/session/live-control.js +0 -1
  302. package/dist/session/observed-state.d.ts +12 -2
  303. package/dist/session/observed-state.js +29 -2
  304. package/dist/session/output-budget.d.ts +0 -1
  305. package/dist/session/output-budget.js +0 -1
  306. package/dist/session/pending-commands.d.ts +12 -0
  307. package/dist/session/pending-commands.js +36 -0
  308. package/dist/session/presenter-args.d.ts +0 -1
  309. package/dist/session/presenter-args.js +0 -1
  310. package/dist/session/review-store.d.ts +0 -1
  311. package/dist/session/review-store.js +0 -1
  312. package/dist/session/session-health.d.ts +26 -2
  313. package/dist/session/session-health.js +13 -1
  314. package/dist/session/session-info.d.ts +29 -0
  315. package/dist/session/session-info.js +1 -0
  316. package/dist/session/session-lease.d.ts +25 -0
  317. package/dist/session/session-lease.js +14 -0
  318. package/dist/session/session-manager.d.ts +7 -19
  319. package/dist/session/session-manager.js +29 -2
  320. package/dist/session/session-readiness.d.ts +0 -1
  321. package/dist/session/session-readiness.js +3 -4
  322. package/dist/session/session-reaper.d.ts +0 -1
  323. package/dist/session/session-reaper.js +0 -1
  324. package/dist/session/session-recommendation.d.ts +0 -1
  325. package/dist/session/session-recommendation.js +0 -1
  326. package/dist/session/session-tools.d.ts +0 -1
  327. package/dist/session/session-tools.js +2 -3
  328. package/dist/session/session.d.ts +27 -53
  329. package/dist/session/session.js +95 -66
  330. package/dist/session/state-select.d.ts +0 -1
  331. package/dist/session/state-select.js +0 -1
  332. package/dist/telemetry/argument-shape.d.ts +41 -0
  333. package/dist/telemetry/argument-shape.js +87 -0
  334. package/dist/telemetry/bug-found.d.ts +33 -0
  335. package/dist/telemetry/bug-found.js +95 -0
  336. package/dist/telemetry/cli-telemetry.d.ts +13 -0
  337. package/dist/telemetry/cli-telemetry.js +43 -0
  338. package/dist/telemetry/connect-failure.d.ts +16 -0
  339. package/dist/telemetry/connect-failure.js +44 -0
  340. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  341. package/dist/telemetry/daemon-telemetry.js +76 -0
  342. package/dist/telemetry/dev-repo.d.ts +10 -0
  343. package/dist/telemetry/dev-repo.js +80 -0
  344. package/dist/telemetry/error-fingerprint.d.ts +24 -0
  345. package/dist/telemetry/error-fingerprint.js +88 -0
  346. package/dist/telemetry/feedback-cli.d.ts +31 -0
  347. package/dist/telemetry/feedback-cli.js +102 -0
  348. package/dist/telemetry/feedback-context.d.ts +32 -0
  349. package/dist/telemetry/feedback-context.js +143 -0
  350. package/dist/telemetry/feedback.d.ts +66 -0
  351. package/dist/telemetry/feedback.js +175 -0
  352. package/dist/telemetry/git-facts.d.ts +21 -0
  353. package/dist/telemetry/git-facts.js +109 -0
  354. package/dist/telemetry/identify.d.ts +22 -0
  355. package/dist/telemetry/identify.js +73 -0
  356. package/dist/telemetry/init-telemetry.d.ts +26 -0
  357. package/dist/telemetry/init-telemetry.js +33 -0
  358. package/dist/telemetry/machine-snapshot.d.ts +6 -0
  359. package/dist/telemetry/machine-snapshot.js +39 -0
  360. package/dist/telemetry/mcp-connection.d.ts +5 -0
  361. package/dist/telemetry/mcp-connection.js +44 -0
  362. package/dist/telemetry/project-profile.d.ts +44 -0
  363. package/dist/telemetry/project-profile.js +205 -0
  364. package/dist/telemetry/run-telemetry.d.ts +21 -0
  365. package/dist/telemetry/run-telemetry.js +62 -0
  366. package/dist/telemetry/session-metrics.d.ts +103 -0
  367. package/dist/telemetry/session-metrics.js +329 -0
  368. package/dist/telemetry/telemetry.d.ts +35 -8
  369. package/dist/telemetry/telemetry.js +105 -12
  370. package/dist/token-auth.d.ts +0 -1
  371. package/dist/token-auth.js +0 -1
  372. package/dist/tools/act-capsule.d.ts +23 -0
  373. package/dist/tools/act-capsule.js +47 -0
  374. package/dist/tools/act-danger.d.ts +7 -0
  375. package/dist/tools/act-danger.js +44 -0
  376. package/dist/tools/act-helpers.d.ts +17 -0
  377. package/dist/tools/act-helpers.js +40 -0
  378. package/dist/tools/act-tools.d.ts +8 -1
  379. package/dist/tools/act-tools.js +96 -98
  380. package/dist/tools/act-view.d.ts +7 -1
  381. package/dist/tools/act-view.js +13 -1
  382. package/dist/tools/assert-grade.d.ts +22 -1
  383. package/dist/tools/assert-grade.js +46 -1
  384. package/dist/tools/assert-verdict.d.ts +21 -0
  385. package/dist/tools/assert-verdict.js +62 -0
  386. package/dist/tools/browser-tools.d.ts +0 -1
  387. package/dist/tools/browser-tools.js +2 -2
  388. package/dist/tools/contract-tools.d.ts +0 -1
  389. package/dist/tools/contract-tools.js +0 -1
  390. package/dist/tools/coverage-tools.d.ts +9 -0
  391. package/dist/tools/coverage-tools.js +92 -0
  392. package/dist/tools/dynamic-tools.d.ts +0 -1
  393. package/dist/tools/dynamic-tools.js +21 -2
  394. package/dist/tools/error-recovery.d.ts +18 -2
  395. package/dist/tools/error-recovery.js +42 -7
  396. package/dist/tools/feedback-tools.d.ts +24 -0
  397. package/dist/tools/feedback-tools.js +180 -0
  398. package/dist/tools/invoke-tool.d.ts +0 -1
  399. package/dist/tools/invoke-tool.js +115 -5
  400. package/dist/tools/lease-tools.d.ts +0 -1
  401. package/dist/tools/lease-tools.js +6 -5
  402. package/dist/tools/merge-tools.d.ts +0 -1
  403. package/dist/tools/merge-tools.js +0 -1
  404. package/dist/tools/observe-tools.d.ts +0 -1
  405. package/dist/tools/observe-tools.js +91 -39
  406. package/dist/tools/profiles.d.ts +1 -2
  407. package/dist/tools/profiles.js +43 -16
  408. package/dist/tools/query-paginate.d.ts +0 -1
  409. package/dist/tools/query-paginate.js +0 -1
  410. package/dist/tools/read-tools.d.ts +0 -1
  411. package/dist/tools/read-tools.js +10 -8
  412. package/dist/tools/reconcile-tools.d.ts +2 -0
  413. package/dist/tools/reconcile-tools.js +111 -0
  414. package/dist/tools/snapshot-delta.d.ts +0 -1
  415. package/dist/tools/snapshot-delta.js +0 -0
  416. package/dist/tools/tool-invoker.d.ts +0 -1
  417. package/dist/tools/tool-invoker.js +0 -1
  418. package/dist/tools/tool-kit.d.ts +16 -1
  419. package/dist/tools/tool-kit.js +8 -2
  420. package/dist/tools/tool-names.d.ts +6 -1
  421. package/dist/tools/tool-names.js +6 -1
  422. package/dist/tools/tools-helpers.d.ts +0 -1
  423. package/dist/tools/tools-helpers.js +0 -1
  424. package/dist/tools/tools.d.ts +0 -1
  425. package/dist/tools/tools.js +24 -4
  426. package/dist/update/update-checker.d.ts +0 -1
  427. package/dist/update/update-checker.js +0 -1
  428. package/dist/update/update-nudge.d.ts +20 -0
  429. package/dist/update/update-nudge.js +72 -0
  430. package/dist/update/updater.d.ts +14 -1
  431. package/dist/update/updater.js +33 -2
  432. package/dist/visual/visual-diff.d.ts +0 -1
  433. package/dist/visual/visual-diff.js +0 -1
  434. package/dist/visual/visual-store.d.ts +0 -1
  435. package/dist/visual/visual-store.js +0 -1
  436. package/dist/visual/visual-tools.d.ts +0 -1
  437. package/dist/visual/visual-tools.js +103 -16
  438. package/package.json +2 -2
  439. package/dist/bridge-security.d.ts.map +0 -1
  440. package/dist/bridge-security.js.map +0 -1
  441. package/dist/bridge.d.ts.map +0 -1
  442. package/dist/bridge.js.map +0 -1
  443. package/dist/bridge.test-harness.d.ts.map +0 -1
  444. package/dist/bridge.test-harness.js.map +0 -1
  445. package/dist/capsule/capsule-store.d.ts.map +0 -1
  446. package/dist/capsule/capsule-store.js.map +0 -1
  447. package/dist/capsule/capsule.d.ts.map +0 -1
  448. package/dist/capsule/capsule.js.map +0 -1
  449. package/dist/capsule/causal-summary.d.ts.map +0 -1
  450. package/dist/capsule/causal-summary.js.map +0 -1
  451. package/dist/capsule/divergence.d.ts.map +0 -1
  452. package/dist/capsule/divergence.js.map +0 -1
  453. package/dist/capsule/minimize.d.ts.map +0 -1
  454. package/dist/capsule/minimize.js.map +0 -1
  455. package/dist/capsule/predicate-to-links.d.ts.map +0 -1
  456. package/dist/capsule/predicate-to-links.js.map +0 -1
  457. package/dist/cli-flow-commands.d.ts.map +0 -1
  458. package/dist/cli-flow-commands.js.map +0 -1
  459. package/dist/cli-launch.d.ts.map +0 -1
  460. package/dist/cli-launch.js.map +0 -1
  461. package/dist/cli-parse.d.ts.map +0 -1
  462. package/dist/cli-parse.js.map +0 -1
  463. package/dist/cli-port.d.ts.map +0 -1
  464. package/dist/cli-port.js.map +0 -1
  465. package/dist/cli-verify.d.ts.map +0 -1
  466. package/dist/cli-verify.js.map +0 -1
  467. package/dist/cli.d.ts.map +0 -1
  468. package/dist/cli.js.map +0 -1
  469. package/dist/cloud/cloud-config.d.ts.map +0 -1
  470. package/dist/cloud/cloud-config.js.map +0 -1
  471. package/dist/cloud/cloud-sync.d.ts.map +0 -1
  472. package/dist/cloud/cloud-sync.js.map +0 -1
  473. package/dist/cloud-cli.d.ts.map +0 -1
  474. package/dist/cloud-cli.js.map +0 -1
  475. package/dist/crawl/crawl-tools.d.ts.map +0 -1
  476. package/dist/crawl/crawl-tools.js.map +0 -1
  477. package/dist/crawl/crawl.d.ts.map +0 -1
  478. package/dist/crawl/crawl.js.map +0 -1
  479. package/dist/daemon-resilience.d.ts.map +0 -1
  480. package/dist/daemon-resilience.js.map +0 -1
  481. package/dist/daemon.d.ts.map +0 -1
  482. package/dist/daemon.js.map +0 -1
  483. package/dist/domain/domain-model.d.ts.map +0 -1
  484. package/dist/domain/domain-model.js.map +0 -1
  485. package/dist/domain/domain-tools.d.ts.map +0 -1
  486. package/dist/domain/domain-tools.js.map +0 -1
  487. package/dist/domain/flow-risk.d.ts.map +0 -1
  488. package/dist/domain/flow-risk.js.map +0 -1
  489. package/dist/ee/audit-log.d.ts.map +0 -1
  490. package/dist/ee/audit-log.js.map +0 -1
  491. package/dist/events/event-filters.d.ts.map +0 -1
  492. package/dist/events/event-filters.js.map +0 -1
  493. package/dist/events/predicate-eval.d.ts.map +0 -1
  494. package/dist/events/predicate-eval.js.map +0 -1
  495. package/dist/events/predicate.d.ts.map +0 -1
  496. package/dist/events/predicate.js.map +0 -1
  497. package/dist/events/reaction.d.ts.map +0 -1
  498. package/dist/events/reaction.js.map +0 -1
  499. package/dist/events/ring-buffer.d.ts.map +0 -1
  500. package/dist/events/ring-buffer.js.map +0 -1
  501. package/dist/flows/affected.d.ts.map +0 -1
  502. package/dist/flows/affected.js.map +0 -1
  503. package/dist/flows/annotate-tools.d.ts.map +0 -1
  504. package/dist/flows/annotate-tools.js.map +0 -1
  505. package/dist/flows/annotate.d.ts.map +0 -1
  506. package/dist/flows/annotate.js.map +0 -1
  507. package/dist/flows/annotation-store.d.ts.map +0 -1
  508. package/dist/flows/annotation-store.js.map +0 -1
  509. package/dist/flows/assertion-integrity.d.ts.map +0 -1
  510. package/dist/flows/assertion-integrity.js.map +0 -1
  511. package/dist/flows/assertion-tiers-store.d.ts.map +0 -1
  512. package/dist/flows/assertion-tiers-store.js.map +0 -1
  513. package/dist/flows/buddy-status.d.ts.map +0 -1
  514. package/dist/flows/buddy-status.js.map +0 -1
  515. package/dist/flows/coverage.d.ts.map +0 -1
  516. package/dist/flows/coverage.js.map +0 -1
  517. package/dist/flows/decision.d.ts.map +0 -1
  518. package/dist/flows/decision.js.map +0 -1
  519. package/dist/flows/flake-store.d.ts.map +0 -1
  520. package/dist/flows/flake-store.js.map +0 -1
  521. package/dist/flows/flake.d.ts.map +0 -1
  522. package/dist/flows/flake.js.map +0 -1
  523. package/dist/flows/flow-classify.d.ts.map +0 -1
  524. package/dist/flows/flow-classify.js.map +0 -1
  525. package/dist/flows/flow-replay-run.d.ts.map +0 -1
  526. package/dist/flows/flow-replay-run.js.map +0 -1
  527. package/dist/flows/flow-replay.d.ts.map +0 -1
  528. package/dist/flows/flow-replay.js.map +0 -1
  529. package/dist/flows/flow-report.d.ts.map +0 -1
  530. package/dist/flows/flow-report.js.map +0 -1
  531. package/dist/flows/flow-scope.d.ts.map +0 -1
  532. package/dist/flows/flow-scope.js.map +0 -1
  533. package/dist/flows/flow-sources.d.ts.map +0 -1
  534. package/dist/flows/flow-sources.js.map +0 -1
  535. package/dist/flows/flow-success.d.ts.map +0 -1
  536. package/dist/flows/flow-success.js.map +0 -1
  537. package/dist/flows/flow-tools.d.ts.map +0 -1
  538. package/dist/flows/flow-tools.js.map +0 -1
  539. package/dist/flows/flows.d.ts.map +0 -1
  540. package/dist/flows/flows.js.map +0 -1
  541. package/dist/flows/gate.d.ts.map +0 -1
  542. package/dist/flows/gate.js.map +0 -1
  543. package/dist/flows/git-changed.d.ts.map +0 -1
  544. package/dist/flows/git-changed.js.map +0 -1
  545. package/dist/flows/heal-run.d.ts.map +0 -1
  546. package/dist/flows/heal-run.js.map +0 -1
  547. package/dist/flows/heal.d.ts.map +0 -1
  548. package/dist/flows/heal.js.map +0 -1
  549. package/dist/flows/parallel-suite.d.ts.map +0 -1
  550. package/dist/flows/parallel-suite.js.map +0 -1
  551. package/dist/flows/recordings.d.ts.map +0 -1
  552. package/dist/flows/recordings.js.map +0 -1
  553. package/dist/flows/replay.d.ts.map +0 -1
  554. package/dist/flows/replay.js.map +0 -1
  555. package/dist/flows/server-verify.d.ts.map +0 -1
  556. package/dist/flows/server-verify.js.map +0 -1
  557. package/dist/flows/watch-batcher.d.ts.map +0 -1
  558. package/dist/flows/watch-batcher.js.map +0 -1
  559. package/dist/honesty/blind-spots.d.ts.map +0 -1
  560. package/dist/honesty/blind-spots.js.map +0 -1
  561. package/dist/honesty/honesty.d.ts.map +0 -1
  562. package/dist/honesty/honesty.js.map +0 -1
  563. package/dist/http-server.d.ts.map +0 -1
  564. package/dist/http-server.js.map +0 -1
  565. package/dist/idle-shutdown.d.ts.map +0 -1
  566. package/dist/idle-shutdown.js.map +0 -1
  567. package/dist/index.d.ts.map +0 -1
  568. package/dist/index.js.map +0 -1
  569. package/dist/init/agent-rules.d.ts.map +0 -1
  570. package/dist/init/agent-rules.js.map +0 -1
  571. package/dist/init/cursor.d.ts.map +0 -1
  572. package/dist/init/cursor.js.map +0 -1
  573. package/dist/init/detect.d.ts.map +0 -1
  574. package/dist/init/detect.js.map +0 -1
  575. package/dist/init/mcp.d.ts.map +0 -1
  576. package/dist/init/mcp.js.map +0 -1
  577. package/dist/init/node-io.d.ts.map +0 -1
  578. package/dist/init/node-io.js.map +0 -1
  579. package/dist/init/plan.d.ts.map +0 -1
  580. package/dist/init/plan.js.map +0 -1
  581. package/dist/init/project-id.d.ts.map +0 -1
  582. package/dist/init/project-id.js.map +0 -1
  583. package/dist/init/run.d.ts.map +0 -1
  584. package/dist/init/run.js.map +0 -1
  585. package/dist/init/snippets.d.ts.map +0 -1
  586. package/dist/init/snippets.js.map +0 -1
  587. package/dist/init/vite-config.d.ts.map +0 -1
  588. package/dist/init/vite-config.js.map +0 -1
  589. package/dist/input/network-detail.d.ts.map +0 -1
  590. package/dist/input/network-detail.js.map +0 -1
  591. package/dist/input/network-mock-tools.d.ts.map +0 -1
  592. package/dist/input/network-mock-tools.js.map +0 -1
  593. package/dist/input/network-mock.d.ts.map +0 -1
  594. package/dist/input/network-mock.js.map +0 -1
  595. package/dist/input/real-input.d.ts.map +0 -1
  596. package/dist/input/real-input.js.map +0 -1
  597. package/dist/input/scroll-find.d.ts.map +0 -1
  598. package/dist/input/scroll-find.js.map +0 -1
  599. package/dist/input/scroll-tools.d.ts.map +0 -1
  600. package/dist/input/scroll-tools.js.map +0 -1
  601. package/dist/input/viewport-tools.d.ts.map +0 -1
  602. package/dist/input/viewport-tools.js.map +0 -1
  603. package/dist/journal/ambient-store.d.ts.map +0 -1
  604. package/dist/journal/ambient-store.js.map +0 -1
  605. package/dist/journal/ambient.d.ts.map +0 -1
  606. package/dist/journal/ambient.js.map +0 -1
  607. package/dist/journal/attach-journal.d.ts.map +0 -1
  608. package/dist/journal/attach-journal.js.map +0 -1
  609. package/dist/journal/deviation-report.d.ts.map +0 -1
  610. package/dist/journal/deviation-report.js.map +0 -1
  611. package/dist/journal/deviation-service.d.ts.map +0 -1
  612. package/dist/journal/deviation-service.js.map +0 -1
  613. package/dist/journal/envelope-store.d.ts.map +0 -1
  614. package/dist/journal/envelope-store.js.map +0 -1
  615. package/dist/journal/envelope.d.ts.map +0 -1
  616. package/dist/journal/envelope.js.map +0 -1
  617. package/dist/journal/journal-query.d.ts.map +0 -1
  618. package/dist/journal/journal-query.js.map +0 -1
  619. package/dist/journal/journal-recorder.d.ts.map +0 -1
  620. package/dist/journal/journal-recorder.js.map +0 -1
  621. package/dist/journal/retention.d.ts.map +0 -1
  622. package/dist/journal/retention.js.map +0 -1
  623. package/dist/journal/rollups.d.ts.map +0 -1
  624. package/dist/journal/rollups.js.map +0 -1
  625. package/dist/journal/session-end.d.ts.map +0 -1
  626. package/dist/journal/session-end.js.map +0 -1
  627. package/dist/journal/session-journal.d.ts.map +0 -1
  628. package/dist/journal/session-journal.js.map +0 -1
  629. package/dist/license/license.d.ts.map +0 -1
  630. package/dist/license/license.js.map +0 -1
  631. package/dist/log.d.ts.map +0 -1
  632. package/dist/log.js.map +0 -1
  633. package/dist/mcp-proxy.d.ts.map +0 -1
  634. package/dist/mcp-proxy.js.map +0 -1
  635. package/dist/mcp.d.ts.map +0 -1
  636. package/dist/mcp.js.map +0 -1
  637. package/dist/oracles/flow-instrument-gaps.d.ts.map +0 -1
  638. package/dist/oracles/flow-instrument-gaps.js.map +0 -1
  639. package/dist/oracles/propose-consequences.d.ts.map +0 -1
  640. package/dist/oracles/propose-consequences.js.map +0 -1
  641. package/dist/oracles/self-instrument.d.ts.map +0 -1
  642. package/dist/oracles/self-instrument.js.map +0 -1
  643. package/dist/pairing-token.d.ts.map +0 -1
  644. package/dist/pairing-token.js.map +0 -1
  645. package/dist/phenomena/phenomena.d.ts.map +0 -1
  646. package/dist/phenomena/phenomena.js.map +0 -1
  647. package/dist/pool/browser-pool.d.ts.map +0 -1
  648. package/dist/pool/browser-pool.js.map +0 -1
  649. package/dist/pool/lease-reaper.d.ts.map +0 -1
  650. package/dist/pool/lease-reaper.js.map +0 -1
  651. package/dist/pool/playwright-launcher.d.ts.map +0 -1
  652. package/dist/pool/playwright-launcher.js.map +0 -1
  653. package/dist/project/baselines.d.ts.map +0 -1
  654. package/dist/project/baselines.js.map +0 -1
  655. package/dist/project/file-lock.d.ts.map +0 -1
  656. package/dist/project/file-lock.js.map +0 -1
  657. package/dist/project/fs-port.d.ts.map +0 -1
  658. package/dist/project/fs-port.js.map +0 -1
  659. package/dist/project/project-store.d.ts.map +0 -1
  660. package/dist/project/project-store.js.map +0 -1
  661. package/dist/project/project-tools.d.ts.map +0 -1
  662. package/dist/project/project-tools.js.map +0 -1
  663. package/dist/project/reticle-dir.d.ts.map +0 -1
  664. package/dist/project/reticle-dir.js.map +0 -1
  665. package/dist/runs/build-verification-run.d.ts.map +0 -1
  666. package/dist/runs/build-verification-run.js.map +0 -1
  667. package/dist/runs/profile-redact.d.ts.map +0 -1
  668. package/dist/runs/profile-redact.js.map +0 -1
  669. package/dist/runs/render-report.d.ts.map +0 -1
  670. package/dist/runs/render-report.js.map +0 -1
  671. package/dist/runs/repair-prompt.d.ts.map +0 -1
  672. package/dist/runs/repair-prompt.js.map +0 -1
  673. package/dist/runs/replay-mapping.d.ts.map +0 -1
  674. package/dist/runs/replay-mapping.js.map +0 -1
  675. package/dist/runs/reticle-runner.d.ts.map +0 -1
  676. package/dist/runs/reticle-runner.js.map +0 -1
  677. package/dist/runs/risk-classify.d.ts.map +0 -1
  678. package/dist/runs/risk-classify.js.map +0 -1
  679. package/dist/runs/run-diff.d.ts.map +0 -1
  680. package/dist/runs/run-diff.js.map +0 -1
  681. package/dist/runs/run-store.d.ts.map +0 -1
  682. package/dist/runs/run-store.js.map +0 -1
  683. package/dist/runs/run-tools.d.ts.map +0 -1
  684. package/dist/runs/run-tools.js.map +0 -1
  685. package/dist/runs/runner-port.d.ts.map +0 -1
  686. package/dist/runs/runner-port.js.map +0 -1
  687. package/dist/runs/verification-sync.d.ts.map +0 -1
  688. package/dist/runs/verification-sync.js.map +0 -1
  689. package/dist/runs/verify-http.d.ts.map +0 -1
  690. package/dist/runs/verify-http.js.map +0 -1
  691. package/dist/runs/verify-server.d.ts.map +0 -1
  692. package/dist/runs/verify-server.js.map +0 -1
  693. package/dist/server-version.d.ts.map +0 -1
  694. package/dist/server-version.js.map +0 -1
  695. package/dist/session/control-envelope.d.ts.map +0 -1
  696. package/dist/session/control-envelope.js.map +0 -1
  697. package/dist/session/live-control-tools.d.ts.map +0 -1
  698. package/dist/session/live-control-tools.js.map +0 -1
  699. package/dist/session/live-control.d.ts.map +0 -1
  700. package/dist/session/live-control.js.map +0 -1
  701. package/dist/session/observed-state.d.ts.map +0 -1
  702. package/dist/session/observed-state.js.map +0 -1
  703. package/dist/session/output-budget.d.ts.map +0 -1
  704. package/dist/session/output-budget.js.map +0 -1
  705. package/dist/session/presenter-args.d.ts.map +0 -1
  706. package/dist/session/presenter-args.js.map +0 -1
  707. package/dist/session/review-store.d.ts.map +0 -1
  708. package/dist/session/review-store.js.map +0 -1
  709. package/dist/session/session-health.d.ts.map +0 -1
  710. package/dist/session/session-health.js.map +0 -1
  711. package/dist/session/session-manager.d.ts.map +0 -1
  712. package/dist/session/session-manager.js.map +0 -1
  713. package/dist/session/session-readiness.d.ts.map +0 -1
  714. package/dist/session/session-readiness.js.map +0 -1
  715. package/dist/session/session-reaper.d.ts.map +0 -1
  716. package/dist/session/session-reaper.js.map +0 -1
  717. package/dist/session/session-recommendation.d.ts.map +0 -1
  718. package/dist/session/session-recommendation.js.map +0 -1
  719. package/dist/session/session-tools.d.ts.map +0 -1
  720. package/dist/session/session-tools.js.map +0 -1
  721. package/dist/session/session.d.ts.map +0 -1
  722. package/dist/session/session.js.map +0 -1
  723. package/dist/session/state-select.d.ts.map +0 -1
  724. package/dist/session/state-select.js.map +0 -1
  725. package/dist/telemetry/telemetry.d.ts.map +0 -1
  726. package/dist/telemetry/telemetry.js.map +0 -1
  727. package/dist/token-auth.d.ts.map +0 -1
  728. package/dist/token-auth.js.map +0 -1
  729. package/dist/tools/act-tools.d.ts.map +0 -1
  730. package/dist/tools/act-tools.js.map +0 -1
  731. package/dist/tools/act-view.d.ts.map +0 -1
  732. package/dist/tools/act-view.js.map +0 -1
  733. package/dist/tools/assert-grade.d.ts.map +0 -1
  734. package/dist/tools/assert-grade.js.map +0 -1
  735. package/dist/tools/browser-tools.d.ts.map +0 -1
  736. package/dist/tools/browser-tools.js.map +0 -1
  737. package/dist/tools/contract-tools.d.ts.map +0 -1
  738. package/dist/tools/contract-tools.js.map +0 -1
  739. package/dist/tools/dynamic-tools.d.ts.map +0 -1
  740. package/dist/tools/dynamic-tools.js.map +0 -1
  741. package/dist/tools/error-recovery.d.ts.map +0 -1
  742. package/dist/tools/error-recovery.js.map +0 -1
  743. package/dist/tools/invoke-tool.d.ts.map +0 -1
  744. package/dist/tools/invoke-tool.js.map +0 -1
  745. package/dist/tools/lease-tools.d.ts.map +0 -1
  746. package/dist/tools/lease-tools.js.map +0 -1
  747. package/dist/tools/merge-tools.d.ts.map +0 -1
  748. package/dist/tools/merge-tools.js.map +0 -1
  749. package/dist/tools/observe-tools.d.ts.map +0 -1
  750. package/dist/tools/observe-tools.js.map +0 -1
  751. package/dist/tools/profiles.d.ts.map +0 -1
  752. package/dist/tools/profiles.js.map +0 -1
  753. package/dist/tools/query-paginate.d.ts.map +0 -1
  754. package/dist/tools/query-paginate.js.map +0 -1
  755. package/dist/tools/read-tools.d.ts.map +0 -1
  756. package/dist/tools/read-tools.js.map +0 -1
  757. package/dist/tools/snapshot-delta.d.ts.map +0 -1
  758. package/dist/tools/snapshot-delta.js.map +0 -1
  759. package/dist/tools/tool-invoker.d.ts.map +0 -1
  760. package/dist/tools/tool-invoker.js.map +0 -1
  761. package/dist/tools/tool-kit.d.ts.map +0 -1
  762. package/dist/tools/tool-kit.js.map +0 -1
  763. package/dist/tools/tool-names.d.ts.map +0 -1
  764. package/dist/tools/tool-names.js.map +0 -1
  765. package/dist/tools/tools-helpers.d.ts.map +0 -1
  766. package/dist/tools/tools-helpers.js.map +0 -1
  767. package/dist/tools/tools.d.ts.map +0 -1
  768. package/dist/tools/tools.js.map +0 -1
  769. package/dist/update/update-checker.d.ts.map +0 -1
  770. package/dist/update/update-checker.js.map +0 -1
  771. package/dist/update/updater.d.ts.map +0 -1
  772. package/dist/update/updater.js.map +0 -1
  773. package/dist/visual/visual-diff.d.ts.map +0 -1
  774. package/dist/visual/visual-diff.js.map +0 -1
  775. package/dist/visual/visual-store.d.ts.map +0 -1
  776. package/dist/visual/visual-store.js.map +0 -1
  777. package/dist/visual/visual-tools.d.ts.map +0 -1
  778. package/dist/visual/visual-tools.js.map +0 -1
@@ -0,0 +1,109 @@
1
+ /**
2
+ * What we can honestly know about a project's git setup, read from `.git/` on disk.
3
+ *
4
+ * This exists to make three counting questions answerable and, just as importantly, to make it
5
+ * visible when they are NOT answerable:
6
+ *
7
+ * - how many users does one project have? (distinct anonymousId per projectId)
8
+ * - how many projects does one user have? (distinct projectId per anonymousId)
9
+ * - how many people work alone vs on a shared repo?
10
+ *
11
+ * All three depend on `projectId` meaning the same thing on two different machines, which is only
12
+ * true when it was derived from a git ORIGIN. Outside a repo, or in a repo nobody has pushed, the
13
+ * fingerprint falls back to a hash of the directory path — and two teammates on the same unpushed
14
+ * project then look like two unrelated projects. That is not a bug we can fix (there is nothing
15
+ * shared to hash), so instead every event reports WHICH source it used, and the analytics can filter
16
+ * to the rows where "users per project" is a real number instead of quietly averaging in the ones
17
+ * where it is always 1.
18
+ *
19
+ * Read from `.git/config` directly rather than by shelling out to `git`: this runs on the CLI's
20
+ * startup path, where a ~30ms subprocess spawn would tax every single `reticle` command, and it must
21
+ * work on a machine with no `git` binary installed at all.
22
+ */
23
+ import { readFileSync, statSync } from 'node:fs';
24
+ import { dirname, join } from 'node:path';
25
+ import { GitState, RepoForge } from '@reticlehq/core';
26
+ /** How far up the tree to look for a `.git` before giving up. Deep monorepos are still well inside. */
27
+ const MAX_PARENT_WALK = 20;
28
+ /**
29
+ * The public forges we name. Everything else reports `self_hosted` WITHOUT its hostname — an internal
30
+ * git host is often `git.<company>.com`, which would identify the company outright and is exactly the
31
+ * covert identification the telemetry policy promises not to do. The bucket alone is the useful
32
+ * signal anyway: self-hosted git is a strong enterprise tell, which is the question actually being
33
+ * asked.
34
+ */
35
+ const PUBLIC_FORGES = [
36
+ ['github.com', RepoForge.GITHUB],
37
+ ['gitlab.com', RepoForge.GITLAB],
38
+ ['bitbucket.org', RepoForge.BITBUCKET],
39
+ ['dev.azure.com', RepoForge.AZURE],
40
+ ['git.sr.ht', RepoForge.SOURCEHUT],
41
+ ['codeberg.org', RepoForge.CODEBERG],
42
+ ];
43
+ /** `git@github.com:Acme/Web.git` / `https://u:p@github.com/Acme/Web` → `github.com/acme/web`. */
44
+ export const normalizeGitOrigin = (url) => url
45
+ .trim()
46
+ .replace(/^[a-z][a-z0-9+.-]*:\/\/[^@/]*@/i, '') // strip scheme + any inline credentials
47
+ .replace(/^[a-z][a-z0-9+.-]*:\/\//i, '') // strip a bare scheme
48
+ .replace(/^[^@/]+@/, '') // strip `git@` from the scp-style form
49
+ .replace(/:/g, '/') // scp-style `host:path` → `host/path`
50
+ // Trailing slashes BEFORE the `.git` suffix: a url written `…/web.git/` would otherwise keep its
51
+ // `.git` and count as a different project from the same repo cloned without the slash.
52
+ .replace(/\/+$/, '')
53
+ .replace(/\.git$/i, '')
54
+ .replace(/\/+$/, '')
55
+ .toLowerCase();
56
+ /** Bucket a normalized origin by its host. Unknown hosts are `self_hosted` — never the host itself. */
57
+ export function forgeOf(normalizedOrigin) {
58
+ const host = normalizedOrigin.split('/')[0] ?? '';
59
+ for (const [domain, forge] of PUBLIC_FORGES) {
60
+ // `ssh.dev.azure.com` and `www.github.com` are the same forge; match on suffix, not equality.
61
+ if (host === domain || host.endsWith(`.${domain}`))
62
+ return forge;
63
+ }
64
+ return RepoForge.SELF_HOSTED;
65
+ }
66
+ const defaultRead = (path) => readFileSync(path, 'utf8');
67
+ const defaultExists = (path) => {
68
+ try {
69
+ statSync(path);
70
+ return true;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ };
76
+ /**
77
+ * Walk up from `cwd` looking for a `.git`, and report what is there. Never throws: an unreadable or
78
+ * malformed config reports the state we could confirm rather than failing a startup path.
79
+ */
80
+ export function gitFacts(cwd, read = defaultRead, exists = defaultExists) {
81
+ let dir = cwd;
82
+ for (let depth = 0; depth < MAX_PARENT_WALK; depth += 1) {
83
+ const gitDir = join(dir, '.git');
84
+ if (exists(gitDir)) {
85
+ let config = '';
86
+ try {
87
+ config = read(join(gitDir, 'config'));
88
+ }
89
+ catch {
90
+ // A `.git` with no readable config is still a repo — report it as one rather than as absent.
91
+ return { state: GitState.LOCAL_ONLY };
92
+ }
93
+ // The url line inside the [remote "origin"] section — the first url after that header wins.
94
+ const section = config.split(/\[remote\s+"origin"\]/)[1];
95
+ const url = section?.match(/^\s*url\s*=\s*(.+)$/m)?.[1];
96
+ if (url === undefined)
97
+ return { state: GitState.LOCAL_ONLY };
98
+ const origin = normalizeGitOrigin(url);
99
+ if (origin === '')
100
+ return { state: GitState.LOCAL_ONLY };
101
+ return { state: GitState.REMOTE, origin, forge: forgeOf(origin) };
102
+ }
103
+ const parent = dirname(dir);
104
+ if (parent === dir)
105
+ break; // hit the filesystem root
106
+ dir = parent;
107
+ }
108
+ return { state: GitState.NONE };
109
+ }
@@ -0,0 +1,22 @@
1
+ import { UsageContextKind, type Identity } from '@reticlehq/core';
2
+ /** Re-exported so the CLI has one import for the whole feature; the shape itself lives in core. */
3
+ export { UsageContextKind };
4
+ export type { Identity };
5
+ export declare function readIdentity(file?: string): Identity | undefined;
6
+ export declare function saveIdentity(identity: Identity, file?: string): void;
7
+ export declare function clearIdentity(file?: string): void;
8
+ /**
9
+ * What identifying yourself actually does, in plain words, shown BEFORE anything is sent.
10
+ *
11
+ * The second sentence is the one that matters and the one a consent notice usually omits: the
12
+ * identity is keyed to the same anonymous id the previous events carried, so opting in links the
13
+ * usage history that was already collected. That is exactly what makes it useful to us, which is
14
+ * exactly why the user has to be told before they choose, not after.
15
+ */
16
+ export declare const IDENTIFY_NOTICE: string;
17
+ /**
18
+ * Report an identity. Emitted as its own event so it is trivially separable from the anonymous
19
+ * stream — a distinct name is what lets the analytics side hold it under a different retention rule,
20
+ * or drop it entirely, without touching the adoption metrics.
21
+ */
22
+ export declare function submitIdentity(identity: Identity): Promise<boolean>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `reticle identify` — the ONLY way Reticle ever learns who you are, and it is a thing you type.
3
+ *
4
+ * The product question behind this is real: which of these anonymous ids are companies who would pay
5
+ * for support, an enterprise licence, or a design-partner slot? The tempting answer is to infer it —
6
+ * match a git remote against a company domain, sniff an email out of `git config`, resolve an npm org.
7
+ * Reticle does none of that, and the refusal is deliberate rather than squeamish:
8
+ *
9
+ * - it would contradict a promise made in four places (README, usage, enterprise FAQ, telemetry
10
+ * policy) that project names, git remotes and account identifiers are never sent;
11
+ * - covert identification of a data subject is not "anonymous usage metrics", so it would also
12
+ * break the legal basis the policy states — legitimate interest, minimized to the listed fields;
13
+ * - and it is worse at the actual job. A scraped lead is a cold email from a tool the recipient
14
+ * trusted, which is the fastest way to turn an advocate into someone who runs
15
+ * `reticle telemetry disable` and tells their team to do the same. A self-identified one already
16
+ * wants to talk to you.
17
+ *
18
+ * So this is opt-in, explicit, reversible, and it tells the user exactly what it links before it
19
+ * sends anything.
20
+ */
21
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
22
+ import { homedir } from 'node:os';
23
+ import { join } from 'node:path';
24
+ import { TelemetryEventKind, UsageContextKind } from '@reticlehq/core';
25
+ import { getTelemetry } from './telemetry.js';
26
+ const RETICLE_DIR = join(homedir(), '.reticle');
27
+ /** Persisted so the identity survives shells and is attached to later sessions — and so it can be shown back. */
28
+ const IDENTITY_FILE = join(RETICLE_DIR, 'identity.json');
29
+ /** Re-exported so the CLI has one import for the whole feature; the shape itself lives in core. */
30
+ export { UsageContextKind };
31
+ export function readIdentity(file = IDENTITY_FILE) {
32
+ try {
33
+ return JSON.parse(readFileSync(file, 'utf8'));
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }
39
+ export function saveIdentity(identity, file = IDENTITY_FILE) {
40
+ mkdirSync(RETICLE_DIR, { recursive: true });
41
+ writeFileSync(file, JSON.stringify(identity, null, 2), 'utf8');
42
+ }
43
+ export function clearIdentity(file = IDENTITY_FILE) {
44
+ rmSync(file, { force: true });
45
+ }
46
+ /**
47
+ * What identifying yourself actually does, in plain words, shown BEFORE anything is sent.
48
+ *
49
+ * The second sentence is the one that matters and the one a consent notice usually omits: the
50
+ * identity is keyed to the same anonymous id the previous events carried, so opting in links the
51
+ * usage history that was already collected. That is exactly what makes it useful to us, which is
52
+ * exactly why the user has to be told before they choose, not after.
53
+ */
54
+ export const IDENTIFY_NOTICE = [
55
+ 'This sends the details you type here to the Reticle maintainers, so we can contact you and',
56
+ "understand how teams use Reticle. It is linked to this machine's anonymous id, which means it",
57
+ 'also connects the anonymous usage already recorded from this machine to what you enter now.',
58
+ 'Nothing about your app, your code, or your repository is included, and nothing else changes.',
59
+ 'Undo it any time with `reticle identify --forget` (that stops future sends and deletes the local file;',
60
+ 'email support@reticlehq.com to have what was already sent removed).',
61
+ ].join('\n');
62
+ /**
63
+ * Report an identity. Emitted as its own event so it is trivially separable from the anonymous
64
+ * stream — a distinct name is what lets the analytics side hold it under a different retention rule,
65
+ * or drop it entirely, without touching the adoption metrics.
66
+ */
67
+ export async function submitIdentity(identity) {
68
+ const telemetry = getTelemetry();
69
+ if (!telemetry.enabled)
70
+ return false;
71
+ await telemetry.emit(TelemetryEventKind.IDENTIFIED, { identity });
72
+ return true;
73
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * How `reticle init` went.
3
+ *
4
+ * The onboarding funnel had no instrumentation at all, which meant the two most different failure
5
+ * modes were indistinguishable: someone whose setup died on a missing dependency looked exactly like
6
+ * someone who installed the package and never ran the command. One is a bug we can fix; the other is
7
+ * a marketing problem. Both showed up as silence.
8
+ *
9
+ * `reticle init` runs as a one-shot CLI command that exits immediately, so the send is DETACHED — the
10
+ * same treatment `cli_command_run` gets, for the same reason: an in-process fetch would keep the
11
+ * event loop alive and tax the command by most of a second.
12
+ */
13
+ import { type InitOutcome } from '@reticlehq/core';
14
+ /** Classified init failures — our own vocabulary, never a raw error or a path. */
15
+ export declare const InitFailure: {
16
+ /** Run outside a project root. The single most common first-run mistake. */
17
+ readonly NO_PACKAGE_JSON: "no_package_json";
18
+ /** The package manager failed: offline, a locked registry, a broken install. */
19
+ readonly DEPENDENCY_INSTALL: "dependency_install";
20
+ /** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
21
+ readonly MCP_REGISTRATION: "mcp_registration";
22
+ readonly OTHER: "other";
23
+ };
24
+ export type InitFailure = (typeof InitFailure)[keyof typeof InitFailure];
25
+ /** Report one init outcome. Best-effort; setup must never fail because a metric did. */
26
+ export declare function reportInitOutcome(init: InitOutcome): void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * How `reticle init` went.
3
+ *
4
+ * The onboarding funnel had no instrumentation at all, which meant the two most different failure
5
+ * modes were indistinguishable: someone whose setup died on a missing dependency looked exactly like
6
+ * someone who installed the package and never ran the command. One is a bug we can fix; the other is
7
+ * a marketing problem. Both showed up as silence.
8
+ *
9
+ * `reticle init` runs as a one-shot CLI command that exits immediately, so the send is DETACHED — the
10
+ * same treatment `cli_command_run` gets, for the same reason: an in-process fetch would keep the
11
+ * event loop alive and tax the command by most of a second.
12
+ */
13
+ import { TelemetryEventKind } from '@reticlehq/core';
14
+ import { getTelemetry } from './telemetry.js';
15
+ /** Classified init failures — our own vocabulary, never a raw error or a path. */
16
+ export const InitFailure = {
17
+ /** Run outside a project root. The single most common first-run mistake. */
18
+ NO_PACKAGE_JSON: 'no_package_json',
19
+ /** The package manager failed: offline, a locked registry, a broken install. */
20
+ DEPENDENCY_INSTALL: 'dependency_install',
21
+ /** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
22
+ MCP_REGISTRATION: 'mcp_registration',
23
+ OTHER: 'other',
24
+ };
25
+ /** Report one init outcome. Best-effort; setup must never fail because a metric did. */
26
+ export function reportInitOutcome(init) {
27
+ try {
28
+ void getTelemetry().emit(TelemetryEventKind.INIT_COMPLETED, { init, detach: true });
29
+ }
30
+ catch {
31
+ /* a metric must never break `reticle init` */
32
+ }
33
+ }
@@ -0,0 +1,6 @@
1
+ import type { MachineSnapshot } from '@reticlehq/core';
2
+ /**
3
+ * Sample the machine. Never throws — this is called from a crash handler, where the one unacceptable
4
+ * outcome is making the crash worse.
5
+ */
6
+ export declare function machineSnapshot(): MachineSnapshot | undefined;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What the machine looked like at a moment worth remembering.
3
+ *
4
+ * "Out of memory" and "our bug" produce identical-looking stack traces and are completely different
5
+ * problems — one is a docs/limits issue on the user's side, the other is ours to fix. Without this,
6
+ * every crash report arrived with that ambiguity baked in and no way to resolve it after the fact.
7
+ *
8
+ * Sampled at MOMENTS — a crash, the end of a session — never per tool call. `os.loadavg()` and
9
+ * `process.memoryUsage()` are cheap but not free, and taking them two hundred times a session to
10
+ * answer a question only ever asked about the bad moments would be a continuous cost for an
11
+ * occasional need.
12
+ *
13
+ * Nothing here describes the person: it is our own process's footprint plus coarse machine capacity.
14
+ * No hostname, no username, no disk paths, no process list.
15
+ */
16
+ import { cpus, freemem, loadavg, totalmem } from 'node:os';
17
+ const MB = 1024 * 1024;
18
+ /**
19
+ * Sample the machine. Never throws — this is called from a crash handler, where the one unacceptable
20
+ * outcome is making the crash worse.
21
+ */
22
+ export function machineSnapshot() {
23
+ try {
24
+ const memory = process.memoryUsage();
25
+ return {
26
+ rssMb: Math.round(memory.rss / MB),
27
+ heapUsedMb: Math.round(memory.heapUsed / MB),
28
+ freeMemMb: Math.round(freemem() / MB),
29
+ totalMemMb: Math.round(totalmem() / MB),
30
+ // ×100 so it stays an integer property rather than a float. Windows always reports 0 here,
31
+ // which is a platform fact rather than an idle machine — read it alongside `os`.
32
+ load1x100: Math.max(0, Math.round((loadavg()[0] ?? 0) * 100)),
33
+ cpuCount: cpus().length,
34
+ };
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ }
@@ -0,0 +1,5 @@
1
+ export declare function markDaemonStart(now: number): void;
2
+ /** Report one client attaching. Best-effort; a metric must never affect whether a client connects. */
3
+ export declare function reportMcpConnected(client?: string, now?: () => number): void;
4
+ /** Tests only. */
5
+ export declare function resetMcpConnections(): void;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * When an MCP client actually attaches.
3
+ *
4
+ * This is the signal that separates "Reticle is installed and the daemon is running" from "a person
5
+ * is using it". A daemon can sit up for days with no agent ever attaching — which looks like healthy
6
+ * adoption in `daemon_started` and is in fact an onboarding failure nobody would think to report.
7
+ *
8
+ * It also exposes reconnect CHURN. A client whose transport keeps dropping reattaches every few
9
+ * minutes, and in every other metric that is indistinguishable from enthusiastic use: more sessions,
10
+ * more connects, more activity. The `reconnect` flag is what tells those two stories apart.
11
+ */
12
+ import { TelemetryEventKind } from '@reticlehq/core';
13
+ import { getTelemetry } from './telemetry.js';
14
+ let connectsSeen = 0;
15
+ /** Set when the daemon starts, so the FIRST connect can report how long Reticle sat unused. */
16
+ let daemonStartedAt;
17
+ export function markDaemonStart(now) {
18
+ daemonStartedAt = now;
19
+ connectsSeen = 0;
20
+ }
21
+ /** Report one client attaching. Best-effort; a metric must never affect whether a client connects. */
22
+ export function reportMcpConnected(client, now = () => Date.now()) {
23
+ try {
24
+ const reconnect = connectsSeen > 0;
25
+ connectsSeen += 1;
26
+ void getTelemetry().emit(TelemetryEventKind.MCP_CLIENT_CONNECTED, {
27
+ connection: {
28
+ reconnect,
29
+ // A large value on the FIRST connect is the interesting case: Reticle was started and then
30
+ // sat there. On a reconnect it is just how long the daemon has been up.
31
+ daemonAgeMs: daemonStartedAt === undefined ? 0 : Math.max(0, now() - daemonStartedAt),
32
+ ...(client !== undefined ? { client } : {}),
33
+ },
34
+ });
35
+ }
36
+ catch {
37
+ /* never let a metric interfere with a client attaching */
38
+ }
39
+ }
40
+ /** Tests only. */
41
+ export function resetMcpConnections() {
42
+ connectsSeen = 0;
43
+ daemonStartedAt = undefined;
44
+ }
@@ -0,0 +1,44 @@
1
+ import { ProjectSize, type ProjectProfile } from '@reticlehq/core';
2
+ /**
3
+ * The feature FAMILIES, and the on-disk evidence that a project has adopted each. Named rather than
4
+ * counted so the vocabulary stays stable as tools are added, renamed, or merged into action-dispatched
5
+ * families — a count would silently change meaning the next time the tool surface is reorganized.
6
+ */
7
+ export declare const FeatureFamily: {
8
+ readonly DETERMINISTIC_REPLAY: "deterministic_replay";
9
+ readonly VISUAL_BASELINE: "visual_baseline";
10
+ readonly TEXT_BASELINE: "text_baseline";
11
+ readonly CAPABILITY_CONTRACT: "capability_contract";
12
+ readonly RUN_ARTIFACTS: "run_artifacts";
13
+ readonly BUG_CAPSULES: "bug_capsules";
14
+ readonly CROSS_RUN_HISTORY: "cross_run_history";
15
+ };
16
+ export type FeatureFamily = (typeof FeatureFamily)[keyof typeof FeatureFamily];
17
+ /** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
18
+ export declare function countSourceFiles(root: string, readDir?: DirReader): number;
19
+ export declare function sizeBucket(sourceFiles: number): ProjectSize;
20
+ /**
21
+ * Project age in whole WEEKS, from the repo's first commit.
22
+ *
23
+ * Weeks rather than a date, deliberately: an exact creation timestamp combined with a framework and a
24
+ * size bucket starts to narrow toward a specific repository, and the question being asked — "is
25
+ * Reticle being adopted on greenfield spikes or on mature codebases?" — does not need that precision.
26
+ *
27
+ * Read from git's own reflog tail rather than by shelling out, for the same reason the origin lookup
28
+ * does: this runs on a startup path and must not cost a subprocess or require a `git` binary.
29
+ */
30
+ export declare function projectAgeWeeks(root: string, now: number, read?: FileReader): number | undefined;
31
+ /** True when package.json declares workspaces — monorepos exercise very different code paths. */
32
+ export declare function isMonorepo(root: string, read?: FileReader): boolean;
33
+ interface DirEntry {
34
+ name: string;
35
+ isDirectory: boolean;
36
+ }
37
+ type DirReader = (dir: string) => DirEntry[];
38
+ type FileReader = (path: string) => string;
39
+ /**
40
+ * Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
41
+ * rather than an exception, because a telemetry snapshot must never be able to fail a daemon start.
42
+ */
43
+ export declare function profileProject(cwd: string, now: number): ProjectProfile;
44
+ export {};
@@ -0,0 +1,205 @@
1
+ /**
2
+ * What kind of project is this, and how much of Reticle does it actually use?
3
+ *
4
+ * A DAU chart cannot tell apart someone running forty saved flows with visual baselines and a
5
+ * checked-in contract from someone who called `reticle_snapshot` twice and never came back. Both are
6
+ * one active user. Only one of them is retained, and only one of them is a company. `featureDepth`
7
+ * is the number that separates them, and it is the closest thing here to an activation metric.
8
+ *
9
+ * Everything is derived from files already on disk in the project, and everything is reduced before
10
+ * it leaves: sizes become buckets, ages become whole weeks, feature use becomes a fixed vocabulary of
11
+ * family names we define ourselves. No file names, no paths, no flow names, no dependency inventory.
12
+ */
13
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { ProjectSize, ReticleDir } from '@reticlehq/core';
16
+ import { gitFacts } from './git-facts.js';
17
+ import { detectStack } from './feedback-context.js';
18
+ /**
19
+ * The feature FAMILIES, and the on-disk evidence that a project has adopted each. Named rather than
20
+ * counted so the vocabulary stays stable as tools are added, renamed, or merged into action-dispatched
21
+ * families — a count would silently change meaning the next time the tool surface is reorganized.
22
+ */
23
+ export const FeatureFamily = {
24
+ DETERMINISTIC_REPLAY: 'deterministic_replay',
25
+ VISUAL_BASELINE: 'visual_baseline',
26
+ TEXT_BASELINE: 'text_baseline',
27
+ CAPABILITY_CONTRACT: 'capability_contract',
28
+ RUN_ARTIFACTS: 'run_artifacts',
29
+ BUG_CAPSULES: 'bug_capsules',
30
+ CROSS_RUN_HISTORY: 'cross_run_history',
31
+ };
32
+ const ALL_FAMILIES = Object.values(FeatureFamily);
33
+ /** Source-file thresholds for each bucket. A bucket answers "toy or real codebase"; a count fingerprints. */
34
+ const SIZE_THRESHOLDS = [
35
+ [50, ProjectSize.TINY],
36
+ [250, ProjectSize.SMALL],
37
+ [1000, ProjectSize.MEDIUM],
38
+ [5000, ProjectSize.LARGE],
39
+ ];
40
+ const SOURCE_EXTENSIONS = /\.(?:tsx?|jsx?|mjs|cjs|vue|svelte|astro)$/;
41
+ /** Never walked: cost, and nothing in them describes the project the team actually wrote. */
42
+ const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.turbo', 'coverage']);
43
+ /** Bound the walk — a huge monorepo must not turn a telemetry snapshot into a filesystem crawl. */
44
+ const MAX_FILES_SCANNED = 6000;
45
+ const MAX_WALK_DEPTH = 8;
46
+ const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
47
+ /** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
48
+ export function countSourceFiles(root, readDir = defaultReadDir) {
49
+ let count = 0;
50
+ const walk = (dir, depth) => {
51
+ if (depth > MAX_WALK_DEPTH || count >= MAX_FILES_SCANNED)
52
+ return;
53
+ for (const entry of readDir(dir)) {
54
+ if (count >= MAX_FILES_SCANNED)
55
+ return;
56
+ if (entry.isDirectory) {
57
+ if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith('.')) {
58
+ walk(join(dir, entry.name), depth + 1);
59
+ }
60
+ }
61
+ else if (SOURCE_EXTENSIONS.test(entry.name)) {
62
+ count += 1;
63
+ }
64
+ }
65
+ };
66
+ walk(root, 0);
67
+ return count;
68
+ }
69
+ export function sizeBucket(sourceFiles) {
70
+ for (const [threshold, bucket] of SIZE_THRESHOLDS) {
71
+ if (sourceFiles < threshold)
72
+ return bucket;
73
+ }
74
+ return ProjectSize.HUGE;
75
+ }
76
+ /**
77
+ * Project age in whole WEEKS, from the repo's first commit.
78
+ *
79
+ * Weeks rather than a date, deliberately: an exact creation timestamp combined with a framework and a
80
+ * size bucket starts to narrow toward a specific repository, and the question being asked — "is
81
+ * Reticle being adopted on greenfield spikes or on mature codebases?" — does not need that precision.
82
+ *
83
+ * Read from git's own reflog tail rather than by shelling out, for the same reason the origin lookup
84
+ * does: this runs on a startup path and must not cost a subprocess or require a `git` binary.
85
+ */
86
+ export function projectAgeWeeks(root, now, read = defaultRead) {
87
+ for (const path of [join(root, '.git', 'logs', 'HEAD')]) {
88
+ try {
89
+ const first = read(path).split('\n')[0];
90
+ // reflog line: `<old> <new> <name> <email> <unix-ts> <tz>\t<message>`
91
+ const seconds = first?.match(/\s(\d{9,11})\s[+-]\d{4}/)?.[1];
92
+ if (seconds === undefined)
93
+ continue;
94
+ const ageMs = now - parseInt(seconds, 10) * 1000;
95
+ if (ageMs < 0)
96
+ continue;
97
+ return Math.floor(ageMs / MS_PER_WEEK);
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ }
103
+ return undefined;
104
+ }
105
+ /** True when package.json declares workspaces — monorepos exercise very different code paths. */
106
+ export function isMonorepo(root, read = defaultRead) {
107
+ try {
108
+ const pkg = JSON.parse(read(join(root, 'package.json')));
109
+ if (pkg.workspaces !== undefined)
110
+ return true;
111
+ }
112
+ catch {
113
+ /* no package.json — fall through to the pnpm check */
114
+ }
115
+ try {
116
+ read(join(root, 'pnpm-workspace.yaml'));
117
+ return true;
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ }
123
+ const defaultReadDir = (dir) => {
124
+ try {
125
+ return readdirSync(dir, { withFileTypes: true }).map((e) => ({
126
+ name: e.name,
127
+ isDirectory: e.isDirectory(),
128
+ }));
129
+ }
130
+ catch {
131
+ return [];
132
+ }
133
+ };
134
+ const defaultRead = (path) => readFileSync(path, 'utf8');
135
+ /** How many entries a `.reticle` subdirectory holds, matching an optional extension. 0 when absent. */
136
+ function countIn(reticleRoot, subdir, extension) {
137
+ try {
138
+ const names = readdirSync(join(reticleRoot, subdir));
139
+ return extension === undefined ? names.length : names.filter((n) => extension.test(n)).length;
140
+ }
141
+ catch {
142
+ return 0;
143
+ }
144
+ }
145
+ function exists(path) {
146
+ try {
147
+ statSync(path);
148
+ return true;
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }
154
+ /**
155
+ * Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
156
+ * rather than an exception, because a telemetry snapshot must never be able to fail a daemon start.
157
+ */
158
+ export function profileProject(cwd, now) {
159
+ const reticleRoot = join(cwd, ReticleDir.ROOT);
160
+ const flowCount = countIn(reticleRoot, ReticleDir.FLOWS_SUBDIR, /\.json$/);
161
+ const baselineCount = countIn(reticleRoot, ReticleDir.BASELINES_SUBDIR);
162
+ // `.diff.png` files are diff OUTPUT, not baselines — counting them would double every visual user.
163
+ const visualBaselineCount = countIn(reticleRoot, ReticleDir.VISUAL_SUBDIR, /(?<!\.diff)\.png$/);
164
+ const runCount = countIn(reticleRoot, ReticleDir.RUNS_SUBDIR, /\.json$/);
165
+ const capsuleCount = countIn(reticleRoot, ReticleDir.CAPSULES_SUBDIR, /\.json$/);
166
+ const hasContract = exists(join(reticleRoot, ReticleDir.CONTRACT_FILE));
167
+ const hasHistory = exists(join(reticleRoot, ReticleDir.PROJECT_FILE));
168
+ const featuresUsed = [];
169
+ if (flowCount > 0)
170
+ featuresUsed.push(FeatureFamily.DETERMINISTIC_REPLAY);
171
+ if (visualBaselineCount > 0)
172
+ featuresUsed.push(FeatureFamily.VISUAL_BASELINE);
173
+ if (baselineCount > 0)
174
+ featuresUsed.push(FeatureFamily.TEXT_BASELINE);
175
+ if (hasContract)
176
+ featuresUsed.push(FeatureFamily.CAPABILITY_CONTRACT);
177
+ if (runCount > 0)
178
+ featuresUsed.push(FeatureFamily.RUN_ARTIFACTS);
179
+ if (capsuleCount > 0)
180
+ featuresUsed.push(FeatureFamily.BUG_CAPSULES);
181
+ if (hasHistory)
182
+ featuresUsed.push(FeatureFamily.CROSS_RUN_HISTORY);
183
+ const { stack, stackMajor } = detectStack(cwd);
184
+ const ageWeeks = projectAgeWeeks(cwd, now);
185
+ const git = gitFacts(cwd);
186
+ return {
187
+ git: git.state,
188
+ ...(git.forge !== undefined ? { forge: git.forge } : {}),
189
+ ...(stack !== undefined ? { stack } : {}),
190
+ ...(stackMajor !== undefined ? { stackMajor } : {}),
191
+ size: sizeBucket(countSourceFiles(cwd)),
192
+ monorepo: isMonorepo(cwd),
193
+ ...(ageWeeks !== undefined ? { ageWeeks } : {}),
194
+ flowCount,
195
+ baselineCount,
196
+ visualBaselineCount,
197
+ runCount,
198
+ hasContract,
199
+ capsuleCount,
200
+ featuresUsed,
201
+ // Rounded to 2dp: the exact ratio is a function of the counts above, and an unrounded float would
202
+ // just be a higher-cardinality restatement of them.
203
+ featureDepth: Math.round((featuresUsed.length / ALL_FAMILIES.length) * 100) / 100,
204
+ };
205
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Telemetry for the verification RUNNER — the path `reticle verify` and the HTTP verify surface take.
3
+ *
4
+ * This path does not go through `runTool`, so for a while it produced nothing at all: no timing, no
5
+ * `verification_completed`, and — worst — no `bug_found`. Which meant every defect Reticle caught in
6
+ * CI was invisible to the one number we intend to publish, on precisely the surface most likely to
7
+ * run at scale and unattended. A metric that is blind to CI is not measuring the product.
8
+ *
9
+ * Reported from the assembled run rather than from inside the replay loop: the run artifact is the
10
+ * finished, already-classified verdict, so this cannot double-count and does not need to understand
11
+ * how a replay works.
12
+ */
13
+ import { type ReticleVerificationRun } from '@reticlehq/core';
14
+ /** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
15
+ export declare const VERIFY_SURFACE = "reticle_verify";
16
+ /**
17
+ * Report one completed verification run: the verdict, and one bug per failing flow.
18
+ *
19
+ * Best-effort and wrapped — a verification's exit code must never depend on whether a metric sent.
20
+ */
21
+ export declare function reportRunTelemetry(run: ReticleVerificationRun): void;