@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,383 @@
|
|
|
1
|
+
import { escapeHtml as e } from '../html-report-components.js';
|
|
2
|
+
import { inLanguages, labelAttributes } from '../report-copy.js';
|
|
3
|
+
import { avatar } from '../ui/avatar.js';
|
|
4
|
+
import { pill } from '../ui/button.js';
|
|
5
|
+
import { drawer } from '../ui/drawer.js';
|
|
6
|
+
import { foldLine } from '../ui/fold-line.js';
|
|
7
|
+
import { hero } from '../ui/hero.js';
|
|
8
|
+
import { pillTabs } from '../ui/pill-tabs.js';
|
|
9
|
+
import { LOOKS } from '../ui/status-look.js';
|
|
10
|
+
import { opener, opens } from '../ui/popup.js';
|
|
11
|
+
import { helperViews } from './helpers.js';
|
|
12
|
+
import { titleOf } from './item-names.js';
|
|
13
|
+
import { fixId, storyId } from './to-do-view.js';
|
|
14
|
+
/**
|
|
15
|
+
* "Helpers" (the report page spec P26-P30; design file *agentwhy App*): who your AI brought in, and which of them saw your
|
|
16
|
+
* private files - as the diagram of guidelines §4, shown first, and as a list, where what saw nothing real is folded
|
|
17
|
+
* away. Each AI opens its drawer.
|
|
18
|
+
*/
|
|
19
|
+
export function helpersView(report, items, names) {
|
|
20
|
+
const views = helperViews(report);
|
|
21
|
+
const helpers = views.filter((view) => view.agent.ordinal !== undefined);
|
|
22
|
+
// With no helper, your AI still did the work: the same list and diagram, of it alone.
|
|
23
|
+
const heading = helpers.length === 0 ? aloneHeading(report, views[0]) : helpersHeading(helpers);
|
|
24
|
+
return '<div class="hv' + (heading.tone === 'mint' ? ' hv-clean' : heading.tone === 'amber' ? ' hv-amber' : heading.tone === 'blue' ? ' hv-blue' : '') + '">' +
|
|
25
|
+
hero({ eyebrow: inLanguages((t) => t('hp.eyebrow')), fact: heading.fact, action: heading.action, lead: heading.lead }) +
|
|
26
|
+
pillTabs([
|
|
27
|
+
{ label: inLanguages((t) => t('hp.tab.diagram')), panel: diagramPanel(views, items, names) },
|
|
28
|
+
{ label: inLanguages((t) => t('hp.tab.list')), panel: listPanel(views) },
|
|
29
|
+
], 'page') + '</div>';
|
|
30
|
+
}
|
|
31
|
+
/** P26: how many helpers, and how many of them read something private. */
|
|
32
|
+
function helpersHeading(helpers) {
|
|
33
|
+
const readers = helpers.filter((view) => view.status === 'read').length;
|
|
34
|
+
return {
|
|
35
|
+
fact: inLanguages((t) => t('hp.fact', { n: helpers.length })),
|
|
36
|
+
action: readers === 0 ? inLanguages((t) => t('hp.noneRead'))
|
|
37
|
+
: readers === helpers.length ? inLanguages((t) => t('hp.all', { n: helpers.length }))
|
|
38
|
+
: inLanguages((t) => t('hp.some', { n: readers })),
|
|
39
|
+
lead: inLanguages((t) => t('hp.lead')),
|
|
40
|
+
tone: readers === 0 ? 'mint' : 'coral',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* P26 with no helper: your AI worked alone, and what it did to private files, said for each status it can have.
|
|
45
|
+
* "Worked alone" only where the record could have shown a helper: a delegation whose helper is not recorded, a partial
|
|
46
|
+
* record, or no action of your AI at all say "no helper's work was recorded" instead (invariant 4).
|
|
47
|
+
*/
|
|
48
|
+
function aloneHeading(report, main) {
|
|
49
|
+
const unsure = main.status === 'empty' || report.delegations.length > 0 || report.unattributedAgents.length > 0 || report.scope.completeness !== 'complete';
|
|
50
|
+
const n = main.files.filter((file) => file.reach === (main.status === 'read' ? 'read' : 'named')).length;
|
|
51
|
+
return {
|
|
52
|
+
fact: inLanguages((t) => t(unsure ? 'hp.alone.unsure' : 'hp.alone.fact')),
|
|
53
|
+
action: inLanguages((t) => t('hp.alone.' + main.status, { n })),
|
|
54
|
+
lead: inLanguages((t) => t(main.status === 'empty' ? 'hp.alone.empty.lead' : unsure ? 'hp.alone.unsure.lead' : 'hp.alone.lead')),
|
|
55
|
+
tone: main.status === 'read' ? 'coral' : main.status === 'named' ? 'blue' : main.status === 'unknown' || main.status === 'empty' ? 'amber' : 'mint',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Every AI's drawer. Written outside the views, beside the page's other windows: Advanced and the diagram open them too,
|
|
60
|
+
* and a window inside a view that is not shown would open and not be seen.
|
|
61
|
+
*/
|
|
62
|
+
export function helperDrawers(report, items, done, names) {
|
|
63
|
+
return helperViews(report).map((view, at) => helperDrawer(view, at, items, done, names)).join('');
|
|
64
|
+
}
|
|
65
|
+
const helperId = (at) => 'helper-' + at;
|
|
66
|
+
function whoOf(agent, t) {
|
|
67
|
+
return agent.ordinal === undefined ? t('st.main') : t('st.helper', { ordinal: agent.ordinal });
|
|
68
|
+
}
|
|
69
|
+
function initialsOf(agent) {
|
|
70
|
+
return agent.ordinal === undefined ? 'AI' : 'H' + agent.ordinal;
|
|
71
|
+
}
|
|
72
|
+
function toneOf(view) {
|
|
73
|
+
return view.status === 'read' ? 'coral' : view.status === 'stopped' ? 'mint' : 'grey';
|
|
74
|
+
}
|
|
75
|
+
/** "The AI you talked to", or who brought a helper in, where the record says. */
|
|
76
|
+
function roleOf(agent, t) {
|
|
77
|
+
return agent.ordinal === undefined ? t('st.role.main')
|
|
78
|
+
: agent.broughtBy === undefined ? t('st.role.helperAny')
|
|
79
|
+
: t('st.role.helper', { who: agent.broughtBy === 'main' ? t('st.mainLower') : t('st.helper', { ordinal: agent.broughtBy }) });
|
|
80
|
+
}
|
|
81
|
+
function lineOf(view, t) {
|
|
82
|
+
return view.status === 'read' ? t('hp.line.read', { n: view.files.filter((file) => file.reach === 'read').length }) : t('hp.line.' + view.status);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* P28: your AI's card first, then, hanging from it on the diagram's grey "gave work to" line, a card per helper that
|
|
86
|
+
* read a private file, then, folded, the helpers that saw nothing real. Your AI is never folded, and neither is an AI
|
|
87
|
+
* whose outcome is not recorded: "nothing real" is not said of it.
|
|
88
|
+
*/
|
|
89
|
+
function listPanel(views) {
|
|
90
|
+
const card = (view, at) => '<a class="hl-card' + (view.status === 'read' ? ' hl-risk' : '') + '"' + opens(helperId(at)) + '>' +
|
|
91
|
+
avatar(initialsOf(view.agent), toneOf(view), 42) +
|
|
92
|
+
'<span class="hl-text"><span class="hl-name">' + inLanguages((t) => whoOf(view.agent, t)) +
|
|
93
|
+
// Who brought a helper in, beside its name: the line it hangs from says it was brought in, this says by whom.
|
|
94
|
+
(view.agent.ordinal === undefined ? '' : '<span class="hl-by">' + inLanguages((t) => roleOf(view.agent, t)) + '</span>') + '</span>' +
|
|
95
|
+
'<span class="hl-line">' + inLanguages((t) => lineOf(view, t)) + '</span></span>' +
|
|
96
|
+
'<span class="hl-see">' + inLanguages((t) => t('hp.seeWhat')) + '</span></a>';
|
|
97
|
+
const branch = (inner, fold = false) => '<div class="hl-branch' + (fold ? ' hl-branch-fold' : '') + '"><span class="hl-arrow" aria-hidden="true"></span>' + inner + '</div>';
|
|
98
|
+
const indexed = views.map((view, at) => ({ view, at }));
|
|
99
|
+
// Neither an outcome not recorded nor actions not recorded is "nothing real": both stay on the list (invariant 4).
|
|
100
|
+
const folded = ({ view }) => view.agent.ordinal !== undefined && view.status !== 'read' && view.status !== 'unknown' && view.status !== 'empty';
|
|
101
|
+
const shown = indexed.filter((each) => !folded(each) && each.view.agent.ordinal !== undefined);
|
|
102
|
+
const rest = indexed.filter(folded);
|
|
103
|
+
const fold = rest.length === 0 ? [] : [branch('<div class="hl-fold">' + foldLine({
|
|
104
|
+
summary: inLanguages((t) => t('hp.safe', { n: rest.length })),
|
|
105
|
+
body: '<div class="hl-list">' + rest.map(({ view, at }) => card(view, at)).join('') + '</div>',
|
|
106
|
+
}) + '</div>', true)];
|
|
107
|
+
const children = [...shown.map(({ view, at }) => branch(card(view, at))), ...fold];
|
|
108
|
+
return '<div class="hl-list">' + card(views[0], 0) +
|
|
109
|
+
(children.length === 0 ? '' : '<div class="hl-tree">' + children.join('') + '</div>') + '</div>';
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Node geometry, drawn on a board 1138 wide: four columns, boxes of 240 by 56, a row every 74. You and your AI are
|
|
113
|
+
* one box each and say little, so theirs are narrower, and what that frees goes to the gaps before the helpers and the
|
|
114
|
+
* files, where the lines cross. The board then takes the page's width, and every x and width with it, from 0.8 to 1.6
|
|
115
|
+
* times its own.
|
|
116
|
+
*/
|
|
117
|
+
const NODE_WIDTH = 240;
|
|
118
|
+
const START_WIDTH = 170;
|
|
119
|
+
const MAIN_WIDTH = 200;
|
|
120
|
+
const COLUMNS = [24, 224, 524, 874];
|
|
121
|
+
const rowY = (row) => 90 + row * 74;
|
|
122
|
+
/**
|
|
123
|
+
* P29: Start → your AI → the helpers → the private files, on dots. With no helper: Start → your AI → the files. A line is coral where it read, dashed blue where it
|
|
124
|
+
* only saw a name or the outcome is not recorded (one key in the legend), dashed mint where it was stopped, grey where work was handed over.
|
|
125
|
+
* Drawn in the page with no script; the script only dims what a hovered box is not joined to.
|
|
126
|
+
*/
|
|
127
|
+
function diagramPanel(views, items, names) {
|
|
128
|
+
const main = views[0];
|
|
129
|
+
const helpers = views.slice(1);
|
|
130
|
+
const files = [...new Map(views.flatMap((view) => view.files).map((file) => [file.path, file.path])).values()];
|
|
131
|
+
const height = 90 + Math.max(helpers.length, files.length, 1) * 74 + 20;
|
|
132
|
+
// A column is headed only where it has boxes: with no helper the files move up beside your AI, and no heading stands
|
|
133
|
+
// over an empty column. The board keeps the width of its files' column, so the legend always has room.
|
|
134
|
+
const filesX = helpers.length === 0 ? COLUMNS[2] : COLUMNS[3];
|
|
135
|
+
const columns = [
|
|
136
|
+
{ key: 'hp.col.start', x: COLUMNS[0] },
|
|
137
|
+
{ key: 'hp.col.main', x: COLUMNS[1] },
|
|
138
|
+
...(helpers.length === 0 ? [] : [{ key: 'hp.col.helpers', x: COLUMNS[2] }]),
|
|
139
|
+
...(files.length === 0 ? [] : [{ key: 'hp.col.files', x: filesX }]),
|
|
140
|
+
];
|
|
141
|
+
const middle = (90 + height - 20 - 74) / 2;
|
|
142
|
+
const itemAt = (path) => items.findIndex((item) => item.path === path);
|
|
143
|
+
const nodes = [
|
|
144
|
+
{ id: 'you', x: COLUMNS[0], y: middle, label: inLanguages((t) => t('st.you')), sub: inLanguages((t) => t('hp.you.sub')), icon: 'You', tone: 'you', first: true },
|
|
145
|
+
{
|
|
146
|
+
id: 'a0', x: COLUMNS[1], y: middle, label: inLanguages((t) => t('st.main')), sub: inLanguages((t) => lineOf(main, t)), icon: 'AI',
|
|
147
|
+
tone: main.status === 'read' ? 'coral' : 'grey', opens: helperId(0),
|
|
148
|
+
// No line leaves it: no connector dot on its right promises one.
|
|
149
|
+
...(helpers.length === 0 && files.length === 0 ? { last: true } : {}),
|
|
150
|
+
},
|
|
151
|
+
...helpers.map((view, row) => ({
|
|
152
|
+
id: 'a' + (row + 1), x: COLUMNS[2], y: rowY(row), label: inLanguages((t) => whoOf(view.agent, t)),
|
|
153
|
+
sub: view.agent.askedTo === undefined ? inLanguages((t) => lineOf(view, t)) : e(view.agent.askedTo),
|
|
154
|
+
icon: initialsOf(view.agent), tone: view.status === 'read' ? 'coral' : view.status === 'stopped' ? 'mint' : 'grey', opens: helperId(row + 1),
|
|
155
|
+
})),
|
|
156
|
+
...files.map((path, row) => {
|
|
157
|
+
const at = itemAt(path);
|
|
158
|
+
const item = items[at];
|
|
159
|
+
const reach = strongest(views, path);
|
|
160
|
+
return {
|
|
161
|
+
id: 'f' + row, x: filesX, y: rowY(row), label: e(names(path)), mono: true, last: true,
|
|
162
|
+
sub: item === undefined ? inLanguages((t) => t('hp.file.' + reach)) : inLanguages((t, lang) => titleOf(item, t, lang)),
|
|
163
|
+
icon: item !== undefined ? '!' : reach === 'named' ? LOOKS.name.glyph : '✓', tone: item !== undefined ? 'coral' : reach === 'named' ? 'blue' : 'mint', ...(item === undefined ? {} : { opens: storyId(at) }),
|
|
164
|
+
};
|
|
165
|
+
}),
|
|
166
|
+
];
|
|
167
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
168
|
+
const edges = [
|
|
169
|
+
{ from: 'you', to: 'a0', kind: 'work' },
|
|
170
|
+
...helpers.map((view, row) => ({ from: parentNode(view, helpers), to: 'a' + (row + 1), kind: 'work' })),
|
|
171
|
+
...views.flatMap((view, at) => view.files.map((file) => ({ from: 'a' + at, to: 'f' + files.indexOf(file.path), kind: file.reach }))),
|
|
172
|
+
];
|
|
173
|
+
const width = filesX + NODE_WIDTH + 24;
|
|
174
|
+
// Across, the board is as wide as the page: an x is a share of it, and the lines stretch with the boxes.
|
|
175
|
+
const across = (x) => Number((x / width * 100).toFixed(3)) + '%';
|
|
176
|
+
const widthOf = (node) => node.first === true ? START_WIDTH : node.id === 'a0' ? MAIN_WIDTH : NODE_WIDTH;
|
|
177
|
+
// The two narrow boxes keep their words: a second line for them, the box as much taller, and its middle where it was.
|
|
178
|
+
const narrow = (node) => widthOf(node) < NODE_WIDTH;
|
|
179
|
+
const path = (from, to) => {
|
|
180
|
+
const x1 = from.x + widthOf(from);
|
|
181
|
+
const y1 = from.y + 28;
|
|
182
|
+
const x2 = to.x;
|
|
183
|
+
const y2 = to.y + 28;
|
|
184
|
+
const d = (x2 - x1) / 2;
|
|
185
|
+
return 'M' + x1 + ',' + y1 + ' C' + (x1 + d) + ',' + y1 + ' ' + (x2 - d) + ',' + y2 + ' ' + x2 + ',' + y2;
|
|
186
|
+
};
|
|
187
|
+
const lines = edges.flatMap((edge) => {
|
|
188
|
+
const from = byId.get(edge.from);
|
|
189
|
+
const to = byId.get(edge.to);
|
|
190
|
+
return from === undefined || to === undefined ? [] : ['<path class="hd-edge hd-' + edge.kind + '" data-from="' + edge.from + '" data-to="' + edge.to + '" d="' + path(from, to) + '"/>'];
|
|
191
|
+
});
|
|
192
|
+
const box = (node) => {
|
|
193
|
+
const inner = (node.first === true ? '' : '<span class="hd-port hd-in"></span>') + (node.last === true ? '' : '<span class="hd-port hd-out"></span>') +
|
|
194
|
+
'<span class="hd-icon hd-icon-' + node.tone + '">' + node.icon + '</span>' +
|
|
195
|
+
'<span class="hd-text"><span class="hd-label' + (node.mono === true ? ' hd-mono' : '') + '">' + node.label + '</span>' +
|
|
196
|
+
'<span class="hd-sub' + (node.tone === 'coral' ? ' hd-sub-coral' : '') + '">' + node.sub + '</span></span>';
|
|
197
|
+
const at = ' class="hd-node' + (node.tone === 'coral' ? ' hd-risk' : '') + (narrow(node) ? ' hd-narrow' : '') + '" data-node="' + node.id + '"' +
|
|
198
|
+
' style="left:' + across(node.x) + ';top:' + (narrow(node) ? node.y - 8 : node.y) + 'px;width:' + across(widthOf(node)) + '"';
|
|
199
|
+
return node.opens === undefined ? '<span' + at + '>' + inner + '</span>' : '<a' + at + opens(node.opens) + '>' + inner + '</a>';
|
|
200
|
+
};
|
|
201
|
+
const legend = '<div class="hd-legend">' + ['read', 'named', 'stopped', 'work'].map((kind) => '<span><span class="hd-key hd-key-' + kind + '"></span>' + inLanguages((t) => t('hp.key.' + kind)) + '</span>').join('') + '</div>';
|
|
202
|
+
const headings = columns.map(({ key, x }) => '<span class="hd-col" style="left:' + across(x) + '">' + inLanguages((t) => t(key)) + '</span>').join('');
|
|
203
|
+
return '<div class="hd-scroll"><div class="hd-board" data-diagram style="min-width:' + Math.round(width * 0.8) + 'px;max-width:' + Math.round(width * 1.6) + 'px;height:' + height + 'px">' +
|
|
204
|
+
'<svg class="hd-lines" viewBox="0 0 ' + width + ' ' + height + '" preserveAspectRatio="none" aria-hidden="true">' + lines.join('') + '</svg>' +
|
|
205
|
+
legend + headings + nodes.map(box).join('') + '</div></div>' +
|
|
206
|
+
'<p class="hd-hint js-only">' + inLanguages((t) => t('hp.hint')) + '</p>';
|
|
207
|
+
}
|
|
208
|
+
/** Who a helper's line starts from: the helper that brought it in, where the record names one, else your AI. */
|
|
209
|
+
function parentNode(view, helpers) {
|
|
210
|
+
const by = view.agent.broughtBy;
|
|
211
|
+
if (by === undefined || by === 'main')
|
|
212
|
+
return 'a0';
|
|
213
|
+
const at = helpers.findIndex((each) => each.agent.ordinal === by);
|
|
214
|
+
return at < 0 ? 'a0' : 'a' + (at + 1);
|
|
215
|
+
}
|
|
216
|
+
/** What anyone did to a file that nobody read, for its box: its name seen, an outcome not recorded, or stopped. */
|
|
217
|
+
function strongest(views, path) {
|
|
218
|
+
const reaches = views.flatMap((view) => view.files.filter((file) => file.path === path).map((file) => file.reach));
|
|
219
|
+
return reaches.includes('unknown') ? 'unknown' : reaches.includes('named') ? 'named' : 'stopped';
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* P30: the drawer of one AI - its job as it was asked, whether it opened a private file, what it did next, the same
|
|
223
|
+
* facts in sentences (F53: no model), and the way to the files it read.
|
|
224
|
+
*/
|
|
225
|
+
function helperDrawer(view, at, items, done, names) {
|
|
226
|
+
const label = (key, vars = {}) => inLanguages((t) => t(key, vars));
|
|
227
|
+
const read = view.files.filter((file) => file.reach === 'read').map((file) => file.path);
|
|
228
|
+
const yes = view.status === 'read';
|
|
229
|
+
const job = '<div><div class="hw-label">' + label('hp.job') + '</div><div class="hw-job">' +
|
|
230
|
+
(view.agent.ordinal === undefined ? label('hp.mainJob') : view.agent.askedTo === undefined ? label('st.sub.notAsked') : '“' + e(view.agent.askedTo) + '”') + '</div></div>';
|
|
231
|
+
const box = '<div class="hw-box' + (yes ? ' hw-yes' : '') + '"><div class="hw-q">' + label('hp.opened') + '</div>' +
|
|
232
|
+
'<div class="hw-a">' + label(yes ? 'hp.yes' : 'hp.no') + '</div><div class="hw-text">' + label('hp.answer.' + view.status) + '</div>' +
|
|
233
|
+
(yes ? '<div class="hw-chips">' + read.map((path) => '<span class="hw-chip" title="' + e(path) + '">' + e(names(path)) + '</span>').join('') + '</div>' : '') + '</div>';
|
|
234
|
+
// What it did next is said wherever something went on from it - an AI that never opened the file can still have
|
|
235
|
+
// saved or sent on a value it was given, and a drawer that hid that would read as "stayed away" alone.
|
|
236
|
+
const wentOn = view.passed || view.passedUnknown || view.saved || view.repeated || view.used;
|
|
237
|
+
const next = yes || wentOn ? '<div><div class="hw-label">' + label('hp.next') + '</div>' +
|
|
238
|
+
[['hp.next.passed', view.passed ? 'yes' : view.passedUnknown ? 'unknown' : 'no'], ['hp.next.saved', view.saved ? 'yes' : 'no'],
|
|
239
|
+
['hp.next.repeated', view.repeated ? 'yes' : 'no'], ['hp.next.used', view.used ? 'yes' : 'no']].map(([key, answer]) => '<div class="hw-row"><span>' + label(key) + '</span><span class="sw-yn' + (answer === 'yes' ? ' sw-yes' : answer === 'unknown' ? ' hw-unknown' : '') + '">' +
|
|
240
|
+
label(answer === 'yes' ? 'st.yes' : answer === 'unknown' ? 'st.unknownYn' : 'st.no') + '</span></div>').join('') + '</div>' : '';
|
|
241
|
+
// F53: the facts again, as sentences. Said only where there is something beyond the job and the answer above.
|
|
242
|
+
const sentences = (t) => [
|
|
243
|
+
...(view.agent.broughtBy === undefined ? [] : [t('hp.ex.brought', { who: view.agent.broughtBy === 'main' ? t('st.main') : t('st.helper', { ordinal: view.agent.broughtBy }) })]),
|
|
244
|
+
...(read.length === 0 ? [] : [t('hp.ex.read', { files: read.map((path) => '<code>' + e(names(path)) + '</code>').join(', ') })]),
|
|
245
|
+
...(view.passed ? [t('hp.ex.passed')] : []),
|
|
246
|
+
...(view.saved ? [t('hp.ex.saved')] : []),
|
|
247
|
+
...(view.repeated ? [t('hp.ex.repeated')] : []),
|
|
248
|
+
...(view.used ? [t('hp.ex.used')] : []),
|
|
249
|
+
...(read.length === 0 ? [] : [t('hp.ex.company')]),
|
|
250
|
+
];
|
|
251
|
+
const explain = read.length === 0 && !wentOn ? '' :
|
|
252
|
+
'<details class="hw-explain"><summary class="text-link">' + label('hp.explain') + '</summary><div class="hw-explained">' +
|
|
253
|
+
inLanguages((t) => sentences(t).join(' ')) + '</div></details>';
|
|
254
|
+
const firstToFix = items.findIndex((item) => read.some((path) => path === item.path) && !done.has(item.path));
|
|
255
|
+
const anyToFix = firstToFix >= 0 ? firstToFix : items.findIndex((item) => read.some((path) => path === item.path));
|
|
256
|
+
const foot = !yes || anyToFix < 0 ? undefined
|
|
257
|
+
: pill({ label: label('hp.fix'), tone: 'primary', size: 'lg', href: '#' + fixId(anyToFix), attributes: opener(fixId(anyToFix)) });
|
|
258
|
+
return drawer({
|
|
259
|
+
id: helperId(at),
|
|
260
|
+
labelledBy: helperId(at) + '-title',
|
|
261
|
+
tone: view.status === 'read' ? 'coral' : view.status === 'stopped' ? 'mint' : view.status === 'none' ? 'grey' : view.status === 'named' ? 'blue' : 'amber',
|
|
262
|
+
title: inLanguages((t) => whoOf(view.agent, t)),
|
|
263
|
+
sub: inLanguages((t) => roleOf(view.agent, t)),
|
|
264
|
+
body: job + box + next + explain + '<p class="hw-note">' + label('st.foot') + '</p>',
|
|
265
|
+
...(foot === undefined ? {} : { foot }),
|
|
266
|
+
closeLabel: labelAttributes((t) => t('app.close')),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/** P29, P15: a hovered box keeps its own lines and the boxes they join; the rest steps back. */
|
|
270
|
+
export const HELPERS_SCRIPT = String.raw `
|
|
271
|
+
(() => {
|
|
272
|
+
// A page that adds a diagram later - a file's window brought into Conversations (F58) - starts it the same way.
|
|
273
|
+
const started = new WeakSet();
|
|
274
|
+
window.agentwhyDiagrams = (scope) => scope.querySelectorAll('[data-diagram]').forEach((board) => {
|
|
275
|
+
if (started.has(board)) return;
|
|
276
|
+
started.add(board);
|
|
277
|
+
const nodes = [...board.querySelectorAll('[data-node]')];
|
|
278
|
+
const edges = [...board.querySelectorAll('[data-from]')];
|
|
279
|
+
const show = (id) => {
|
|
280
|
+
const joined = new Set([id]);
|
|
281
|
+
edges.forEach((edge) => {
|
|
282
|
+
const on = edge.dataset.from === id || edge.dataset.to === id;
|
|
283
|
+
if (on) { joined.add(edge.dataset.from); joined.add(edge.dataset.to); }
|
|
284
|
+
edge.classList.toggle('hd-dim', !on);
|
|
285
|
+
edge.classList.toggle('hd-lit', on);
|
|
286
|
+
});
|
|
287
|
+
nodes.forEach((node) => node.classList.toggle('hd-dim', !joined.has(node.dataset.node)));
|
|
288
|
+
};
|
|
289
|
+
const clear = () => {
|
|
290
|
+
edges.forEach((edge) => edge.classList.remove('hd-dim', 'hd-lit'));
|
|
291
|
+
nodes.forEach((node) => node.classList.remove('hd-dim'));
|
|
292
|
+
};
|
|
293
|
+
nodes.forEach((node) => {
|
|
294
|
+
node.addEventListener('mouseenter', () => show(node.dataset.node));
|
|
295
|
+
node.addEventListener('mouseleave', clear);
|
|
296
|
+
node.addEventListener('focus', () => show(node.dataset.node));
|
|
297
|
+
node.addEventListener('blur', clear);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
window.agentwhyDiagrams(document);
|
|
301
|
+
})();
|
|
302
|
+
`;
|
|
303
|
+
export const HELPERS_VIEW_STYLE = String.raw `
|
|
304
|
+
.hv{max-width:840px;margin:0 auto;transition:max-width .35s ease}
|
|
305
|
+
/* The diagram takes the page's width, up to 1600px: its boxes widen with it (P29). Between the list and the diagram the
|
|
306
|
+
page widens and narrows, and the panel that comes fades in, rather than jumping. */
|
|
307
|
+
.hv:has(> .tabs > [data-tab-panel="0"].tabs-on){max-width:100%}
|
|
308
|
+
.shell-main:has(#helpers.rv-on){transition:max-width .35s ease}
|
|
309
|
+
.shell-main:has(#helpers.rv-on > .hv > .tabs > [data-tab-panel="0"].tabs-on){max-width:1600px}
|
|
310
|
+
.js .hv > .tabs > .tabs-panel.tabs-on{animation:hv-in .3s ease both}
|
|
311
|
+
@keyframes hv-in{from{opacity:0;transform:translateY(6px)}}
|
|
312
|
+
@media (prefers-reduced-motion:reduce){.js .hv > .tabs > .tabs-panel.tabs-on{animation:none}}
|
|
313
|
+
.hv .hero{margin-bottom:28px}
|
|
314
|
+
.hv .hero-fact,.hv .hero-action{font-size:34px;line-height:1.15}
|
|
315
|
+
.hv .hero-lead{font-size:16px;max-width:560px}
|
|
316
|
+
.hv-clean .hero-action{color:var(--mint)}.hv-amber .hero-action{color:var(--amber)}.hv-blue .hero-action{color:var(--blue)}
|
|
317
|
+
.hl-list{display:flex;flex-direction:column;gap:10px}
|
|
318
|
+
.hl-card{width:100%;display:flex;align-items:center;gap:16px;padding:18px 22px;border-radius:16px;background:var(--card);border:1px solid var(--white-09);color:inherit;text-decoration:none}
|
|
319
|
+
.hl-card:hover{border-color:var(--white-28);color:inherit}
|
|
320
|
+
.hl-risk{border-color:var(--coral-35)}
|
|
321
|
+
.hl-text{flex:1;min-width:0}
|
|
322
|
+
.hl-name{display:flex;align-items:baseline;flex-wrap:wrap;column-gap:10px;font-size:17px;font-weight:650}
|
|
323
|
+
.hl-line{display:block;font-size:15px;color:var(--text-2);margin-top:3px}.hl-risk .hl-line{color:var(--coral-text)}
|
|
324
|
+
.hl-see{flex:none;font-size:14px;font-weight:600;color:var(--text-2);white-space:nowrap}
|
|
325
|
+
.hl-by{font-size:13.5px;font-weight:500;color:var(--text-3)}
|
|
326
|
+
/* The line leaves the middle of your AI's avatar (its card's border, padding and half the avatar in; its padding and
|
|
327
|
+
the avatar's lower half above the list's gap) and reaches each card at its middle; the fold line, whose card can open below it, 29px down. */
|
|
328
|
+
.hl-tree{--hl-in:34px;--hl-top:-29px;display:flex;flex-direction:column;gap:10px;margin-left:43px;padding-left:var(--hl-in)}
|
|
329
|
+
.hl-branch{position:relative;--hl-mid:50%}.hl-branch-fold{--hl-mid:29px}
|
|
330
|
+
.hl-branch::before{content:"";position:absolute;left:calc(-1 * var(--hl-in));top:-10px;bottom:-10px;border-left:1.5px solid var(--white-28)}
|
|
331
|
+
.hl-branch:first-child::before{top:var(--hl-top)}
|
|
332
|
+
.hl-branch:last-child::before{bottom:auto;height:calc(var(--hl-mid) + 10px)}
|
|
333
|
+
.hl-branch:first-child:last-child::before{height:calc(var(--hl-mid) - var(--hl-top))}
|
|
334
|
+
.hl-branch::after{content:"";position:absolute;left:calc(-1 * var(--hl-in));top:var(--hl-mid);width:calc(var(--hl-in) - 8px);border-top:1.5px solid var(--white-28)}
|
|
335
|
+
.hl-arrow{position:absolute;left:-9px;top:calc(var(--hl-mid) - 4.25px);border-left:8px solid var(--white-28);border-top:5px solid transparent;border-bottom:5px solid transparent}
|
|
336
|
+
@media (max-width:640px){.hl-card{padding:16px;gap:12px}.hl-tree{--hl-in:18px;--hl-top:-27px;margin-left:37px}}
|
|
337
|
+
.hd-scroll{border-radius:18px;border:1px solid var(--white-09);background-color:var(--panel);background-image:radial-gradient(var(--white-09) 1px,transparent 1px);background-size:22px 22px;overflow:auto}
|
|
338
|
+
.hd-board{position:relative;margin:0 auto}
|
|
339
|
+
.hd-lines{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
|
|
340
|
+
.hd-edge{fill:none;stroke-width:1.6;vector-effect:non-scaling-stroke;transition:opacity .15s}
|
|
341
|
+
.hd-work{stroke:var(--white-18)}.hd-read{stroke:var(--coral)}
|
|
342
|
+
.hd-named,.hd-unknown{stroke:var(--blue);stroke-dasharray:6 5}.hd-stopped{stroke:var(--mint);stroke-dasharray:6 5}
|
|
343
|
+
.hd-edge.hd-dim{opacity:.08}.hd-edge.hd-lit{stroke-width:2.5}
|
|
344
|
+
.hd-legend{position:absolute;left:24px;top:18px;display:flex;gap:18px;flex-wrap:wrap;font-size:12.5px;color:var(--text-2)}
|
|
345
|
+
.hd-legend>span{display:flex;align-items:center;gap:7px}
|
|
346
|
+
.hd-key{width:22px}.hd-key-read{height:2px;background:var(--coral)}.hd-key-work{height:2px;background:var(--white-25)}
|
|
347
|
+
.hd-key-named{border-top:2px dashed var(--blue)}.hd-key-stopped{border-top:2px dashed var(--mint)}
|
|
348
|
+
.hd-col{position:absolute;top:52px;font-size:12px;font-weight:600;letter-spacing:0.04em;text-transform:uppercase;color:var(--text-4)}
|
|
349
|
+
.hd-node{position:absolute;height:56px;display:flex;align-items:center;gap:10px;padding:0 12px;border-radius:12px;background:var(--raised);border:1px solid var(--white-12);box-shadow:0 6px 18px var(--node-shadow);color:inherit;text-decoration:none;transition:opacity .15s,border-color .15s}
|
|
350
|
+
a.hd-node:hover{border-color:var(--white-40);color:inherit}
|
|
351
|
+
.hd-risk{border-color:var(--coral-50)}
|
|
352
|
+
.hd-node.hd-dim{opacity:.3}
|
|
353
|
+
.hd-narrow{height:72px}
|
|
354
|
+
.hd-narrow .hd-sub{white-space:normal;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-height:1.3}
|
|
355
|
+
.hd-port{position:absolute;top:calc(50% - 4.5px);width:9px;height:9px;border-radius:50%;background:var(--raised-2);border:1.5px solid var(--white-25)}
|
|
356
|
+
.hd-in{left:-5px}.hd-out{right:-5px}
|
|
357
|
+
.hd-icon{flex:none;width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}
|
|
358
|
+
.hd-icon-coral{background:var(--coral-18);color:var(--coral-text)}.hd-icon-mint{background:var(--mint-14);color:var(--mint)}
|
|
359
|
+
.hd-icon-blue{background:var(--blue-16);color:var(--blue)}.hd-icon-blue svg{width:16px;height:16px}
|
|
360
|
+
.hd-icon-grey{background:var(--raised-2);color:var(--text-2)}.hd-icon-you{background:var(--raised-2);color:var(--text)}
|
|
361
|
+
.hd-text{min-width:0}
|
|
362
|
+
.hd-label{display:block;font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
363
|
+
.hd-mono{font-family:var(--mono)}
|
|
364
|
+
.hd-sub{display:block;font-size:12px;color:var(--text-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}.hd-sub-coral{color:var(--coral-text)}
|
|
365
|
+
.hd-hint{margin:12px 4px 0;font-size:13px;color:var(--text-3)}
|
|
366
|
+
.hw-label{font-size:13px;font-weight:600;color:var(--text-3);margin-bottom:8px}
|
|
367
|
+
.hw-job{font-size:16px;line-height:1.5}
|
|
368
|
+
.hw-box{padding:20px;border-radius:14px;background:var(--mint-06);border:1px solid var(--mint-30)}
|
|
369
|
+
.hw-yes{background:var(--coral-07);border-color:var(--coral-35)}
|
|
370
|
+
.hw-q{font-size:15px;font-weight:500;color:var(--mint);margin-bottom:6px}.hw-yes .hw-q{color:var(--coral-text)}
|
|
371
|
+
.hw-a{font-size:40px;font-weight:700;letter-spacing:-0.02em;color:var(--mint);line-height:1.1;margin-bottom:8px}.hw-yes .hw-a{color:var(--coral-text)}
|
|
372
|
+
.hw-text{font-size:15px;line-height:1.5;color:var(--text-soft)}
|
|
373
|
+
.hw-chips{display:flex;gap:6px;flex-wrap:wrap;margin-top:12px}
|
|
374
|
+
.hw-chip{font-family:var(--mono);font-size:13px;font-weight:500;color:var(--text);background:var(--white-07);border:1px solid var(--white-10);border-radius:6px;padding:2px 7px}
|
|
375
|
+
.hw-row{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--white-06);font-size:15px}
|
|
376
|
+
.hw-explain>summary{list-style:none;cursor:pointer;font-size:14px;font-weight:500;width:fit-content}
|
|
377
|
+
.hw-explain>summary::-webkit-details-marker{display:none}
|
|
378
|
+
.hw-explained{margin-top:12px;font-size:15px;line-height:1.55;background:var(--panel);border:1px solid var(--white-08);border-radius:12px;padding:14px 16px}
|
|
379
|
+
.hw-explained code{font-family:var(--mono);font-size:13.5px}
|
|
380
|
+
.hw-unknown{color:var(--amber);background:var(--amber-16)}
|
|
381
|
+
.hw-note{margin:auto 0 0;font-size:13px;line-height:1.5;color:var(--text-3)}
|
|
382
|
+
.dr-foot .pill{width:100%;justify-content:center}
|
|
383
|
+
`;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { isTemplate } from '../../check/session-actions.js';
|
|
2
|
+
import { entryOf, storyAgents } from './file-story.js';
|
|
3
|
+
const RANK = ['read', 'unknown', 'named', 'stopped'];
|
|
4
|
+
export function helperViews(report) {
|
|
5
|
+
const agentOf = storyAgents(report);
|
|
6
|
+
const helpers = [...report.graph.agents].sort((a, b) => (a.ordinal ?? Infinity) - (b.ordinal ?? Infinity) || a.index - b.index);
|
|
7
|
+
const order = [report.graph.main, ...helpers];
|
|
8
|
+
return order.map((graphAgent) => {
|
|
9
|
+
const agent = agentOf(graphAgent.index);
|
|
10
|
+
const flow = report.flows.find((each) => each.agentIndex === graphAgent.index);
|
|
11
|
+
const files = new Map();
|
|
12
|
+
const kinds = new Set();
|
|
13
|
+
let unknownReply = false;
|
|
14
|
+
for (const step of flow?.steps ?? []) {
|
|
15
|
+
if (step.kind === 'returned' && (step.awaited === true || step.strength === 'unknown'))
|
|
16
|
+
unknownReply = true;
|
|
17
|
+
const paths = step.kind === 'reached' || step.kind === 'used' || step.kind === 'returned' || step.kind === 'received' ? step.files : [];
|
|
18
|
+
for (const path of paths) {
|
|
19
|
+
const entry = entryOf(step, path, agent, agentOf);
|
|
20
|
+
if (entry === undefined)
|
|
21
|
+
continue;
|
|
22
|
+
kinds.add(entry.kind);
|
|
23
|
+
const reach = reachOf(entry.kind);
|
|
24
|
+
if (reach === undefined)
|
|
25
|
+
continue;
|
|
26
|
+
const known = files.get(path);
|
|
27
|
+
if (known === undefined || RANK.indexOf(reach) < RANK.indexOf(known.reach))
|
|
28
|
+
files.set(path, { path, reach });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const reached = [...files.values()];
|
|
32
|
+
const has = (reach) => reached.some((file) => file.reach === reach);
|
|
33
|
+
const status = has('read') ? 'read'
|
|
34
|
+
: has('unknown') ? 'unknown'
|
|
35
|
+
: has('named') ? (reached.filter((file) => file.reach === 'named').every((file) => isTemplate(file.path)) ? 'sample' : 'named')
|
|
36
|
+
: has('stopped') || graphAgent.refusedAttempts > 0 ? 'stopped' : graphAgent.actions === 0 ? 'empty' : 'none';
|
|
37
|
+
return {
|
|
38
|
+
agent,
|
|
39
|
+
status,
|
|
40
|
+
files: reached,
|
|
41
|
+
passed: kinds.has('passed') || kinds.has('handed'),
|
|
42
|
+
passedUnknown: unknownReply && !kinds.has('passed') && !kinds.has('handed'),
|
|
43
|
+
saved: kinds.has('saved'),
|
|
44
|
+
repeated: kinds.has('repeated'),
|
|
45
|
+
used: kinds.has('used'),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function reachOf(kind) {
|
|
50
|
+
return kind === 'read' || kind === 'named' || kind === 'unknown' || kind === 'stopped' ? kind : undefined;
|
|
51
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { escapeHtml as e } from '../html-report-components.js';
|
|
2
|
+
import { distinctDepth } from '../path-tail.js';
|
|
3
|
+
const LOCALES = { en: 'en-US', pl: 'pl-PL', de: 'de-DE' };
|
|
4
|
+
/** The human name of each built-in rule (the parent spec's F33), by the pattern the policy lists. */
|
|
5
|
+
const RULE_NAMES = {
|
|
6
|
+
'**/.env*': 'rule.env',
|
|
7
|
+
'**/*.env': 'rule.env',
|
|
8
|
+
'**/.npmrc': 'rule.npmrc',
|
|
9
|
+
'**/secrets/**': 'rule.secrets',
|
|
10
|
+
'**/.ssh/**': 'rule.ssh',
|
|
11
|
+
'**/id_rsa*': 'rule.ssh',
|
|
12
|
+
};
|
|
13
|
+
/** "Stripe and Supabase", "Stripe, Supabase und Google" - a list, as each language joins one. */
|
|
14
|
+
export function listOf(names, lang) {
|
|
15
|
+
return new Intl.ListFormat(LOCALES[lang], { type: 'conjunction' }).format(names);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* What a file is, in words (F3): the services its keys belong to, or the rule that protects it, or "A private file".
|
|
19
|
+
* Never read from its name: `.env.local` is not "your AI account" until an OpenAI key is found in it.
|
|
20
|
+
*/
|
|
21
|
+
export function titleOf(item, t, lang) {
|
|
22
|
+
if (item.template)
|
|
23
|
+
return t('rp.item.template');
|
|
24
|
+
if (item.kind === 'keys' && item.providers.length > 0)
|
|
25
|
+
return t('rp.item.keys', { providers: e(listOf(item.providers.map((each) => each.name), lang)) });
|
|
26
|
+
if (item.kind === 'data')
|
|
27
|
+
return t('rp.item.private');
|
|
28
|
+
return t(ruleKey(item.pattern) ?? 'rp.item.private');
|
|
29
|
+
}
|
|
30
|
+
/** The word key of a built-in rule's human name (`rule.env`), or `undefined` for a pattern nobody named. */
|
|
31
|
+
export function ruleKey(pattern) {
|
|
32
|
+
return pattern === undefined ? undefined : RULE_NAMES[pattern];
|
|
33
|
+
}
|
|
34
|
+
/** The file's own name, which a chip and a sentence show; its whole path is its title. */
|
|
35
|
+
export function nameOf(path) {
|
|
36
|
+
return segmentsOf(path).pop() ?? path;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* How a page names each file it shows (`specs/2026-09-14-path-display-and-share.md` R5): its name, and where another
|
|
40
|
+
* file on the page has that name, as much of its path as tells the two apart - `web/.env` and `widget/.env`, never
|
|
41
|
+
* `.env` twice. Where no part of the path does, the path whole. A path the page did not list is named against those it did.
|
|
42
|
+
*
|
|
43
|
+
* The paths are every one the page names, so a file is lengthened for one a sentence names too. Found by a review: the
|
|
44
|
+
* name alone came first, so a path of one segment was never named whole - `.env` and `/.env`, `secrets` and
|
|
45
|
+
* `secrets/`, read alike.
|
|
46
|
+
*/
|
|
47
|
+
export function fileNames(paths) {
|
|
48
|
+
const byName = new Map();
|
|
49
|
+
for (const path of new Set(paths))
|
|
50
|
+
byName.set(nameOf(path), [...(byName.get(nameOf(path)) ?? []), path]);
|
|
51
|
+
return (path) => {
|
|
52
|
+
const alike = (byName.get(nameOf(path)) ?? []).filter((other) => other !== path);
|
|
53
|
+
if (alike.length === 0)
|
|
54
|
+
return nameOf(path);
|
|
55
|
+
const parts = segmentsOf(path);
|
|
56
|
+
// Every path alike ends in the same name, so the shortest tail that tells them apart has two parts or more.
|
|
57
|
+
const depth = distinctDepth(parts, alike.map(segmentsOf));
|
|
58
|
+
return depth === parts.length ? path : parts.slice(-depth).join('/');
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function segmentsOf(path) {
|
|
62
|
+
return path.split(/[\\/]/).filter((part) => part !== '');
|
|
63
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { insideProject } from '../../../core/project-root.js';
|
|
2
|
+
import { nameOf } from './item-names.js';
|
|
3
|
+
/**
|
|
4
|
+
* The patterns for a file the report names inside the project, or `undefined`: a display path is relative only where
|
|
5
|
+
* the file lies under the project root (`path-display-and-share` R1), and a rule written from an absolute path or one
|
|
6
|
+
* that climbs out would protect something the page cannot name for the person. A shared page writes nothing (P46).
|
|
7
|
+
*/
|
|
8
|
+
export function protectPatterns(path, shared) {
|
|
9
|
+
// With no root given, only a relative path that does not climb is inside; `~` names a home, whatever `isAbsolute` says.
|
|
10
|
+
if (shared || path.startsWith('~') || !insideProject(path, { kind: 'absent' }))
|
|
11
|
+
return undefined;
|
|
12
|
+
return { exact: './' + path.replace(/^\.\//, ''), every: '**/' + nameOf(path) };
|
|
13
|
+
}
|