@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.
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +181 -0
- package/THIRD_PARTY_NOTICES.md +31 -0
- package/dist/adapter/claude-code/contract/assumptions.js +208 -0
- package/dist/adapter/claude-code/contract/denials.js +15 -0
- package/dist/adapter/claude-code/contract/entry-points.js +15 -0
- package/dist/adapter/claude-code/contract/fields.js +73 -0
- package/dist/adapter/claude-code/contract/hooks.js +96 -0
- package/dist/adapter/claude-code/contract/identifiers.js +15 -0
- package/dist/adapter/claude-code/contract/layout.js +29 -0
- package/dist/adapter/claude-code/contract/line-types.js +26 -0
- package/dist/adapter/claude-code/contract/projects.js +23 -0
- package/dist/adapter/claude-code/contract/recognition.js +14 -0
- package/dist/adapter/claude-code/contract/session-title.js +20 -0
- package/dist/adapter/claude-code/contract/settings.js +27 -0
- package/dist/adapter/claude-code/contract/task-notifications.js +33 -0
- package/dist/adapter/claude-code/contract/tools.js +37 -0
- package/dist/adapter/claude-code/contract/version.js +51 -0
- package/dist/adapter/claude-code/discovery/claude-code-project-catalogue.js +129 -0
- package/dist/adapter/claude-code/discovery/claude-code-recognition.js +46 -0
- package/dist/adapter/claude-code/discovery/claude-code-session-catalogue.js +100 -0
- package/dist/adapter/claude-code/discovery/claude-code-session-discovery.js +61 -0
- package/dist/adapter/claude-code/discovery/claude-code-session-titles.js +28 -0
- package/dist/adapter/claude-code/discovery/discovered-session.js +1 -0
- package/dist/adapter/claude-code/discovery/session-entries.js +41 -0
- package/dist/adapter/claude-code/discovery/transcript-tail.js +81 -0
- package/dist/adapter/claude-code/events/claude-code-session-source.js +197 -0
- package/dist/adapter/claude-code/events/delegation-index.js +15 -0
- package/dist/adapter/claude-code/events/transcript-scan.js +179 -0
- package/dist/adapter/claude-code/hooks/hook-output.js +42 -0
- package/dist/adapter/claude-code/hooks/pre-tool-use-input.js +23 -0
- package/dist/adapter/claude-code/hooks/stop-input.js +25 -0
- package/dist/adapter/claude-code/hooks/subagent-stop-input.js +26 -0
- package/dist/adapter/claude-code/policy/deny-rules.js +46 -0
- package/dist/adapter/claude-code/probe/claude-code-probe.js +86 -0
- package/dist/adapter/claude-code/probe/collectors/agent-tool-input-key-collector.js +24 -0
- package/dist/adapter/claude-code/probe/collectors/denial-kind-collector.js +20 -0
- package/dist/adapter/claude-code/probe/collectors/denied-call-collector.js +11 -0
- package/dist/adapter/claude-code/probe/collectors/identifier-collector.js +19 -0
- package/dist/adapter/claude-code/probe/collectors/line-collector.js +1 -0
- package/dist/adapter/claude-code/probe/collectors/line-type-collector.js +29 -0
- package/dist/adapter/claude-code/probe/collectors/sidechain-collector.js +20 -0
- package/dist/adapter/claude-code/probe/collectors/task-notification-collector.js +51 -0
- package/dist/adapter/claude-code/probe/collectors/tool-result-reference-collector.js +31 -0
- package/dist/adapter/claude-code/probe/collectors/tool-use-collector.js +24 -0
- package/dist/adapter/claude-code/probe/collectors/tool-version-collector.js +14 -0
- package/dist/adapter/claude-code/probe/collectors/top-level-key-collector.js +14 -0
- package/dist/adapter/claude-code/probe/collectors/unknown-line-type-key-collector.js +24 -0
- package/dist/adapter/claude-code/probe/collectors/working-directory-collector.js +28 -0
- package/dist/adapter/claude-code/probe/doctor-report.js +1 -0
- package/dist/adapter/claude-code/probe/meta-tally.js +42 -0
- package/dist/adapter/claude-code/probe/session-probe.js +1 -0
- package/dist/adapter/claude-code/probe/session-tally.js +41 -0
- package/dist/adapter/claude-code/probe/transcript-line.js +30 -0
- package/dist/adapter/claude-code/probe/transcript-tally.js +74 -0
- package/dist/adapter/claude-code/settings/deny-entries.js +65 -0
- package/dist/adapter/claude-code/settings/hook-entries.js +303 -0
- package/dist/adapter/codex/contract/actions.js +69 -0
- package/dist/adapter/codex/contract/assumptions.js +55 -0
- package/dist/adapter/codex/contract/capabilities.js +24 -0
- package/dist/adapter/codex/contract/delegations.js +30 -0
- package/dist/adapter/codex/contract/deliveries.js +25 -0
- package/dist/adapter/codex/contract/envelope.js +33 -0
- package/dist/adapter/codex/contract/hook-refusals.js +14 -0
- package/dist/adapter/codex/contract/hooks.js +46 -0
- package/dist/adapter/codex/contract/messages.js +47 -0
- package/dist/adapter/codex/contract/permissions.js +27 -0
- package/dist/adapter/codex/contract/reviews.js +13 -0
- package/dist/adapter/codex/contract/session.js +31 -0
- package/dist/adapter/codex/contract/turns.js +23 -0
- package/dist/adapter/codex/contract/version.js +15 -0
- package/dist/adapter/codex/discovery/codex-conversations.js +44 -0
- package/dist/adapter/codex/discovery/codex-project-catalogue.js +49 -0
- package/dist/adapter/codex/discovery/codex-recognition.js +20 -0
- package/dist/adapter/codex/discovery/codex-session-catalogue.js +28 -0
- package/dist/adapter/codex/discovery/codex-session-discovery.js +115 -0
- package/dist/adapter/codex/discovery/codex-session-index.js +21 -0
- package/dist/adapter/codex/discovery/codex-session-titles.js +135 -0
- package/dist/adapter/codex/discovery/session-header.js +86 -0
- package/dist/adapter/codex/discovery/session-owners.js +11 -0
- package/dist/adapter/codex/discovery/session-roots.js +49 -0
- package/dist/adapter/codex/events/action-items.js +138 -0
- package/dist/adapter/codex/events/capability-records.js +26 -0
- package/dist/adapter/codex/events/codex-session-source.js +121 -0
- package/dist/adapter/codex/events/hook-refusals.js +27 -0
- package/dist/adapter/codex/events/rollout-scan.js +614 -0
- package/dist/adapter/codex/events/turn-permissions.js +60 -0
- package/dist/adapter/codex/hooks/pre-tool-use-input.js +24 -0
- package/dist/adapter/codex/hooks/stop-refusals.js +57 -0
- package/dist/adapter/codex/probe/codex-doctor-report.js +1 -0
- package/dist/adapter/codex/probe/codex-probe.js +268 -0
- package/dist/adapter/codex/settings/codex-hooks.js +83 -0
- package/dist/cli/cli-command.js +1 -0
- package/dist/cli/command-router.js +42 -0
- package/dist/cli/commands/check-cli-command.js +128 -0
- package/dist/cli/commands/codex-stop-cli-command.js +27 -0
- package/dist/cli/commands/doctor-arguments.js +32 -0
- package/dist/cli/commands/doctor-cli-command.js +31 -0
- package/dist/cli/commands/doctor-usage.js +14 -0
- package/dist/cli/commands/init-cli-command.js +123 -0
- package/dist/cli/commands/menu-cli-command.js +58 -0
- package/dist/cli/commands/notify-cli-command.js +158 -0
- package/dist/cli/commands/refuse-cli-command.js +79 -0
- package/dist/cli/commands/report-arguments.js +62 -0
- package/dist/cli/commands/report-cli-command.js +36 -0
- package/dist/cli/commands/report-usage.js +41 -0
- package/dist/cli/commands/sessions-cli-command.js +184 -0
- package/dist/cli/commands/start-cli-command.js +83 -0
- package/dist/cli/commands/watch-cli-command.js +142 -0
- package/dist/cli/exit-codes.js +13 -0
- package/dist/cli/project-switches.js +46 -0
- package/dist/cli.js +80 -0
- package/dist/composition-root.js +411 -0
- package/dist/core/access/command-line.js +454 -0
- package/dist/core/access/listing.js +58 -0
- package/dist/core/access/path-shape.js +59 -0
- package/dist/core/access/path-tokens.js +37 -0
- package/dist/core/access/protected-access.js +310 -0
- package/dist/core/access/protected-values.js +102 -0
- package/dist/core/access/recorded-effect.js +67 -0
- package/dist/core/access/returns.js +173 -0
- package/dist/core/access/search-output.js +34 -0
- package/dist/core/access/search-reach.js +268 -0
- package/dist/core/access/uses.js +143 -0
- package/dist/core/access/words-before.js +52 -0
- package/dist/core/agent.js +1 -0
- package/dist/core/capability.js +11 -0
- package/dist/core/combined-project-catalogue.js +36 -0
- package/dist/core/combined-session-catalogue.js +25 -0
- package/dist/core/completeness.js +10 -0
- package/dist/core/context.js +1 -0
- package/dist/core/correlation/correlate.js +329 -0
- package/dist/core/delegation.js +1 -0
- package/dist/core/detector/prompt-signals.js +78 -0
- package/dist/core/entry-point.js +1 -0
- package/dist/core/event.js +1 -0
- package/dist/core/evidence.js +9 -0
- package/dist/core/late-message.js +32 -0
- package/dist/core/message.js +1 -0
- package/dist/core/nearest-project.js +28 -0
- package/dist/core/policy/default-policy.js +32 -0
- package/dist/core/policy/glob.js +56 -0
- package/dist/core/policy/parse-policy.js +70 -0
- package/dist/core/policy/policy.js +20 -0
- package/dist/core/policy/resolve-policy.js +16 -0
- package/dist/core/project-catalogue.js +1 -0
- package/dist/core/project-root.js +133 -0
- package/dist/core/provider-titles.js +10 -0
- package/dist/core/redaction/entropy.js +46 -0
- package/dist/core/redaction/redacted.js +4 -0
- package/dist/core/redaction/redactor.js +339 -0
- package/dist/core/redaction/secret-patterns.js +22 -0
- package/dist/core/redaction/value-shapes.js +81 -0
- package/dist/core/redaction/value-trace.js +110 -0
- package/dist/core/review.js +1 -0
- package/dist/core/session-catalogue.js +19 -0
- package/dist/core/session-filter.js +42 -0
- package/dist/core/session-format.js +27 -0
- package/dist/core/session-model.js +1 -0
- package/dist/core/session-reader.js +22 -0
- package/dist/core/session-records.js +1 -0
- package/dist/core/session-source.js +1 -0
- package/dist/core/session-titles.js +21 -0
- package/dist/core/turn.js +1 -0
- package/dist/doctor/codex-doctor.js +14 -0
- package/dist/doctor/doctor-use-case.js +1 -0
- package/dist/doctor/format-selecting-doctor.js +28 -0
- package/dist/doctor/render/attention/attention-rule.js +1 -0
- package/dist/doctor/render/attention/doctor-attention-rules.js +28 -0
- package/dist/doctor/render/attention/rules/damaged-meta-files-rule.js +8 -0
- package/dist/doctor/render/attention/rules/damaged-transcripts-rule.js +9 -0
- package/dist/doctor/render/attention/rules/incomplete-subagent-pairs-rule.js +6 -0
- package/dist/doctor/render/attention/rules/missing-main-transcript-rule.js +5 -0
- package/dist/doctor/render/attention/rules/missing-spilled-results-rule.js +6 -0
- package/dist/doctor/render/attention/rules/multiple-working-directories-rule.js +15 -0
- package/dist/doctor/render/attention/rules/unknown-denial-kinds-rule.js +5 -0
- package/dist/doctor/render/attention/rules/unknown-line-types-rule.js +6 -0
- package/dist/doctor/render/attention/rules/unrecognised-entries-rule.js +6 -0
- package/dist/doctor/render/attention/rules/unusable-directories-rule.js +12 -0
- package/dist/doctor/render/attention/transcript-scopes.js +6 -0
- package/dist/doctor/render/codex-text-doctor-renderer.js +97 -0
- package/dist/doctor/render/doctor-renderers.js +1 -0
- package/dist/doctor/render/json-doctor-renderer.js +7 -0
- package/dist/doctor/render/output-format.js +1 -0
- package/dist/doctor/render/text-doctor-renderer.js +124 -0
- package/dist/doctor/session-doctor.js +16 -0
- package/dist/infrastructure/clack-asker.js +24 -0
- package/dist/infrastructure/clack-chooser.js +40 -0
- package/dist/infrastructure/clack-multi-chooser.js +31 -0
- package/dist/infrastructure/file-alert-store.js +120 -0
- package/dist/infrastructure/file-checked-store.js +55 -0
- package/dist/infrastructure/file-mark-store.js +67 -0
- package/dist/infrastructure/file-onboarding-store.js +93 -0
- package/dist/infrastructure/folder-window.js +26 -0
- package/dist/infrastructure/node-agentwhy-invocation.js +99 -0
- package/dist/infrastructure/node-browser.js +40 -0
- package/dist/infrastructure/node-file-system.js +111 -0
- package/dist/infrastructure/node-local-server.js +115 -0
- package/dist/infrastructure/node-text-input.js +26 -0
- package/dist/infrastructure/os-notifier.js +21 -0
- package/dist/infrastructure/osascript-folder-chooser.js +53 -0
- package/dist/infrastructure/powershell-folder-chooser.js +78 -0
- package/dist/infrastructure/real-directory.js +18 -0
- package/dist/infrastructure/stream-printer.js +11 -0
- package/dist/infrastructure/terminal-banner.js +19 -0
- package/dist/ports/agentwhy-invocation.js +1 -0
- package/dist/ports/alert-store.js +1 -0
- package/dist/ports/asker.js +1 -0
- package/dist/ports/browser.js +1 -0
- package/dist/ports/checked-store.js +1 -0
- package/dist/ports/chooser.js +1 -0
- package/dist/ports/directory-reader.js +1 -0
- package/dist/ports/file-access-error.js +11 -0
- package/dist/ports/file-reader.js +1 -0
- package/dist/ports/file-tail-reader.js +1 -0
- package/dist/ports/file-writer.js +1 -0
- package/dist/ports/folder-chooser.js +1 -0
- package/dist/ports/local-server.js +1 -0
- package/dist/ports/mark-store.js +10 -0
- package/dist/ports/multi-chooser.js +1 -0
- package/dist/ports/notifier.js +1 -0
- package/dist/ports/onboarding-store.js +1 -0
- package/dist/ports/printer.js +1 -0
- package/dist/ports/text-input.js +1 -0
- package/dist/refuse/command-reach.js +135 -0
- package/dist/refuse/command-refusal.js +88 -0
- package/dist/refuse/render/codex-stop-words.js +21 -0
- package/dist/refuse/render/refusal-words.js +34 -0
- package/dist/report/build-report.js +1037 -0
- package/dist/report/check/actions-digest.js +73 -0
- package/dist/report/check/check-lines.js +83 -0
- package/dist/report/check/mark-request.js +90 -0
- package/dist/report/check/marks.js +84 -0
- package/dist/report/check/render/text-digest-renderer.js +332 -0
- package/dist/report/check/session-actions.js +90 -0
- package/dist/report/check/session-check.js +91 -0
- package/dist/report/choose-policy.js +61 -0
- package/dist/report/flow-reads.js +18 -0
- package/dist/report/private-files/tell-lists.js +84 -0
- package/dist/report/project-rules.js +33 -0
- package/dist/report/render/home-relative.js +18 -0
- package/dist/report/render/html-head.js +26 -0
- package/dist/report/render/html-report-components.js +48 -0
- package/dist/report/render/html-report-styles.js +500 -0
- package/dist/report/render/html-sidebar.js +42 -0
- package/dist/report/render/path-tail.js +14 -0
- package/dist/report/render/report-copy.js +1177 -0
- package/dist/report/render/report-page/advanced-view.js +167 -0
- package/dist/report/render/report-page/file-story.js +114 -0
- package/dist/report/render/report-page/files-view.js +381 -0
- package/dist/report/render/report-page/files.js +77 -0
- package/dist/report/render/report-page/fix-wizard-script.js +269 -0
- package/dist/report/render/report-page/fix-wizard.js +310 -0
- package/dist/report/render/report-page/helpers-view.js +383 -0
- package/dist/report/render/report-page/helpers.js +51 -0
- package/dist/report/render/report-page/item-names.js +63 -0
- package/dist/report/render/report-page/protect-patterns.js +13 -0
- package/dist/report/render/report-page/providers.js +208 -0
- package/dist/report/render/report-page/record-view.js +367 -0
- package/dist/report/render/report-page/report-page-renderer.js +129 -0
- package/dist/report/render/report-page/report-views.js +31 -0
- package/dist/report/render/report-page/story-window.js +356 -0
- package/dist/report/render/report-page/times.js +25 -0
- package/dist/report/render/report-page/to-do-view.js +299 -0
- package/dist/report/render/report-page/to-do.js +152 -0
- package/dist/report/render/report-page.js +1 -0
- package/dist/report/render/session-view.js +250 -0
- package/dist/report/render/text-report-renderer.js +981 -0
- package/dist/report/render/ui/add-file-popup.js +122 -0
- package/dist/report/render/ui/app-sidebar.js +78 -0
- package/dist/report/render/ui/ask-panel.js +80 -0
- package/dist/report/render/ui/avatar.js +19 -0
- package/dist/report/render/ui/backdrop.js +29 -0
- package/dist/report/render/ui/brand-mark.js +4 -0
- package/dist/report/render/ui/button.js +67 -0
- package/dist/report/render/ui/callout.js +20 -0
- package/dist/report/render/ui/chat-example.js +17 -0
- package/dist/report/render/ui/checklist.js +50 -0
- package/dist/report/render/ui/confirm-dialog.js +34 -0
- package/dist/report/render/ui/data-table.js +49 -0
- package/dist/report/render/ui/drawer.js +25 -0
- package/dist/report/render/ui/file-chip.js +37 -0
- package/dist/report/render/ui/fold-line.js +22 -0
- package/dist/report/render/ui/guide-card.js +25 -0
- package/dist/report/render/ui/hero.js +37 -0
- package/dist/report/render/ui/labelled-select.js +10 -0
- package/dist/report/render/ui/live-script.js +222 -0
- package/dist/report/render/ui/local-date.js +58 -0
- package/dist/report/render/ui/mode-icon.js +6 -0
- package/dist/report/render/ui/page-shell.js +101 -0
- package/dist/report/render/ui/pill-tabs.js +47 -0
- package/dist/report/render/ui/popup.js +77 -0
- package/dist/report/render/ui/progress.js +35 -0
- package/dist/report/render/ui/project-list.js +284 -0
- package/dist/report/render/ui/scope-choice.js +19 -0
- package/dist/report/render/ui/stats.js +21 -0
- package/dist/report/render/ui/status-icon.js +30 -0
- package/dist/report/render/ui/status-look.js +21 -0
- package/dist/report/render/ui/switch.js +11 -0
- package/dist/report/render/ui/tag.js +19 -0
- package/dist/report/render/ui/task-list.js +56 -0
- package/dist/report/render/ui/tokens.js +46 -0
- package/dist/report/render/ui/update-notice.js +146 -0
- package/dist/report/render/ui/words/app-words.js +163 -0
- package/dist/report/render/ui/words/conversations-words.js +457 -0
- package/dist/report/render/ui/words/month-words.js +59 -0
- package/dist/report/render/ui/words/onboarding-words.js +446 -0
- package/dist/report/render/ui/words/projects-words.js +217 -0
- package/dist/report/render/ui/words/report-words.js +2099 -0
- package/dist/report/render/ui/words/settings-words.js +611 -0
- package/dist/report/render/ui/words/to-fix-words.js +287 -0
- package/dist/report/report-model.js +2 -0
- package/dist/report/report-use-case.js +1 -0
- package/dist/report/rule-names.js +20 -0
- package/dist/report/session-report.js +163 -0
- package/dist/report/start/app-nav.js +37 -0
- package/dist/report/start/conversations/calendar-view.js +104 -0
- package/dist/report/start/conversations/conversation-columns.js +187 -0
- package/dist/report/start/conversations/conversations-renderer.js +54 -0
- package/dist/report/start/conversations/empty-week.js +120 -0
- package/dist/report/start/conversations/files-window.js +166 -0
- package/dist/report/start/conversations/period-section.js +280 -0
- package/dist/report/start/conversations/periods-script.js +440 -0
- package/dist/report/start/conversations/periods.js +110 -0
- package/dist/report/start/conversations/week-view.js +30 -0
- package/dist/report/start/conversations/weeks.js +17 -0
- package/dist/report/start/month/month-renderer.js +50 -0
- package/dist/report/start/month/month-view.js +135 -0
- package/dist/report/start/month/months.js +17 -0
- package/dist/report/start/onboarding/done.js +171 -0
- package/dist/report/start/onboarding/finish-onboarding.js +93 -0
- package/dist/report/start/onboarding/intro.js +100 -0
- package/dist/report/start/onboarding/onboarding-changes.js +40 -0
- package/dist/report/start/onboarding/onboarding-renderer.js +195 -0
- package/dist/report/start/onboarding/onboarding-script.js +400 -0
- package/dist/report/start/onboarding/onboarding-view.js +98 -0
- package/dist/report/start/onboarding/project-step.js +210 -0
- package/dist/report/start/onboarding/steps.js +239 -0
- package/dist/report/start/onboarding/welcome.js +66 -0
- package/dist/report/start/page-notice.js +10 -0
- package/dist/report/start/projects/index-projects.js +43 -0
- package/dist/report/start/projects/moved-page.js +82 -0
- package/dist/report/start/projects/project-switch.js +138 -0
- package/dist/report/start/projects/projects-window.js +67 -0
- package/dist/report/start/projects/temporary-space.js +18 -0
- package/dist/report/start/render/air-datepicker-vendor.js +27 -0
- package/dist/report/start/render/session-status.js +41 -0
- package/dist/report/start/render/start-words.js +84 -0
- package/dist/report/start/repository.js +28 -0
- package/dist/report/start/serve/index-handler.js +372 -0
- package/dist/report/start/serve/onboarding-request.js +50 -0
- package/dist/report/start/serve/settings-request.js +1 -0
- package/dist/report/start/serve/settings-setup.js +193 -0
- package/dist/report/start/session-index.js +1 -0
- package/dist/report/start/session-start.js +974 -0
- package/dist/report/start/settings/alerts-tab.js +104 -0
- package/dist/report/start/settings/files-tab.js +169 -0
- package/dist/report/start/settings/general-tab.js +71 -0
- package/dist/report/start/settings/settings-renderer.js +254 -0
- package/dist/report/start/settings/settings-script.js +161 -0
- package/dist/report/start/settings/settings-view.js +240 -0
- package/dist/report/start/settings/settings-windows.js +334 -0
- package/dist/report/start/settings-files.js +43 -0
- package/dist/report/start/to-fix/done-list.js +45 -0
- package/dist/report/start/to-fix/file-window.js +110 -0
- package/dist/report/start/to-fix/to-fix-list.js +70 -0
- package/dist/report/start/to-fix/to-fix-renderer.js +237 -0
- package/dist/report/start/to-fix/to-fix-script.js +94 -0
- package/dist/report/start/to-fix/to-fix-view.js +124 -0
- package/dist/report/watch/agent-alert.js +72 -0
- package/dist/report/watch/notice-choices.js +35 -0
- package/dist/report/watch/notice-settings.js +65 -0
- package/dist/report/watch/preferences.js +100 -0
- package/dist/report/watch/render/instruction-words.js +54 -0
- package/dist/report/watch/render/notice-words.js +182 -0
- package/dist/report/watch/subagent-watch.js +345 -0
- package/dist/report/watch/watch-notice.js +1 -0
- package/dist/setup/behind.js +18 -0
- package/dist/setup/codex-mirror.js +171 -0
- package/dist/setup/not-a-project.js +19 -0
- package/dist/setup/project-setup.js +644 -0
- package/dist/setup/protected-patterns.js +30 -0
- package/dist/shared/colour.js +23 -0
- package/dist/shared/compare.js +4 -0
- package/dist/shared/content-version.js +13 -0
- package/dist/shared/counter.js +11 -0
- package/dist/shared/file-stamp.js +4 -0
- package/dist/shared/json.js +13 -0
- package/dist/shared/label.js +13 -0
- package/dist/shared/package-name.js +6 -0
- package/dist/shared/plain-invocation.js +44 -0
- package/dist/shared/plain-version.js +21 -0
- package/dist/shared/printable.js +25 -0
- package/dist/shared/renderer.js +1 -0
- package/dist/shared/sentence.js +20 -0
- package/dist/shared/terminal-logo.js +41 -0
- package/dist/shared/text-table.js +15 -0
- package/dist/shared/wrap.js +29 -0
- package/package.json +70 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which way into Claude Code a session was started by, as its transcript records it in `entrypoint` - by the words a
|
|
3
|
+
* person knows it by (`.ai/specs/2026-09-27-which-project.md` V4). These are the values Claude Code also gives its hooks
|
|
4
|
+
* as `CLAUDE_CODE_ENTRYPOINT` (`the-agent-tells-you.md` B9e2: terminal `claude`, the editor extension, `claude -p`).
|
|
5
|
+
*
|
|
6
|
+
* Measured, not guessed (which-project VB5, 2026-09-28): the last megabyte of each of 244 transcripts carries the field,
|
|
7
|
+
* `claude-vscode` in 180, `cli` in 62, `sdk-cli` in 2, and no transcript carries two values. No other value has been
|
|
8
|
+
* seen: one that is not listed here is said to be unknown, never read as the nearest of these. What the extension writes
|
|
9
|
+
* inside Cursor, and the plugin inside JetBrains, is not measured (VB4), so `editor` is a code editor and not "VS Code".
|
|
10
|
+
*/
|
|
11
|
+
export const ENTRY_POINT_VALUES = {
|
|
12
|
+
terminal: 'cli',
|
|
13
|
+
editor: 'claude-vscode',
|
|
14
|
+
script: 'sdk-cli',
|
|
15
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const FIELDS = {
|
|
2
|
+
lineType: 'type',
|
|
3
|
+
sidechain: 'isSidechain',
|
|
4
|
+
denialKind: 'toolDenialKind',
|
|
5
|
+
toolUseResult: 'toolUseResult',
|
|
6
|
+
resultSourceAssistant: 'sourceToolAssistantUUID',
|
|
7
|
+
toolVersion: 'version',
|
|
8
|
+
/** The session's working directory: the project root a report makes paths relative to. On conversation lines only. */
|
|
9
|
+
workingDirectory: 'cwd',
|
|
10
|
+
/** Which way into Claude Code the session was started by: the values of `entry-points.ts`. On conversation lines. */
|
|
11
|
+
entryPoint: 'entrypoint',
|
|
12
|
+
/**
|
|
13
|
+
* When the line was written: ISO 8601 in UTC with milliseconds. For display only (the report page spec M4) - a line
|
|
14
|
+
* can be earlier than the one before it, so it never orders or joins anything (architecture invariant 3).
|
|
15
|
+
*/
|
|
16
|
+
recordedAt: 'timestamp',
|
|
17
|
+
systemSubtype: 'subtype',
|
|
18
|
+
message: 'message',
|
|
19
|
+
messageContent: 'content',
|
|
20
|
+
blockType: 'type',
|
|
21
|
+
toolName: 'name',
|
|
22
|
+
toolInput: 'input',
|
|
23
|
+
blockContent: 'content',
|
|
24
|
+
messageRole: 'role',
|
|
25
|
+
agentId: 'agentId',
|
|
26
|
+
/** The id of a tool_use block: what a result names to say which call it answers. */
|
|
27
|
+
blockId: 'id',
|
|
28
|
+
/** On a tool_result block, the id of the call it answers - the join of §4.3 rule 1. */
|
|
29
|
+
resultCallId: 'tool_use_id',
|
|
30
|
+
/** On a tool_result block, `true` when the tool answered with an error rather than a result. */
|
|
31
|
+
resultIsError: 'is_error',
|
|
32
|
+
/** On a text block, the words themselves. */
|
|
33
|
+
blockText: 'text',
|
|
34
|
+
/** On a reasoning block, the words themselves. The field carries the block's own name. */
|
|
35
|
+
blockThinking: 'thinking',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* How a subagent hands its report back when it does not write it as a message: a call of its own, whose input
|
|
39
|
+
* carries the words (`.ai/specs/2026-09-16-when-an-agent-finishes.md` D9).
|
|
40
|
+
*
|
|
41
|
+
* Measured, not guessed (`.ai/skills/update-format-contract/SKILL.md` step 1), on a session
|
|
42
|
+
* of 2026-09-19 (Claude Code 2.x auto mode): 2 calls, both on sidechain lines, one input key
|
|
43
|
+
* - `message` - holding a string. The call names no delegation, so the join is the one the subagent's own index
|
|
44
|
+
* entry already makes: the report belongs to the delegation that started the agent whose transcript it is on.
|
|
45
|
+
*/
|
|
46
|
+
export const HANDBACK_TOOL = {
|
|
47
|
+
name: 'SubagentHandback',
|
|
48
|
+
reportInputKey: 'message',
|
|
49
|
+
};
|
|
50
|
+
export const TOOL_USE_BLOCK_TYPE = 'tool_use';
|
|
51
|
+
export const TOOL_RESULT_BLOCK_TYPE = 'tool_result';
|
|
52
|
+
/** What an agent writes, as opposed to what it calls: a block of its own on an assistant line. */
|
|
53
|
+
export const TEXT_BLOCK_TYPE = 'text';
|
|
54
|
+
/** What an agent works out before it calls, in its own words (`specs/2026-09-15-why-this-call.md` R1). */
|
|
55
|
+
export const THINKING_BLOCK_TYPE = 'thinking';
|
|
56
|
+
export const AGENT_TOOL = {
|
|
57
|
+
name: 'Agent',
|
|
58
|
+
inputKeys: ['description', 'prompt', 'subagent_type'],
|
|
59
|
+
optionalInputKeys: ['run_in_background'],
|
|
60
|
+
/** The one input key whose value is an enumeration - the agent type - rather than free text. */
|
|
61
|
+
typeInputKey: 'subagent_type',
|
|
62
|
+
promptInputKey: 'prompt',
|
|
63
|
+
descriptionInputKey: 'description',
|
|
64
|
+
};
|
|
65
|
+
export const META_FIELD = {
|
|
66
|
+
agentType: 'agentType',
|
|
67
|
+
description: 'description',
|
|
68
|
+
toolUseId: 'toolUseId',
|
|
69
|
+
spawnDepth: 'spawnDepth',
|
|
70
|
+
requestShape: 'requestShape',
|
|
71
|
+
requestNonInteractive: 'requestNonInteractive',
|
|
72
|
+
};
|
|
73
|
+
export const META_KEYS = Object.values(META_FIELD);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a Claude Code hook is handed and what it may hand back (`specs/2026-09-16-when-an-agent-finishes.md` §2).
|
|
3
|
+
*
|
|
4
|
+
* Read from the hooks reference on 2026-09-16, and **measured the same day** (B4, spec §3.1): on 4 of 4 real
|
|
5
|
+
* `SubagentStop` inputs every field below was present and `agent_id` was the id in the agent's file name. A change
|
|
6
|
+
* goes through `update-format-contract`.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The field every hook input carries, whichever event it was: where the session is running. Measured on 5 of 5 real
|
|
10
|
+
* `Stop` inputs (`the-agent-nobody-watches.md` B8b) and present on the `SubagentStop` inputs of B4. It is what says
|
|
11
|
+
* which project a session belongs to before anything of that session has been read.
|
|
12
|
+
*/
|
|
13
|
+
export const HOOK_INPUT = {
|
|
14
|
+
fields: { workingDirectory: 'cwd' },
|
|
15
|
+
};
|
|
16
|
+
export const SUBAGENT_STOP = {
|
|
17
|
+
event: 'SubagentStop',
|
|
18
|
+
fields: {
|
|
19
|
+
event: 'hook_event_name',
|
|
20
|
+
/** The main session's transcript: D2. */
|
|
21
|
+
transcriptPath: 'transcript_path',
|
|
22
|
+
/** The id in the agent's own file name, `agent-<id>.jsonl`: B4b, 4 of 4. */
|
|
23
|
+
agentId: 'agent_id',
|
|
24
|
+
/** The agent's final text. B4c: not yet on disk when the hook ran, on 4 of 4 agents. */
|
|
25
|
+
lastMessage: 'last_assistant_message',
|
|
26
|
+
/** The session the agent belongs to: what an alert is remembered under until its turn ends. */
|
|
27
|
+
sessionId: 'session_id',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* What a `Stop` hook is handed, and the event that shows a `systemMessage`
|
|
32
|
+
* (`specs/2026-09-16-a-notice-in-the-conversation.md` §3a, B6a). Measured on v2.1.236: the field is rendered under
|
|
33
|
+
* the assistant's reply as `Stop says: …`, and B6b found it absent from the model's context.
|
|
34
|
+
*/
|
|
35
|
+
export const STOP = {
|
|
36
|
+
event: 'Stop',
|
|
37
|
+
fields: {
|
|
38
|
+
event: 'hook_event_name',
|
|
39
|
+
sessionId: 'session_id',
|
|
40
|
+
/**
|
|
41
|
+
* That this hook run is already inside a continuation it caused before (D15). Measured `true` on the `Stop`
|
|
42
|
+
* that ended such a continuation and `false` on the next turn's (B9c), so it is what keeps one notice from
|
|
43
|
+
* becoming a loop.
|
|
44
|
+
*/
|
|
45
|
+
active: 'stop_hook_active',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* What a `PreToolUse` hook on the shell is handed (`specs/2026-09-16-worth-running-every-day.md` R18, R19). **Documented,
|
|
50
|
+
* not measured**: B7 of that specification records what a real input carries and what exit 2 does.
|
|
51
|
+
*/
|
|
52
|
+
export const PRE_TOOL_USE = {
|
|
53
|
+
event: 'PreToolUse',
|
|
54
|
+
fields: {
|
|
55
|
+
event: 'hook_event_name',
|
|
56
|
+
toolName: 'tool_name',
|
|
57
|
+
toolInput: 'tool_input',
|
|
58
|
+
/** Inside `tool_input` for the shell tool. */
|
|
59
|
+
command: 'command',
|
|
60
|
+
/** Where the shell is when the command runs: what `.` and a glob in it are read against. */
|
|
61
|
+
cwd: 'cwd',
|
|
62
|
+
},
|
|
63
|
+
/** The one tool `refuse` reads a command line from. */
|
|
64
|
+
shellTool: 'Bash',
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The universal output fields a hook's JSON on stdout may carry to reach a person. `systemMessage` is documented as a
|
|
68
|
+
* warning shown to the user (D5): from `SubagentStop` it reached neither the user nor the model (B4d, B4f), and from
|
|
69
|
+
* `Stop` it reached the user and not the model (B6a, B6b). `terminalSequence` showed in the terminal interface (B4f).
|
|
70
|
+
*/
|
|
71
|
+
export const HOOK_OUTPUT = {
|
|
72
|
+
systemMessage: 'systemMessage',
|
|
73
|
+
terminalSequence: 'terminalSequence',
|
|
74
|
+
/**
|
|
75
|
+
* What keeps a turn from ending and hands `reason` to the session's own agent, returned with exit 0
|
|
76
|
+
* (`the-agent-tells-you.md` D16). Measured on v2.1.236: the agent answers it in a message of its own, and the
|
|
77
|
+
* person sees the reason too, framed by Claude Code as `Stop hook error:` (B9a).
|
|
78
|
+
*/
|
|
79
|
+
decision: 'decision',
|
|
80
|
+
block: 'block',
|
|
81
|
+
reason: 'reason',
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* The escape sequences the reference allows in `terminalSequence`. Anything outside the allowlist makes Claude Code
|
|
85
|
+
* ignore the whole field, so a control character inside the words would silence the notice.
|
|
86
|
+
*/
|
|
87
|
+
export const TERMINAL = {
|
|
88
|
+
osc: '\u001b]',
|
|
89
|
+
bel: '\u0007',
|
|
90
|
+
/** iTerm2, Windows Terminal, WezTerm, ConEmu: `9;<text>`. */
|
|
91
|
+
notify: '9',
|
|
92
|
+
/** urxvt, Ghostty, Warp: `777;notify;<title>;<body>`. */
|
|
93
|
+
notifyWithTitle: '777;notify',
|
|
94
|
+
};
|
|
95
|
+
/** A path the reference writes with a leading `~/` in its examples. Expanded rather than read as relative. */
|
|
96
|
+
export const HOME_PREFIX = '~/';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const TOOL_USE_ID = /toolu_[A-Za-z0-9]{20,}/g;
|
|
2
|
+
const UUID = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g;
|
|
3
|
+
const AGENT_ID = /^(agent-)?a[0-9a-f]{16}$/;
|
|
4
|
+
/**
|
|
5
|
+
* The id of a subagent, as it appears in a file name and in the `agentId` field of its lines. An identifier, not
|
|
6
|
+
* content: spec §5.4 already excludes this shape from the entropy class. Whole-value match only, so a longer
|
|
7
|
+
* string that merely contains one is not mistaken for an identifier.
|
|
8
|
+
*/
|
|
9
|
+
export function isAgentId(value) {
|
|
10
|
+
return AGENT_ID.test(value);
|
|
11
|
+
}
|
|
12
|
+
/** Identifier shapes that the secret scanner must never mistake for secrets (lesson L001). */
|
|
13
|
+
export function identifierMatches(text) {
|
|
14
|
+
return { toolUseIds: text.match(TOOL_USE_ID) ?? [], uuids: text.match(UUID) ?? [] };
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const LAYOUT = {
|
|
2
|
+
transcriptSuffix: '.jsonl',
|
|
3
|
+
subagentsDir: 'subagents',
|
|
4
|
+
toolResultsDir: 'tool-results',
|
|
5
|
+
toolResultSuffix: '.txt',
|
|
6
|
+
};
|
|
7
|
+
const SUBAGENT_FILE_SUFFIX = {
|
|
8
|
+
transcript: '.jsonl',
|
|
9
|
+
meta: '.meta.json',
|
|
10
|
+
};
|
|
11
|
+
const SUBAGENT_FILE = /^agent-([A-Za-z0-9]+)(\.jsonl|\.meta\.json)$/;
|
|
12
|
+
const TOOL_RESULT_REFERENCE = /tool-results\/([A-Za-z0-9_-]+\.txt)/g;
|
|
13
|
+
export function parseSubagentFileName(name) {
|
|
14
|
+
const match = SUBAGENT_FILE.exec(name);
|
|
15
|
+
const fileId = match?.[1];
|
|
16
|
+
if (fileId === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
return { fileId, kind: match?.[2] === SUBAGENT_FILE_SUFFIX.meta ? 'meta' : 'transcript' };
|
|
19
|
+
}
|
|
20
|
+
export function subagentFileName(fileId, kind) {
|
|
21
|
+
return `agent-${fileId}${SUBAGENT_FILE_SUFFIX[kind]}`;
|
|
22
|
+
}
|
|
23
|
+
export function isToolResultFileName(name) {
|
|
24
|
+
return name.endsWith(LAYOUT.toolResultSuffix);
|
|
25
|
+
}
|
|
26
|
+
/** File names of spilled results that a piece of transcript text points at. */
|
|
27
|
+
export function toolResultReferences(text) {
|
|
28
|
+
return Array.from(text.matchAll(TOOL_RESULT_REFERENCE), (match) => match[1] ?? '').filter((name) => name !== '');
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const CONVERSATION_LINE_TYPES = ['user', 'assistant'];
|
|
2
|
+
export const SKIPPED_LINE_TYPES = [
|
|
3
|
+
'attachment',
|
|
4
|
+
'queue-operation',
|
|
5
|
+
'atis-latch',
|
|
6
|
+
'last-prompt',
|
|
7
|
+
'ai-title',
|
|
8
|
+
'system',
|
|
9
|
+
'file-history-snapshot',
|
|
10
|
+
'file-history-delta',
|
|
11
|
+
'bridge-session',
|
|
12
|
+
];
|
|
13
|
+
export const SYSTEM_LINE_TYPE = 'system';
|
|
14
|
+
/** The line an agent writes on. A user line carries text blocks too, and they are not the agent's words. */
|
|
15
|
+
export const ASSISTANT_LINE_TYPE = 'assistant';
|
|
16
|
+
/** The line a result arrives on, and a report delivered late. Its words are not the words of the agent it reaches. */
|
|
17
|
+
export const USER_LINE_TYPE = 'user';
|
|
18
|
+
export function classifyLineType(type) {
|
|
19
|
+
if (typeof type !== 'string')
|
|
20
|
+
return 'unknown';
|
|
21
|
+
if (CONVERSATION_LINE_TYPES.includes(type))
|
|
22
|
+
return 'conversation';
|
|
23
|
+
if (SKIPPED_LINE_TYPES.includes(type))
|
|
24
|
+
return 'skipped';
|
|
25
|
+
return 'unknown';
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a project's sessions live: `~/.claude/projects/<encoded working directory>/`.
|
|
3
|
+
*
|
|
4
|
+
* The encoding replaces **every run of non-alphanumeric characters** with a single hyphen, not only the path
|
|
5
|
+
* separators. Corrected on 2026-09-14 by the first real use outside this repository: a project under
|
|
6
|
+
* `…/Web Portal/…` is stored as `…-Web-Portal-…`, and a rule that only replaced separators looked in a
|
|
7
|
+
* directory that does not exist and reported "no sessions" for a project with plenty.
|
|
8
|
+
*
|
|
9
|
+
* The rule is still inferred from examples, so it is not trusted on its own: `matches` lets a caller compare an
|
|
10
|
+
* existing directory name against a working directory, and the catalogue falls back to scanning. An encoding
|
|
11
|
+
* we have not seen costs a slower lookup, not a wrong answer.
|
|
12
|
+
*/
|
|
13
|
+
export const PROJECTS_DIRECTORY = ['.claude', 'projects'];
|
|
14
|
+
export function projectDirectoryName(workingDirectory) {
|
|
15
|
+
return normalise(workingDirectory);
|
|
16
|
+
}
|
|
17
|
+
/** Whether this stored directory is the one for that working directory, whatever else the encoder did to it. */
|
|
18
|
+
export function matchesProject(directoryName, workingDirectory) {
|
|
19
|
+
return normalise(directoryName) === normalise(workingDirectory);
|
|
20
|
+
}
|
|
21
|
+
function normalise(text) {
|
|
22
|
+
return text.replace(/[^A-Za-z0-9]+/g, '-');
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What makes a file a Claude Code transcript by its content (`2026-09-27-what-codex-wrote.md` X2): its first non-blank
|
|
3
|
+
* line is a JSON object with a string `type`, and either a string `sessionId` or a `type` the contract lists.
|
|
4
|
+
*
|
|
5
|
+
* Measured 2026-09-29 over the first lines of 257 main transcripts, printing only fixed labels and counts: a string
|
|
6
|
+
* `type` in 257, a string `sessionId` equal to the file's own name in 257, a `payload` in none; the `type` was one the
|
|
7
|
+
* contract lists in 193 and another in 64 - which is why the id, not the type, carries the rule. The hand-built
|
|
8
|
+
* fixtures carry a listed type and no `sessionId`, and are recognised by the second half. A Codex rollout's first line
|
|
9
|
+
* is `session_meta` with a `payload` and no `sessionId`, so the two rules never both hold.
|
|
10
|
+
*/
|
|
11
|
+
export const RECOGNITION = {
|
|
12
|
+
lineType: 'type',
|
|
13
|
+
sessionId: 'sessionId',
|
|
14
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The title Claude Code gives a session - the one its own session list shows. Measured for contract v7: every
|
|
3
|
+
* `ai-title` line of the measured session is `{type, aiTitle, sessionId}` with a string `aiTitle`, and the title
|
|
4
|
+
* is written again as the session goes on, so the last such line is the latest.
|
|
5
|
+
*
|
|
6
|
+
* A model writes it from what the user typed. That makes it **content**, not structure (lesson L009): it crosses
|
|
7
|
+
* the redaction boundary before anything shows it (spec §13.2, pitfall 7).
|
|
8
|
+
*/
|
|
9
|
+
export const SESSION_TITLE = {
|
|
10
|
+
lineType: 'ai-title',
|
|
11
|
+
field: 'aiTitle',
|
|
12
|
+
/**
|
|
13
|
+
* How much of the end of a transcript is read to find it. In the corpus, the redacted measured session, the last
|
|
14
|
+
* title ended 17 KB before the end of the file and no two titles were more than 30 KB apart. Redaction changes the
|
|
15
|
+
* length of content, so those distances are indicative, and the window is a megabyte - a session smaller than that
|
|
16
|
+
* is read whole. A session whose last title lies further back shows no title, which a person can see, rather than
|
|
17
|
+
* an older one.
|
|
18
|
+
*/
|
|
19
|
+
tailBytes: 1024 * 1024,
|
|
20
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a project keeps Claude Code settings, and how hooks are written in them (`specs/2026-09-16-worth-running-every-day.md`
|
|
3
|
+
* R4-R8). **Documented, not measured** against this project's oracle: read from the settings and hooks references.
|
|
4
|
+
*/
|
|
5
|
+
export const SETTINGS_FILES = {
|
|
6
|
+
directory: '.claude',
|
|
7
|
+
/** Shared with everyone who clones the project. `init` reads deny rules here and never writes. */
|
|
8
|
+
shared: 'settings.json',
|
|
9
|
+
/** One person's, kept out of the repository by Claude Code. The only file `init` writes. */
|
|
10
|
+
local: 'settings.local.json',
|
|
11
|
+
};
|
|
12
|
+
export const HOOK_SETTINGS = {
|
|
13
|
+
hooks: 'hooks',
|
|
14
|
+
/** One entry of an event's list: an optional matcher, and the commands it runs. */
|
|
15
|
+
matcher: 'matcher',
|
|
16
|
+
commands: 'hooks',
|
|
17
|
+
type: 'type',
|
|
18
|
+
commandType: 'command',
|
|
19
|
+
command: 'command',
|
|
20
|
+
/** The variable a hook command is expanded with, naming the project it runs for. */
|
|
21
|
+
projectDirectory: '$CLAUDE_PROJECT_DIR',
|
|
22
|
+
};
|
|
23
|
+
/** Where a settings file keeps the rules that refuse a tool call (`specs/2026-09-13-agentwhy-spec.md` §6.1). */
|
|
24
|
+
export const PERMISSIONS = {
|
|
25
|
+
permissions: 'permissions',
|
|
26
|
+
deny: 'deny',
|
|
27
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A report delivered late (`specs/2026-09-15-where-the-value-went.md` R1, R5). An agent started in the background
|
|
3
|
+
* answers its delegating call with a launch notice, and its report reaches the calling agent afterwards, in a
|
|
4
|
+
* notification on a user line of its own. A `queue-operation` line queues the same words first; it is not a delivery,
|
|
5
|
+
* since a queued line can be removed, and it stays skipped.
|
|
6
|
+
*/
|
|
7
|
+
export const TASK_NOTIFICATION = {
|
|
8
|
+
opening: '<task-notification>',
|
|
9
|
+
closing: '</task-notification>',
|
|
10
|
+
/** The tag holding the id of the call a notification answers: a delegating call's, 6 of 6 measured. */
|
|
11
|
+
callIdTag: 'tool-use-id',
|
|
12
|
+
};
|
|
13
|
+
/** On a delegating result's payload: the field, and the value that makes the result a launch notice. */
|
|
14
|
+
export const LAUNCH_NOTICE = {
|
|
15
|
+
statusField: 'status',
|
|
16
|
+
launchedStatus: 'async_launched',
|
|
17
|
+
};
|
|
18
|
+
const CALL_ID = new RegExp(`<${TASK_NOTIFICATION.callIdTag}>\\s*([^<\\s]+)\\s*</${TASK_NOTIFICATION.callIdTag}>`);
|
|
19
|
+
/** Every notification in a line's words, in the order written. */
|
|
20
|
+
export function notificationsIn(words) {
|
|
21
|
+
const { opening, closing } = TASK_NOTIFICATION;
|
|
22
|
+
const notifications = [];
|
|
23
|
+
for (let from = words.indexOf(opening); from !== -1;) {
|
|
24
|
+
const next = words.indexOf(opening, from + opening.length);
|
|
25
|
+
const close = words.indexOf(closing, from);
|
|
26
|
+
const end = close !== -1 && (next === -1 || close < next) ? close + closing.length : next === -1 ? words.length : next;
|
|
27
|
+
const text = words.slice(from, end);
|
|
28
|
+
const callId = CALL_ID.exec(text)?.[1];
|
|
29
|
+
notifications.push(callId === undefined ? { text } : { callId, text });
|
|
30
|
+
from = next;
|
|
31
|
+
}
|
|
32
|
+
return notifications;
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function profile(pathKeys, commandKeys, result, writes) {
|
|
2
|
+
return { pathKeys, commandKeys, searchWholeInput: false, result, ...(writes === undefined ? {} : { writes }) };
|
|
3
|
+
}
|
|
4
|
+
export const TOOL_PROFILES = {
|
|
5
|
+
Read: profile(['file_path'], [], 'content'),
|
|
6
|
+
Edit: profile(['file_path'], [], 'none', { removedTextKeys: ['old_string'] }),
|
|
7
|
+
Write: profile(['file_path'], [], 'none', { removedTextKeys: [] }),
|
|
8
|
+
NotebookEdit: profile(['notebook_path'], [], 'none'),
|
|
9
|
+
Bash: profile([], ['command'], 'listing'),
|
|
10
|
+
Grep: profile(['path'], [], 'listing'),
|
|
11
|
+
Glob: profile(['path'], [], 'listing'),
|
|
12
|
+
Agent: profile([], [], 'none'),
|
|
13
|
+
Skill: profile([], [], 'none'),
|
|
14
|
+
ToolSearch: profile([], [], 'none'),
|
|
15
|
+
WebFetch: profile([], [], 'none'),
|
|
16
|
+
WebSearch: profile([], [], 'none'),
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* An unknown tool is treated as the noisiest possibility rather than passed over: everything it was given may
|
|
20
|
+
* name a target, and its result may enumerate what it reached. Callers are told the tool was not recognised, so
|
|
21
|
+
* a report can say so instead of presenting the guess as knowledge.
|
|
22
|
+
*/
|
|
23
|
+
export const UNKNOWN_TOOL = { pathKeys: [], commandKeys: [], searchWholeInput: true, result: 'listing' };
|
|
24
|
+
export function profileOf(toolName) {
|
|
25
|
+
// `Object.hasOwn`, because a tool named `toString` or `constructor` would otherwise find something on the
|
|
26
|
+
// prototype, report itself as known, and hand back a profile with no keys at all.
|
|
27
|
+
if (!Object.hasOwn(TOOL_PROFILES, toolName))
|
|
28
|
+
return { profile: UNKNOWN_TOOL, known: false };
|
|
29
|
+
const profile = TOOL_PROFILES[toolName];
|
|
30
|
+
return profile === undefined ? { profile: UNKNOWN_TOOL, known: false } : { profile, known: true };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The Grep tool prints the lines it matched only when its input asks for them; by default it prints file names
|
|
34
|
+
* (`search-hits-are-reads` H1). Measured on the maintainer's transcripts of 2026-09-24: no call of it at all - every
|
|
35
|
+
* search there went through the shell - so this is written from the tool's documented input, not from a record.
|
|
36
|
+
*/
|
|
37
|
+
export const GREP_TOOL = { name: 'Grep', outputModeKey: 'output_mode', contentMode: 'content' };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The format contract: the only code that knows the Claude Code transcript format, one subject per file in this
|
|
2
|
+
// directory. Everything here was measured against a real session (spec §4.0, tests/fixtures/oracle.json). When
|
|
3
|
+
// `agentwhy doctor` reports drift, follow .ai/skills/update-format-contract/SKILL.md instead of patching callers.
|
|
4
|
+
// v1 (2026-09-12): measured on a live Claude Code 2.1.268 session. v2 (2026-09-13): re-measured on a copy of it;
|
|
5
|
+
// `bridge-session` classified as skipped, `run_in_background` recorded as an optional Agent input key.
|
|
6
|
+
// v3 (2026-09-13): `role`, `subagent_type` and the subagent id recorded as structure rather than content, so the
|
|
7
|
+
// corpus keeps them and M1 can assert the file-id-to-agentId relation on it.
|
|
8
|
+
// v4 (2026-09-13): the two identifier fields the M1 join needs - a tool_use block's `id` and a tool_result
|
|
9
|
+
// block's `tool_use_id`, measured as present on 373 of 373 result lines.
|
|
10
|
+
// v5 (2026-09-14): `cwd`, the session working directory, recorded as a per-line field. It is the project root
|
|
11
|
+
// that the display paths of `specs/2026-09-14-path-display-and-share.md` are relative to, so where it comes from is
|
|
12
|
+
// contract knowledge rather than something a renderer works out.
|
|
13
|
+
// v6 (2026-09-15): the text block of an assistant line - what an agent writes - measured as 149 of 149 carrying a
|
|
14
|
+
// string `text`, never beside a tool_use; and a delegating call's result as a list of text blocks, 15 of 15 on the
|
|
15
|
+
// measured session. `specs/2026-09-15-what-came-back.md` R1 and R1b need both: a delegated agent's report is one of each.
|
|
16
|
+
// v7 (2026-09-15): the session title - `aiTitle` on the `ai-title` line - measured as a string on 82 of 82 such
|
|
17
|
+
// lines. `sessions` shows it, redacted, so that a person choosing a session can tell one from another.
|
|
18
|
+
// v8 (2026-09-15): the reasoning block of an assistant line - what an agent works out before it calls - measured
|
|
19
|
+
// as 249 of 249 carrying a string `thinking`, with the keys type, thinking and signature.
|
|
20
|
+
// `specs/2026-09-15-why-this-call.md` R1 needs it: the answer to "why this call" is written there, by the agent itself.
|
|
21
|
+
// v9 (2026-09-15): a report delivered late - a <task-notification> on a user line naming the delegating call by its
|
|
22
|
+
// tool-use-id, 6 of 6 on the measured session - and the payload status async_launched that makes a delegating result a
|
|
23
|
+
// launch notice, 6 of 15. `specs/2026-09-15-where-the-value-went.md` R1 and R5 need both: the motivating case's value
|
|
24
|
+
// came back on such a report, and was read from the notice. And which tools write a file, with the key of the text an edit takes
|
|
25
|
+
// out - Edit 45 of 45, Write 7 of 7 - so a value only in that text is not said to have been written (R7).
|
|
26
|
+
// v10 (2026-09-19): `SubagentHandback` - how a subagent hands its report back in auto mode from v2.1.271, as one
|
|
27
|
+
// call of its own whose input key `message` holds the words. Measured on a session of
|
|
28
|
+
// 2026-09-19: 2 calls, both on sidechain lines, one input key, a string of 200-999 characters. This closes the gap
|
|
29
|
+
// `.ai/specs/2026-09-16-when-an-agent-finishes.md` states as D9 and §6.1: until now a report delivered that way was
|
|
30
|
+
// read by nothing, so `what came back` measured the wrong text and a value that did reach the delegating agent was
|
|
31
|
+
// reported as never having left the one that read it.
|
|
32
|
+
// v11 (2026-09-23): `timestamp`, when a line was written, recorded as a per-line field for display only (the report page
|
|
33
|
+
// spec M4, Q1). Measured on three sessions of Claude Code 2.1.280: on every
|
|
34
|
+
// assistant, user, attachment, queue-operation, file-history-delta and system line of the main transcripts and every
|
|
35
|
+
// line of 2 subagent transcripts, always a string of ISO 8601 in UTC with milliseconds, all of which parse; absent on
|
|
36
|
+
// ai-title, atis-latch, last-prompt, cost-state and file-history-snapshot lines. 54 of 4020 consecutive pairs within
|
|
37
|
+
// one file run backwards in time - which is why it is shown and never used to order or join.
|
|
38
|
+
// v12 (2026-09-28): `entrypoint`, which way into Claude Code a session was started by, recorded with its three measured
|
|
39
|
+
// values (`entry-points.ts`); and `cwd` corrected - one session can carry more than one. Measured over the last megabyte
|
|
40
|
+
// of 244 transcripts (`.ai/specs/2026-09-27-which-project.md` VB5): `entrypoint` in all 244, one value each; `cwd` in all
|
|
41
|
+
// 244, a value matching the project directory's name in all 244, and more than one value in 14. The list of a person's
|
|
42
|
+
// projects reads both (V4, V9): the value that names the project is the matching one, never the last.
|
|
43
|
+
// v13 (2026-09-29): recognition by content (`recognition.ts`), so a Codex rollout is never read as a transcript
|
|
44
|
+
// (`.ai/specs/2026-09-27-what-codex-wrote.md` X2, XD7). Measured over the first lines of 257 main transcripts: a string
|
|
45
|
+
// `type` and a string `sessionId` equal to the file's name in all 257; a listed `type` in 193.
|
|
46
|
+
export const CONTRACT_VERSION = 13;
|
|
47
|
+
export const VERIFIED_AGAINST = {
|
|
48
|
+
claudeCode: '2.1.268',
|
|
49
|
+
session: '951c4f76-9d2a-40d2-9c76-1e57cf47ae4e',
|
|
50
|
+
date: '2026-09-13',
|
|
51
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FileAccessError } from '../../../ports/file-access-error.js';
|
|
3
|
+
import { LAYOUT } from '../contract/layout.js';
|
|
4
|
+
import { PROJECTS_DIRECTORY, matchesProject } from '../contract/projects.js';
|
|
5
|
+
import { SESSION_TITLE } from '../contract/session-title.js';
|
|
6
|
+
import { recognitionIn, workingDirectoryIn } from './transcript-tail.js';
|
|
7
|
+
/**
|
|
8
|
+
* How many transcripts of one project are read, newest first, to find its folder. The newest said it for 14 of 14
|
|
9
|
+
* projects measured (`which-project.md` VB5); a session begun a moment ago may not have a conversation line yet.
|
|
10
|
+
*/
|
|
11
|
+
const READS_PER_PROJECT = 3;
|
|
12
|
+
/**
|
|
13
|
+
* How many projects are read at once. Each holds up to a megabyte of a transcript's end, and a descriptor for it, until
|
|
14
|
+
* it answers; every project at once would hold all of that at the same moment, as `start`'s titles would.
|
|
15
|
+
*/
|
|
16
|
+
const PROJECTS_AT_ONCE = 8;
|
|
17
|
+
/**
|
|
18
|
+
* The projects Claude Code keeps conversations for, under `~/.claude/projects/` (`.ai/specs/2026-09-27-which-project.md`
|
|
19
|
+
* V9). A directory's name is a lossy encoding of the project's path (§13.2 pitfall 6), so the path is the `cwd` a
|
|
20
|
+
* conversation carries that matches the name - one conversation can carry several (contract v12) - and a directory whose
|
|
21
|
+
* conversations carry none is counted, never decoded. One transcript's end is read per project, the newest, and one or
|
|
22
|
+
* two more only where it does not say the folder.
|
|
23
|
+
*/
|
|
24
|
+
export class ClaudeCodeProjectCatalogue {
|
|
25
|
+
#dependencies;
|
|
26
|
+
constructor(dependencies) {
|
|
27
|
+
this.#dependencies = dependencies;
|
|
28
|
+
}
|
|
29
|
+
async list() {
|
|
30
|
+
const root = join(this.#dependencies.home, ...PROJECTS_DIRECTORY);
|
|
31
|
+
let entries;
|
|
32
|
+
try {
|
|
33
|
+
entries = await this.#dependencies.directories.list(root);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (!(error instanceof FileAccessError))
|
|
37
|
+
throw error;
|
|
38
|
+
// No conversations kept on this computer at all is an answer, not a failure.
|
|
39
|
+
return { projects: [], unreadable: 0 };
|
|
40
|
+
}
|
|
41
|
+
const directories = entries.filter((entry) => entry.kind === 'directory');
|
|
42
|
+
const found = [];
|
|
43
|
+
for (let from = 0; from < directories.length; from += PROJECTS_AT_ONCE) {
|
|
44
|
+
const batch = directories.slice(from, from + PROJECTS_AT_ONCE);
|
|
45
|
+
found.push(...(await Promise.all(batch.map((entry) => this.#project(join(root, entry.name), entry.name)))));
|
|
46
|
+
}
|
|
47
|
+
const projects = found.filter((project) => project !== undefined && project !== 'unreadable');
|
|
48
|
+
const unreadable = found.filter((project) => project === 'unreadable').length;
|
|
49
|
+
return { projects: projects.sort((a, b) => b.newest.modifiedAt - a.newest.modifiedAt), unreadable };
|
|
50
|
+
}
|
|
51
|
+
/** One directory as a project; `undefined` where it keeps no conversation, `unreadable` where none says the folder. */
|
|
52
|
+
async #project(directory, id) {
|
|
53
|
+
const transcripts = await this.#transcripts(directory);
|
|
54
|
+
const newest = transcripts[0];
|
|
55
|
+
if (newest === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
let path;
|
|
58
|
+
let recognised = {};
|
|
59
|
+
for (const [at, transcript] of transcripts.slice(0, READS_PER_PROJECT).entries()) {
|
|
60
|
+
const tail = await this.#tail(transcript.path);
|
|
61
|
+
if (tail === undefined)
|
|
62
|
+
continue;
|
|
63
|
+
if (at === 0)
|
|
64
|
+
recognised = recognitionIn(tail, this.#dependencies.redactor);
|
|
65
|
+
path = workingDirectoryIn(tail, (cwd) => matchesProject(id, cwd));
|
|
66
|
+
if (path !== undefined)
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
if (path === undefined)
|
|
70
|
+
return 'unreadable';
|
|
71
|
+
return {
|
|
72
|
+
id,
|
|
73
|
+
path,
|
|
74
|
+
exists: await this.#isDirectory(path),
|
|
75
|
+
conversations: transcripts.length,
|
|
76
|
+
newest: { modifiedAt: newest.modifiedAt, ...recognised },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** The directory's transcripts, newest first. The files inside a session's own directory are not conversations. */
|
|
80
|
+
async #transcripts(directory) {
|
|
81
|
+
const { directories } = this.#dependencies;
|
|
82
|
+
let entries;
|
|
83
|
+
try {
|
|
84
|
+
entries = await directories.list(directory);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (!(error instanceof FileAccessError))
|
|
88
|
+
throw error;
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const found = await Promise.all(entries
|
|
92
|
+
.filter((entry) => entry.kind === 'file' && entry.name.endsWith(LAYOUT.transcriptSuffix))
|
|
93
|
+
.map(async (entry) => {
|
|
94
|
+
const path = join(directory, entry.name);
|
|
95
|
+
return { path, modifiedAt: await this.#modifiedAt(path) };
|
|
96
|
+
}));
|
|
97
|
+
return found.sort((a, b) => b.modifiedAt - a.modifiedAt);
|
|
98
|
+
}
|
|
99
|
+
async #tail(path) {
|
|
100
|
+
try {
|
|
101
|
+
return await this.#dependencies.transcripts.readTail(path, SESSION_TITLE.tailBytes);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (!(error instanceof FileAccessError))
|
|
105
|
+
throw error;
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async #modifiedAt(path) {
|
|
110
|
+
try {
|
|
111
|
+
return await this.#dependencies.directories.modifiedAt(path);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof FileAccessError))
|
|
115
|
+
throw error;
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async #isDirectory(path) {
|
|
120
|
+
try {
|
|
121
|
+
return (await this.#dependencies.directories.kindOf(path)) === 'directory';
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (!(error instanceof FileAccessError))
|
|
125
|
+
throw error;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { FileAccessError } from '../../../ports/file-access-error.js';
|
|
3
|
+
import { parseJsonObject } from '../../../shared/json.js';
|
|
4
|
+
import { LAYOUT } from '../contract/layout.js';
|
|
5
|
+
import { classifyLineType } from '../contract/line-types.js';
|
|
6
|
+
import { RECOGNITION } from '../contract/recognition.js';
|
|
7
|
+
/** How many damaged lines before the first that parses recognition passes over, so a file of noise is not read whole. */
|
|
8
|
+
const DAMAGED_LINES_SKIPPED = 8;
|
|
9
|
+
/**
|
|
10
|
+
* Whether an input is a Claude Code session, by its main transcript's first non-blank line that parses (`recognition.ts`).
|
|
11
|
+
* The input is named as `ClaudeCodeSessionDiscovery` names it - a session directory or its `.jsonl` - and nothing past
|
|
12
|
+
* that line is read. A line that does not parse says how a write failed, not which format wrote it, and diagnosing it is
|
|
13
|
+
* `doctor`'s job, so up to `DAMAGED_LINES_SKIPPED` of them are passed over; a file of nothing else stays unknown. A
|
|
14
|
+
* transcript that is not there is `unavailable`, which `doctor` still reports on as Claude Code's.
|
|
15
|
+
*/
|
|
16
|
+
export async function recogniseClaudeCode(files, input) {
|
|
17
|
+
const target = resolve(input);
|
|
18
|
+
const transcript = target.endsWith(LAYOUT.transcriptSuffix) ? target : target + LAYOUT.transcriptSuffix;
|
|
19
|
+
let damaged = 0;
|
|
20
|
+
try {
|
|
21
|
+
for await (const text of files.readLines(transcript)) {
|
|
22
|
+
if (text.trim() === '')
|
|
23
|
+
continue;
|
|
24
|
+
const line = parseJsonObject(text);
|
|
25
|
+
if (line === undefined) {
|
|
26
|
+
damaged += 1;
|
|
27
|
+
if (damaged > DAMAGED_LINES_SKIPPED)
|
|
28
|
+
return 'unknown';
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
return isClaudeLine(line) ? 'recognised' : 'unknown';
|
|
32
|
+
}
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof FileAccessError)
|
|
37
|
+
return 'unavailable';
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isClaudeLine(line) {
|
|
42
|
+
const type = line[RECOGNITION.lineType];
|
|
43
|
+
if (typeof type !== 'string')
|
|
44
|
+
return false;
|
|
45
|
+
return typeof line[RECOGNITION.sessionId] === 'string' || classifyLineType(type) !== 'unknown';
|
|
46
|
+
}
|