@agentwhy/cli 0.0.0-dev

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 (400) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +181 -0
  4. package/THIRD_PARTY_NOTICES.md +31 -0
  5. package/dist/adapter/claude-code/contract/assumptions.js +208 -0
  6. package/dist/adapter/claude-code/contract/denials.js +15 -0
  7. package/dist/adapter/claude-code/contract/entry-points.js +15 -0
  8. package/dist/adapter/claude-code/contract/fields.js +73 -0
  9. package/dist/adapter/claude-code/contract/hooks.js +96 -0
  10. package/dist/adapter/claude-code/contract/identifiers.js +15 -0
  11. package/dist/adapter/claude-code/contract/layout.js +29 -0
  12. package/dist/adapter/claude-code/contract/line-types.js +26 -0
  13. package/dist/adapter/claude-code/contract/projects.js +23 -0
  14. package/dist/adapter/claude-code/contract/recognition.js +14 -0
  15. package/dist/adapter/claude-code/contract/session-title.js +20 -0
  16. package/dist/adapter/claude-code/contract/settings.js +27 -0
  17. package/dist/adapter/claude-code/contract/task-notifications.js +33 -0
  18. package/dist/adapter/claude-code/contract/tools.js +37 -0
  19. package/dist/adapter/claude-code/contract/version.js +51 -0
  20. package/dist/adapter/claude-code/discovery/claude-code-project-catalogue.js +129 -0
  21. package/dist/adapter/claude-code/discovery/claude-code-recognition.js +46 -0
  22. package/dist/adapter/claude-code/discovery/claude-code-session-catalogue.js +100 -0
  23. package/dist/adapter/claude-code/discovery/claude-code-session-discovery.js +61 -0
  24. package/dist/adapter/claude-code/discovery/claude-code-session-titles.js +28 -0
  25. package/dist/adapter/claude-code/discovery/discovered-session.js +1 -0
  26. package/dist/adapter/claude-code/discovery/session-entries.js +41 -0
  27. package/dist/adapter/claude-code/discovery/transcript-tail.js +81 -0
  28. package/dist/adapter/claude-code/events/claude-code-session-source.js +197 -0
  29. package/dist/adapter/claude-code/events/delegation-index.js +15 -0
  30. package/dist/adapter/claude-code/events/transcript-scan.js +179 -0
  31. package/dist/adapter/claude-code/hooks/hook-output.js +42 -0
  32. package/dist/adapter/claude-code/hooks/pre-tool-use-input.js +23 -0
  33. package/dist/adapter/claude-code/hooks/stop-input.js +25 -0
  34. package/dist/adapter/claude-code/hooks/subagent-stop-input.js +26 -0
  35. package/dist/adapter/claude-code/policy/deny-rules.js +46 -0
  36. package/dist/adapter/claude-code/probe/claude-code-probe.js +86 -0
  37. package/dist/adapter/claude-code/probe/collectors/agent-tool-input-key-collector.js +24 -0
  38. package/dist/adapter/claude-code/probe/collectors/denial-kind-collector.js +20 -0
  39. package/dist/adapter/claude-code/probe/collectors/denied-call-collector.js +11 -0
  40. package/dist/adapter/claude-code/probe/collectors/identifier-collector.js +19 -0
  41. package/dist/adapter/claude-code/probe/collectors/line-collector.js +1 -0
  42. package/dist/adapter/claude-code/probe/collectors/line-type-collector.js +29 -0
  43. package/dist/adapter/claude-code/probe/collectors/sidechain-collector.js +20 -0
  44. package/dist/adapter/claude-code/probe/collectors/task-notification-collector.js +51 -0
  45. package/dist/adapter/claude-code/probe/collectors/tool-result-reference-collector.js +31 -0
  46. package/dist/adapter/claude-code/probe/collectors/tool-use-collector.js +24 -0
  47. package/dist/adapter/claude-code/probe/collectors/tool-version-collector.js +14 -0
  48. package/dist/adapter/claude-code/probe/collectors/top-level-key-collector.js +14 -0
  49. package/dist/adapter/claude-code/probe/collectors/unknown-line-type-key-collector.js +24 -0
  50. package/dist/adapter/claude-code/probe/collectors/working-directory-collector.js +28 -0
  51. package/dist/adapter/claude-code/probe/doctor-report.js +1 -0
  52. package/dist/adapter/claude-code/probe/meta-tally.js +42 -0
  53. package/dist/adapter/claude-code/probe/session-probe.js +1 -0
  54. package/dist/adapter/claude-code/probe/session-tally.js +41 -0
  55. package/dist/adapter/claude-code/probe/transcript-line.js +30 -0
  56. package/dist/adapter/claude-code/probe/transcript-tally.js +74 -0
  57. package/dist/adapter/claude-code/settings/deny-entries.js +65 -0
  58. package/dist/adapter/claude-code/settings/hook-entries.js +303 -0
  59. package/dist/adapter/codex/contract/actions.js +69 -0
  60. package/dist/adapter/codex/contract/assumptions.js +55 -0
  61. package/dist/adapter/codex/contract/capabilities.js +24 -0
  62. package/dist/adapter/codex/contract/delegations.js +30 -0
  63. package/dist/adapter/codex/contract/deliveries.js +25 -0
  64. package/dist/adapter/codex/contract/envelope.js +33 -0
  65. package/dist/adapter/codex/contract/hook-refusals.js +14 -0
  66. package/dist/adapter/codex/contract/hooks.js +46 -0
  67. package/dist/adapter/codex/contract/messages.js +47 -0
  68. package/dist/adapter/codex/contract/permissions.js +27 -0
  69. package/dist/adapter/codex/contract/reviews.js +13 -0
  70. package/dist/adapter/codex/contract/session.js +31 -0
  71. package/dist/adapter/codex/contract/turns.js +23 -0
  72. package/dist/adapter/codex/contract/version.js +15 -0
  73. package/dist/adapter/codex/discovery/codex-conversations.js +44 -0
  74. package/dist/adapter/codex/discovery/codex-project-catalogue.js +49 -0
  75. package/dist/adapter/codex/discovery/codex-recognition.js +20 -0
  76. package/dist/adapter/codex/discovery/codex-session-catalogue.js +28 -0
  77. package/dist/adapter/codex/discovery/codex-session-discovery.js +115 -0
  78. package/dist/adapter/codex/discovery/codex-session-index.js +21 -0
  79. package/dist/adapter/codex/discovery/codex-session-titles.js +135 -0
  80. package/dist/adapter/codex/discovery/session-header.js +86 -0
  81. package/dist/adapter/codex/discovery/session-owners.js +11 -0
  82. package/dist/adapter/codex/discovery/session-roots.js +49 -0
  83. package/dist/adapter/codex/events/action-items.js +138 -0
  84. package/dist/adapter/codex/events/capability-records.js +26 -0
  85. package/dist/adapter/codex/events/codex-session-source.js +121 -0
  86. package/dist/adapter/codex/events/hook-refusals.js +27 -0
  87. package/dist/adapter/codex/events/rollout-scan.js +614 -0
  88. package/dist/adapter/codex/events/turn-permissions.js +60 -0
  89. package/dist/adapter/codex/hooks/pre-tool-use-input.js +24 -0
  90. package/dist/adapter/codex/hooks/stop-refusals.js +57 -0
  91. package/dist/adapter/codex/probe/codex-doctor-report.js +1 -0
  92. package/dist/adapter/codex/probe/codex-probe.js +268 -0
  93. package/dist/adapter/codex/settings/codex-hooks.js +83 -0
  94. package/dist/cli/cli-command.js +1 -0
  95. package/dist/cli/command-router.js +42 -0
  96. package/dist/cli/commands/check-cli-command.js +128 -0
  97. package/dist/cli/commands/codex-stop-cli-command.js +27 -0
  98. package/dist/cli/commands/doctor-arguments.js +32 -0
  99. package/dist/cli/commands/doctor-cli-command.js +31 -0
  100. package/dist/cli/commands/doctor-usage.js +14 -0
  101. package/dist/cli/commands/init-cli-command.js +123 -0
  102. package/dist/cli/commands/menu-cli-command.js +58 -0
  103. package/dist/cli/commands/notify-cli-command.js +158 -0
  104. package/dist/cli/commands/refuse-cli-command.js +79 -0
  105. package/dist/cli/commands/report-arguments.js +62 -0
  106. package/dist/cli/commands/report-cli-command.js +36 -0
  107. package/dist/cli/commands/report-usage.js +41 -0
  108. package/dist/cli/commands/sessions-cli-command.js +184 -0
  109. package/dist/cli/commands/start-cli-command.js +83 -0
  110. package/dist/cli/commands/watch-cli-command.js +142 -0
  111. package/dist/cli/exit-codes.js +13 -0
  112. package/dist/cli/project-switches.js +46 -0
  113. package/dist/cli.js +80 -0
  114. package/dist/composition-root.js +411 -0
  115. package/dist/core/access/command-line.js +454 -0
  116. package/dist/core/access/listing.js +58 -0
  117. package/dist/core/access/path-shape.js +59 -0
  118. package/dist/core/access/path-tokens.js +37 -0
  119. package/dist/core/access/protected-access.js +310 -0
  120. package/dist/core/access/protected-values.js +102 -0
  121. package/dist/core/access/recorded-effect.js +67 -0
  122. package/dist/core/access/returns.js +173 -0
  123. package/dist/core/access/search-output.js +34 -0
  124. package/dist/core/access/search-reach.js +268 -0
  125. package/dist/core/access/uses.js +143 -0
  126. package/dist/core/access/words-before.js +52 -0
  127. package/dist/core/agent.js +1 -0
  128. package/dist/core/capability.js +11 -0
  129. package/dist/core/combined-project-catalogue.js +36 -0
  130. package/dist/core/combined-session-catalogue.js +25 -0
  131. package/dist/core/completeness.js +10 -0
  132. package/dist/core/context.js +1 -0
  133. package/dist/core/correlation/correlate.js +329 -0
  134. package/dist/core/delegation.js +1 -0
  135. package/dist/core/detector/prompt-signals.js +78 -0
  136. package/dist/core/entry-point.js +1 -0
  137. package/dist/core/event.js +1 -0
  138. package/dist/core/evidence.js +9 -0
  139. package/dist/core/late-message.js +32 -0
  140. package/dist/core/message.js +1 -0
  141. package/dist/core/nearest-project.js +28 -0
  142. package/dist/core/policy/default-policy.js +32 -0
  143. package/dist/core/policy/glob.js +56 -0
  144. package/dist/core/policy/parse-policy.js +70 -0
  145. package/dist/core/policy/policy.js +20 -0
  146. package/dist/core/policy/resolve-policy.js +16 -0
  147. package/dist/core/project-catalogue.js +1 -0
  148. package/dist/core/project-root.js +133 -0
  149. package/dist/core/provider-titles.js +10 -0
  150. package/dist/core/redaction/entropy.js +46 -0
  151. package/dist/core/redaction/redacted.js +4 -0
  152. package/dist/core/redaction/redactor.js +339 -0
  153. package/dist/core/redaction/secret-patterns.js +22 -0
  154. package/dist/core/redaction/value-shapes.js +81 -0
  155. package/dist/core/redaction/value-trace.js +110 -0
  156. package/dist/core/review.js +1 -0
  157. package/dist/core/session-catalogue.js +19 -0
  158. package/dist/core/session-filter.js +42 -0
  159. package/dist/core/session-format.js +27 -0
  160. package/dist/core/session-model.js +1 -0
  161. package/dist/core/session-reader.js +22 -0
  162. package/dist/core/session-records.js +1 -0
  163. package/dist/core/session-source.js +1 -0
  164. package/dist/core/session-titles.js +21 -0
  165. package/dist/core/turn.js +1 -0
  166. package/dist/doctor/codex-doctor.js +14 -0
  167. package/dist/doctor/doctor-use-case.js +1 -0
  168. package/dist/doctor/format-selecting-doctor.js +28 -0
  169. package/dist/doctor/render/attention/attention-rule.js +1 -0
  170. package/dist/doctor/render/attention/doctor-attention-rules.js +28 -0
  171. package/dist/doctor/render/attention/rules/damaged-meta-files-rule.js +8 -0
  172. package/dist/doctor/render/attention/rules/damaged-transcripts-rule.js +9 -0
  173. package/dist/doctor/render/attention/rules/incomplete-subagent-pairs-rule.js +6 -0
  174. package/dist/doctor/render/attention/rules/missing-main-transcript-rule.js +5 -0
  175. package/dist/doctor/render/attention/rules/missing-spilled-results-rule.js +6 -0
  176. package/dist/doctor/render/attention/rules/multiple-working-directories-rule.js +15 -0
  177. package/dist/doctor/render/attention/rules/unknown-denial-kinds-rule.js +5 -0
  178. package/dist/doctor/render/attention/rules/unknown-line-types-rule.js +6 -0
  179. package/dist/doctor/render/attention/rules/unrecognised-entries-rule.js +6 -0
  180. package/dist/doctor/render/attention/rules/unusable-directories-rule.js +12 -0
  181. package/dist/doctor/render/attention/transcript-scopes.js +6 -0
  182. package/dist/doctor/render/codex-text-doctor-renderer.js +97 -0
  183. package/dist/doctor/render/doctor-renderers.js +1 -0
  184. package/dist/doctor/render/json-doctor-renderer.js +7 -0
  185. package/dist/doctor/render/output-format.js +1 -0
  186. package/dist/doctor/render/text-doctor-renderer.js +124 -0
  187. package/dist/doctor/session-doctor.js +16 -0
  188. package/dist/infrastructure/clack-asker.js +24 -0
  189. package/dist/infrastructure/clack-chooser.js +40 -0
  190. package/dist/infrastructure/clack-multi-chooser.js +31 -0
  191. package/dist/infrastructure/file-alert-store.js +120 -0
  192. package/dist/infrastructure/file-checked-store.js +55 -0
  193. package/dist/infrastructure/file-mark-store.js +67 -0
  194. package/dist/infrastructure/file-onboarding-store.js +93 -0
  195. package/dist/infrastructure/folder-window.js +26 -0
  196. package/dist/infrastructure/node-agentwhy-invocation.js +99 -0
  197. package/dist/infrastructure/node-browser.js +40 -0
  198. package/dist/infrastructure/node-file-system.js +111 -0
  199. package/dist/infrastructure/node-local-server.js +115 -0
  200. package/dist/infrastructure/node-text-input.js +26 -0
  201. package/dist/infrastructure/os-notifier.js +21 -0
  202. package/dist/infrastructure/osascript-folder-chooser.js +53 -0
  203. package/dist/infrastructure/powershell-folder-chooser.js +78 -0
  204. package/dist/infrastructure/real-directory.js +18 -0
  205. package/dist/infrastructure/stream-printer.js +11 -0
  206. package/dist/infrastructure/terminal-banner.js +19 -0
  207. package/dist/ports/agentwhy-invocation.js +1 -0
  208. package/dist/ports/alert-store.js +1 -0
  209. package/dist/ports/asker.js +1 -0
  210. package/dist/ports/browser.js +1 -0
  211. package/dist/ports/checked-store.js +1 -0
  212. package/dist/ports/chooser.js +1 -0
  213. package/dist/ports/directory-reader.js +1 -0
  214. package/dist/ports/file-access-error.js +11 -0
  215. package/dist/ports/file-reader.js +1 -0
  216. package/dist/ports/file-tail-reader.js +1 -0
  217. package/dist/ports/file-writer.js +1 -0
  218. package/dist/ports/folder-chooser.js +1 -0
  219. package/dist/ports/local-server.js +1 -0
  220. package/dist/ports/mark-store.js +10 -0
  221. package/dist/ports/multi-chooser.js +1 -0
  222. package/dist/ports/notifier.js +1 -0
  223. package/dist/ports/onboarding-store.js +1 -0
  224. package/dist/ports/printer.js +1 -0
  225. package/dist/ports/text-input.js +1 -0
  226. package/dist/refuse/command-reach.js +135 -0
  227. package/dist/refuse/command-refusal.js +88 -0
  228. package/dist/refuse/render/codex-stop-words.js +21 -0
  229. package/dist/refuse/render/refusal-words.js +34 -0
  230. package/dist/report/build-report.js +1037 -0
  231. package/dist/report/check/actions-digest.js +73 -0
  232. package/dist/report/check/check-lines.js +83 -0
  233. package/dist/report/check/mark-request.js +90 -0
  234. package/dist/report/check/marks.js +84 -0
  235. package/dist/report/check/render/text-digest-renderer.js +332 -0
  236. package/dist/report/check/session-actions.js +90 -0
  237. package/dist/report/check/session-check.js +91 -0
  238. package/dist/report/choose-policy.js +61 -0
  239. package/dist/report/flow-reads.js +18 -0
  240. package/dist/report/private-files/tell-lists.js +84 -0
  241. package/dist/report/project-rules.js +33 -0
  242. package/dist/report/render/home-relative.js +18 -0
  243. package/dist/report/render/html-head.js +26 -0
  244. package/dist/report/render/html-report-components.js +48 -0
  245. package/dist/report/render/html-report-styles.js +500 -0
  246. package/dist/report/render/html-sidebar.js +42 -0
  247. package/dist/report/render/path-tail.js +14 -0
  248. package/dist/report/render/report-copy.js +1177 -0
  249. package/dist/report/render/report-page/advanced-view.js +167 -0
  250. package/dist/report/render/report-page/file-story.js +114 -0
  251. package/dist/report/render/report-page/files-view.js +381 -0
  252. package/dist/report/render/report-page/files.js +77 -0
  253. package/dist/report/render/report-page/fix-wizard-script.js +269 -0
  254. package/dist/report/render/report-page/fix-wizard.js +310 -0
  255. package/dist/report/render/report-page/helpers-view.js +383 -0
  256. package/dist/report/render/report-page/helpers.js +51 -0
  257. package/dist/report/render/report-page/item-names.js +63 -0
  258. package/dist/report/render/report-page/protect-patterns.js +13 -0
  259. package/dist/report/render/report-page/providers.js +208 -0
  260. package/dist/report/render/report-page/record-view.js +367 -0
  261. package/dist/report/render/report-page/report-page-renderer.js +129 -0
  262. package/dist/report/render/report-page/report-views.js +31 -0
  263. package/dist/report/render/report-page/story-window.js +356 -0
  264. package/dist/report/render/report-page/times.js +25 -0
  265. package/dist/report/render/report-page/to-do-view.js +299 -0
  266. package/dist/report/render/report-page/to-do.js +152 -0
  267. package/dist/report/render/report-page.js +1 -0
  268. package/dist/report/render/session-view.js +250 -0
  269. package/dist/report/render/text-report-renderer.js +981 -0
  270. package/dist/report/render/ui/add-file-popup.js +122 -0
  271. package/dist/report/render/ui/app-sidebar.js +78 -0
  272. package/dist/report/render/ui/ask-panel.js +80 -0
  273. package/dist/report/render/ui/avatar.js +19 -0
  274. package/dist/report/render/ui/backdrop.js +29 -0
  275. package/dist/report/render/ui/brand-mark.js +4 -0
  276. package/dist/report/render/ui/button.js +67 -0
  277. package/dist/report/render/ui/callout.js +20 -0
  278. package/dist/report/render/ui/chat-example.js +17 -0
  279. package/dist/report/render/ui/checklist.js +50 -0
  280. package/dist/report/render/ui/confirm-dialog.js +34 -0
  281. package/dist/report/render/ui/data-table.js +49 -0
  282. package/dist/report/render/ui/drawer.js +25 -0
  283. package/dist/report/render/ui/file-chip.js +37 -0
  284. package/dist/report/render/ui/fold-line.js +22 -0
  285. package/dist/report/render/ui/guide-card.js +25 -0
  286. package/dist/report/render/ui/hero.js +37 -0
  287. package/dist/report/render/ui/labelled-select.js +10 -0
  288. package/dist/report/render/ui/live-script.js +222 -0
  289. package/dist/report/render/ui/local-date.js +58 -0
  290. package/dist/report/render/ui/mode-icon.js +6 -0
  291. package/dist/report/render/ui/page-shell.js +101 -0
  292. package/dist/report/render/ui/pill-tabs.js +47 -0
  293. package/dist/report/render/ui/popup.js +77 -0
  294. package/dist/report/render/ui/progress.js +35 -0
  295. package/dist/report/render/ui/project-list.js +284 -0
  296. package/dist/report/render/ui/scope-choice.js +19 -0
  297. package/dist/report/render/ui/stats.js +21 -0
  298. package/dist/report/render/ui/status-icon.js +30 -0
  299. package/dist/report/render/ui/status-look.js +21 -0
  300. package/dist/report/render/ui/switch.js +11 -0
  301. package/dist/report/render/ui/tag.js +19 -0
  302. package/dist/report/render/ui/task-list.js +56 -0
  303. package/dist/report/render/ui/tokens.js +46 -0
  304. package/dist/report/render/ui/update-notice.js +146 -0
  305. package/dist/report/render/ui/words/app-words.js +163 -0
  306. package/dist/report/render/ui/words/conversations-words.js +457 -0
  307. package/dist/report/render/ui/words/month-words.js +59 -0
  308. package/dist/report/render/ui/words/onboarding-words.js +446 -0
  309. package/dist/report/render/ui/words/projects-words.js +217 -0
  310. package/dist/report/render/ui/words/report-words.js +2099 -0
  311. package/dist/report/render/ui/words/settings-words.js +611 -0
  312. package/dist/report/render/ui/words/to-fix-words.js +287 -0
  313. package/dist/report/report-model.js +2 -0
  314. package/dist/report/report-use-case.js +1 -0
  315. package/dist/report/rule-names.js +20 -0
  316. package/dist/report/session-report.js +163 -0
  317. package/dist/report/start/app-nav.js +37 -0
  318. package/dist/report/start/conversations/calendar-view.js +104 -0
  319. package/dist/report/start/conversations/conversation-columns.js +187 -0
  320. package/dist/report/start/conversations/conversations-renderer.js +54 -0
  321. package/dist/report/start/conversations/empty-week.js +120 -0
  322. package/dist/report/start/conversations/files-window.js +166 -0
  323. package/dist/report/start/conversations/period-section.js +280 -0
  324. package/dist/report/start/conversations/periods-script.js +440 -0
  325. package/dist/report/start/conversations/periods.js +110 -0
  326. package/dist/report/start/conversations/week-view.js +30 -0
  327. package/dist/report/start/conversations/weeks.js +17 -0
  328. package/dist/report/start/month/month-renderer.js +50 -0
  329. package/dist/report/start/month/month-view.js +135 -0
  330. package/dist/report/start/month/months.js +17 -0
  331. package/dist/report/start/onboarding/done.js +171 -0
  332. package/dist/report/start/onboarding/finish-onboarding.js +93 -0
  333. package/dist/report/start/onboarding/intro.js +100 -0
  334. package/dist/report/start/onboarding/onboarding-changes.js +40 -0
  335. package/dist/report/start/onboarding/onboarding-renderer.js +195 -0
  336. package/dist/report/start/onboarding/onboarding-script.js +400 -0
  337. package/dist/report/start/onboarding/onboarding-view.js +98 -0
  338. package/dist/report/start/onboarding/project-step.js +210 -0
  339. package/dist/report/start/onboarding/steps.js +239 -0
  340. package/dist/report/start/onboarding/welcome.js +66 -0
  341. package/dist/report/start/page-notice.js +10 -0
  342. package/dist/report/start/projects/index-projects.js +43 -0
  343. package/dist/report/start/projects/moved-page.js +82 -0
  344. package/dist/report/start/projects/project-switch.js +138 -0
  345. package/dist/report/start/projects/projects-window.js +67 -0
  346. package/dist/report/start/projects/temporary-space.js +18 -0
  347. package/dist/report/start/render/air-datepicker-vendor.js +27 -0
  348. package/dist/report/start/render/session-status.js +41 -0
  349. package/dist/report/start/render/start-words.js +84 -0
  350. package/dist/report/start/repository.js +28 -0
  351. package/dist/report/start/serve/index-handler.js +372 -0
  352. package/dist/report/start/serve/onboarding-request.js +50 -0
  353. package/dist/report/start/serve/settings-request.js +1 -0
  354. package/dist/report/start/serve/settings-setup.js +193 -0
  355. package/dist/report/start/session-index.js +1 -0
  356. package/dist/report/start/session-start.js +974 -0
  357. package/dist/report/start/settings/alerts-tab.js +104 -0
  358. package/dist/report/start/settings/files-tab.js +169 -0
  359. package/dist/report/start/settings/general-tab.js +71 -0
  360. package/dist/report/start/settings/settings-renderer.js +254 -0
  361. package/dist/report/start/settings/settings-script.js +161 -0
  362. package/dist/report/start/settings/settings-view.js +240 -0
  363. package/dist/report/start/settings/settings-windows.js +334 -0
  364. package/dist/report/start/settings-files.js +43 -0
  365. package/dist/report/start/to-fix/done-list.js +45 -0
  366. package/dist/report/start/to-fix/file-window.js +110 -0
  367. package/dist/report/start/to-fix/to-fix-list.js +70 -0
  368. package/dist/report/start/to-fix/to-fix-renderer.js +237 -0
  369. package/dist/report/start/to-fix/to-fix-script.js +94 -0
  370. package/dist/report/start/to-fix/to-fix-view.js +124 -0
  371. package/dist/report/watch/agent-alert.js +72 -0
  372. package/dist/report/watch/notice-choices.js +35 -0
  373. package/dist/report/watch/notice-settings.js +65 -0
  374. package/dist/report/watch/preferences.js +100 -0
  375. package/dist/report/watch/render/instruction-words.js +54 -0
  376. package/dist/report/watch/render/notice-words.js +182 -0
  377. package/dist/report/watch/subagent-watch.js +345 -0
  378. package/dist/report/watch/watch-notice.js +1 -0
  379. package/dist/setup/behind.js +18 -0
  380. package/dist/setup/codex-mirror.js +171 -0
  381. package/dist/setup/not-a-project.js +19 -0
  382. package/dist/setup/project-setup.js +644 -0
  383. package/dist/setup/protected-patterns.js +30 -0
  384. package/dist/shared/colour.js +23 -0
  385. package/dist/shared/compare.js +4 -0
  386. package/dist/shared/content-version.js +13 -0
  387. package/dist/shared/counter.js +11 -0
  388. package/dist/shared/file-stamp.js +4 -0
  389. package/dist/shared/json.js +13 -0
  390. package/dist/shared/label.js +13 -0
  391. package/dist/shared/package-name.js +6 -0
  392. package/dist/shared/plain-invocation.js +44 -0
  393. package/dist/shared/plain-version.js +21 -0
  394. package/dist/shared/printable.js +25 -0
  395. package/dist/shared/renderer.js +1 -0
  396. package/dist/shared/sentence.js +20 -0
  397. package/dist/shared/terminal-logo.js +41 -0
  398. package/dist/shared/text-table.js +15 -0
  399. package/dist/shared/wrap.js +29 -0
  400. package/package.json +70 -0
@@ -0,0 +1,34 @@
1
+ import { listingPathCandidates } from './listing.js';
2
+ const LINE_NUMBER = /^\d+[:-]/;
3
+ const CONTEXT_LINE = /^(.+)-(\d+)-(.*)$/;
4
+ export function hitLines(output, withContext) {
5
+ const lines = output.split('\n');
6
+ const found = [];
7
+ for (const line of lines) {
8
+ const colon = line.indexOf(':');
9
+ if (colon <= 0)
10
+ continue;
11
+ const path = line.slice(0, colon);
12
+ // The same rule a listing uses for the field before a hit's first colon: a path because of where it sits.
13
+ if (!listingPathCandidates(line).some(([first]) => first?.positional === true && first.text === path))
14
+ continue;
15
+ found.push({ path, kind: 'hit', text: line.slice(colon + 1).replace(LINE_NUMBER, '') });
16
+ }
17
+ if (!withContext || found.length === 0)
18
+ return found;
19
+ // A context line with a colon in its text - `.env-3-URL=https://…` - reads as a hit on a path no file has: where its
20
+ // "path" is another hit path's line number, it is that file's context line, and is taken below as one.
21
+ const around = (path) => found.some((other) => other.path !== path && path.startsWith(other.path + '-') && /^\d+-/.test(path.slice(other.path.length + 1)));
22
+ const hits = found.filter((hit) => !around(hit.path));
23
+ const hitPaths = new Set(hits.map((hit) => hit.path));
24
+ const context = lines.flatMap((line) => {
25
+ const match = CONTEXT_LINE.exec(line);
26
+ // The longest prefix that is a hit path: `my-file-2-x` is `my-file`'s line 2 where `my-file` had a hit.
27
+ if (match === null || line.indexOf(':') !== -1 && hitPaths.has(line.slice(0, line.indexOf(':'))))
28
+ return [];
29
+ const path = [...hitPaths].filter((candidate) => line.startsWith(candidate + '-') && /^\d+-/.test(line.slice(candidate.length + 1)))
30
+ .sort((one, other) => other.length - one.length)[0];
31
+ return path === undefined ? [] : [{ path, kind: 'context', text: line.slice(path.length + 1).replace(/^\d+-/, '') }];
32
+ });
33
+ return [...hits, ...context];
34
+ }
@@ -0,0 +1,268 @@
1
+ import { addressesNothing, simpleCommandsIn } from './command-line.js';
2
+ const GREPS = new Set(['grep', 'egrep', 'fgrep']);
3
+ /** A glob character the shell expands in an unquoted word. */
4
+ const GLOB = /[*?[]/;
5
+ export function hasGlob(word) {
6
+ return GLOB.test(word);
7
+ }
8
+ /**
9
+ * The searches a command line runs and the words its shell expands. The pattern a search looks for is neither: `grep
10
+ * "KEY.*" src` expands nothing, because the word is quoted and is a pattern. Only the words a program takes as files
11
+ * are offered as globs.
12
+ */
13
+ export function reachOf(command) {
14
+ const searches = [];
15
+ const globs = [];
16
+ for (const { program, args } of simpleCommandsIn(command)) {
17
+ if (addressesNothing(program))
18
+ continue;
19
+ const search = GREPS.has(program) ? grepArguments(args) : program === 'rg' ? rgArguments(args) : undefined;
20
+ const files = search === undefined ? args.filter((word) => !word.startsWith('-')) : search.paths;
21
+ globs.push(...files.filter(hasGlob));
22
+ if (search?.recursive === true) {
23
+ const { includes, excludes, excludeDirs, hidden } = search;
24
+ searches.push({ roots: search.paths.length === 0 ? ['.'] : search.paths, includes, excludes, excludeDirs, hidden });
25
+ }
26
+ }
27
+ return { searches, globs };
28
+ }
29
+ /**
30
+ * Splits a program's words into its options and its positional words, the way `getopt_long` reads them: options may
31
+ * follow positional words, a cluster (`-rn`) holds several, and `--` ends them.
32
+ */
33
+ export function readOptions(args, table) {
34
+ const options = [];
35
+ const positionals = [];
36
+ let parsing = true;
37
+ for (let at = 0; at < args.length; at += 1) {
38
+ const word = args[at] ?? '';
39
+ if (parsing && word === '--') {
40
+ parsing = false;
41
+ }
42
+ else if (parsing && word.startsWith('--')) {
43
+ const separator = word.indexOf('=');
44
+ const name = separator === -1 ? word : word.slice(0, separator);
45
+ if (separator !== -1)
46
+ options.push({ name, value: word.slice(separator + 1) });
47
+ else if (table.longWithValue.has(name))
48
+ options.push({ name, value: args[(at += 1)] ?? '' });
49
+ else
50
+ options.push({ name });
51
+ }
52
+ else if (parsing && word.startsWith('-') && word.length > 1) {
53
+ for (let index = 1; index < word.length; index += 1) {
54
+ const flag = word.charAt(index);
55
+ if (table.shortWithValue.has(flag)) {
56
+ const value = index + 1 < word.length ? word.slice(index + 1) : (args[(at += 1)] ?? '');
57
+ options.push({ name: `-${flag}`, value });
58
+ break;
59
+ }
60
+ options.push({ name: `-${flag}` });
61
+ }
62
+ }
63
+ else {
64
+ positionals.push(word);
65
+ }
66
+ }
67
+ return { options, positionals };
68
+ }
69
+ const GREP_OPTIONS = {
70
+ shortWithValue: new Set(['e', 'f', 'm', 'A', 'B', 'C', 'd', 'D']),
71
+ longWithValue: new Set([
72
+ '--regexp', '--file', '--max-count', '--after-context', '--before-context', '--context', '--directories',
73
+ '--devices', '--include', '--exclude', '--exclude-dir', '--include-dir', '--exclude-from', '--label',
74
+ '--binary-files', '--group-separator',
75
+ ]),
76
+ };
77
+ /** GNU and BSD `grep`: `-r`, `-R`, `--recursive`, or `-d recurse`; the first positional word is the pattern unless `-e` or `-f` gave one. */
78
+ function grepArguments(args) {
79
+ const { options, positionals } = readOptions(args, GREP_OPTIONS);
80
+ // grep matches `--include` and its kin against a file's base name, and minds case.
81
+ const valuesOf = (...names) => options.filter((option) => names.includes(option.name)).map((option) => ({ glob: option.value ?? '' }));
82
+ const recursive = options.some((option) => ['-r', '-R', '--recursive', '--dereference-recursive'].includes(option.name) ||
83
+ (['-d', '--directories'].includes(option.name) && option.value === 'recurse'));
84
+ const patternGiven = options.some((option) => ['-e', '-f', '--regexp', '--file'].includes(option.name));
85
+ return {
86
+ recursive,
87
+ paths: patternGiven ? positionals : positionals.slice(1),
88
+ includes: valuesOf('--include'),
89
+ excludes: valuesOf('--exclude'),
90
+ excludeDirs: valuesOf('--exclude-dir'),
91
+ hidden: true,
92
+ };
93
+ }
94
+ const RG_OPTIONS = {
95
+ // `-r` is `--replace` here, not "recursive": ripgrep always recurses.
96
+ shortWithValue: new Set(['e', 'f', 'g', 't', 'T', 'm', 'A', 'B', 'C', 'j', 'M', 'E', 'd', 'r']),
97
+ longWithValue: new Set([
98
+ '--regexp', '--file', '--glob', '--iglob', '--type', '--type-not', '--type-add', '--type-clear', '--max-count',
99
+ '--after-context', '--before-context', '--context', '--threads', '--max-columns', '--encoding', '--max-depth',
100
+ '--replace', '--pre', '--pre-glob', '--sort', '--sortr', '--colors', '--color', '--path-separator', '--max-filesize',
101
+ '--ignore-file', '--context-separator', '--field-match-separator', '--field-context-separator', '--engine',
102
+ '--dfa-size-limit', '--regex-size-limit', '--hyperlink-format', '--generate',
103
+ ]),
104
+ };
105
+ /**
106
+ * ripgrep. It skips hidden files unless `--hidden`, `-.` or `-uu` says otherwise; a `.env` is hidden. A glob of `-g`
107
+ * narrows it, and a `!` in front turns the glob into an exclusion, of files and directories alike. What `.gitignore`
108
+ * leaves out is not read here, so a search it would narrow is taken to reach everything.
109
+ */
110
+ function rgArguments(args) {
111
+ const { options, positionals } = readOptions(args, RG_OPTIONS);
112
+ const names = options.map((option) => option.name);
113
+ // `--files` lists what would be searched and opens nothing; `--type-list` and friends search nothing at all.
114
+ if (names.some((name) => ['--files', '--type-list', '--help', '-h', '--version', '-V', '--generate'].includes(name)))
115
+ return undefined;
116
+ const unrestricted = names.filter((name) => name === '-u' || name === '--unrestricted').length;
117
+ // `--glob-case-insensitive` makes every `-g` an `--iglob`; the last of it and its `--no-` form wins.
118
+ const everyCaseless = names.lastIndexOf('--glob-case-insensitive') > names.lastIndexOf('--no-glob-case-insensitive');
119
+ const globs = options
120
+ .filter((option) => ['-g', '--glob', '--iglob'].includes(option.name))
121
+ .map((option) => ({ glob: option.value ?? '', caseless: everyCaseless || option.name === '--iglob' }));
122
+ const nameGlob = (glob, caseless) => (caseless ? { glob, caseless: true } : { glob });
123
+ const excluded = globs.filter(({ glob }) => glob.startsWith('!')).map(({ glob, caseless }) => nameGlob(atAnyDepth(glob.slice(1)), caseless));
124
+ const patternGiven = names.some((name) => ['-e', '-f', '--regexp', '--file'].includes(name));
125
+ return {
126
+ recursive: true,
127
+ paths: patternGiven ? positionals : positionals.slice(1),
128
+ includes: globs.filter(({ glob }) => !glob.startsWith('!')).map(({ glob, caseless }) => nameGlob(includedName(glob), caseless)),
129
+ excludes: excluded,
130
+ excludeDirs: excluded,
131
+ hidden: unrestricted >= 2 || names.includes('--hidden') || names.includes('-.'),
132
+ };
133
+ }
134
+ // ripgrep matches a glob as `.gitignore` does (measured, ripgrep 14.1.1): `**/` in front matches at any depth
135
+ // (`**/.env` took every `.env` below the search), and a glob with any other `/` in it is matched against the path, not
136
+ // the name (`config/.env` took nothing from a search of `.`, `app/config/.env` took that one file). The walk here meets
137
+ // names, so the first is dropped, which is exact. The second is read so that it never leaves out a file ripgrep would
138
+ // open: an include keeps its last part, and so takes every file of that name wherever it is; an exclusion keeps the
139
+ // `/`, matches no name, and leaves nothing out.
140
+ function atAnyDepth(glob) {
141
+ return glob.replace(/^(?:\*\*\/)+/, '');
142
+ }
143
+ function includedName(glob) {
144
+ const name = atAnyDepth(glob);
145
+ if (!name.includes('/'))
146
+ return name === '' ? '*' : name;
147
+ const last = name.slice(name.lastIndexOf('/') + 1);
148
+ // A `/` inside an alternative (`{.env,config/.env}`) cannot be cut at, and a glob that ends in one names directories;
149
+ // what either would include is not worked out here, so it includes every file.
150
+ return last === '' || /[{}]/.test(name) ? '*' : last;
151
+ }
152
+ /**
153
+ * Whether a file name matches a glob the way `fnmatch` and a shell match one: `*` and `?` within the name, `[...]`
154
+ * classes, and `{a,b}` alternatives, which a shell expands in an unquoted word before the program sees it.
155
+ *
156
+ * `period`: a shell does not let a wildcard match the dot that starts a hidden name, so `cat *` leaves `.env` out while
157
+ * `cat .env*` does not. A program matching its own `--include` has no such rule.
158
+ *
159
+ * `caseless`: `rg --iglob '*.ENV'` opens `.env`.
160
+ */
161
+ export function matchesName(name, glob, options) {
162
+ if (options.period && name.startsWith('.') && !glob.startsWith('.'))
163
+ return false;
164
+ return nameGlobToRegExp(glob, options.caseless === true ? 'i' : '').test(name);
165
+ }
166
+ const SPECIAL = /[.+^$()|\\/]/g;
167
+ function nameGlobToRegExp(glob, flags) {
168
+ let source = '';
169
+ let braces = 0;
170
+ for (let index = 0; index < glob.length; index += 1) {
171
+ const character = glob.charAt(index);
172
+ if (character === '*')
173
+ source += '.*';
174
+ else if (character === '?')
175
+ source += '.';
176
+ else if (character === '[') {
177
+ const close = glob.indexOf(']', index + 2);
178
+ if (close === -1) {
179
+ source += '\\[';
180
+ }
181
+ else {
182
+ const body = glob.slice(index + 1, close).replace(/^[!^]/, '^').replace(/\\/g, '\\\\');
183
+ source += `[${body}]`;
184
+ index = close;
185
+ }
186
+ }
187
+ else if (character === '{') {
188
+ braces += 1;
189
+ source += '(?:';
190
+ }
191
+ else if (character === '}' && braces > 0) {
192
+ braces -= 1;
193
+ source += ')';
194
+ }
195
+ else if (character === ',' && braces > 0)
196
+ source += '|';
197
+ else if (character === '}' || character === '{')
198
+ source += `\\${character}`;
199
+ else
200
+ source += character.replace(SPECIAL, '\\$&');
201
+ }
202
+ // An alternative left open is not one, and a class a regular expression cannot hold (`[z-a]`) is not a class a shell
203
+ // would expand either: both are read as the text they are. Any word may arrive here - a sed script, inline code - and
204
+ // a hook that threw over one would be a hook that failed on an ordinary command.
205
+ if (braces > 0)
206
+ return literally(glob, flags);
207
+ try {
208
+ return new RegExp(`^${source}$`, flags);
209
+ }
210
+ catch {
211
+ return literally(glob, flags);
212
+ }
213
+ }
214
+ function literally(text, flags) {
215
+ return new RegExp(`^${text.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&')}$`, flags);
216
+ }
217
+ /**
218
+ * Whether a command line runs a search that prints the lines it matched (`search-hits-are-reads` H1), and how. Undefined
219
+ * where it runs no search, or only ones that print no text - names (`-l`, `-L`), counts (`-c`), nothing (`-q`), or the
220
+ * files it would search (`rg --files`). Several searches in one line (`grep -rn KEY . | grep -v test`) are bare only
221
+ * when all are: the first one's names are still on the lines the last one prints.
222
+ */
223
+ export function printedSearch(command) {
224
+ let found;
225
+ for (const { program, args } of simpleCommandsIn(command)) {
226
+ const git = program === 'git' && args[0] === 'grep';
227
+ const searched = git || GREPS.has(program) ? GREP_PRINTS : program === 'ag' || program === 'ack' ? LISTER_PRINTS : program === 'rg' ? RG_PRINTS : undefined;
228
+ if (searched === undefined)
229
+ continue;
230
+ const { options, positionals } = readOptions(git ? args.slice(1) : args, searched.table);
231
+ const names = options.map((option) => option.name);
232
+ if (names.some((name) => searched.quiet.has(name)))
233
+ continue;
234
+ const operands = names.some((name) => PATTERN_GIVEN.has(name)) ? positionals.length : positionals.length - 1;
235
+ // grep recurses when asked to; ripgrep, ag and ack always do.
236
+ const recursive = searched !== GREP_PRINTS || options.some((option) => RECURSIVE.has(option.name) || ((option.name === '-d' || option.name === '--directories') && option.value === 'recurse'));
237
+ const said = names.some((name) => searched.unnamed.has(name)) ? 'never'
238
+ : git || names.some((name) => searched.named.has(name)) || operands > 1 ? 'always'
239
+ // With no file, grep reads what was piped to it, and a recursive search the directory it is in.
240
+ : operands === 0 ? (recursive ? 'always' : 'never')
241
+ : recursive ? 'maybe' : 'never';
242
+ found = {
243
+ context: (found?.context ?? false) || names.some((name) => CONTEXT.has(name)),
244
+ names: found === undefined ? said : NAMES_RANK.indexOf(said) < NAMES_RANK.indexOf(found.names) ? said : found.names,
245
+ numbered: (found?.numbered ?? false) || names.includes('-n') || names.includes('--line-number'),
246
+ };
247
+ }
248
+ return found === undefined ? undefined
249
+ : { context: found.context, names: found.names, ...(found.numbered ? { numbered: true } : {}) };
250
+ }
251
+ /** In a pipeline, a name any search puts on the lines stays on them through the searches after it. */
252
+ const NAMES_RANK = ['always', 'maybe', 'never'];
253
+ const RECURSIVE = new Set(['-r', '-R', '--recursive', '--dereference-recursive']);
254
+ const QUIET = ['-l', '-L', '--files-with-matches', '--files-without-match', '--files-without-matches', '--name-only', '-c', '--count', '-q', '--quiet', '--silent'];
255
+ const GREP_PRINTS = {
256
+ table: GREP_OPTIONS, quiet: new Set(QUIET), named: new Set(['-H', '--with-filename']), unnamed: new Set(['-h', '--no-filename']),
257
+ };
258
+ /** ag and ack: `-g` and ack's `-f` list the files a search would read. */
259
+ const LISTER_PRINTS = { ...GREP_PRINTS, quiet: new Set([...QUIET, '-g', '-f']), named: new Set(['-H', '--with-filename', '--filename']) };
260
+ /** ripgrep: `-f` is a file of patterns, and `-I`, not `-h`, drops the name. */
261
+ const RG_PRINTS = {
262
+ table: RG_OPTIONS,
263
+ quiet: new Set(['-l', '--files-with-matches', '--files-without-match', '-c', '--count', '--count-matches', '-q', '--quiet', '--files', '--type-list']),
264
+ named: new Set(['-H', '--with-filename']),
265
+ unnamed: new Set(['-I', '--no-filename']),
266
+ };
267
+ const PATTERN_GIVEN = new Set(['-e', '-f', '--regexp', '--file']);
268
+ const CONTEXT = new Set(['-A', '-B', '-C', '--after-context', '--before-context', '--context']);
@@ -0,0 +1,143 @@
1
+ import { commitsIn, programsIn } from './command-line.js';
2
+ /** On one record, the more direct source is named: a protected read, then a return, then anything else. */
3
+ const SOURCE_ORDER = ['read', 'returned', 'delivered', 'unprotected'];
4
+ /**
5
+ * Every use of a traced value (R7, R8): a run of it in an agent's message or in the input of a call, with where it
6
+ * landed and the first thing earlier in that agent's own record stream that carried it. Order is a record's position
7
+ * within one agent's stream: a use is never placed against another agent's records, and never by time. The claim is
8
+ * sameness - the same value appears here - and nothing about how it travelled (R10).
9
+ */
10
+ export function valueUses(model, accesses, traced) {
11
+ if (traced.values.length === 0)
12
+ return [];
13
+ const { values, trace } = traced;
14
+ const delegating = new Set(model.delegations.map((delegation) => delegation.id));
15
+ const protectedInputs = new Set(accesses.filter((access) => access.source === 'input').map((access) => access.eventId));
16
+ const sources = sourcesOf(model, accesses, traced);
17
+ const foundIn = (texts) => new Set(texts.flatMap((text) => [...trace.foundIn(text)]));
18
+ const uses = [];
19
+ const use = (agentId, evidence, found, landing) => {
20
+ if (found.size === 0)
21
+ return;
22
+ const files = new Set();
23
+ let uncertain = false;
24
+ for (const index of found) {
25
+ const value = values[index];
26
+ if (value === undefined)
27
+ continue;
28
+ for (const path of value.paths)
29
+ files.add(path);
30
+ if (value.paths.length > 1)
31
+ uncertain = true;
32
+ }
33
+ const [first] = (sources.get(agentId) ?? []).filter((source) => source.record < evidence.record && [...found].some((index) => source.found.has(index)));
34
+ uses.push({
35
+ agentId,
36
+ files: [...files].sort(),
37
+ ...(uncertain ? { fileUncertain: true } : {}),
38
+ ...landing,
39
+ source: first?.kind ?? 'none',
40
+ ...(first?.kind === 'unprotected' && first.targets.length > 0 ? { sourceTargets: first.targets } : {}),
41
+ ...(first === undefined ? {} : { sourceRecord: first.record }),
42
+ ...(first?.delegationId === undefined ? {} : { sourceDelegationId: first.delegationId }),
43
+ evidence,
44
+ });
45
+ };
46
+ for (const message of model.messages) {
47
+ use(message.agentId, message.evidence, trace.foundIn(message.text), { landed: message.kind });
48
+ }
49
+ // X17: later words to a helper hand a value to it as the first instruction does - once per instruction, where the
50
+ // sender wrote them.
51
+ for (const followUp of model.delegations.flatMap((delegation) => delegation.followUps)) {
52
+ use(followUp.senderAgentId, followUp.evidence, trace.foundIn(followUp.text), { landed: 'delegation' });
53
+ }
54
+ // Words an agent wrote that are no call of the model: code it handed its runtime (X14), and instructions that reached
55
+ // no helper by id (X17). Each says only that the agent put the value into what it sent, never that it ran or arrived.
56
+ for (const context of model.contexts) {
57
+ if (context.author !== 'agent' || context.agentId === undefined)
58
+ continue;
59
+ if (context.kind !== 'code' && context.kind !== 'unjoined-instruction')
60
+ continue;
61
+ use(context.agentId, context.evidence, trace.foundIn(context.text), { landed: 'call' });
62
+ }
63
+ for (const event of model.events) {
64
+ if (event.written !== undefined) {
65
+ // What a call puts into a file, never the text an edit takes out: a value only there was in the file already.
66
+ const landing = { landed: 'file', targets: event.targets, intoProtected: protectedInputs.has(event.id) };
67
+ use(event.agentId, event.evidence, foundIn(event.written), landing);
68
+ continue;
69
+ }
70
+ const found = foundIn(stringsIn(event.input));
71
+ if (delegating.has(event.id))
72
+ use(event.agentId, event.evidence, found, { landed: 'delegation' });
73
+ else if (event.commands.length > 0) {
74
+ const programs = [...new Set(event.commands.flatMap((command) => programsIn(command)))];
75
+ use(event.agentId, event.evidence, found, { landed: 'command', programs, commits: event.commands.some((command) => commitsIn(command)) });
76
+ }
77
+ else
78
+ use(event.agentId, event.evidence, found, { landed: 'call' });
79
+ }
80
+ const order = new Map(model.agents.map((agent, index) => [agent.id, index]));
81
+ const position = (agentId) => order.get(agentId) ?? order.size;
82
+ return uses.sort((first, second) => position(first.agentId) - position(second.agentId) || first.evidence.record - second.evidence.record);
83
+ }
84
+ /** The texts each agent received that carry a traced value, earliest first. */
85
+ function sourcesOf(model, accesses, { trace }) {
86
+ const events = new Map(model.events.map((event) => [event.id, event]));
87
+ const delegating = new Set(model.delegations.map((delegation) => delegation.id));
88
+ // A write to a protected file is an access as well, and reads nothing: only a call that returns something reads.
89
+ const reads = new Set(accesses
90
+ .filter((access) => access.outcome === 'succeeded' && events.get(access.eventId)?.resultShape !== 'none')
91
+ .map((access) => access.eventId));
92
+ const byAgent = new Map();
93
+ const add = (agentId, source) => {
94
+ if (source.found.size === 0)
95
+ return;
96
+ const list = byAgent.get(agentId);
97
+ if (list === undefined)
98
+ byAgent.set(agentId, [source]);
99
+ else
100
+ list.push(source);
101
+ };
102
+ for (const event of model.events) {
103
+ const result = event.result;
104
+ // X10: only output the agent's model was handed carried a value to it. What a process printed, as a runtime
105
+ // recorded it, reached the agent only where it is also in something delivered.
106
+ if (result?.content === undefined || result.stage !== 'model' || delegating.has(event.id))
107
+ continue;
108
+ const kind = reads.has(event.id) ? 'read' : 'unprotected';
109
+ add(event.agentId, { record: result.evidence.record, kind, found: trace.foundIn(result.content), targets: event.targets });
110
+ }
111
+ for (const delivery of model.deliveries) {
112
+ if (delivery.status !== 'confirmed')
113
+ continue;
114
+ // Joined to a call by id, it is that call's output; joined to none, it is delivered output and no more.
115
+ const source = delivery.sourceId === undefined ? undefined : events.get(delivery.sourceId);
116
+ const kind = source !== undefined && reads.has(source.id) ? 'read' : source !== undefined ? 'unprotected' : 'delivered';
117
+ add(delivery.recipientAgentId, { record: delivery.evidence.record, kind, found: trace.foundIn(delivery.text), targets: source?.targets ?? [] });
118
+ }
119
+ // What came back from a delegation is a source for the agent it came back to: its result, unless that is a launch
120
+ // notice, and every report delivered for it (R3).
121
+ for (const delegation of model.delegations) {
122
+ const result = events.get(delegation.id)?.result;
123
+ if (result?.content !== undefined && result.launchNotice !== true) {
124
+ add(delegation.parentAgentId, { record: result.evidence.record, kind: 'returned', found: trace.foundIn(result.content), targets: [], delegationId: delegation.id });
125
+ }
126
+ for (const report of delegation.reports) {
127
+ add(delegation.parentAgentId, { record: report.evidence.record, kind: 'returned', found: trace.foundIn(report.content), targets: [], delegationId: delegation.id });
128
+ }
129
+ }
130
+ for (const list of byAgent.values()) {
131
+ list.sort((first, second) => first.record - second.record || SOURCE_ORDER.indexOf(first.kind) - SOURCE_ORDER.indexOf(second.kind));
132
+ }
133
+ return byAgent;
134
+ }
135
+ function stringsIn(value) {
136
+ if (typeof value === 'string')
137
+ return [value];
138
+ if (Array.isArray(value))
139
+ return value.flatMap(stringsIn);
140
+ if (value !== null && typeof value === 'object')
141
+ return Object.values(value).flatMap(stringsIn);
142
+ return [];
143
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The words before every call of every agent.
3
+ *
4
+ * A block belongs to the **run** of calls that follows it, not to the first of them: measured on one
5
+ * session, one block stands before 1 call in 136 runs, 2 in 76, 3 in 15 and 4 in 10. A call that follows another
6
+ * call directly has no words of its own - 186 of 462 - and that is reported as the absence it is, never filled in
7
+ * from the run before.
8
+ *
9
+ * Order is the record's, which is the order the transcript was written in; nothing here is joined by time.
10
+ */
11
+ export function wordsBeforeCalls(model) {
12
+ const before = new Map();
13
+ for (const agentId of new Set(model.events.map((event) => event.agentId))) {
14
+ for (const run of runsOf(model, agentId)) {
15
+ for (const call of run.calls)
16
+ before.set(call, { words: run.words, covers: run.calls.length });
17
+ }
18
+ }
19
+ return before;
20
+ }
21
+ /** One agent's records in order: each stretch of writing, and the calls that follow it before it writes again. */
22
+ function runsOf(model, agentId) {
23
+ const calls = model.events
24
+ .filter((event) => event.agentId === agentId)
25
+ .map((event) => ({ at: event.evidence.record, id: event.id }));
26
+ const written = model.messages.filter((message) => message.agentId === agentId);
27
+ // A call and a message never share a record - measured, 149 of 149 text blocks and 249 of 249 reasoning blocks
28
+ // sit on lines of their own - so the record alone orders them.
29
+ const items = [
30
+ ...written.map((message) => ({ at: message.evidence.record, message })),
31
+ ...calls.map((call) => ({ at: call.at, call: call.id })),
32
+ ].sort((first, second) => first.at - second.at);
33
+ const runs = [];
34
+ let words = [];
35
+ let following = [];
36
+ for (const item of items) {
37
+ if ('message' in item) {
38
+ // Writing after a run closes it: what comes next stands before the calls that come next.
39
+ if (following.length > 0) {
40
+ runs.push({ words, calls: following });
41
+ words = [];
42
+ following = [];
43
+ }
44
+ words.push(item.message);
45
+ continue;
46
+ }
47
+ following.push(item.call);
48
+ }
49
+ if (following.length > 0)
50
+ runs.push({ words, calls: following });
51
+ return runs;
52
+ }
@@ -0,0 +1 @@
1
+ export const MAIN_AGENT_TYPE = 'main';
@@ -0,0 +1,11 @@
1
+ /** The gap each unanswered question leaves: absent and unmeasured stay different answers to a reader. */
2
+ export function capabilityGaps(records) {
3
+ return records.flatMap((record) => record.state === 'supported'
4
+ ? []
5
+ : [{
6
+ kind: record.state === 'absent' ? 'capability-absent' : 'capability-unmeasured',
7
+ question: record.question,
8
+ source: record.source,
9
+ ...(record.agentId === undefined ? {} : { agentId: record.agentId }),
10
+ }]);
11
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * One list of the projects a person worked on with any AI (`which-project.md` V9; `2026-09-27-what-codex-wrote.md` X28).
3
+ * A folder with conversations in several AIs is one project: the same id for the same path. An id another project with a
4
+ * different path already holds is never merged into it and never renamed - its folder cannot be named without taking
5
+ * another's name, so it is counted, as a project whose folder was not said is (V17: a page names a project by its id).
6
+ */
7
+ export class CombinedProjectCatalogue {
8
+ #catalogues;
9
+ constructor(catalogues) {
10
+ this.#catalogues = catalogues;
11
+ }
12
+ async list() {
13
+ const listings = await Promise.all(this.#catalogues.map((catalogue) => catalogue.list()));
14
+ const byId = new Map();
15
+ let unreadable = listings.reduce((sum, listing) => sum + listing.unreadable, 0);
16
+ for (const project of listings.flatMap((listing) => listing.projects)) {
17
+ const known = byId.get(project.id);
18
+ if (known === undefined)
19
+ byId.set(project.id, project);
20
+ else if (known.path === project.path)
21
+ byId.set(project.id, merged(known, project));
22
+ else
23
+ unreadable += 1;
24
+ }
25
+ return { projects: [...byId.values()].sort((a, b) => b.newest.modifiedAt - a.newest.modifiedAt), unreadable };
26
+ }
27
+ }
28
+ /** One folder's conversations in two AIs: counted together, and recognised by whichever holds the newer one. */
29
+ function merged(first, second) {
30
+ return {
31
+ ...first,
32
+ exists: first.exists || second.exists,
33
+ conversations: first.conversations + second.conversations,
34
+ newest: second.newest.modifiedAt > first.newest.modifiedAt ? second.newest : first.newest,
35
+ };
36
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * One list of a project's conversations, whichever AI held them (`2026-09-27-what-codex-wrote.md` XD5, X28): every
3
+ * catalogue is asked, every session kept with its provider - two AIs may use one id - and every place looked in is kept
4
+ * with whether it was there. The listing was `found` where any place was: a project only one AI worked in is still a
5
+ * project with conversations. Found by looking at a Codex-only project, which said it had none.
6
+ */
7
+ export class CombinedSessionCatalogue {
8
+ #catalogues;
9
+ constructor(catalogues) {
10
+ if (catalogues.length === 0)
11
+ throw new Error('A combined catalogue needs a catalogue to combine.');
12
+ this.#catalogues = catalogues;
13
+ }
14
+ async list(workingDirectory) {
15
+ const listings = await Promise.all(this.#catalogues.map((catalogue) => catalogue.list(workingDirectory)));
16
+ const [first] = listings;
17
+ const found = listings.find((listing) => listing.found) ?? first;
18
+ return {
19
+ directory: found.directory,
20
+ found: found.found,
21
+ searched: listings.flatMap((listing) => listing.searched),
22
+ sessions: listings.flatMap((listing) => listing.sessions).sort((a, b) => b.modifiedAt - a.modifiedAt),
23
+ };
24
+ }
25
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A gap that leaves a relation unjoined makes the whole thing `unresolved`; any other gap makes it `partial`.
3
+ * The distinction matters because an unresolved relation means the event gets no outcome at all (§4.3 rule 6),
4
+ * while a partial one means the picture has holes we can name.
5
+ */
6
+ export function completenessOf(gaps) {
7
+ if (gaps.some((gap) => gap.kind === 'relation-unresolved'))
8
+ return 'unresolved';
9
+ return gaps.length === 0 ? 'complete' : 'partial';
10
+ }
@@ -0,0 +1 @@
1
+ export {};