@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,237 @@
1
+ import { matchesGlob } from '../../../core/policy/glob.js';
2
+ import { idInKey } from '../../../core/session-catalogue.js';
3
+ import { escapeHtml as e } from '../../render/html-report-components.js';
4
+ import { INDEX_CONTENT_SECURITY_POLICY_META } from '../../render/html-head.js';
5
+ import { inLanguages, LANGS, translator } from '../../render/report-copy.js';
6
+ import { FIX_WIZARD_SCRIPT } from '../../render/report-page/fix-wizard-script.js';
7
+ import { FIX_WIZARD_STYLE } from '../../render/report-page/fix-wizard.js';
8
+ import { HELPERS_SCRIPT, HELPERS_VIEW_STYLE } from '../../render/report-page/helpers-view.js';
9
+ import { STORY_WINDOW_STYLE } from '../../render/report-page/story-window.js';
10
+ import { clockIn } from '../../render/report-page/times.js';
11
+ import { AVATAR_STYLE } from '../../render/ui/avatar.js';
12
+ import { STAT_STYLE } from '../../render/ui/stats.js';
13
+ import { appSidebar, APP_SIDEBAR_STYLE } from '../../render/ui/app-sidebar.js';
14
+ import { ASK_PANEL_SCRIPT, ASK_PANEL_STYLE } from '../../render/ui/ask-panel.js';
15
+ import { CHECKLIST_SCRIPT, CHECKLIST_STYLE } from '../../render/ui/checklist.js';
16
+ import { BUTTON_STYLE } from '../../render/ui/button.js';
17
+ import { CALLOUT_STYLE } from '../../render/ui/callout.js';
18
+ import { CONFIRM_DIALOG_STYLE } from '../../render/ui/confirm-dialog.js';
19
+ import { dataTable, DATA_TABLE_STYLE } from '../../render/ui/data-table.js';
20
+ import { fileChips, FILE_CHIP_SCRIPT, FILE_CHIP_STYLE } from '../../render/ui/file-chip.js';
21
+ import { FOLD_LINE_STYLE } from '../../render/ui/fold-line.js';
22
+ import { hero, HERO_STYLE } from '../../render/ui/hero.js';
23
+ import { localClock, rangeName } from '../../render/ui/local-date.js';
24
+ import { pageShell } from '../../render/ui/page-shell.js';
25
+ import { pillTabs, PILL_TABS_SCRIPT, PILL_TABS_STYLE } from '../../render/ui/pill-tabs.js';
26
+ import { POPUP_SCRIPT, POPUP_STYLE } from '../../render/ui/popup.js';
27
+ import { PROGRESS_STYLE } from '../../render/ui/progress.js';
28
+ import { TAG_STYLE } from '../../render/ui/tag.js';
29
+ import { TASK_LIST_STYLE } from '../../render/ui/task-list.js';
30
+ import { LABELS } from '../../check/render/text-digest-renderer.js';
31
+ import { appNav, sidebarProject } from '../app-nav.js';
32
+ import { projectsWindows } from '../projects/projects-window.js';
33
+ import { doneList, undoWindows } from './done-list.js';
34
+ import { fixWindow, happenedWindow } from './file-window.js';
35
+ import { TO_FIX_SCRIPT } from './to-fix-script.js';
36
+ import { toFixList } from './to-fix-list.js';
37
+ import { toFixView } from './to-fix-view.js';
38
+ import { pageNotice } from '../page-notice.js';
39
+ /**
40
+ * To fix, written as `to-fix.html` beside the index (`.ai/specs/2026-09-23-to-fix.md`; design file *agentwhy To fix*;
41
+ * `.ai/plans/2026-09-23-to-fix-redesign.md`): every file still to fix across the run's conversations, what to do about
42
+ * each, and what was done. Every mark and every undo goes through the server, and the page is read again (R60).
43
+ */
44
+ export class ToFixRenderer {
45
+ #links;
46
+ constructor(links) {
47
+ this.#links = links;
48
+ }
49
+ render(index) {
50
+ const view = toFixView(index);
51
+ // A shared page writes nothing and carries no note (T19, R37).
52
+ const writable = !index.shared;
53
+ // Each file with its group, in the order the page draws them: the window's number is its place here.
54
+ const files = [...view.keys.map((file) => ({ file, kind: 'keys' })), ...view.look.map((file) => ({ file, kind: 'look' }))];
55
+ const numbered = new Map(files.map(({ file }, at) => [file, at]));
56
+ // F57: files the person chose only to be told about, so a file's Fix it does not offer to protect what they let through.
57
+ const toldPatterns = [index.settings?.told?.local, index.settings?.told?.shared].flatMap((list) => (list === undefined || list === 'unreadable' ? [] : list));
58
+ const told = new Set(files.map(({ file }) => file.item.path).filter((path) => toldPatterns.some((pattern) => matchesGlob(path, pattern))));
59
+ const windows = { writable, since: index.asked, settings: this.#links.settings, clock: clockIn(index.timeZone), told };
60
+ const done = writable ? view.done : view.done.map(({ note: _note, ...rest }) => rest);
61
+ return pageShell({
62
+ // which-project V2, V11: the person's projects, opened from the sidebar's card.
63
+ windows: projectsWindows(index),
64
+ title: 'fix.title',
65
+ // nothing-updates-by-itself U4: the update notice, where the project's hooks run an older release.
66
+ ...pageNotice(index),
67
+ policy: INDEX_CONTENT_SECURITY_POLICY_META,
68
+ width: 'list',
69
+ styles: [APP_SIDEBAR_STYLE, HERO_STYLE, BUTTON_STYLE, PILL_TABS_STYLE, TASK_LIST_STYLE, TAG_STYLE, CALLOUT_STYLE, FOLD_LINE_STYLE,
70
+ FILE_CHIP_STYLE, DATA_TABLE_STYLE, POPUP_STYLE, CONFIRM_DIALOG_STYLE, PROGRESS_STYLE, CHECKLIST_STYLE, ASK_PANEL_STYLE, FIX_WIZARD_STYLE, AVATAR_STYLE, STAT_STYLE,
71
+ // The story's Diagram is drawn with the Helpers diagram's boxes and lines, and lit by its script, as on the report.
72
+ HELPERS_VIEW_STYLE, STORY_WINDOW_STYLE, TO_FIX_STYLE],
73
+ scripts: [PILL_TABS_SCRIPT, POPUP_SCRIPT, CHECKLIST_SCRIPT, FILE_CHIP_SCRIPT, ASK_PANEL_SCRIPT, FIX_WIZARD_SCRIPT, HELPERS_SCRIPT, TO_FIX_SCRIPT],
74
+ sidebar: appSidebar({
75
+ ...sidebarProject(index),
76
+ home: this.#links.conversations,
77
+ items: appNav(index, 'to-fix', this.#links),
78
+ }),
79
+ main: '<div class="tf" data-to-fix data-fix-words="' + e(JSON.stringify(scriptWords())) + '">' +
80
+ hero({
81
+ eyebrow: inLanguages((t, lang) => eyebrow(index, t, lang)),
82
+ fact: inLanguages((t) => (view.total === 0 ? t('fix.hero.none') : t('fix.hero.count', { n: view.total }))),
83
+ action: inLanguages((t) => (view.state === 'keys' ? t('fix.hero.keys', { n: view.keys.length }) : view.state === 'look' ? t('fix.hero.look') : t('fix.hero.nice'))),
84
+ lead: inLanguages((t) => t('fix.lead')),
85
+ calm: view.state === 'none',
86
+ }) +
87
+ pillTabs([
88
+ { label: tabLabel('fix.tab.todo', view.total), panel: toFixList(view, numbered) },
89
+ { label: tabLabel('fix.tab.done', done.length), panel: doneList(done, writable) },
90
+ ], 'state', 0, view.refusedAttempts === 0 ? '' :
91
+ '<span class="tf-stopped"><span class="tf-stopped-mark" aria-hidden="true">✓</span>' +
92
+ inLanguages((t) => t('fix.stopped', { n: view.refusedAttempts })) + '</span>') +
93
+ developer(view, index) +
94
+ files.map(({ file, kind }, at) => happenedWindow(file, kind, at, windows) + fixWindow(files.map((each) => each.file), at, windows)).join('') +
95
+ (writable ? undoWindows(done) : '') +
96
+ // The wizard's words; whether the page is served is known only to its address (T17).
97
+ '<div id="wizard-words" data-served="auto" hidden>' + ['wz.all', 'wz.finish', 'wz.saving', 'wz.tickFirst', 'wz.pickFirst']
98
+ .map((key) => '<span data-word="' + key + '">' + inLanguages((t) => t(key)) + '</span>').join('') + '</div>' +
99
+ '</div>',
100
+ });
101
+ }
102
+ }
103
+ /** "To fix 5", the count beside the word, as the design draws the two tabs. */
104
+ function tabLabel(key, n) {
105
+ return inLanguages((t) => t(key)) + ' <span class="tf-tab-count">' + n + '</span>';
106
+ }
107
+ /** T2, D3: the run's range - "Last 30 days · Aug 25 – Sep 23", or "Since Sep 1" for a date. */
108
+ function eyebrow(index, t, lang) {
109
+ const clock = localClock(index.timeZone ?? 'UTC');
110
+ const dates = rangeName(clock(index.since).day, clock(index.now).day, lang);
111
+ const span = /^(\d+)([hdw])$/.exec(index.asked);
112
+ return span === null
113
+ ? t('fix.range.since', { dates })
114
+ : t('fix.range', { range: t('fix.span.' + span[2], { n: Number(span[1]) }), dates });
115
+ }
116
+ /** T16: the conversations a row names before "+N more" - two, as a cell names at most two files (guidelines §9.1). */
117
+ const SHOWN_IDS = 2;
118
+ /**
119
+ * T16: the tool's own words, for a developer: what `check` says of each file with what the label means, the
120
+ * conversations it says it of, and the commands, each with what it does. A row of bare ids read as leaked values.
121
+ */
122
+ function developer(view, index) {
123
+ const files = [...view.keys, ...view.look, ...view.seen];
124
+ const say = (key) => inLanguages((t) => t(key));
125
+ return '<details class="tf-dev"><summary class="tf-dev-toggle">' + say('fix.dev') + '</summary>' +
126
+ '<div class="tf-dev-body"><p class="tf-dev-lead">' + say(index.shared ? 'fix.dev.lead.shared' : 'fix.dev.lead') + '</p>' +
127
+ (files.length === 0 ? '' : dataTable({
128
+ head: [say('fix.dev.col.says'), say('fix.dev.col.file'), say('fix.dev.col.in')],
129
+ columns: 'minmax(150px,1fr) minmax(140px,1fr) minmax(220px,1.4fr)',
130
+ minWidth: 560,
131
+ rows: files.map((file) => ({
132
+ cells: [
133
+ '<span class="tf-dev-label">' + LABELS[file.label] + '</span><span class="tf-dev-means">' + say('fix.dev.means.' + file.label) + '</span>',
134
+ fileChips([file.path], 1),
135
+ '<span class="tf-dev-count">' + inLanguages((t) => t('fix.dev.count', { n: file.count })) + '</span>' +
136
+ fileChips(file.conversations.map((each) => each.entry.name), SHOWN_IDS, shortId),
137
+ ],
138
+ })),
139
+ })) +
140
+ '<div class="tf-dev-commands"><h3 class="tf-dev-h">' + say('fix.dev.commands') + '</h3>' +
141
+ command('fix.dev.cmd.full', 'agentwhy check --full --since ' + e(index.asked)) +
142
+ command('fix.dev.cmd.mark', 'agentwhy check --mark rotated &lt;path&gt;') + '</div></div></details>';
143
+ }
144
+ /**
145
+ * A session id is a UUID; its first 8 characters tell a run's conversations apart, and the chip's tooltip holds all of it.
146
+ * They are cut from the id, not the key, which for Codex starts with `codex-`.
147
+ */
148
+ function shortId(name) {
149
+ const id = idInKey(name);
150
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-/i.test(id) ? id.slice(0, 8) : name;
151
+ }
152
+ /** One command, under what it does. `line` is already escaped. */
153
+ function command(key, line) {
154
+ return '<div class="tf-dev-command"><span class="tf-dev-does">' + inLanguages((t) => t(key)) + '</span><code>' + line + '</code></div>';
155
+ }
156
+ /** What the script says, in every language: one text node cannot hold three. */
157
+ function scriptWords() {
158
+ return Object.fromEntries(LANGS.map((lang) => {
159
+ const t = translator(lang);
160
+ return [lang, { saving: t('fix.saving'), refused: t('fix.refused'), asFile: t('fix.asFile'), unreachable: t('fix.unreachable') }];
161
+ }));
162
+ }
163
+ /** What only this page lays out. Colours come from the tokens (plan rule 1). */
164
+ const TO_FIX_STYLE = String.raw `
165
+ .tf-stopped{display:inline-flex;align-items:center;gap:10px;font-size:14px;font-weight:600;color:var(--mint);background:var(--mint-06);border:1px solid var(--mint-35);border-radius:999px;padding:7px 16px 7px 8px}
166
+ .tf-stopped-mark{width:20px;height:20px;border-radius:50%;background:var(--mint);color:var(--on-mint);font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center}
167
+ .tf-tab-count{margin-left:6px;font-size:13px;opacity:.7}
168
+ .tf-group{margin-bottom:28px}
169
+ .tf-group-head{display:flex;align-items:center;gap:10px;margin-bottom:6px}
170
+ .tf-dot{width:10px;height:10px;border-radius:50%}
171
+ .tf-group-coral .tf-dot{background:var(--coral)}.tf-group-amber .tf-dot{background:var(--amber)}
172
+ .tf-h2{margin:0;font-size:20px;font-weight:650}
173
+ .tf-group-count{font-size:15px;font-weight:650}
174
+ .tf-group-coral .tf-group-count{color:var(--coral-text)}.tf-group-amber .tf-group-count{color:var(--amber)}
175
+ .tf-group-lead{margin:0 0 14px 20px;font-size:14.5px;line-height:1.5;color:var(--text-2)}
176
+ .tf-empty{font-size:15px;color:var(--text-2);margin:8px 0 24px}
177
+ .tf-soft{color:var(--text-2);font-weight:400}
178
+ .tf-context{font-size:13.5px;color:var(--text-3)}
179
+ .tf-seen{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:8px}
180
+ .tf-seen-row{display:flex;align-items:center;gap:14px;padding:14px 20px;border-radius:14px;background:var(--card);border:1px solid var(--white-07)}
181
+ .tf-seen-text{flex:1;min-width:0}
182
+ .tf-seen-title{display:block;font-size:15px;font-weight:600}
183
+ .tf-seen-sub{display:flex;align-items:center;gap:10px;margin-top:6px;flex-wrap:wrap}
184
+ .tf-seen-label{font-size:13px;color:var(--text-3);white-space:nowrap}
185
+ .tf-done-lead{font-size:14.5px;color:var(--text-2);margin:0 0 14px}
186
+ .tf-done{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
187
+ .tf-done-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:18px;align-items:center;padding:16px 20px;border-radius:14px;background:var(--card);border:1px solid var(--mint-20)}
188
+ .tf-done-title{display:block;font-size:15.5px;font-weight:600;color:var(--text-3)}
189
+ .tf-done-sub{display:flex;align-items:center;gap:10px;margin-top:6px;flex-wrap:wrap}
190
+ .tf-note{font-size:13.5px;color:var(--text-2);font-style:italic}
191
+ .tf-fixed{font-size:13.5px;font-weight:600;color:var(--mint);white-space:nowrap}
192
+ .tf-undo{color:var(--text-3);font-size:13.5px;font-weight:400}.tf-undo:hover{color:var(--coral-text)}
193
+ .tf-window-head{padding:26px 32px 20px;border-bottom:1px solid var(--white-07)}
194
+ .tf-window-top{display:flex;justify-content:space-between;align-items:center;gap:16px}
195
+ .tf-window-group{font-size:14px;font-weight:600}.tf-window-keys{color:var(--coral-text)}.tf-window-look{color:var(--amber)}
196
+ .tf-window-title{margin:6px 0 10px;font-size:24px;line-height:1.25;font-weight:650}
197
+ .tf-window-body{display:flex;flex-direction:column;gap:22px;padding:24px 32px 28px}
198
+ .tf-fact-text{margin:0;font-size:15px;line-height:1.55;color:var(--text-2)}
199
+ .tf-h3{margin:0 0 12px;font-size:16px;font-weight:650}
200
+ .tf-say{margin:10px 0 0;font-size:13.5px;line-height:1.5;color:var(--coral-text)}
201
+ .tf-say[hidden]{display:none}
202
+ .tf-say-busy{color:var(--text-3)}
203
+ .tf-say code{display:block;margin-top:6px;font-family:var(--mono);font-size:12.5px;color:var(--text);background:var(--panel);border:1px solid var(--white-08);border-radius:8px;padding:8px 10px;word-break:break-all;user-select:all}
204
+ .tf-story-where{max-width:760px;margin:0 auto}
205
+ .tf-where{list-style:none;margin:0;padding:0;border-radius:12px;border:1px solid var(--white-08);overflow:hidden}
206
+ .tf-where-row{display:grid;grid-template-columns:96px minmax(0,1fr) auto;gap:14px;align-items:center;padding:12px 14px;border-top:1px solid var(--white-06);background:var(--card)}
207
+ .tf-where-row:first-child{border-top:0}
208
+ .js .tf-where-row.tf-more{display:none}
209
+ .tf-where-day{display:block;font-size:14px;font-weight:600}
210
+ .tf-where-time{display:block;font-size:13px;color:var(--text-3)}
211
+ .tf-where-ask{font-size:14.5px;overflow-wrap:anywhere}
212
+ .tf-where-none{font-size:13px;color:var(--text-4)}
213
+ .tf-where-foot{display:flex;justify-content:space-between;gap:12px;margin-top:8px}
214
+ .tf-where-foot:empty{display:none}
215
+ .tf-all{background:transparent;border:none;color:var(--coral-text);font:inherit;font-size:13.5px;font-weight:600;cursor:pointer;padding:4px 0}
216
+ .tf-dev{margin-top:28px}
217
+ .tf-dev-toggle{display:inline-block;cursor:pointer;list-style:none;color:var(--text-2);font-size:14px;font-weight:600;padding:4px 0}
218
+ .tf-dev-toggle::-webkit-details-marker{display:none}
219
+ .tf-dev-toggle:after{content:" ▾"}.tf-dev[open] .tf-dev-toggle:after{content:" ▴"}
220
+ .tf-dev-body{display:flex;flex-direction:column;gap:16px;margin-top:12px}
221
+ .tf-dev-lead{margin:0;max-width:640px;font-size:14px;line-height:1.5;color:var(--text-2)}
222
+ .tf-dev .dt-row{align-items:start;padding:14px 20px}
223
+ .tf-dev-label{display:block;font-family:var(--mono);font-size:12.5px;font-weight:600;letter-spacing:0.02em;color:var(--text);white-space:nowrap}
224
+ .tf-dev-means{display:block;margin-top:4px;font-size:13px;line-height:1.4;color:var(--text-3)}
225
+ .tf-dev-count{display:block;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--text-2)}
226
+ .tf-dev-commands{display:flex;flex-direction:column;gap:12px;border-radius:12px;background:var(--panel);border:1px solid var(--white-08);padding:14px 18px}
227
+ .tf-dev-h{margin:0;font-size:12.5px;font-weight:600;color:var(--text-3)}
228
+ .tf-dev-command{display:flex;flex-direction:column;gap:3px}
229
+ .tf-dev-does{font-size:13px;color:var(--text-2)}
230
+ .tf-dev-command code{font-family:var(--mono);font-size:13px;color:var(--text-soft);overflow-wrap:anywhere}
231
+ @media (max-width:640px){.tf-window-head{padding:20px 18px 16px}.tf-window-body{padding:20px 18px}
232
+ .tf-where-row{grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"when see" "ask see";row-gap:4px}
233
+ .tf-where-when{grid-area:when;display:flex;gap:8px;align-items:baseline}.tf-where-ask{grid-area:ask}.tf-where-row>:last-child{grid-area:see}}
234
+ @media (max-width:760px){.tf-done-row{grid-template-columns:1fr}}
235
+ .sw-body .tabs-inset .tabs-pill{white-space:nowrap;flex:none}
236
+ @media (max-width:640px){.sw-body>.tabs>.tabs-bar{max-width:100%;overflow-x:auto;scrollbar-width:none}.sw-body .tabs-inset .tabs-pill{padding:7px 12px}}
237
+ `;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * What only To fix does (`.ai/plans/2026-09-23-to-fix-redesign.md`, step 3). The kit opens and closes the windows and
3
+ * switches the tabs, and the Fix it wizard writes a mark; this posts an undo, and reads the page again once the server
4
+ * has written either (R60), so the lists show what the record of marks holds and never what was clicked - after a mark,
5
+ * once the last window is closed, so "Next file" still opens the next one. A refusal is said in the server's words
6
+ * (R59). Opened as a file, nothing can be written, and the same button shows the command that would do it (T17).
7
+ */
8
+ export const TO_FIX_SCRIPT = String.raw `
9
+ (() => {
10
+ const root = document.querySelector('[data-to-fix]');
11
+ if (!root) return;
12
+ const served = location.protocol === 'http:' || location.protocol === 'https:';
13
+ const words = JSON.parse(root.dataset.fixWords || '{}');
14
+ const word = (key) => (words[document.documentElement.dataset.lang] || words.en || {})[key] || '';
15
+ const TABS = ['todo', 'done'];
16
+
17
+ // The tab a person was on survives the page being read again after a write.
18
+ const pills = [...root.querySelectorAll(':scope .tabs-bar [data-tab]')];
19
+ if (location.hash === '#done' && pills[1]) pills[1].click();
20
+ pills.forEach((pill, at) => pill.addEventListener('click', () => history.replaceState(null, '', '#' + TABS[at])));
21
+
22
+ const say = (where, text, command, busy) => {
23
+ const line = where && where.querySelector('[data-fix-say]');
24
+ if (!line) return;
25
+ line.textContent = text;
26
+ line.classList.toggle('tf-say-busy', busy === true);
27
+ if (command) {
28
+ const code = document.createElement('code');
29
+ code.textContent = command;
30
+ line.append(code);
31
+ }
32
+ line.hidden = false;
33
+ };
34
+
35
+ const quote = (text) => /^[\w@%+=:,./-]+$/.test(text) ? text : "'" + text.replace(/'/g, "'\\''") + "'";
36
+
37
+ const reload = () => {
38
+ const tab = TABS[pills.findIndex((pill) => pill.classList.contains('tabs-on'))] || TABS[0];
39
+ location.replace(location.pathname + '?at=' + Date.now() + '#' + tab);
40
+ };
41
+
42
+ // A mark the wizard wrote: the page is read again when no window is left open.
43
+ let marked = false;
44
+ root.addEventListener('wizard-marked', () => { marked = true; });
45
+ document.addEventListener('close', () => {
46
+ if (marked) setTimeout(() => { if (!document.querySelector('dialog[open]')) reload(); }, 0);
47
+ }, true);
48
+
49
+ const post = (url, body, where, button) => {
50
+ button.disabled = true;
51
+ say(where, word('saving'), '', true);
52
+ fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
53
+ // The server answers a refusal in JSON, and a request it does not take (a wrong origin, a lost token) in plain
54
+ // text: both are its answer, said as it gave it. Only a request that got no answer at all is "unreachable".
55
+ .then((response) => response.text().then((text) => {
56
+ let answer = {};
57
+ try { answer = JSON.parse(text); } catch { answer = { message: text.trim() }; }
58
+ return { ok: response.ok && answer.ok === true, message: answer.message || String(response.status) };
59
+ }), () => ({ ok: false, message: word('unreachable') }))
60
+ .then((answer) => {
61
+ if (answer.ok) { reload(); return; }
62
+ button.disabled = false;
63
+ say(where, word('refused') + ' ' + answer.message);
64
+ });
65
+ };
66
+
67
+ root.addEventListener('click', (event) => {
68
+ // "Fix it" in What happened: the wizard opens in its place, not over it.
69
+ const from = event.target.closest('[data-fix-from]');
70
+ if (from) {
71
+ const where = from.closest('dialog');
72
+ if (where && where.open) setTimeout(() => where.close(), 0);
73
+ return;
74
+ }
75
+
76
+ const unmark = event.target.closest('[data-fix-unmark]');
77
+ if (unmark) {
78
+ const where = unmark.closest('dialog');
79
+ const body = JSON.parse(unmark.dataset.fixUnmark);
80
+ if (!served) { say(where, word('asFile'), 'agentwhy check --unmark ' + quote(body.path)); return; }
81
+ post('api/unmark', body, where, unmark);
82
+ return;
83
+ }
84
+
85
+ const all = event.target.closest('[data-fix-all]');
86
+ if (all) {
87
+ // The list is the same in the wizard's fold, in What happened's Conversations tab and in the small window.
88
+ const section = all.closest('.tf-where-block');
89
+ if (section) section.querySelectorAll('.tf-more').forEach((row) => row.classList.remove('tf-more'));
90
+ all.hidden = true;
91
+ }
92
+ });
93
+ })();
94
+ `;
@@ -0,0 +1,124 @@
1
+ import { resultAllowed } from '../../check/marks.js';
2
+ import { matchesGlob } from '../../../core/policy/glob.js';
3
+ import { ruleKey } from '../../render/report-page/item-names.js';
4
+ import { NOTHING_READ, toDoItem } from '../../render/report-page/to-do.js';
5
+ import { localClock } from '../../render/ui/local-date.js';
6
+ import { ruleNameOf } from '../../rule-names.js';
7
+ const GROUP_OF = {
8
+ rotate: 'keys',
9
+ template: 'look',
10
+ // D1: an outcome nobody recorded is looked at, like a sample file, and closed as handled.
11
+ unknown: 'look',
12
+ route: 'seen',
13
+ result: 'seen',
14
+ };
15
+ /**
16
+ * T14: the buttons a file's window offers, in the order they are drawn, each the mark it writes. Only those
17
+ * `resultAllowed` lets a line take are offered: a file a key was read from is closed as rotated and nothing else.
18
+ */
19
+ const RESULTS_OF = {
20
+ rotate: ['rotated'],
21
+ template: ['not-secret', 'rotated'],
22
+ unknown: ['handled'],
23
+ route: [],
24
+ result: [],
25
+ };
26
+ /**
27
+ * T14: the one mark besides the wizard's Done: a sample file's "No real keys here", an unrecorded outcome's "I checked
28
+ * it", a file with no key in it "Nothing private in it" - each only where its line may take it.
29
+ */
30
+ const SKIP_OF = {
31
+ rotate: (item, allowed) => (item.kind === 'data' && allowed('not-private') ? 'not-private' : null),
32
+ template: (_item, allowed) => (allowed('not-secret') ? 'not-secret' : null),
33
+ // A file with no key in it is closed as handled by the wizard's Done already; its skip is the one left.
34
+ unknown: (item, allowed) => (item.kind === 'data' ? (allowed('not-private') ? 'not-private' : null) : allowed('handled') ? 'handled' : null),
35
+ route: () => null,
36
+ result: () => null,
37
+ };
38
+ export function toFixView(index) {
39
+ const clock = localClock(index.timeZone ?? 'UTC');
40
+ const today = clock(index.now).day.number;
41
+ const when = (at) => {
42
+ const { day, time } = clock(at);
43
+ const relative = day.number === today ? 'today' : day.number === today - 1 ? 'yesterday' : undefined;
44
+ return { at, day, time, ...(relative === undefined ? {} : { relative }) };
45
+ };
46
+ const patterns = index.settings?.protected ?? [];
47
+ const byName = new Map(index.entries.map((entry) => [entry.name, entry]));
48
+ const check = index.check;
49
+ const file = (row) => {
50
+ const name = ruleNameOf(row.path, patterns);
51
+ const conversations = row.sessions
52
+ .flatMap((session) => {
53
+ const entry = byName.get(session);
54
+ return entry === undefined ? [] : [{
55
+ entry,
56
+ when: when(entry.modifiedAt),
57
+ ...(entry.report.kind === 'generated' ? { report: entry.report.file } : {}),
58
+ }];
59
+ })
60
+ .sort((a, b) => b.when.at - a.when.at);
61
+ // A key was read from it, so "No real keys" would contradict what the record holds: only rotated closes it,
62
+ // though the server would take not-secret for any sample file (`resultAllowed`).
63
+ const allowed = (result) => resultAllowed(row, result) && !(row.keyed === true && result === 'not-secret');
64
+ // The built-in rule's own pattern names the file for the wizard (F45): a rule's file holds keys.
65
+ const pattern = patterns.find((each) => ruleKey(each) !== undefined && matchesGlob(row.path, each));
66
+ const item = toDoItem(row.path, row.read ?? NOTHING_READ, {
67
+ template: row.label === 'template',
68
+ further: false,
69
+ readers: 1,
70
+ ...(pattern === undefined ? {} : { pattern: pattern }),
71
+ });
72
+ const told = conversations.flatMap((conversation) => {
73
+ const stories = conversation.entry.report.kind === 'generated' ? conversation.entry.report.stories : undefined;
74
+ const story = stories?.files.get(row.path);
75
+ return story === undefined || stories === undefined ? [] : [{ story, sessionId: stories.sessionId, conversation }];
76
+ })[0];
77
+ return {
78
+ path: row.path,
79
+ label: row.label,
80
+ ...(told === undefined ? {} : { story: told }),
81
+ ...(name === undefined ? {} : { name }),
82
+ ...(row.reopened === undefined ? {} : { reopened: when(row.reopened.at) }),
83
+ results: RESULTS_OF[row.label].filter(allowed),
84
+ item,
85
+ skip: SKIP_OF[row.label](item, allowed),
86
+ conversations,
87
+ count: row.sessions.length,
88
+ };
89
+ };
90
+ const rows = (check?.rows ?? []).map((row) => ({ row, fix: file(row) }));
91
+ const inGroup = (group) => rows.filter(({ row }) => GROUP_OF[row.label] === group).map(({ fix }) => fix).sort(byUrgency);
92
+ const keys = inGroup('keys');
93
+ const look = inGroup('look');
94
+ const done = (check?.history ?? [])
95
+ .filter((line) => line.status === 'standing' && !line.reopened)
96
+ .map((line) => {
97
+ const name = ruleNameOf(line.path, patterns);
98
+ return {
99
+ path: line.path,
100
+ label: line.label,
101
+ ...(name === undefined ? {} : { name }),
102
+ result: line.result,
103
+ when: when(line.at),
104
+ ...(line.note === undefined ? {} : { note: line.note }),
105
+ };
106
+ })
107
+ .sort((a, b) => b.when.at - a.when.at);
108
+ return {
109
+ keys,
110
+ look,
111
+ seen: inGroup('seen'),
112
+ done,
113
+ refusedAttempts: check?.refusedAttempts ?? 0,
114
+ state: keys.length > 0 ? 'keys' : look.length > 0 ? 'look' : 'none',
115
+ total: keys.length + look.length,
116
+ marksUnreadable: check?.marksUnreadable === true,
117
+ };
118
+ }
119
+ /** T8: the files more conversations reached first, as the design orders them, then by path so the order is stable. */
120
+ function byUrgency(a, b) {
121
+ if (a.count !== b.count)
122
+ return b.count - a.count;
123
+ return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
124
+ }
@@ -0,0 +1,72 @@
1
+ export const ALERT_THRESHOLDS = ['value', 'reached', 'refused'];
2
+ /**
3
+ * What is said when nobody has said otherwise: only a value written from a protected file. The other two are worth
4
+ * having and worth asking for; on every session they are a line about work that went as it should.
5
+ */
6
+ export const DEFAULT_THRESHOLD = 'value';
7
+ /**
8
+ * What can be said about one agent of a report, read from the report model and nothing else (R6): its return, which
9
+ * already looks at every agent below it, and the graph for an agent no delegation recorded, which has no return.
10
+ */
11
+ export function alertOf(report, agentIndex) {
12
+ // The session's own agent is asked about too, on `Stop` (`the-agent-nobody-watches.md` R2): it is the one agent
13
+ // with no `SubagentStop`, and the graph keeps it apart from the agents it delegated to.
14
+ const everyone = [report.graph.main, ...report.graph.agents];
15
+ const node = everyone.find((agent) => agent.index === agentIndex);
16
+ if (node === undefined)
17
+ return undefined;
18
+ // Handed the root, this subtree is every agent of the session, which is what a session-wide question needs (R5).
19
+ const subtree = subtreeOf(everyone, node);
20
+ const own = report.returns.filter((statement) => statement.agentIndex === agentIndex);
21
+ const wroteOrReturnedValue = own.some((statement) => statement.strength === 'value' || statement.writtenFrom.length > 0);
22
+ const reachedFromReturn = own.reduce((most, statement) => Math.max(most, statement.filesReached), 0);
23
+ const reachedBelow = subtree.filter((agent) => agent !== node).some((agent) => agent.filesReached > 0);
24
+ /*
25
+ * The session's own agent answers a person, not an agent that delegated to it, so no return statement says what it
26
+ * carried, and `wroteValue` and `wroteOnward` - written for what came back from a delegated agent - stay unset on
27
+ * it. What there is to read for it is the traced use itself: measured on a real session where the main agent
28
+ * searched, printed a value from a protected file and the model recorded one `use`, while the flags read false and
29
+ * the level came out `reached`. A value in the conversation is not a path in a result, and it is not said as one.
30
+ */
31
+ const usedItself = report.uses.some((statement) => statement.agentIndex === node.index);
32
+ const wroteItself = node.wroteValue === true || node.wroteOnward === true || usedItself;
33
+ const reached = reachedFromReturn > 0 || node.filesReached > 0 || reachedBelow;
34
+ const refusedAttempts = subtree.reduce((sum, agent) => sum + agent.refusedAttempts, 0);
35
+ const filesReached = own.length > 0 ? reachedFromReturn : reachedBelow ? undefined : node.filesReached;
36
+ const level = wroteOrReturnedValue || wroteItself ? 'value' : reached ? 'reached' : refusedAttempts > 0 ? 'refused' : 'nothing';
37
+ return {
38
+ level,
39
+ ...(node === report.graph.main ? { own: true } : {}),
40
+ ...(node.type === undefined ? {} : { type: node.type }),
41
+ ...(filesReached === undefined ? {} : { filesReached }),
42
+ refusedAttempts,
43
+ };
44
+ }
45
+ /** Whether a level is strong enough to be said, under the threshold asked for. */
46
+ export function alerts(level, threshold) {
47
+ switch (level) {
48
+ case 'value':
49
+ return true;
50
+ case 'reached':
51
+ return threshold !== 'value';
52
+ case 'refused':
53
+ return threshold === 'refused';
54
+ case 'nothing':
55
+ return false;
56
+ }
57
+ }
58
+ /** The agent and every agent below it, by the parents the graph established - never by depth or order. */
59
+ function subtreeOf(agents, root) {
60
+ const below = new Set([root.index]);
61
+ let grew = true;
62
+ while (grew) {
63
+ grew = false;
64
+ for (const agent of agents) {
65
+ if (agent.parentIndex !== undefined && below.has(agent.parentIndex) && !below.has(agent.index)) {
66
+ below.add(agent.index);
67
+ grew = true;
68
+ }
69
+ }
70
+ }
71
+ return agents.filter((agent) => below.has(agent.index));
72
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The choices a notice can be given, in a module of their own so that both the code that says a notice and the code
3
+ * that remembers what a person chose can name them without depending on each other.
4
+ */
5
+ export const NOTICE_CHANNELS = ['chat', 'terminal', 'os'];
6
+ /**
7
+ * What `--notify` uses when nobody says otherwise: the line in the conversation, and no pop-up. The other two
8
+ * arrive the moment an agent finishes rather than at the end of the turn, which is worth having and worth asking
9
+ * for; on a first run they are three notices for one finding.
10
+ */
11
+ export const DEFAULT_CHANNELS = ['chat'];
12
+ export const CLEAN_MODES = ['off', 'once', 'every-turn'];
13
+ /**
14
+ * What `--clean` does when nobody says otherwise. Silence is ambiguous - a hook that failed quietly reads exactly
15
+ * like an agent that did nothing - and one line per session is what it costs to tell those apart.
16
+ */
17
+ export const DEFAULT_CLEAN = 'once';
18
+ export const SAID_AS = ['line', 'agent'];
19
+ /**
20
+ * What a `value` does when nobody has said otherwise. B9a measured that the agent is continued and answers; B9d
21
+ * measured eleven sessions of eleven relaying these words, never going back to the file and never repeating a
22
+ * value. A grey line under a reply is read as a debug note; this is the finding that only rotating a key undoes.
23
+ */
24
+ export const DEFAULT_SAID_AS = 'agent';
25
+ export const NOTICE_LANGS = ['en', 'pl', 'de'];
26
+ /** What a line is written in when nobody chose and the system says nothing this tool knows. */
27
+ export const DEFAULT_LANG = 'en';
28
+ /**
29
+ * The language a system locale names - `pl_PL.UTF-8`, `de-DE` - when it is one a line is written in. `C` and `POSIX`
30
+ * name none, and neither does a locale this tool has no words for.
31
+ */
32
+ export function langOfLocale(locale) {
33
+ const code = locale?.slice(0, 2).toLowerCase();
34
+ return NOTICE_LANGS.find((lang) => lang === code);
35
+ }
@@ -0,0 +1,65 @@
1
+ import { dirname } from 'node:path';
2
+ import { longestMatch, NO_PREFERENCES, preferencesFor, readPreferences, withProjectChoices, writtenPreferences, } from './preferences.js';
3
+ /**
4
+ * Reading and changing what a person wants to be told (`specs/2026-09-21-the-agent-tells-you.md` R22-R26). The hook
5
+ * only ever reads this file; this is the one place that writes it, and the page's Settings view writes through here
6
+ * rather than through a second copy of the rules.
7
+ */
8
+ export class NoticeSettings {
9
+ #dependencies;
10
+ constructor(dependencies) {
11
+ this.#dependencies = dependencies;
12
+ }
13
+ async view() {
14
+ const { path, workingDirectory } = this.#dependencies;
15
+ const read = readPreferences(await this.#text());
16
+ const preferences = read.kind === 'read' ? read.preferences : NO_PREFERENCES;
17
+ return {
18
+ path,
19
+ project: workingDirectory,
20
+ defaults: preferences.defaults,
21
+ // The same project `effective` resolves to (R24): the longest ancestor match, not an exact key alone - a
22
+ // directory inside a project a choice was made for is still governed by it, and must be attributed to it too.
23
+ forProject: longestMatch(preferences.projects, workingDirectory) ?? {},
24
+ effective: preferencesFor(preferences, workingDirectory),
25
+ unusable: read.kind === 'unusable',
26
+ };
27
+ }
28
+ /**
29
+ * One change, written whole. A file this version cannot read is not edited around: it is replaced by what is being
30
+ * asked for now, and the answer says so, because merging into a shape nobody recognised is how a choice quietly
31
+ * becomes something else.
32
+ */
33
+ async change(change) {
34
+ const { files: _files, writer, path, workingDirectory } = this.#dependencies;
35
+ const read = readPreferences(await this.#text());
36
+ const before = read.kind === 'read' ? read.preferences : NO_PREFERENCES;
37
+ const after = changed(before, change, workingDirectory);
38
+ try {
39
+ await writer.ensureDirectory(dirname(path));
40
+ await writer.writeText(path, writtenPreferences(after));
41
+ }
42
+ catch {
43
+ return { written: false, said: `Nothing was written: ${path} could not be written to.` };
44
+ }
45
+ const where = change.scope === 'project' ? workingDirectory : 'every project';
46
+ const what = change.reset === true ? 'Answers taken out' : 'Answers written';
47
+ const replaced = read.kind === 'unusable' ? ' The file could not be read, so it was replaced.' : '';
48
+ return { written: true, said: `${what} for ${where}, in ${path}.${replaced}` };
49
+ }
50
+ async #text() {
51
+ try {
52
+ return await this.#dependencies.files.readText(this.#dependencies.path);
53
+ }
54
+ catch {
55
+ return undefined;
56
+ }
57
+ }
58
+ }
59
+ function changed(before, change, workingDirectory) {
60
+ if (change.scope === 'everywhere') {
61
+ return { defaults: change.reset === true ? {} : { ...before.defaults, ...change.choices }, projects: before.projects };
62
+ }
63
+ const project = before.projects[workingDirectory] ?? {};
64
+ return withProjectChoices(before, workingDirectory, change.reset === true ? {} : { ...project, ...change.choices });
65
+ }