harnery 0.0.1 → 0.2.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 (445) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -2
  3. package/bin/agent-coord +42 -0
  4. package/bin/agent-hook +44 -0
  5. package/bin/harn +40 -0
  6. package/dist/cli.d.ts +9 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +18 -0
  9. package/dist/commander.d.ts +128 -0
  10. package/dist/commander.d.ts.map +1 -0
  11. package/dist/commander.js +126 -0
  12. package/dist/commands/agents.d.ts +18 -0
  13. package/dist/commands/agents.d.ts.map +1 -0
  14. package/dist/commands/agents.js +3946 -0
  15. package/dist/commands/backup.d.ts +22 -0
  16. package/dist/commands/backup.d.ts.map +1 -0
  17. package/dist/commands/backup.js +262 -0
  18. package/dist/commands/browse-ai.d.ts +4 -0
  19. package/dist/commands/browse-ai.d.ts.map +1 -0
  20. package/dist/commands/browse-ai.js +156 -0
  21. package/dist/commands/browse.d.ts +4 -0
  22. package/dist/commands/browse.d.ts.map +1 -0
  23. package/dist/commands/browse.js +590 -0
  24. package/dist/commands/callers.d.ts +4 -0
  25. package/dist/commands/callers.d.ts.map +1 -0
  26. package/dist/commands/callers.js +276 -0
  27. package/dist/commands/completion.d.ts +17 -0
  28. package/dist/commands/completion.d.ts.map +1 -0
  29. package/dist/commands/completion.js +158 -0
  30. package/dist/commands/config-get.d.ts +4 -0
  31. package/dist/commands/config-get.d.ts.map +1 -0
  32. package/dist/commands/config-get.js +131 -0
  33. package/dist/commands/context.d.ts +11 -0
  34. package/dist/commands/context.d.ts.map +1 -0
  35. package/dist/commands/context.js +185 -0
  36. package/dist/commands/cookies.d.ts +4 -0
  37. package/dist/commands/cookies.d.ts.map +1 -0
  38. package/dist/commands/cookies.js +140 -0
  39. package/dist/commands/docs.d.ts +4 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +137 -0
  42. package/dist/commands/doctor.d.ts +25 -0
  43. package/dist/commands/doctor.d.ts.map +1 -0
  44. package/dist/commands/doctor.js +200 -0
  45. package/dist/commands/edit-batch.d.ts +18 -0
  46. package/dist/commands/edit-batch.d.ts.map +1 -0
  47. package/dist/commands/edit-batch.js +172 -0
  48. package/dist/commands/eml.d.ts +4 -0
  49. package/dist/commands/eml.d.ts.map +1 -0
  50. package/dist/commands/eml.js +428 -0
  51. package/dist/commands/env.d.ts +4 -0
  52. package/dist/commands/env.d.ts.map +1 -0
  53. package/dist/commands/env.js +201 -0
  54. package/dist/commands/fetch.d.ts +4 -0
  55. package/dist/commands/fetch.d.ts.map +1 -0
  56. package/dist/commands/fetch.js +99 -0
  57. package/dist/commands/file-history.d.ts +4 -0
  58. package/dist/commands/file-history.d.ts.map +1 -0
  59. package/dist/commands/file-history.js +152 -0
  60. package/dist/commands/grep.d.ts +4 -0
  61. package/dist/commands/grep.d.ts.map +1 -0
  62. package/dist/commands/grep.js +317 -0
  63. package/dist/commands/init.d.ts +82 -0
  64. package/dist/commands/init.d.ts.map +1 -0
  65. package/dist/commands/init.js +288 -0
  66. package/dist/commands/outline.d.ts +4 -0
  67. package/dist/commands/outline.d.ts.map +1 -0
  68. package/dist/commands/outline.js +509 -0
  69. package/dist/commands/presence.d.ts +12 -0
  70. package/dist/commands/presence.d.ts.map +1 -0
  71. package/dist/commands/presence.js +123 -0
  72. package/dist/commands/read.d.ts +7 -0
  73. package/dist/commands/read.d.ts.map +1 -0
  74. package/dist/commands/read.js +46 -0
  75. package/dist/commands/scratch.d.ts +4 -0
  76. package/dist/commands/scratch.d.ts.map +1 -0
  77. package/dist/commands/scratch.js +426 -0
  78. package/dist/commands/session.d.ts +4 -0
  79. package/dist/commands/session.d.ts.map +1 -0
  80. package/dist/commands/session.js +162 -0
  81. package/dist/commands/sync.d.ts +24 -0
  82. package/dist/commands/sync.d.ts.map +1 -0
  83. package/dist/commands/sync.js +275 -0
  84. package/dist/commands/toc.d.ts +5 -0
  85. package/dist/commands/toc.d.ts.map +1 -0
  86. package/dist/commands/toc.js +153 -0
  87. package/dist/commands/tokens.d.ts +4 -0
  88. package/dist/commands/tokens.d.ts.map +1 -0
  89. package/dist/commands/tokens.js +48 -0
  90. package/dist/commands/tunnel.d.ts +4 -0
  91. package/dist/commands/tunnel.d.ts.map +1 -0
  92. package/dist/commands/tunnel.js +513 -0
  93. package/dist/commands/uninstall.d.ts +22 -0
  94. package/dist/commands/uninstall.d.ts.map +1 -0
  95. package/dist/commands/uninstall.js +126 -0
  96. package/dist/commands/web.d.ts +4 -0
  97. package/dist/commands/web.d.ts.map +1 -0
  98. package/dist/commands/web.js +165 -0
  99. package/dist/core/agents/canonical-emit.d.ts +27 -0
  100. package/dist/core/agents/canonical-emit.d.ts.map +1 -0
  101. package/dist/core/agents/canonical-emit.js +72 -0
  102. package/dist/core/agents/cli-emit.d.ts +27 -0
  103. package/dist/core/agents/cli-emit.d.ts.map +1 -0
  104. package/dist/core/agents/cli-emit.js +57 -0
  105. package/dist/core/agents/cli.d.ts +10 -0
  106. package/dist/core/agents/cli.d.ts.map +1 -0
  107. package/dist/core/agents/cli.js +757 -0
  108. package/dist/core/agents/codex-replay.d.ts +29 -0
  109. package/dist/core/agents/codex-replay.d.ts.map +1 -0
  110. package/dist/core/agents/codex-replay.js +138 -0
  111. package/dist/core/agents/coord-client.d.ts +98 -0
  112. package/dist/core/agents/coord-client.d.ts.map +1 -0
  113. package/dist/core/agents/coord-client.js +212 -0
  114. package/dist/core/agents/events/consume.d.ts +59 -0
  115. package/dist/core/agents/events/consume.d.ts.map +1 -0
  116. package/dist/core/agents/events/consume.js +147 -0
  117. package/dist/core/agents/events/emit.d.ts +42 -0
  118. package/dist/core/agents/events/emit.d.ts.map +1 -0
  119. package/dist/core/agents/events/emit.js +70 -0
  120. package/dist/core/agents/events/ulid.d.ts +11 -0
  121. package/dist/core/agents/events/ulid.d.ts.map +1 -0
  122. package/dist/core/agents/events/ulid.js +47 -0
  123. package/dist/core/agents/index.d.ts +14 -0
  124. package/dist/core/agents/index.d.ts.map +1 -0
  125. package/dist/core/agents/index.js +13 -0
  126. package/dist/core/agents/paths.d.ts +6 -0
  127. package/dist/core/agents/paths.d.ts.map +1 -0
  128. package/dist/core/agents/paths.js +17 -0
  129. package/dist/core/agents/render/prompt-context.d.ts +43 -0
  130. package/dist/core/agents/render/prompt-context.d.ts.map +1 -0
  131. package/dist/core/agents/render/prompt-context.js +335 -0
  132. package/dist/core/agents/render/session-context.d.ts +39 -0
  133. package/dist/core/agents/render/session-context.d.ts.map +1 -0
  134. package/dist/core/agents/render/session-context.js +283 -0
  135. package/dist/core/agents/rules/claim-conflict.d.ts +35 -0
  136. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -0
  137. package/dist/core/agents/rules/claim-conflict.js +244 -0
  138. package/dist/core/agents/rules/commit-conflict.d.ts +59 -0
  139. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -0
  140. package/dist/core/agents/rules/commit-conflict.js +244 -0
  141. package/dist/core/agents/rules/stop-hook.d.ts +44 -0
  142. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -0
  143. package/dist/core/agents/rules/stop-hook.js +161 -0
  144. package/dist/core/agents/session-events.d.ts +41 -0
  145. package/dist/core/agents/session-events.d.ts.map +1 -0
  146. package/dist/core/agents/session-events.js +205 -0
  147. package/dist/core/agents/state/activity-log.d.ts +18 -0
  148. package/dist/core/agents/state/activity-log.d.ts.map +1 -0
  149. package/dist/core/agents/state/activity-log.js +34 -0
  150. package/dist/core/agents/state/council.d.ts +39 -0
  151. package/dist/core/agents/state/council.d.ts.map +1 -0
  152. package/dist/core/agents/state/council.js +216 -0
  153. package/dist/core/agents/state/heartbeat-projector.d.ts +59 -0
  154. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -0
  155. package/dist/core/agents/state/heartbeat-projector.js +436 -0
  156. package/dist/core/agents/state/heartbeat-writer.d.ts +64 -0
  157. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -0
  158. package/dist/core/agents/state/heartbeat-writer.js +271 -0
  159. package/dist/core/agents/state/names.d.ts +35 -0
  160. package/dist/core/agents/state/names.d.ts.map +1 -0
  161. package/dist/core/agents/state/names.js +376 -0
  162. package/dist/core/agents/state/pidmap.d.ts +11 -0
  163. package/dist/core/agents/state/pidmap.d.ts.map +1 -0
  164. package/dist/core/agents/state/pidmap.js +32 -0
  165. package/dist/core/agents/state/scratch.d.ts +27 -0
  166. package/dist/core/agents/state/scratch.d.ts.map +1 -0
  167. package/dist/core/agents/state/scratch.js +90 -0
  168. package/dist/core/agents/state/shell-mutation.d.ts +17 -0
  169. package/dist/core/agents/state/shell-mutation.d.ts.map +1 -0
  170. package/dist/core/agents/state/shell-mutation.js +41 -0
  171. package/dist/core/agents/state/stale-sweep.d.ts +16 -0
  172. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -0
  173. package/dist/core/agents/state/stale-sweep.js +166 -0
  174. package/dist/core/config.d.ts +29 -0
  175. package/dist/core/config.d.ts.map +1 -0
  176. package/dist/core/config.js +108 -0
  177. package/dist/core/hooks/cli.d.ts +21 -0
  178. package/dist/core/hooks/cli.d.ts.map +1 -0
  179. package/dist/core/hooks/cli.js +1123 -0
  180. package/dist/core/hooks/effects/image-capture.d.ts +43 -0
  181. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -0
  182. package/dist/core/hooks/effects/image-capture.js +288 -0
  183. package/dist/core/hooks/effects/index.d.ts +64 -0
  184. package/dist/core/hooks/effects/index.d.ts.map +1 -0
  185. package/dist/core/hooks/effects/index.js +197 -0
  186. package/dist/core/hooks/events/emit.d.ts +31 -0
  187. package/dist/core/hooks/events/emit.d.ts.map +1 -0
  188. package/dist/core/hooks/events/emit.js +89 -0
  189. package/dist/core/hooks/events/schema.d.ts +235 -0
  190. package/dist/core/hooks/events/schema.d.ts.map +1 -0
  191. package/dist/core/hooks/events/schema.js +12 -0
  192. package/dist/core/hooks/events/ulid.d.ts +10 -0
  193. package/dist/core/hooks/events/ulid.d.ts.map +1 -0
  194. package/dist/core/hooks/events/ulid.js +47 -0
  195. package/dist/core/hooks/harness/detect.d.ts +9 -0
  196. package/dist/core/hooks/harness/detect.d.ts.map +1 -0
  197. package/dist/core/hooks/harness/detect.js +29 -0
  198. package/dist/core/hooks/harness/events.d.ts +45 -0
  199. package/dist/core/hooks/harness/events.d.ts.map +1 -0
  200. package/dist/core/hooks/harness/events.js +71 -0
  201. package/dist/core/hooks/harness/output.d.ts +46 -0
  202. package/dist/core/hooks/harness/output.d.ts.map +1 -0
  203. package/dist/core/hooks/harness/output.js +87 -0
  204. package/dist/core/hooks/harness/parse.d.ts +67 -0
  205. package/dist/core/hooks/harness/parse.d.ts.map +1 -0
  206. package/dist/core/hooks/harness/parse.js +132 -0
  207. package/dist/core/hooks/index.d.ts +8 -0
  208. package/dist/core/hooks/index.d.ts.map +1 -0
  209. package/dist/core/hooks/index.js +7 -0
  210. package/dist/core/hooks/resolve/anchor.d.ts +37 -0
  211. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -0
  212. package/dist/core/hooks/resolve/anchor.js +48 -0
  213. package/dist/core/hooks/resolve/coord-root.d.ts +6 -0
  214. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -0
  215. package/dist/core/hooks/resolve/coord-root.js +27 -0
  216. package/dist/core/hooks/resolve/intent.d.ts +33 -0
  217. package/dist/core/hooks/resolve/intent.d.ts.map +1 -0
  218. package/dist/core/hooks/resolve/intent.js +79 -0
  219. package/dist/core/hooks/resolve/owner.d.ts +42 -0
  220. package/dist/core/hooks/resolve/owner.d.ts.map +1 -0
  221. package/dist/core/hooks/resolve/owner.js +140 -0
  222. package/dist/core/hooks/resolve/transcript.d.ts +26 -0
  223. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -0
  224. package/dist/core/hooks/resolve/transcript.js +73 -0
  225. package/dist/index.d.ts +15 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +13 -0
  228. package/dist/lib/agent-browser/client.d.ts +99 -0
  229. package/dist/lib/agent-browser/client.d.ts.map +1 -0
  230. package/dist/lib/agent-browser/client.js +177 -0
  231. package/dist/lib/agent-browser/index.d.ts +2 -0
  232. package/dist/lib/agent-browser/index.d.ts.map +1 -0
  233. package/dist/lib/agent-browser/index.js +1 -0
  234. package/dist/lib/browser/client.d.ts +193 -0
  235. package/dist/lib/browser/client.d.ts.map +1 -0
  236. package/dist/lib/browser/client.js +325 -0
  237. package/dist/lib/browser/dev-overlay.d.ts +23 -0
  238. package/dist/lib/browser/dev-overlay.d.ts.map +1 -0
  239. package/dist/lib/browser/dev-overlay.js +153 -0
  240. package/dist/lib/browser/index.d.ts +5 -0
  241. package/dist/lib/browser/index.d.ts.map +1 -0
  242. package/dist/lib/browser/index.js +2 -0
  243. package/dist/lib/browser/layout.d.ts +79 -0
  244. package/dist/lib/browser/layout.d.ts.map +1 -0
  245. package/dist/lib/browser/layout.js +220 -0
  246. package/dist/lib/browser/visibility.d.ts +86 -0
  247. package/dist/lib/browser/visibility.d.ts.map +1 -0
  248. package/dist/lib/browser/visibility.js +333 -0
  249. package/dist/lib/browser/visual-diff.d.ts +38 -0
  250. package/dist/lib/browser/visual-diff.d.ts.map +1 -0
  251. package/dist/lib/browser/visual-diff.js +107 -0
  252. package/dist/lib/completion/bash.d.ts +25 -0
  253. package/dist/lib/completion/bash.d.ts.map +1 -0
  254. package/dist/lib/completion/bash.js +284 -0
  255. package/dist/lib/completion/fish.d.ts +16 -0
  256. package/dist/lib/completion/fish.d.ts.map +1 -0
  257. package/dist/lib/completion/fish.js +118 -0
  258. package/dist/lib/completion/index.d.ts +5 -0
  259. package/dist/lib/completion/index.d.ts.map +1 -0
  260. package/dist/lib/completion/index.js +4 -0
  261. package/dist/lib/completion/walk.d.ts +68 -0
  262. package/dist/lib/completion/walk.d.ts.map +1 -0
  263. package/dist/lib/completion/walk.js +102 -0
  264. package/dist/lib/completion/zsh.d.ts +13 -0
  265. package/dist/lib/completion/zsh.d.ts.map +1 -0
  266. package/dist/lib/completion/zsh.js +249 -0
  267. package/dist/lib/context/index.d.ts +107 -0
  268. package/dist/lib/context/index.d.ts.map +1 -0
  269. package/dist/lib/context/index.js +275 -0
  270. package/dist/lib/cookies/client.d.ts +131 -0
  271. package/dist/lib/cookies/client.d.ts.map +1 -0
  272. package/dist/lib/cookies/client.js +239 -0
  273. package/dist/lib/cookies/index.d.ts +2 -0
  274. package/dist/lib/cookies/index.d.ts.map +1 -0
  275. package/dist/lib/cookies/index.js +1 -0
  276. package/dist/lib/council/index.d.ts +266 -0
  277. package/dist/lib/council/index.d.ts.map +1 -0
  278. package/dist/lib/council/index.js +674 -0
  279. package/dist/lib/docs-index.d.ts +28 -0
  280. package/dist/lib/docs-index.d.ts.map +1 -0
  281. package/dist/lib/docs-index.js +169 -0
  282. package/dist/lib/docs-lint.d.ts +26 -0
  283. package/dist/lib/docs-lint.d.ts.map +1 -0
  284. package/dist/lib/docs-lint.js +378 -0
  285. package/dist/lib/docs-sweep.d.ts +34 -0
  286. package/dist/lib/docs-sweep.d.ts.map +1 -0
  287. package/dist/lib/docs-sweep.js +304 -0
  288. package/dist/lib/docs.d.ts +27 -0
  289. package/dist/lib/docs.d.ts.map +1 -0
  290. package/dist/lib/docs.js +142 -0
  291. package/dist/lib/env.d.ts +11 -0
  292. package/dist/lib/env.d.ts.map +1 -0
  293. package/dist/lib/env.js +12 -0
  294. package/dist/lib/exec.d.ts +32 -0
  295. package/dist/lib/exec.d.ts.map +1 -0
  296. package/dist/lib/exec.js +54 -0
  297. package/dist/lib/format.d.ts +29 -0
  298. package/dist/lib/format.d.ts.map +1 -0
  299. package/dist/lib/format.js +139 -0
  300. package/dist/lib/http/client.d.ts +56 -0
  301. package/dist/lib/http/client.d.ts.map +1 -0
  302. package/dist/lib/http/client.js +160 -0
  303. package/dist/lib/http/index.d.ts +2 -0
  304. package/dist/lib/http/index.d.ts.map +1 -0
  305. package/dist/lib/http/index.js +1 -0
  306. package/dist/lib/identities/index.d.ts +77 -0
  307. package/dist/lib/identities/index.d.ts.map +1 -0
  308. package/dist/lib/identities/index.js +190 -0
  309. package/dist/lib/machine.d.ts +19 -0
  310. package/dist/lib/machine.d.ts.map +1 -0
  311. package/dist/lib/machine.js +61 -0
  312. package/dist/lib/presence.d.ts +48 -0
  313. package/dist/lib/presence.d.ts.map +1 -0
  314. package/dist/lib/presence.js +123 -0
  315. package/dist/lib/readability/client.d.ts +39 -0
  316. package/dist/lib/readability/client.d.ts.map +1 -0
  317. package/dist/lib/readability/client.js +121 -0
  318. package/dist/lib/readability/index.d.ts +2 -0
  319. package/dist/lib/readability/index.d.ts.map +1 -0
  320. package/dist/lib/readability/index.js +1 -0
  321. package/dist/lib/scratch/index.d.ts +74 -0
  322. package/dist/lib/scratch/index.d.ts.map +1 -0
  323. package/dist/lib/scratch/index.js +393 -0
  324. package/dist/lib/tunnel/gate.d.ts +12 -0
  325. package/dist/lib/tunnel/gate.d.ts.map +1 -0
  326. package/dist/lib/tunnel/gate.js +101 -0
  327. package/dist/lib/tunnel/state.d.ts +34 -0
  328. package/dist/lib/tunnel/state.d.ts.map +1 -0
  329. package/dist/lib/tunnel/state.js +132 -0
  330. package/package.json +160 -8
  331. package/schemas/.gitkeep +0 -0
  332. package/schemas/config.schema.json +109 -0
  333. package/src/cli.ts +22 -0
  334. package/src/commander.ts +242 -0
  335. package/src/commands/.gitkeep +0 -0
  336. package/src/commands/agents.ts +4567 -0
  337. package/src/commands/backup.ts +305 -0
  338. package/src/commands/browse-ai.ts +198 -0
  339. package/src/commands/browse.ts +849 -0
  340. package/src/commands/callers.ts +363 -0
  341. package/src/commands/completion.ts +193 -0
  342. package/src/commands/config-get.ts +161 -0
  343. package/src/commands/context.ts +209 -0
  344. package/src/commands/cookies.ts +198 -0
  345. package/src/commands/docs.ts +174 -0
  346. package/src/commands/doctor.ts +231 -0
  347. package/src/commands/edit-batch.ts +233 -0
  348. package/src/commands/eml.ts +519 -0
  349. package/src/commands/env.ts +254 -0
  350. package/src/commands/fetch.ts +136 -0
  351. package/src/commands/file-history.ts +202 -0
  352. package/src/commands/grep.ts +371 -0
  353. package/src/commands/init.ts +335 -0
  354. package/src/commands/outline.ts +583 -0
  355. package/src/commands/presence.ts +152 -0
  356. package/src/commands/read.ts +64 -0
  357. package/src/commands/scratch.ts +445 -0
  358. package/src/commands/session.ts +187 -0
  359. package/src/commands/sync.ts +306 -0
  360. package/src/commands/toc.ts +218 -0
  361. package/src/commands/tokens.ts +79 -0
  362. package/src/commands/tunnel.ts +633 -0
  363. package/src/commands/uninstall.ts +144 -0
  364. package/src/commands/web.ts +193 -0
  365. package/src/core/agents/canonical-emit.ts +77 -0
  366. package/src/core/agents/cli-emit.ts +64 -0
  367. package/src/core/agents/cli.ts +838 -0
  368. package/src/core/agents/codex-replay.ts +163 -0
  369. package/src/core/agents/coord-client.ts +249 -0
  370. package/src/core/agents/events/consume.ts +196 -0
  371. package/src/core/agents/events/emit.ts +108 -0
  372. package/src/core/agents/events/ulid.ts +51 -0
  373. package/src/core/agents/index.ts +14 -0
  374. package/src/core/agents/paths.ts +16 -0
  375. package/src/core/agents/render/prompt-context.ts +401 -0
  376. package/src/core/agents/render/session-context.ts +341 -0
  377. package/src/core/agents/rules/claim-conflict.ts +282 -0
  378. package/src/core/agents/rules/commit-conflict.ts +303 -0
  379. package/src/core/agents/rules/stop-hook.ts +229 -0
  380. package/src/core/agents/session-events.ts +228 -0
  381. package/src/core/agents/state/activity-log.ts +33 -0
  382. package/src/core/agents/state/council.ts +265 -0
  383. package/src/core/agents/state/heartbeat-projector.ts +488 -0
  384. package/src/core/agents/state/heartbeat-writer.ts +333 -0
  385. package/src/core/agents/state/names.ts +399 -0
  386. package/src/core/agents/state/pidmap.ts +38 -0
  387. package/src/core/agents/state/scratch.ts +121 -0
  388. package/src/core/agents/state/shell-mutation.ts +44 -0
  389. package/src/core/agents/state/stale-sweep.ts +190 -0
  390. package/src/core/config.ts +111 -0
  391. package/src/core/hooks/cli.ts +1247 -0
  392. package/src/core/hooks/effects/image-capture.ts +330 -0
  393. package/src/core/hooks/effects/index.ts +210 -0
  394. package/src/core/hooks/events/emit.ts +120 -0
  395. package/src/core/hooks/events/schema.ts +430 -0
  396. package/src/core/hooks/events/ulid.ts +51 -0
  397. package/src/core/hooks/harness/detect.ts +30 -0
  398. package/src/core/hooks/harness/events.ts +102 -0
  399. package/src/core/hooks/harness/output.ts +100 -0
  400. package/src/core/hooks/harness/parse.ts +180 -0
  401. package/src/core/hooks/index.ts +16 -0
  402. package/src/core/hooks/resolve/anchor.ts +51 -0
  403. package/src/core/hooks/resolve/coord-root.ts +25 -0
  404. package/src/core/hooks/resolve/intent.ts +89 -0
  405. package/src/core/hooks/resolve/owner.ts +140 -0
  406. package/src/core/hooks/resolve/transcript.ts +72 -0
  407. package/src/hooks/.gitkeep +0 -0
  408. package/src/index.ts +15 -0
  409. package/src/lib/agent-browser/client.ts +239 -0
  410. package/src/lib/agent-browser/index.ts +1 -0
  411. package/src/lib/browser/client.ts +449 -0
  412. package/src/lib/browser/dev-overlay.ts +207 -0
  413. package/src/lib/browser/index.ts +24 -0
  414. package/src/lib/browser/layout.ts +288 -0
  415. package/src/lib/browser/visibility.ts +419 -0
  416. package/src/lib/browser/visual-diff.ts +150 -0
  417. package/src/lib/completion/bash.ts +291 -0
  418. package/src/lib/completion/fish.ts +134 -0
  419. package/src/lib/completion/index.ts +10 -0
  420. package/src/lib/completion/walk.ts +184 -0
  421. package/src/lib/completion/zsh.ts +262 -0
  422. package/src/lib/context/index.ts +386 -0
  423. package/src/lib/cookies/client.ts +301 -0
  424. package/src/lib/cookies/index.ts +13 -0
  425. package/src/lib/council/index.ts +803 -0
  426. package/src/lib/docs-index.ts +216 -0
  427. package/src/lib/docs-lint.ts +413 -0
  428. package/src/lib/docs-sweep.ts +348 -0
  429. package/src/lib/docs.ts +199 -0
  430. package/src/lib/env.ts +12 -0
  431. package/src/lib/exec.ts +74 -0
  432. package/src/lib/format.ts +147 -0
  433. package/src/lib/http/client.ts +211 -0
  434. package/src/lib/http/index.ts +1 -0
  435. package/src/lib/identities/index.ts +210 -0
  436. package/src/lib/machine.ts +61 -0
  437. package/src/lib/presence.ts +154 -0
  438. package/src/lib/readability/client.ts +169 -0
  439. package/src/lib/readability/index.ts +5 -0
  440. package/src/lib/readability/turndown-plugin-gfm.d.ts +10 -0
  441. package/src/lib/scratch/index.ts +470 -0
  442. package/src/lib/tunnel/gate.ts +113 -0
  443. package/src/lib/tunnel/state.ts +167 -0
  444. package/src/web/.gitkeep +0 -0
  445. package/index.js +0 -1
@@ -0,0 +1,153 @@
1
+ // Capture Next.js dev-overlay issues (React errors + warnings) that don't
2
+ // route through console.error and therefore don't surface via Playwright's
3
+ // standard `page.on("console")` listener.
4
+ //
5
+ // Background: in Next.js 16 + React 19, hydration errors and most React
6
+ // dev warnings fire through React DOM's `onCaughtError` / `onUncaughtError`
7
+ // callbacks → next-devtools' `handleClientError` → a module-scoped
8
+ // `errorQueue`. They never hit `console.error`, so neither Playwright's
9
+ // CDP-backed listener nor an early init-script wrapper can see them. The
10
+ // only externally-observable surface is the dev overlay UI inside the
11
+ // `<nextjs-portal>` shadow root.
12
+ //
13
+ // Strategy: detect the shadow host, click the "Open issues overlay" badge
14
+ // when present, iterate the dialog's error pages via the prev/next buttons,
15
+ // scrape each error's kind + code + message + call-stack, then close the
16
+ // overlay. Returns `{ detected, issuesCount, errors }`.
17
+ /**
18
+ * Detect a Next.js dev overlay on the current page and, if present, capture
19
+ * every queued issue. Idempotent, safe to call multiple times. The function
20
+ * closes the overlay on exit; if it crashes partway, the overlay may be left
21
+ * open (subsequent calls will recover).
22
+ */
23
+ export async function captureDevOverlay(page, opts = {}) {
24
+ const stepDelay = opts.stepDelayMs ?? 400;
25
+ const detected = await page.evaluate(() => !!document.querySelector("nextjs-portal"));
26
+ if (!detected)
27
+ return { detected: false, issuesCount: 0, errors: [] };
28
+ // next-devtools dispatches errors to the overlay via queueMicrotask, so an
29
+ // error fired in the immediately-preceding evaluate() may not yet have
30
+ // propagated. Wait briefly for the badge to materialize before declaring
31
+ // "no issues", capped so the no-error path doesn't pay much.
32
+ await page
33
+ .waitForFunction(() => {
34
+ const portal = document.querySelector("nextjs-portal");
35
+ return !!portal?.shadowRoot?.querySelector("[data-issues-open]");
36
+ }, null, { timeout: 1_500 })
37
+ .catch(() => { });
38
+ // Click the issues-open badge if present. Its mere existence signals issues > 0;
39
+ // the badge's own text ("12 Issues" = index "1" + count "2 Issues" rendered side-
40
+ // by-side) isn't a reliable count. Authoritative total comes from the dialog's
41
+ // [data-nextjs-dialog-header-total-count] span after open.
42
+ const opened = await page.evaluate(() => {
43
+ const portal = document.querySelector("nextjs-portal");
44
+ const shadow = portal?.shadowRoot;
45
+ if (!shadow)
46
+ return false;
47
+ const btn = shadow.querySelector("[data-issues-open]");
48
+ if (!btn)
49
+ return false;
50
+ btn.click();
51
+ return true;
52
+ });
53
+ if (!opened) {
54
+ return { detected: true, issuesCount: 0, errors: [] };
55
+ }
56
+ // Wait for the dialog to render
57
+ await page.waitForFunction(() => {
58
+ const portal = document.querySelector("nextjs-portal");
59
+ const shadow = portal?.shadowRoot;
60
+ return !!shadow?.querySelector("[data-nextjs-dialog]");
61
+ }, null, { timeout: 4_000 });
62
+ // Read the canonical total from the dialog
63
+ const total = await page.evaluate(() => {
64
+ const portal = document.querySelector("nextjs-portal");
65
+ const shadow = portal?.shadowRoot;
66
+ const el = shadow?.querySelector("[data-nextjs-dialog-header-total-count]");
67
+ const n = Number((el?.textContent || "").trim());
68
+ return Number.isFinite(n) && n > 0 ? n : 1;
69
+ });
70
+ const raw = [];
71
+ const expected = total;
72
+ for (let i = 0; i < expected; i++) {
73
+ const error = await readCurrent(page);
74
+ if (error)
75
+ raw.push(error);
76
+ if (i < expected - 1) {
77
+ const advanced = await advance(page);
78
+ if (!advanced)
79
+ break;
80
+ await new Promise((r) => setTimeout(r, stepDelay));
81
+ }
82
+ }
83
+ // Close the overlay (click the collapse button or fall back to Esc)
84
+ await page.evaluate(() => {
85
+ const portal = document.querySelector("nextjs-portal");
86
+ const shadow = portal?.shadowRoot;
87
+ const close = shadow?.querySelector("[data-issues-collapse]");
88
+ if (close)
89
+ close.click();
90
+ });
91
+ await page.keyboard.press("Escape").catch(() => { });
92
+ const errors = raw.map((r, idx) => {
93
+ const message = stripPrefix(r.headerText, r.kind).trim();
94
+ return {
95
+ index: Number(r.index.replace(/[^0-9]/g, "")) || idx + 1,
96
+ kind: r.kind,
97
+ code: r.code,
98
+ message,
99
+ callStack: r.framesText,
100
+ };
101
+ });
102
+ return { detected: true, issuesCount: total, errors };
103
+ }
104
+ async function readCurrent(page) {
105
+ return page.evaluate(() => {
106
+ const portal = document.querySelector("nextjs-portal");
107
+ const shadow = portal?.shadowRoot;
108
+ if (!shadow)
109
+ return null;
110
+ const dialog = shadow.querySelector("[data-nextjs-dialog]");
111
+ if (!dialog)
112
+ return null;
113
+ const text = (el) => (el?.textContent || "").replace(/\s+/g, " ").trim();
114
+ const indexSpan = shadow.querySelector("[data-nextjs-dialog-error-index]");
115
+ const label = shadow.querySelector("[data-nextjs-error-label-group]");
116
+ const codeEl = shadow.querySelector("[data-nextjs-error-code]");
117
+ const header = shadow.querySelector("[data-nextjs-dialog-header]");
118
+ const body = shadow.querySelector("[data-nextjs-dialog-body]");
119
+ const frameEls = shadow.querySelectorAll("[data-nextjs-call-stack-frame]");
120
+ const frames = [];
121
+ for (const f of Array.from(frameEls)) {
122
+ const t = text(f);
123
+ if (t)
124
+ frames.push(t);
125
+ }
126
+ return {
127
+ index: text(indexSpan) || "1",
128
+ kind: text(label) || "Issue",
129
+ code: codeEl?.getAttribute("data-nextjs-error-code") ?? null,
130
+ headerText: text(header),
131
+ bodyText: text(body),
132
+ framesText: frames,
133
+ };
134
+ });
135
+ }
136
+ async function advance(page) {
137
+ return page.evaluate(() => {
138
+ const portal = document.querySelector("nextjs-portal");
139
+ const shadow = portal?.shadowRoot;
140
+ const next = shadow?.querySelector("[data-nextjs-dialog-error-next]");
141
+ if (!next || next.disabled)
142
+ return false;
143
+ next.click();
144
+ return true;
145
+ });
146
+ }
147
+ function stripPrefix(s, prefix) {
148
+ if (!prefix)
149
+ return s;
150
+ if (s.startsWith(prefix))
151
+ return s.slice(prefix.length);
152
+ return s;
153
+ }
@@ -0,0 +1,5 @@
1
+ export { Browser, type BrowserOptions, type ConsoleEvent, type Diagnostics, type FailedRequest, type NavigateResult, type PageErrorEvent, } from "./client.js";
2
+ export { captureDevOverlay, type DevOverlayError, type DevOverlayResult, } from "./dev-overlay.js";
3
+ export type { OverflowElement, OverflowResult, WidthResult, } from "./layout.js";
4
+ export type { CheckVisibilityOptions, VisibilityResult, VisibilitySample, } from "./visibility.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,eAAe,EACf,cAAc,EACd,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Browser, } from "./client.js";
2
+ export { captureDevOverlay, } from "./dev-overlay.js";
@@ -0,0 +1,79 @@
1
+ export interface WidthResult {
2
+ selector: string;
3
+ found: boolean;
4
+ rect: {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ };
10
+ parentTag: string;
11
+ parentWidth: number;
12
+ viewportWidth: number;
13
+ /** `rect.width / viewportWidth`, rounded to 3 decimals. */
14
+ viewportFill: number;
15
+ /** `rect.width / parentWidth`, rounded to 3 decimals. 0 when no parent. */
16
+ parentFill: number;
17
+ }
18
+ /** One element protruding past the viewport. */
19
+ export interface OverflowElement {
20
+ tagName: string;
21
+ className: string;
22
+ id: string;
23
+ rect: {
24
+ x: number;
25
+ y: number;
26
+ width: number;
27
+ height: number;
28
+ };
29
+ /** Pixels this element's own width exceeds the viewport. 0 if it fits. */
30
+ widthOverflowPx: number;
31
+ /** Pixels this element's right edge extends past the viewport's right. */
32
+ rightOverflowPx: number;
33
+ }
34
+ export interface OverflowResult {
35
+ viewport: {
36
+ width: number;
37
+ height: number;
38
+ };
39
+ documentScrollWidth: number;
40
+ hasHorizontalOverflow: boolean;
41
+ /** documentScrollWidth - viewport.width. Negative/zero means no overflow. */
42
+ overflowPx: number;
43
+ /** Top N elements whose own width exceeds viewport, ranked desc. */
44
+ widerThanViewport: OverflowElement[];
45
+ /** Top N elements whose right edge protrudes past viewport (but own width fits). */
46
+ rightOverflow: OverflowElement[];
47
+ }
48
+ /**
49
+ * Build the JS function passed to `page.evaluate`. Returns a function
50
+ * reference (not a string) so Playwright serializes args properly.
51
+ */
52
+ export declare function buildWidthCheck(): (args: {
53
+ selectors: string[];
54
+ }) => WidthResult[];
55
+ /**
56
+ * Build the JS function for overflow detection. Sweeps every element for
57
+ * widthOverflow (own width > viewport) and rightOverflow (right-edge past
58
+ * viewport). Caps each list at `sampleLimit` so a degenerate document with
59
+ * thousands of overflowing elements doesn't blow up the JSON envelope.
60
+ */
61
+ export declare function buildOverflowCheck(): (args: {
62
+ sampleLimit: number;
63
+ }) => OverflowResult;
64
+ /**
65
+ * Annotate width-check + overflow results onto the live page so the captured
66
+ * screenshot carries the diagnostic overlay. Separate root from
67
+ * visibility.ts's annotations so both can coexist on the same screenshot.
68
+ *
69
+ * Width targets: green if `viewportFill >= widthThreshold`, red otherwise.
70
+ * Overflow: dashed amber line at the viewport's right edge + amber boxes
71
+ * around each protruding element.
72
+ */
73
+ export declare function buildLayoutAnnotateScript(): (args: {
74
+ widths: WidthResult[];
75
+ overflow: OverflowResult | null;
76
+ widthThreshold: number;
77
+ }) => void;
78
+ export declare function buildClearLayoutAnnotationsScript(): () => void;
79
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/layout.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,oFAAoF;IACpF,aAAa,EAAE,eAAe,EAAE,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,KAAK,WAAW,EAAE,CA4ClF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,KAAK,cAAc,CAgEtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,IAAI,CAAC,IAAI,EAAE;IAClD,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;CACxB,KAAK,IAAI,CA6FT;AAED,wBAAgB,iCAAiC,IAAI,MAAM,IAAI,CAI9D"}
@@ -0,0 +1,220 @@
1
+ // Width-fill + horizontal-overflow check primitives for `harn browse`.
2
+ //
3
+ // Companions to visibility.ts: visibility catches "the element is hidden /
4
+ // occluded"; layout catches "the element is the wrong size or in the wrong
5
+ // place." The bug this exists to catch: a `<table>` at 85% viewport fill
6
+ // (correct CSS-visibility, no occlusion, where every existing per-element check
7
+ // passes), or a `<nav>` overflowing the viewport's right edge by 92px and
8
+ // triggering horizontal scroll on mobile.
9
+ //
10
+ // Two independent checks, surfaced together because they're often co-symptoms
11
+ // of the same root cause (stacked padding, missing flex-wrap, fixed widths
12
+ // that don't shrink on narrow viewports):
13
+ //
14
+ // --check-width <selector> assert one element's width is >= N% of viewport
15
+ // --check-overflow assert document has no horizontal overflow
16
+ /**
17
+ * Build the JS function passed to `page.evaluate`. Returns a function
18
+ * reference (not a string) so Playwright serializes args properly.
19
+ */
20
+ export function buildWidthCheck() {
21
+ return ({ selectors }) => {
22
+ const vw = window.innerWidth;
23
+ function checkOne(selector) {
24
+ const empty = {
25
+ selector,
26
+ found: false,
27
+ rect: { x: 0, y: 0, width: 0, height: 0 },
28
+ parentTag: "",
29
+ parentWidth: 0,
30
+ viewportWidth: vw,
31
+ viewportFill: 0,
32
+ parentFill: 0,
33
+ };
34
+ const el = document.querySelector(selector);
35
+ if (!(el instanceof Element))
36
+ return empty;
37
+ const rect = el.getBoundingClientRect();
38
+ const parent = el.parentElement;
39
+ const parentRect = parent ? parent.getBoundingClientRect() : null;
40
+ const viewportFill = vw > 0 ? rect.width / vw : 0;
41
+ const parentFill = parentRect && parentRect.width > 0 ? rect.width / parentRect.width : 0;
42
+ return {
43
+ selector,
44
+ found: true,
45
+ rect: {
46
+ x: Math.round(rect.x),
47
+ y: Math.round(rect.y),
48
+ width: Math.round(rect.width),
49
+ height: Math.round(rect.height),
50
+ },
51
+ parentTag: parent ? parent.tagName.toLowerCase() : "",
52
+ parentWidth: parentRect ? Math.round(parentRect.width) : 0,
53
+ viewportWidth: vw,
54
+ viewportFill: Math.round(viewportFill * 1000) / 1000,
55
+ parentFill: Math.round(parentFill * 1000) / 1000,
56
+ };
57
+ }
58
+ return selectors.map(checkOne);
59
+ };
60
+ }
61
+ /**
62
+ * Build the JS function for overflow detection. Sweeps every element for
63
+ * widthOverflow (own width > viewport) and rightOverflow (right-edge past
64
+ * viewport). Caps each list at `sampleLimit` so a degenerate document with
65
+ * thousands of overflowing elements doesn't blow up the JSON envelope.
66
+ */
67
+ export function buildOverflowCheck() {
68
+ return ({ sampleLimit }) => {
69
+ const vw = window.innerWidth;
70
+ const vh = window.innerHeight;
71
+ const docScrollW = document.documentElement.scrollWidth;
72
+ const tolerance = 1;
73
+ const widerThanViewport = [];
74
+ const rightOverflow = [];
75
+ const all = document.querySelectorAll("*");
76
+ for (let i = 0; i < all.length; i++) {
77
+ const el = all[i];
78
+ if (!el)
79
+ continue;
80
+ const r = el.getBoundingClientRect();
81
+ if (r.width <= 0 || r.height <= 0)
82
+ continue;
83
+ const widthOverflow = r.width - vw;
84
+ const rightOverflowPx = r.right - vw;
85
+ const cls = typeof el.className === "string"
86
+ ? el.className.slice(0, 200)
87
+ : (el.getAttribute("class") ?? "");
88
+ const rect = {
89
+ x: Math.round(r.x),
90
+ y: Math.round(r.y),
91
+ width: Math.round(r.width),
92
+ height: Math.round(r.height),
93
+ };
94
+ if (widthOverflow > tolerance) {
95
+ widerThanViewport.push({
96
+ tagName: el.tagName.toLowerCase(),
97
+ className: cls,
98
+ id: el.id || "",
99
+ rect,
100
+ widthOverflowPx: Math.round(widthOverflow),
101
+ rightOverflowPx: Math.max(0, Math.round(rightOverflowPx)),
102
+ });
103
+ }
104
+ else if (rightOverflowPx > tolerance) {
105
+ rightOverflow.push({
106
+ tagName: el.tagName.toLowerCase(),
107
+ className: cls,
108
+ id: el.id || "",
109
+ rect,
110
+ widthOverflowPx: 0,
111
+ rightOverflowPx: Math.round(rightOverflowPx),
112
+ });
113
+ }
114
+ }
115
+ widerThanViewport.sort((a, b) => b.widthOverflowPx - a.widthOverflowPx);
116
+ rightOverflow.sort((a, b) => b.rightOverflowPx - a.rightOverflowPx);
117
+ return {
118
+ viewport: { width: vw, height: vh },
119
+ documentScrollWidth: docScrollW,
120
+ hasHorizontalOverflow: docScrollW > vw + tolerance,
121
+ overflowPx: docScrollW - vw,
122
+ widerThanViewport: widerThanViewport.slice(0, sampleLimit),
123
+ rightOverflow: rightOverflow.slice(0, sampleLimit),
124
+ };
125
+ };
126
+ }
127
+ /**
128
+ * Annotate width-check + overflow results onto the live page so the captured
129
+ * screenshot carries the diagnostic overlay. Separate root from
130
+ * visibility.ts's annotations so both can coexist on the same screenshot.
131
+ *
132
+ * Width targets: green if `viewportFill >= widthThreshold`, red otherwise.
133
+ * Overflow: dashed amber line at the viewport's right edge + amber boxes
134
+ * around each protruding element.
135
+ */
136
+ export function buildLayoutAnnotateScript() {
137
+ return ({ widths, overflow, widthThreshold }) => {
138
+ const ROOT_ID = "__bp-check-layout-annotations__";
139
+ document.getElementById(ROOT_ID)?.remove();
140
+ const root = document.createElement("div");
141
+ root.id = ROOT_ID;
142
+ root.style.cssText = "position: fixed; inset: 0; pointer-events: none; z-index: 2147483646;";
143
+ document.body.appendChild(root);
144
+ const drawBox = (rect, color, label, labelPos = "top") => {
145
+ const box = document.createElement("div");
146
+ box.style.cssText = `
147
+ position: absolute;
148
+ left: ${rect.x}px;
149
+ top: ${rect.y}px;
150
+ width: ${rect.width}px;
151
+ height: ${rect.height}px;
152
+ border: 2px solid ${color};
153
+ box-sizing: border-box;
154
+ background: ${color}1a;
155
+ pointer-events: none;
156
+ `;
157
+ const tag = document.createElement("div");
158
+ tag.textContent = label;
159
+ tag.style.cssText = `
160
+ position: absolute;
161
+ left: 0;
162
+ ${labelPos === "top" ? "top: -18px;" : "bottom: -18px;"}
163
+ background: ${color};
164
+ color: #fff;
165
+ font: 12px/1.4 -apple-system, system-ui, sans-serif;
166
+ padding: 1px 6px;
167
+ border-radius: 3px;
168
+ white-space: nowrap;
169
+ pointer-events: none;
170
+ `;
171
+ box.appendChild(tag);
172
+ root.appendChild(box);
173
+ };
174
+ for (const w of widths) {
175
+ if (!w.found)
176
+ continue;
177
+ const passing = w.viewportFill >= widthThreshold;
178
+ drawBox(w.rect, passing ? "#10b981" : "#ef4444", `${w.selector} vfill=${(w.viewportFill * 100).toFixed(0)}%`);
179
+ }
180
+ if (overflow) {
181
+ const edgeLine = document.createElement("div");
182
+ edgeLine.style.cssText = `
183
+ position: absolute;
184
+ left: ${overflow.viewport.width}px;
185
+ top: 0;
186
+ width: 0;
187
+ height: 100vh;
188
+ border-left: 2px dashed #f59e0b;
189
+ pointer-events: none;
190
+ `;
191
+ root.appendChild(edgeLine);
192
+ const edgeLabel = document.createElement("div");
193
+ edgeLabel.textContent = `viewport edge (${overflow.viewport.width}px)`;
194
+ edgeLabel.style.cssText = `
195
+ position: absolute;
196
+ left: ${overflow.viewport.width + 4}px;
197
+ top: 4px;
198
+ background: #f59e0b;
199
+ color: #fff;
200
+ font: 12px/1.4 -apple-system, system-ui, sans-serif;
201
+ padding: 1px 6px;
202
+ border-radius: 3px;
203
+ white-space: nowrap;
204
+ pointer-events: none;
205
+ `;
206
+ root.appendChild(edgeLabel);
207
+ for (const o of [...overflow.widerThanViewport, ...overflow.rightOverflow]) {
208
+ const label = o.widthOverflowPx > 0
209
+ ? `${o.tagName} +${o.widthOverflowPx}px wider than viewport`
210
+ : `${o.tagName} +${o.rightOverflowPx}px past right edge`;
211
+ drawBox(o.rect, "#f59e0b", label, "bottom");
212
+ }
213
+ }
214
+ };
215
+ }
216
+ export function buildClearLayoutAnnotationsScript() {
217
+ return () => {
218
+ document.getElementById("__bp-check-layout-annotations__")?.remove();
219
+ };
220
+ }
@@ -0,0 +1,86 @@
1
+ export interface VisibilitySample {
2
+ x: number;
3
+ y: number;
4
+ status: "visible" | "occluded" | "outside-viewport" | "no-element";
5
+ occluderTag?: string;
6
+ occluderClass?: string;
7
+ }
8
+ export type CssHiddenReason = "display-none" | "visibility-hidden" | "opacity-zero" | "content-visibility-hidden" | "pointer-events-none-with-opacity-zero" | "unknown";
9
+ export interface VisibilityResult {
10
+ selector: string;
11
+ found: boolean;
12
+ inViewport: boolean;
13
+ partiallyInViewport: boolean;
14
+ rect: {
15
+ x: number;
16
+ y: number;
17
+ width: number;
18
+ height: number;
19
+ };
20
+ /**
21
+ * Pure CSS-level visibility via `Element.checkVisibility()` with opacity +
22
+ * visibility + content-visibility checks enabled, plus an ancestor walk
23
+ * to identify which property is hiding the element. Catches the class of
24
+ * bug where an element is in layout (has rect, is in viewport) but is
25
+ * painted with opacity:0 / visibility:hidden / display:none: invisible
26
+ * to the user but invisible to occlusion-only checks too.
27
+ */
28
+ cssVisible: boolean;
29
+ /** Which property+element actually hides it. Null when cssVisible=true. */
30
+ hiddenBy: {
31
+ reason: CssHiddenReason;
32
+ ancestorTag: string;
33
+ ancestorClass: string;
34
+ ancestorId: string;
35
+ propertyValue: string;
36
+ } | null;
37
+ /** Fraction of in-viewport samples where the target (or a descendant) was the topmost element. 0 = fully occluded, 1 = fully visible. Always 0 when cssVisible=false (sampling skipped). */
38
+ visibleRatio: number;
39
+ samples: VisibilitySample[];
40
+ occludedBy: {
41
+ tagName: string;
42
+ className: string;
43
+ id: string;
44
+ outerHTML: string;
45
+ computedStyles: {
46
+ position?: string;
47
+ zIndex?: string;
48
+ backgroundColor?: string;
49
+ };
50
+ rect: {
51
+ x: number;
52
+ y: number;
53
+ width: number;
54
+ height: number;
55
+ };
56
+ } | null;
57
+ elementOuterHTML: string;
58
+ }
59
+ export interface CheckVisibilityOptions {
60
+ /** Grid size: sampleGrid × sampleGrid points across the target's rect. Default 3 (9 samples). */
61
+ sampleGrid?: number;
62
+ }
63
+ /**
64
+ * Build the JS function passed to Playwright's `page.evaluate`. Returns
65
+ * a function reference (not a string) so Playwright serializes args
66
+ * properly. Caller invokes via `page.evaluate(fn, { selectors, sampleGrid })`.
67
+ */
68
+ export declare function buildVisibilityCheck(): (args: {
69
+ selectors: string[];
70
+ sampleGrid: number;
71
+ }) => VisibilityResult[];
72
+ /**
73
+ * Build a script that injects overlay rectangles for each visibility result.
74
+ * Green border = target, red border = dominant occluder. Caller injects via
75
+ * `page.evaluate`, takes the screenshot, then clears via
76
+ * `buildClearAnnotationsScript`. Boxes are absolutely positioned at the
77
+ * target/occluder's `getBoundingClientRect()` location, so they survive
78
+ * scroll changes between sample-time and screenshot-time only if nothing
79
+ * has scrolled. The recommended flow is sample → annotate → screenshot →
80
+ * clear without intervening scrolls.
81
+ */
82
+ export declare function buildAnnotateScript(): (args: {
83
+ results: VisibilityResult[];
84
+ }) => void;
85
+ export declare function buildClearAnnotationsScript(): () => void;
86
+ //# sourceMappingURL=visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/visibility.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,kBAAkB,GAAG,YAAY,CAAC;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,mBAAmB,GACnB,cAAc,GACd,2BAA2B,GAC3B,uCAAuC,GACvC,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D;;;;;;;OAOG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,EAAE;QACR,MAAM,EAAE,eAAe,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACvB,GAAG,IAAI,CAAC;IACT,4LAA4L;IAC5L,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,eAAe,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACjF,IAAI,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;KAC/D,GAAG,IAAI,CAAC;IACT,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,CAAC,IAAI,EAAE;IAC7C,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,KAAK,gBAAgB,EAAE,CAqPvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,gBAAgB,EAAE,CAAA;CAAE,KAAK,IAAI,CA2ErF;AAED,wBAAgB,2BAA2B,IAAI,MAAM,IAAI,CAIxD"}