@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,128 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { parseSince } from '../../core/session-filter.js';
3
+ import { isMarkResult, MARK_RESULTS } from '../../ports/mark-store.js';
4
+ import { EXIT_CODE } from '../exit-codes.js';
5
+ export const CHECK_USAGE = `Usage: agentwhy check [--since <span | date>] [--full] [--policy <file> | --settings <file>]
6
+ [--share]
7
+ agentwhy check --mark rotated|not-secret|handled|not-private <path> [--note <text>]
8
+ [--since <span | date>]
9
+ agentwhy check --unmark <path>
10
+
11
+ Reads every session of the current directory's project active inside the range and says what to
12
+ do about them: one line per file, the strongest thing known about it first. Writes nothing.
13
+
14
+ ROTATE a value from this file was in an agent's context
15
+ CHECK the same, for a file named as a template - is the value in it real?
16
+ UNKNOWN a call named it and its outcome was not recorded
17
+ REACHED a call opened it and nothing refused the call
18
+ IN RESULT it was printed by a search; no call named it
19
+
20
+ --since <span|date> sessions last active since then: 7d, 12h, 2w, or 2026-09-01 (UTC). Default 7d
21
+ --full every section, with what each one means and the rule that would close it
22
+ --policy <file> the policy the sessions are read under, as for \`report\`
23
+ --settings <file> a settings file whose deny rules stand in for a policy, as for \`report\`.
24
+ Without either, the built-in default is used and the output says so
25
+ --share paths relative to the project root, nothing above it
26
+ --mark <result> <path>
27
+ record that you dealt with one line: rotated (any line), not-secret (a template
28
+ line whose values are placeholders), handled (a file with no key in it, whose
29
+ contents cannot be changed: you did what could be done), or not-private (a file
30
+ with no key in it that holds nothing private). The file leaves the check until a session active
31
+ after the mark reaches it again. Kept for you alone, outside the project:
32
+ ~/.agentwhy/projects/<project>/marks.jsonl
33
+ --note <text> with --mark: one line saying what was done, at most 200 characters
34
+ --unmark <path> undo a standing mark; the history keeps both
35
+ -h, --help this text
36
+
37
+ Exit code 0 when the check ran, whatever it found, and when a mark was recorded; 2 for bad arguments, a refused
38
+ policy or a mark that names no line; 3 when a mark could not be written.
39
+ `;
40
+ // A Record over the outcome union: a new outcome does not compile until it has an exit code.
41
+ const EXIT_CODE_BY_OUTCOME = {
42
+ checked: EXIT_CODE.ok,
43
+ 'no-sessions': EXIT_CODE.ok,
44
+ 'policy-refused': EXIT_CODE.usage,
45
+ marked: EXIT_CODE.ok,
46
+ 'mark-refused': EXIT_CODE.usage,
47
+ 'mark-failed': EXIT_CODE.unexpected,
48
+ };
49
+ /** `agentwhy check` - what to do now (`specs/2026-09-16-worth-running-every-day.md` R11-R16). */
50
+ export class CheckCliCommand {
51
+ name = 'check';
52
+ usage = CHECK_USAGE;
53
+ #dependencies;
54
+ constructor(dependencies) {
55
+ this.#dependencies = dependencies;
56
+ }
57
+ async execute(args) {
58
+ let values;
59
+ let positionals;
60
+ try {
61
+ ({ values, positionals } = parseArgs({
62
+ args: [...args],
63
+ options: {
64
+ since: { type: 'string', default: '7d' },
65
+ full: { type: 'boolean', default: false },
66
+ policy: { type: 'string' },
67
+ settings: { type: 'string' },
68
+ share: { type: 'boolean', default: false },
69
+ mark: { type: 'string' },
70
+ note: { type: 'string' },
71
+ unmark: { type: 'string' },
72
+ help: { type: 'boolean', short: 'h', default: false },
73
+ },
74
+ strict: true,
75
+ allowPositionals: true,
76
+ }));
77
+ }
78
+ catch (error) {
79
+ return { kind: 'usage-error', message: error instanceof Error ? error.message : String(error), usage: this.usage };
80
+ }
81
+ if (values.help)
82
+ return { kind: 'help', usage: this.usage };
83
+ const usageError = (message) => ({ kind: 'usage-error', message, usage: this.usage });
84
+ if (values.unmark !== undefined) {
85
+ if (values.mark !== undefined || values.note !== undefined || positionals.length > 0) {
86
+ return usageError('--unmark takes one path and nothing else');
87
+ }
88
+ const result = await this.#dependencies.check.unmark({ path: values.unmark });
89
+ return { kind: 'completed', output: result.output, exitCode: EXIT_CODE_BY_OUTCOME[result.outcome] };
90
+ }
91
+ if (values.mark === undefined) {
92
+ if (positionals.length > 0)
93
+ return usageError(`unexpected argument: ${positionals[0]}`);
94
+ if (values.note !== undefined)
95
+ return usageError('--note is given with --mark');
96
+ }
97
+ else {
98
+ if (!isMarkResult(values.mark))
99
+ return usageError(`--mark is ${MARK_RESULTS.join(', ')}`);
100
+ if (positionals.length !== 1)
101
+ return usageError('--mark takes one path: agentwhy check --mark rotated <path>');
102
+ if (values.full || values.share)
103
+ return usageError('--mark is not given with --full or --share');
104
+ }
105
+ const since = parseSince(values.since, this.#dependencies.now);
106
+ if ('error' in since)
107
+ return usageError(since.error);
108
+ if (isMarkResult(values.mark)) {
109
+ const result = await this.#dependencies.check.mark({
110
+ since,
111
+ path: positionals[0] ?? '',
112
+ result: values.mark,
113
+ ...(values.note === undefined ? {} : { note: values.note }),
114
+ ...(values.policy === undefined ? {} : { policyPath: values.policy }),
115
+ ...(values.settings === undefined ? {} : { settingsPath: values.settings }),
116
+ });
117
+ return { kind: 'completed', output: result.output, exitCode: EXIT_CODE_BY_OUTCOME[result.outcome] };
118
+ }
119
+ const result = await this.#dependencies.check.run({
120
+ since,
121
+ share: values.share,
122
+ full: values.full,
123
+ ...(values.policy === undefined ? {} : { policyPath: values.policy }),
124
+ ...(values.settings === undefined ? {} : { settingsPath: values.settings }),
125
+ });
126
+ return { kind: 'completed', output: result.output, exitCode: EXIT_CODE_BY_OUTCOME[result.outcome] };
127
+ }
128
+ }
@@ -0,0 +1,27 @@
1
+ import { codexStopInstruction } from '../../refuse/render/codex-stop-words.js';
2
+ import { EXIT_CODE } from '../exit-codes.js';
3
+ const USAGE = `Usage: agentwhy codex-stop --codex
4
+
5
+ Run by the Codex Stop hook. When agentwhy refused a shell command in this turn, asks Codex to
6
+ explain the block in its reply. It reads the turn's local transcript and never sends it anywhere.
7
+ `;
8
+ export class CodexStopCliCommand {
9
+ name = 'codex-stop';
10
+ usage = USAGE;
11
+ #refusals;
12
+ #interactive;
13
+ constructor(refusals, interactive) {
14
+ this.#refusals = refusals;
15
+ this.#interactive = interactive;
16
+ }
17
+ async execute(args) {
18
+ if (args.length !== 1 || args[0] !== '--codex' || this.#interactive)
19
+ return { kind: 'usage-error', message: 'codex-stop reads Codex hook input on standard input', usage: this.usage };
20
+ const refusals = await this.#refusals.find();
21
+ return {
22
+ kind: 'completed',
23
+ output: refusals.length === 0 ? '' : JSON.stringify({ decision: 'block', reason: codexStopInstruction(refusals) }),
24
+ exitCode: EXIT_CODE.ok,
25
+ };
26
+ }
27
+ }
@@ -0,0 +1,32 @@
1
+ import { parseArgs } from 'node:util';
2
+ /** Parses the arguments after `doctor`. Never throws: bad input is a usage error. */
3
+ export function parseDoctorArguments(args) {
4
+ let values;
5
+ try {
6
+ values = parseValues(args);
7
+ }
8
+ catch (error) {
9
+ return usageError(error instanceof Error ? error.message : String(error));
10
+ }
11
+ if (values.help)
12
+ return { kind: 'help' };
13
+ if (values.input === undefined || values.input === '') {
14
+ return usageError('doctor needs --input <session-dir | session.jsonl>');
15
+ }
16
+ return { kind: 'options', options: { input: values.input, format: values.json ? 'json' : 'text' } };
17
+ }
18
+ function parseValues(args) {
19
+ return parseArgs({
20
+ args: [...args],
21
+ options: {
22
+ input: { type: 'string' },
23
+ json: { type: 'boolean', default: false },
24
+ help: { type: 'boolean', short: 'h', default: false },
25
+ },
26
+ strict: true,
27
+ allowPositionals: false,
28
+ }).values;
29
+ }
30
+ function usageError(message) {
31
+ return { kind: 'usage-error', message };
32
+ }
@@ -0,0 +1,31 @@
1
+ import { EXIT_CODE } from '../exit-codes.js';
2
+ import { parseDoctorArguments } from './doctor-arguments.js';
3
+ import { DOCTOR_USAGE } from './doctor-usage.js';
4
+ // A Record over the outcome union: a new outcome does not compile until it has an exit code.
5
+ const EXIT_CODE_BY_OUTCOME = {
6
+ complete: EXIT_CODE.ok,
7
+ 'main-transcript-missing': EXIT_CODE.mainTranscriptMissing,
8
+ // No session found, for the same reason a missing transcript is: nothing here is a report on a session.
9
+ 'unknown-format': EXIT_CODE.mainTranscriptMissing,
10
+ };
11
+ export class DoctorCliCommand {
12
+ name = 'doctor';
13
+ usage = DOCTOR_USAGE;
14
+ #doctor;
15
+ constructor(doctor) {
16
+ this.#doctor = doctor;
17
+ }
18
+ async execute(args) {
19
+ const parsed = parseDoctorArguments(args);
20
+ switch (parsed.kind) {
21
+ case 'help':
22
+ return { kind: 'help', usage: this.usage };
23
+ case 'usage-error':
24
+ return { kind: 'usage-error', message: parsed.message, usage: this.usage };
25
+ case 'options': {
26
+ const result = await this.#doctor.run(parsed.options);
27
+ return { kind: 'completed', output: result.output, exitCode: EXIT_CODE_BY_OUTCOME[result.outcome] };
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,14 @@
1
+ export const DOCTOR_USAGE = `Usage: agentwhy doctor --input <session-dir | session.jsonl | rollout.jsonl | rollout-folder> [--json]
2
+
3
+ Reports the structure of one Claude Code session - its sources, line types, tools,
4
+ denials and meta fields - or of Codex rollouts: their versions, history modes, record
5
+ kinds, joins and what they can answer. The format is told by content, not by name.
6
+ Prints counts and identifiers only, never transcript content.
7
+
8
+ Exit codes:
9
+ 0 report printed
10
+ 1 no session found: the main transcript is missing, or the input is no Claude Code
11
+ or Codex session
12
+ 2 usage error
13
+ 3 unexpected error
14
+ `;
@@ -0,0 +1,123 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { EXIT_CODE } from '../exit-codes.js';
3
+ export const INIT_USAGE = `Usage: agentwhy init [--watch] [--refuse] [--protect <pattern>] [--shared] [--codex]
4
+ [--remove [--unprotect <pattern>]] [--yes] [--command <cmd>]
5
+ agentwhy init --update [--yes]
6
+
7
+ Sets up the current project, and undoes it. At a terminal it asks who it is for, what agentwhy
8
+ should do here and what else to protect, then shows what it will write and asks. It writes to
9
+ .claude/settings.local.json, the settings file that is yours alone and stays out of the repository,
10
+ unless you say the change is for everyone. In a project that uses Codex, it writes refuse to
11
+ .codex/hooks.json too, with the same rules, so a file blocked here is blocked in Codex as well -
12
+ once you approve agentwhy's hook in Codex, which runs a new hook only after that.
13
+
14
+ --watch agentwhy watch, when a delegated agent finishes: a notification when it
15
+ wrote a value from a protected file. Ticked in the list, and the one
16
+ installed when nobody can be asked
17
+ --refuse agentwhy refuse, before every shell command: refuses one that names a
18
+ protected path or searches through one. It also refuses cat .env.example,
19
+ and it matches the text of a command, so it is not a boundary
20
+ --protect <pattern> a path or pattern to protect, written as Read()/Edit() deny rules, with
21
+ refuse installed too, so a shell command cannot print it either. Repeat
22
+ for several. At a terminal you are asked for these as well
23
+ --shared write to .claude/settings.json, which is committed, so the hooks run for
24
+ everyone who clones. Default: .claude/settings.local.json, which is yours
25
+ --codex write Codex's hook too, where nothing shows the project uses Codex yet. Alone,
26
+ it writes only that, to match the refuse Claude Code already runs here
27
+ --remove undo it. At a terminal it asks what to take out - each hook, and each deny
28
+ rule by the path it protects - and removes only what is ticked. With
29
+ --watch or --refuse, only that hook; deny rules then stay
30
+ --unprotect <path> with --remove, off a terminal: a protected path to take the deny rules of.
31
+ Repeat for several. A rule is never removed unless it is named or ticked
32
+ --yes write without asking, and without asking anything else either
33
+ --update pin the hooks that run an older release of agentwhy through npx to this
34
+ one's version, and change nothing else. agentwhy never updates by itself
35
+ --command <cmd> how the hooks run agentwhy. Default: the way the hooks already here run it,
36
+ else the way you ran this - agentwhy where that is the agentwhy on PATH,
37
+ npx @agentwhy/cli@<this version> otherwise, pinned so that nothing updates
38
+ until --update. Give the same --command to --remove when the command
39
+ does not name agentwhy
40
+ -h, --help this text
41
+
42
+ A deny rule names a tool, not a file: Read(.env) stops the Read tool, while cat .env in a shell
43
+ walks past it. refuse narrows that route by reading the command line. Neither is a boundary below the agent.
44
+
45
+ Exit code 0 when the file was written, was already right, or nothing was asked to be written;
46
+ 2 for bad arguments or a settings file that is not a JSON object.
47
+ `;
48
+ // A Record over the outcome union: a new outcome does not compile until it has an exit code.
49
+ const EXIT_CODE_BY_OUTCOME = {
50
+ written: EXIT_CODE.ok,
51
+ unchanged: EXIT_CODE.ok,
52
+ declined: EXIT_CODE.ok,
53
+ 'not-confirmed': EXIT_CODE.ok,
54
+ refused: EXIT_CODE.usage,
55
+ unwritable: EXIT_CODE.usage,
56
+ };
57
+ /** `agentwhy init` - one command to set a project up (`specs/2026-09-16-worth-running-every-day.md` R4-R10). */
58
+ export class InitCliCommand {
59
+ name = 'init';
60
+ usage = INIT_USAGE;
61
+ #setup;
62
+ constructor(setup) {
63
+ this.#setup = setup;
64
+ }
65
+ async execute(args) {
66
+ let values;
67
+ try {
68
+ ({ values } = parseArgs({
69
+ args: [...args],
70
+ options: {
71
+ watch: { type: 'boolean', default: false },
72
+ refuse: { type: 'boolean', default: false },
73
+ protect: { type: 'string', multiple: true, default: [] },
74
+ unprotect: { type: 'string', multiple: true, default: [] },
75
+ shared: { type: 'boolean', default: false },
76
+ remove: { type: 'boolean', default: false },
77
+ yes: { type: 'boolean', short: 'y', default: false },
78
+ command: { type: 'string' },
79
+ update: { type: 'boolean', default: false },
80
+ codex: { type: 'boolean', default: false },
81
+ help: { type: 'boolean', short: 'h', default: false },
82
+ },
83
+ strict: true,
84
+ allowPositionals: false,
85
+ }));
86
+ }
87
+ catch (error) {
88
+ return { kind: 'usage-error', message: error instanceof Error ? error.message : String(error), usage: this.usage };
89
+ }
90
+ if (values.help)
91
+ return { kind: 'help', usage: this.usage };
92
+ // `nothing-updates-by-itself.md` U7: an update changes versions and nothing else, so no other flag means anything beside it.
93
+ if (values.update && (values.watch || values.refuse || values.protect.length > 0 || values.unprotect.length > 0 || values.shared || values.remove || values.codex || values.command !== undefined)) {
94
+ return { kind: 'usage-error', message: '--update takes no other flag but --yes', usage: this.usage };
95
+ }
96
+ if (values.update) {
97
+ const result = await this.#setup.run({ protect: [], remove: false, yes: values.yes, update: true });
98
+ return { kind: 'completed', output: result.output, exitCode: EXIT_CODE_BY_OUTCOME[result.outcome] };
99
+ }
100
+ if (values.command?.trim() === '')
101
+ return { kind: 'usage-error', message: '--command is empty', usage: this.usage };
102
+ if (values.remove && values.protect.length > 0) {
103
+ return { kind: 'usage-error', message: '--remove undoes; to protect a path, run it without --remove', usage: this.usage };
104
+ }
105
+ if (!values.remove && values.unprotect.length > 0) {
106
+ return { kind: 'usage-error', message: '--unprotect takes rules out, so it belongs with --remove', usage: this.usage };
107
+ }
108
+ // Named hooks are the whole answer where they are given; with none, the list asks, or `watch` is installed.
109
+ const hooks = [...(values.watch ? ['watch'] : []), ...(values.refuse ? ['refuse'] : [])];
110
+ const result = await this.#setup.run({
111
+ ...(hooks.length === 0 ? {} : { hooks }),
112
+ ...(values.shared ? { target: 'shared' } : {}),
113
+ protect: values.protect,
114
+ ...(values.unprotect.length === 0 ? {} : { unprotect: values.unprotect }),
115
+ remove: values.remove,
116
+ yes: values.yes,
117
+ ...(values.codex ? { codex: true } : {}),
118
+ // Where none was given, the setup settles it (`a-hook-runs-what-you-ran.md` J1).
119
+ ...(values.command === undefined ? {} : { invoke: values.command.trim() }),
120
+ });
121
+ return { kind: 'completed', output: result.output, exitCode: EXIT_CODE_BY_OUTCOME[result.outcome] };
122
+ }
123
+ }
@@ -0,0 +1,58 @@
1
+ import { EXIT_CODE } from '../exit-codes.js';
2
+ export const MENU_USAGE = `Usage: agentwhy menu
3
+
4
+ At a terminal: a list of the few things to do here. Pick one, it runs, you read what it said, and
5
+ the list comes back so you can do the next thing. Leaving the list ends it, and so does Quit.
6
+ Nothing is served by the list itself and nothing keeps running between those runs. It needs a
7
+ terminal: piped or redirected, it prints this help and exits 2. \`agentwhy\` alone opens the page
8
+ of sessions, the same as \`agentwhy start\`.
9
+ `;
10
+ /**
11
+ * `agentwhy menu` (`specs/2026-09-16-worth-running-every-day.md` R24, R74). A way on, and a way in for someone who
12
+ * does not know the commands: what is picked runs, what it said is printed, and the list comes back until the person
13
+ * leaves it. The list itself browses and serves nothing - each run is the same command it would have been from the
14
+ * shell - the same rule the rest of the tool follows (main spec §2).
15
+ */
16
+ export class MenuCliCommand {
17
+ name = 'menu';
18
+ usage = MENU_USAGE;
19
+ #dependencies;
20
+ constructor(dependencies) {
21
+ this.#dependencies = dependencies;
22
+ }
23
+ async execute(args) {
24
+ const { chooser, printer, entries, interactive } = this.#dependencies;
25
+ if (args.length > 0)
26
+ return { kind: 'usage-error', message: 'menu takes no arguments', usage: this.usage };
27
+ if (!interactive)
28
+ return { kind: 'usage-error', message: 'menu needs a terminal', usage: this.usage };
29
+ const rows = [...entries.map(({ label, detail }) => ({ label, detail })), { label: 'Quit', detail: 'leave agentwhy' }];
30
+ let exitCode = EXIT_CODE.ok;
31
+ for (let first = true;; first = false) {
32
+ const chosen = await chooser.choose(first ? 'What do you want to do?' : 'What now?', rows);
33
+ const entry = chosen === undefined ? undefined : entries[chosen];
34
+ // Leaving the list, or Quit, ends it: the exit code is that of the last thing that ran.
35
+ if (entry === undefined)
36
+ return { kind: 'completed', output: '', exitCode };
37
+ const result = await entry.command.execute([]);
38
+ // Written as it happens, because the list comes back afterwards and an answer held until the end is read last.
39
+ // The blank lines keep what a command said apart from the next question; on screen they run together otherwise.
40
+ printer.write(`\n${textOf(result).replace(/\n*$/, '\n')}\n`);
41
+ if (result.kind === 'completed')
42
+ exitCode = result.exitCode;
43
+ }
44
+ }
45
+ }
46
+ /** What a command said, whatever kind of answer it gave: a usage error is read the same way at a terminal. */
47
+ function textOf(result) {
48
+ switch (result.kind) {
49
+ case 'completed':
50
+ return result.output;
51
+ case 'help':
52
+ return result.usage;
53
+ case 'usage-error':
54
+ return `agentwhy: ${result.message}\n\n${result.usage}`;
55
+ case 'hook-block':
56
+ return result.reason;
57
+ }
58
+ }
@@ -0,0 +1,158 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { ALERT_THRESHOLDS, DEFAULT_THRESHOLD } from '../../report/watch/agent-alert.js';
3
+ import { CLEAN_MODES, DEFAULT_CHANNELS, DEFAULT_CLEAN, DEFAULT_SAID_AS, NOTICE_CHANNELS, NOTICE_LANGS, SAID_AS, } from '../../report/watch/notice-choices.js';
4
+ import { EXIT_CODE } from '../exit-codes.js';
5
+ export const NOTIFY_USAGE = `Usage: agentwhy notify [--on value | reached | refused] [--clean off | once | every-turn]
6
+ [--say agent | line] [--notify chat,terminal,os] [--lang en | pl | de]
7
+ [--everywhere] [--reset]
8
+
9
+ What agentwhy tells you when a turn ends, and when. Run with nothing, it says what is set now and
10
+ where it comes from; with a choice, it writes that choice and the next turn uses it - no session
11
+ restarted, no settings file touched.
12
+
13
+ --on <level> which findings are said: value (default), reached, or refused
14
+ --clean <when> when a turn that found nothing says so: once (default), off, or every-turn
15
+ --say <how> how a value in the conversation is said: agent (default) - your own agent
16
+ says it and offers the report - or line, the grey line under the reply
17
+ --notify <channels> where a notice is shown, comma-separated: chat, terminal, os
18
+ --lang <language> the language of the line in the conversation: en, pl or de. Unset, it is
19
+ your system's where agentwhy has words for it, else en
20
+ --everywhere your answer for every project, instead of this one
21
+ --reset take out the answers for this project, or with --everywhere, yours
22
+ -h, --help this text
23
+
24
+ Answers are read in this order: a flag written into a hook's command line, then this project's
25
+ answer, then yours, then agentwhy's own. They live in one file outside every project, so nothing
26
+ here is ever written into a repository.
27
+ `;
28
+ /** `agentwhy notify` - the command line half of `specs/2026-09-21-the-agent-tells-you.md` R26. */
29
+ export class NotifyCliCommand {
30
+ name = 'notify';
31
+ usage = NOTIFY_USAGE;
32
+ #dependencies;
33
+ constructor(dependencies) {
34
+ this.#dependencies = dependencies;
35
+ }
36
+ async execute(args) {
37
+ let values;
38
+ try {
39
+ ({ values } = parseArgs({
40
+ args: [...args],
41
+ options: {
42
+ on: { type: 'string' },
43
+ clean: { type: 'string' },
44
+ say: { type: 'string' },
45
+ notify: { type: 'string' },
46
+ lang: { type: 'string' },
47
+ everywhere: { type: 'boolean', default: false },
48
+ reset: { type: 'boolean', default: false },
49
+ help: { type: 'boolean', short: 'h', default: false },
50
+ },
51
+ strict: true,
52
+ allowPositionals: false,
53
+ }));
54
+ }
55
+ catch (error) {
56
+ return { kind: 'usage-error', message: error instanceof Error ? error.message : 'that is not an option', usage: this.usage };
57
+ }
58
+ if (values.help)
59
+ return { kind: 'help', usage: this.usage };
60
+ const on = read(values.on, ALERT_THRESHOLDS);
61
+ const clean = read(values.clean, CLEAN_MODES);
62
+ const say = read(values.say, SAID_AS);
63
+ const notify = values.notify === undefined ? undefined : channelsIn(values.notify);
64
+ const lang = read(values.lang, NOTICE_LANGS);
65
+ if (on === 'unknown')
66
+ return this.#unknown('--on', values.on ?? '', ALERT_THRESHOLDS);
67
+ if (clean === 'unknown')
68
+ return this.#unknown('--clean', values.clean ?? '', CLEAN_MODES);
69
+ if (say === 'unknown')
70
+ return this.#unknown('--say', values.say ?? '', SAID_AS);
71
+ if (notify === 'unknown')
72
+ return this.#unknown('--notify', values.notify ?? '', NOTICE_CHANNELS);
73
+ if (lang === 'unknown')
74
+ return this.#unknown('--lang', values.lang ?? '', NOTICE_LANGS);
75
+ const choices = {
76
+ ...(on === undefined ? {} : { on }),
77
+ ...(clean === undefined ? {} : { clean }),
78
+ ...(say === undefined ? {} : { say }),
79
+ ...(notify === undefined ? {} : { notify }),
80
+ ...(lang === undefined ? {} : { lang }),
81
+ };
82
+ const asked = Object.keys(choices).length > 0;
83
+ if (!asked && !values.reset)
84
+ return this.#say();
85
+ // A flag and `--reset` in one run would be a choice written and taken out again: said, rather than guessed at.
86
+ if (asked && values.reset) {
87
+ return { kind: 'usage-error', message: '--reset takes answers out; it cannot be given alongside one', usage: this.usage };
88
+ }
89
+ const result = await this.#dependencies.settings.change({
90
+ scope: values.everywhere ? 'everywhere' : 'project',
91
+ choices,
92
+ ...(values.reset ? { reset: true } : {}),
93
+ });
94
+ const view = await this.#dependencies.settings.view();
95
+ return {
96
+ kind: 'completed',
97
+ output: `${result.said}\n\n${said(view)}`,
98
+ exitCode: result.written ? EXIT_CODE.ok : EXIT_CODE.unexpected,
99
+ };
100
+ }
101
+ async #say() {
102
+ return { kind: 'completed', output: said(await this.#dependencies.settings.view()), exitCode: EXIT_CODE.ok };
103
+ }
104
+ #unknown(flag, given, known) {
105
+ return { kind: 'usage-error', message: `${flag} does not take "${given}". It takes: ${known.join(', ')}`, usage: this.usage };
106
+ }
107
+ }
108
+ /**
109
+ * What is set, and where each answer comes from. A person changing this is deciding how much they hear, so the
110
+ * lines say which of the three levels answered rather than only what the answer is.
111
+ */
112
+ function said(view) {
113
+ const from = (key) => view.forProject[key] !== undefined ? 'this project' : view.defaults[key] !== undefined ? 'you, everywhere' : "agentwhy's own";
114
+ const lines = [
115
+ 'What agentwhy tells you when a turn ends',
116
+ '',
117
+ ` Findings said ${view.effective.on ?? DEFAULT_THRESHOLD}${level(view.effective.on ?? DEFAULT_THRESHOLD)} (${from('on')})`,
118
+ ` A quiet turn ${view.effective.clean ?? DEFAULT_CLEAN}${quiet(view.effective.clean ?? DEFAULT_CLEAN)} (${from('clean')})`,
119
+ ` A value is said ${view.effective.say ?? DEFAULT_SAID_AS}${saidAs(view.effective.say ?? DEFAULT_SAID_AS)} (${from('say')})`,
120
+ ` Shown in ${(view.effective.notify ?? DEFAULT_CHANNELS).join(', ')} (${from('notify')})`,
121
+ ` Written in ${view.effective.lang ?? "your system's language, else en"} (${from('lang')})`,
122
+ '',
123
+ ` This project ${view.project}`,
124
+ ` Answers kept in ${view.path}`,
125
+ ...(view.unusable ? ['', ` That file could not be read, so none of the above is an answer anybody gave.`] : []),
126
+ '',
127
+ ' Change it agentwhy notify --on reached',
128
+ ' For every project agentwhy notify --everywhere --clean every-turn',
129
+ ];
130
+ return `${lines.join('\n')}\n`;
131
+ }
132
+ const LEVEL = {
133
+ value: ' - a value from a protected file is in the conversation',
134
+ reached: ' - that, and a protected file reached',
135
+ refused: ' - that, and an attempt a rule refused',
136
+ };
137
+ const QUIET = {
138
+ off: ' - nothing is said about a turn that found nothing',
139
+ once: " - the first quiet turn says the session is watched",
140
+ 'every-turn': ' - said after every reply',
141
+ };
142
+ const SAID = {
143
+ agent: ' - by your own agent, which offers to open the report',
144
+ line: ' - as the grey line under the reply',
145
+ };
146
+ const saidAs = (how) => SAID[how];
147
+ const level = (threshold) => LEVEL[threshold];
148
+ const quiet = (mode) => QUIET[mode];
149
+ function read(value, known) {
150
+ if (value === undefined)
151
+ return undefined;
152
+ return known.find((candidate) => candidate === value) ?? 'unknown';
153
+ }
154
+ function channelsIn(list) {
155
+ const names = list.split(',').map((name) => name.trim()).filter((name) => name !== '');
156
+ const channels = names.map((name) => NOTICE_CHANNELS.find((channel) => channel === name));
157
+ return channels.length === 0 || channels.includes(undefined) ? 'unknown' : [...new Set(channels)];
158
+ }
@@ -0,0 +1,79 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { notCheckedMessage, refusalReason } from '../../refuse/render/refusal-words.js';
3
+ import { EXIT_CODE } from '../exit-codes.js';
4
+ export const REFUSE_USAGE = `Usage: agentwhy refuse [--codex] [--policy <file> | --settings <file>]
5
+
6
+ Run by a Claude Code or Codex PreToolUse hook on Bash, never by hand: reads the hook input on standard
7
+ input and refuses a shell command that would open a file the policy protects, before it runs:
8
+ one that names it, one with a glob the shell expands to it (cat .env*), and a recursive search
9
+ (grep -r, rg) whose own filters do not leave it out. The agent is told which path and that it
10
+ can ask the user. Anything else runs as it would have.
11
+
12
+ --policy <file> the policy to apply, as for \`report\`
13
+ --settings <file> a settings file whose deny rules stand in for a policy, as for \`report\`
14
+ --codex the input is Codex's; a relative --settings is read in the project, the
15
+ nearest folder at or above the command's that holds .codex/hooks.json
16
+ -h, --help this text
17
+
18
+ It reads a command line, so it does NOT stop: a path held in a variable, reached after cd, or
19
+ built while the command runs; a search it does not recognise (find -exec, xargs, a script); any
20
+ tool other than Bash. Only a boundary below the agent - file permissions, a sandbox - closes
21
+ those. When it cannot read its input or its policy, or a search reaches more files than it can
22
+ look through, it lets the command run and warns the user, so a typo in a settings path cannot
23
+ stop every command.
24
+
25
+ Exit 2 refuses the command; 0 lets it run. agentwhy init --refuse installs the hook, and in a
26
+ project that uses Codex, Codex's too - which Codex runs only once you approve it there.
27
+ `;
28
+ /** `agentwhy refuse` - the shell hook of `specs/2026-09-16-worth-running-every-day.md` R18-R22. */
29
+ export class RefuseCliCommand {
30
+ name = 'refuse';
31
+ usage = REFUSE_USAGE;
32
+ #dependencies;
33
+ constructor(dependencies) {
34
+ this.#dependencies = dependencies;
35
+ }
36
+ async execute(args) {
37
+ let values;
38
+ try {
39
+ ({ values } = parseArgs({
40
+ args: [...args],
41
+ options: {
42
+ policy: { type: 'string' },
43
+ settings: { type: 'string' },
44
+ codex: { type: 'boolean', default: false },
45
+ help: { type: 'boolean', short: 'h', default: false },
46
+ },
47
+ strict: true,
48
+ allowPositionals: false,
49
+ }));
50
+ }
51
+ catch {
52
+ return notChecked('arguments');
53
+ }
54
+ if (values.help)
55
+ return { kind: 'help', usage: this.usage };
56
+ if (this.#dependencies.interactive) {
57
+ return { kind: 'usage-error', message: 'refuse reads a hook input on standard input, and this is a terminal', usage: this.usage };
58
+ }
59
+ const decision = await this.#dependencies.refusal.decide({
60
+ ...(values.codex === true ? { codex: true } : {}),
61
+ ...(values.policy === undefined ? {} : { policyPath: values.policy }),
62
+ ...(values.settings === undefined ? {} : { settingsPath: values.settings }),
63
+ });
64
+ return resultOf(decision);
65
+ }
66
+ }
67
+ function resultOf(decision) {
68
+ switch (decision.kind) {
69
+ case 'allow':
70
+ return { kind: 'completed', output: '', exitCode: EXIT_CODE.ok };
71
+ case 'not-checked':
72
+ return notChecked(decision.reason);
73
+ case 'refuse':
74
+ return { kind: 'hook-block', reason: refusalReason(decision.path, decision.pattern, decision.others, decision.route) };
75
+ }
76
+ }
77
+ function notChecked(reason) {
78
+ return { kind: 'completed', output: notCheckedMessage(reason), exitCode: EXIT_CODE.ok };
79
+ }