@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,66 @@
|
|
|
1
|
+
import { inLanguages } from '../../render/report-copy.js';
|
|
2
|
+
import { pill } from '../../render/ui/button.js';
|
|
3
|
+
/**
|
|
4
|
+
* The welcome (`.ai/specs/2026-09-24-onboarding.md` W7-W9): points of light fly to the centre, the knot draws itself,
|
|
5
|
+
* a flash and a soft glow, and the words come in one after the other. Its promises are the ones W9 keeps true.
|
|
6
|
+
*
|
|
7
|
+
* With no script it is the first thing on the page, and the steps follow it (W27); **Get started** needs a script.
|
|
8
|
+
*/
|
|
9
|
+
export function welcomeScreen() {
|
|
10
|
+
const up = (at) => ' style="--d:' + at + 's"';
|
|
11
|
+
return '<section class="ob-screen ob-welcome" data-ob-screen="welcome" aria-labelledby="ob-welcome-title">' +
|
|
12
|
+
'<div class="ob-mark" aria-hidden="true">' + points() +
|
|
13
|
+
'<span class="ob-flash"></span><span class="ob-bloom"></span>' +
|
|
14
|
+
'<svg class="ob-knot" viewBox="-3 -2 46 44" fill="none" stroke="currentColor" stroke-width="6" stroke-linecap="round">' +
|
|
15
|
+
'<path class="ob-knot-a" d="m17 25-3 3a7.07 7.07 0 0 1-10-10l8-8a7.07 7.07 0 0 1 10 0"/>' +
|
|
16
|
+
'<path class="ob-knot-b" d="m23 15 3-3a7.07 7.07 0 0 1 10 10l-8 8a7.07 7.07 0 0 1-10 0"/></svg></div>' +
|
|
17
|
+
'<p class="ob-eyebrow ob-up"' + up(1.5) + '>' + inLanguages((t) => t('ob.eyebrow')) + '</p>' +
|
|
18
|
+
'<h1 class="ob-h1" id="ob-welcome-title"><span class="ob-h1-a ob-up"' + up(1.7) + '>' + inLanguages((t) => t('ob.h1')) + '</span>' +
|
|
19
|
+
'<span class="ob-h1-b ob-up"' + up(1.95) + '>' + inLanguages((t) => t('ob.h1b')) + '</span></h1>' +
|
|
20
|
+
'<p class="ob-lead ob-up"' + up(2.2) + '>' + inLanguages((t) => t('ob.lead')) + '</p>' +
|
|
21
|
+
'<div class="ob-start js-only ob-up"' + up(2.4) + '>' +
|
|
22
|
+
// White, as every way on in the onboarding (the maintainer, 2026-09-29).
|
|
23
|
+
pill({ label: inLanguages((t) => t('ob.start')), tone: 'light', size: 'lg', button: true, attributes: ' data-ob-go="project"' }) + '</div>' +
|
|
24
|
+
'<ul class="ob-trust ob-up"' + up(2.6) + '>' +
|
|
25
|
+
['local', 'inside', 'free'].map((key) => '<li><span class="ob-tick" aria-hidden="true">✓</span>' + inLanguages((t) => t('ob.trust.' + key)) + '</li>').join('') +
|
|
26
|
+
'</ul></section>';
|
|
27
|
+
}
|
|
28
|
+
/** The design's 22 points, from its own seed, so the page draws the same picture every time and needs no script. */
|
|
29
|
+
function points() {
|
|
30
|
+
let seed = 11;
|
|
31
|
+
const next = () => (seed = (seed * 9301 + 49297) % 233280) / 233280;
|
|
32
|
+
return Array.from({ length: 22 }, () => {
|
|
33
|
+
const angle = next() * Math.PI * 2;
|
|
34
|
+
const distance = 140 + next() * 220;
|
|
35
|
+
const size = 2 + next() * 3;
|
|
36
|
+
const delay = next() * 0.6;
|
|
37
|
+
const mint = next() < 0.3;
|
|
38
|
+
return '<span class="ob-pt ob-pt-' + (mint ? 'mint' : 'coral') + '" style="--x:' + (Math.cos(angle) * distance).toFixed(1) + 'px;--y:' +
|
|
39
|
+
(Math.sin(angle) * distance).toFixed(1) + 'px;--s:' + size.toFixed(1) + 'px;--d:' + delay.toFixed(2) + 's"></span>';
|
|
40
|
+
}).join('');
|
|
41
|
+
}
|
|
42
|
+
/** The welcome's own look. Colours come from the tokens only. */
|
|
43
|
+
export const WELCOME_STYLE = String.raw `
|
|
44
|
+
.ob-welcome{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;max-width:760px;margin:0 auto;padding-top:2vh}
|
|
45
|
+
.ob-mark{position:relative;width:96px;height:96px;margin-bottom:26px;display:flex;align-items:center;justify-content:center;color:var(--coral)}
|
|
46
|
+
.ob-pt{position:absolute;left:50%;top:50%;width:var(--s);height:var(--s);margin:calc(var(--s) / -2) 0 0 calc(var(--s) / -2);border-radius:50%;animation:obConverge 1.1s cubic-bezier(.5,0,.2,1) var(--d) both}
|
|
47
|
+
.ob-pt-coral{background:var(--coral);box-shadow:0 0 8px var(--coral)}
|
|
48
|
+
.ob-pt-mint{background:var(--mint);box-shadow:0 0 8px var(--mint)}
|
|
49
|
+
.ob-flash{position:absolute;inset:-10px;border-radius:50%;background:radial-gradient(circle,var(--text),var(--coral-40) 40%,transparent 70%);animation:obFlash 1s ease-out 1.25s both}
|
|
50
|
+
.ob-bloom{position:absolute;inset:-40px;border-radius:50%;background:radial-gradient(circle,var(--coral-35),transparent 62%);filter:blur(8px);animation:obBloom 1.2s ease-out 1.2s both,obGlow 4s ease-in-out 2.4s infinite}
|
|
51
|
+
.ob-knot{position:relative;width:72px;height:69px;filter:drop-shadow(0 0 10px var(--coral-60));animation:obFloat 4s ease-in-out 2.4s infinite}
|
|
52
|
+
.ob-knot path{stroke-dasharray:120;stroke-dashoffset:120}
|
|
53
|
+
.ob-knot-a{animation:obDraw 1s ease-out .6s forwards}
|
|
54
|
+
.ob-knot-b{animation:obDraw 1s ease-out .85s forwards}
|
|
55
|
+
.ob-up{animation:obUp .8s cubic-bezier(.2,.8,.2,1) var(--d) both}
|
|
56
|
+
.ob-eyebrow{margin:0 0 18px;font-size:17px;font-weight:600;color:var(--coral-text)}
|
|
57
|
+
.ob-h1{margin:0 0 22px;display:flex;flex-direction:column;gap:6px;font-weight:650;text-wrap:balance}
|
|
58
|
+
.ob-h1-a{font-size:60px;line-height:1.05;letter-spacing:-.035em}
|
|
59
|
+
.ob-h1-b{font-size:44px;line-height:1.1;letter-spacing:-.03em;color:var(--coral)}
|
|
60
|
+
.ob-lead{margin:0 0 32px;max-width:500px;font-size:17px;line-height:1.55;color:var(--text-2);text-wrap:pretty}
|
|
61
|
+
.ob-start .pill{padding:15px 30px;font-size:16px;box-shadow:0 10px 40px var(--white-10)}
|
|
62
|
+
.ob-trust{list-style:none;margin:18px 0 0;padding:0;display:flex;gap:18px;flex-wrap:wrap;justify-content:center;font-size:13.5px;color:var(--text-2)}
|
|
63
|
+
.ob-trust li{display:flex;align-items:center;gap:6px}
|
|
64
|
+
.ob-tick{color:var(--mint)}
|
|
65
|
+
@media (max-width:640px){.ob-h1-a{font-size:40px}.ob-h1-b{font-size:30px}.ob-lead{font-size:16px}}
|
|
66
|
+
`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { updateNotice } from '../render/ui/update-notice.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a page `start` serves draws over its content, from the model it is written with: the update notice, where the
|
|
4
|
+
* project's hooks run an older release than this agentwhy (`nothing-updates-by-itself.md` U4), and nothing otherwise.
|
|
5
|
+
* The onboarding does not ask for it: the hooks it writes are this release's.
|
|
6
|
+
*/
|
|
7
|
+
export function pageNotice(index) {
|
|
8
|
+
const behind = index.settings?.hooks?.behind;
|
|
9
|
+
return behind === undefined ? {} : { notice: updateNotice(behind) };
|
|
10
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { join, relative } from 'node:path';
|
|
2
|
+
import { SETTINGS_FILES } from '../../../adapter/claude-code/contract/settings.js';
|
|
3
|
+
import { projectDirectoryName } from '../../../adapter/claude-code/contract/projects.js';
|
|
4
|
+
import { homeRelative } from '../../render/home-relative.js';
|
|
5
|
+
import { projectName } from '../app-nav.js';
|
|
6
|
+
import { readSettingsFile, setUpBy } from '../settings-files.js';
|
|
7
|
+
/**
|
|
8
|
+
* The person's projects as the window draws them (`.ai/specs/2026-09-27-which-project.md` V10): where each is, whether
|
|
9
|
+
* its folder is there, and whether it is set up - W23's two facts, read from the folder's own settings and the record of
|
|
10
|
+
* finished onboardings. Nothing is said of a folder that is gone, or whose settings could not be read.
|
|
11
|
+
*/
|
|
12
|
+
export async function indexProjects(listing, sources) {
|
|
13
|
+
const projects = listing.projects.filter((project) => !sources.noProject(project.path));
|
|
14
|
+
const temporary = sources.temporary;
|
|
15
|
+
const listed = temporary === undefined ? projects : projects.filter((project) => !temporary(project.path));
|
|
16
|
+
const finished = await sources.onboarding?.doneFor(listed.map((project) => projectDirectoryName(project.path)));
|
|
17
|
+
const rows = await Promise.all(listed.map(async (project) => {
|
|
18
|
+
const setUp = project.exists ? await setUpIn(project, sources.files, finished) : undefined;
|
|
19
|
+
return {
|
|
20
|
+
id: project.id,
|
|
21
|
+
place: homeRelative(project.path, sources.home),
|
|
22
|
+
name: projectName(project.path),
|
|
23
|
+
exists: project.exists,
|
|
24
|
+
conversations: project.conversations,
|
|
25
|
+
newest: project.newest,
|
|
26
|
+
...(setUp === undefined ? {} : { setUp }),
|
|
27
|
+
current: relative(project.path, sources.workingDirectory) === '',
|
|
28
|
+
};
|
|
29
|
+
}));
|
|
30
|
+
const hidden = projects.length - listed.length;
|
|
31
|
+
return { rows, unreadable: listing.unreadable, ...(hidden === 0 ? {} : { temporary: hidden }), switchable: sources.switchable, choosable: sources.choosable };
|
|
32
|
+
}
|
|
33
|
+
/** Set up where the settings say so (`setUpBy`), or the onboarding was finished; unknown where either is unread. */
|
|
34
|
+
async function setUpIn(project, files, finished) {
|
|
35
|
+
const directory = join(project.path, SETTINGS_FILES.directory);
|
|
36
|
+
const local = await readSettingsFile(files, join(directory, SETTINGS_FILES.local));
|
|
37
|
+
const shared = await readSettingsFile(files, join(directory, SETTINGS_FILES.shared));
|
|
38
|
+
if (setUpBy([local, shared]))
|
|
39
|
+
return true;
|
|
40
|
+
if (local === 'unreadable' || shared === 'unreadable' || finished === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
return finished.has(projectDirectoryName(project.path));
|
|
43
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { escapeHtml as e } from '../../render/html-report-components.js';
|
|
2
|
+
import { INDEX_CONTENT_SECURITY_POLICY_META } from '../../render/html-head.js';
|
|
3
|
+
import { inLanguages, LANGS, translator } from '../../render/report-copy.js';
|
|
4
|
+
import { BUTTON_STYLE, pill } from '../../render/ui/button.js';
|
|
5
|
+
import { pageShell } from '../../render/ui/page-shell.js';
|
|
6
|
+
/**
|
|
7
|
+
* What a page of a project shown earlier in this process says now (`.ai/specs/2026-09-27-which-project.md` V14, amended
|
|
8
|
+
* 2026-09-28 by the maintainer: "Back" after a switch reached nothing). The process serves one project at a time, at one
|
|
9
|
+
* address, so the pages of the one before - in a tab's history, or reloaded - reach it and are told: "This page was for
|
|
10
|
+
* my-app. agentwhy now shows blog." **Show my-app again** switches back in this tab; **Go to blog** is the project
|
|
11
|
+
* shown. Nothing of the earlier project is served, and nothing is written from its pages but the way back (V17).
|
|
12
|
+
*/
|
|
13
|
+
export function movedPage(was, now, nowUrl) {
|
|
14
|
+
return pageShell({
|
|
15
|
+
title: 'moved.pageTitle',
|
|
16
|
+
policy: INDEX_CONTENT_SECURITY_POLICY_META,
|
|
17
|
+
styles: [BUTTON_STYLE, MOVED_STYLE],
|
|
18
|
+
scripts: [MOVED_SCRIPT],
|
|
19
|
+
live: false,
|
|
20
|
+
main: '<section class="mv" data-moved-words="' + e(JSON.stringify(words(was.name))) + '">' +
|
|
21
|
+
'<p class="mv-eyebrow">agentwhy</p>' +
|
|
22
|
+
'<h1 class="mv-title">' + inLanguages((t) => t('moved.title', { was: '<strong>' + e(was.name) + '</strong>' })) + '</h1>' +
|
|
23
|
+
'<p class="mv-text">' + inLanguages((t) => t('moved.text', { now: '<strong>' + e(now.name) + '</strong>' })) + '</p>' +
|
|
24
|
+
'<div class="mv-go">' +
|
|
25
|
+
'<span class="js-only">' + pill({ label: inLanguages((t) => t('moved.back', { was: e(was.name) })), tone: 'light', size: 'lg', button: true, attributes: ' data-moved-back' }) + '</span>' +
|
|
26
|
+
pill({ label: inLanguages((t) => t('moved.stay', { now: e(now.name) })), tone: 'outline', size: 'lg', href: e(nowUrl) }) +
|
|
27
|
+
'</div>' +
|
|
28
|
+
'<p class="mv-say" role="status" data-moved-say></p>' +
|
|
29
|
+
'<p class="mv-noscript">' + inLanguages((t) => t('moved.noScript', { was: e(was.name) })) + '</p>' +
|
|
30
|
+
'</section>',
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** What the script says, in every language: it cannot hold three in one text node. */
|
|
34
|
+
function words(was) {
|
|
35
|
+
return Object.fromEntries(LANGS.map((lang) => {
|
|
36
|
+
const t = translator(lang);
|
|
37
|
+
return [lang, { switching: t('proj.switching', { name: was }), failed: t('proj.switchFailed', { reason: '{reason}' }), unreachable: t('proj.unreachable') }];
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* **Show my-app again**: the earlier project's run starts again and this tab goes to it; the answer is its address. Asked
|
|
42
|
+
* at this page's own address - the earlier project's - which the process takes for nothing but this.
|
|
43
|
+
*/
|
|
44
|
+
const MOVED_SCRIPT = String.raw `
|
|
45
|
+
(() => {
|
|
46
|
+
const root = document.querySelector('[data-moved-words]');
|
|
47
|
+
const back = document.querySelector('[data-moved-back]');
|
|
48
|
+
const say = document.querySelector('[data-moved-say]');
|
|
49
|
+
if (!root || !back || !say) return;
|
|
50
|
+
const words = JSON.parse(root.getAttribute('data-moved-words'));
|
|
51
|
+
const word = (key) => (words[document.documentElement.dataset.lang] || words.en)[key];
|
|
52
|
+
back.addEventListener('click', () => {
|
|
53
|
+
back.disabled = true;
|
|
54
|
+
back.classList.add('pill-busy');
|
|
55
|
+
say.textContent = word('switching');
|
|
56
|
+
fetch('api/switch-back', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
|
57
|
+
.then((response) => response.json().then((answer) => ({ ok: response.ok && answer.ok === true, answer })))
|
|
58
|
+
.catch(() => ({ ok: false, unreachable: true }))
|
|
59
|
+
.then((answer) => {
|
|
60
|
+
if (answer.ok && typeof answer.answer.url === 'string') { location.replace(answer.answer.url); return; }
|
|
61
|
+
back.disabled = false;
|
|
62
|
+
back.classList.remove('pill-busy');
|
|
63
|
+
say.textContent = answer.unreachable ? word('unreachable') : word('failed').replace('{reason}', (answer.answer && answer.answer.message) || '');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
})();
|
|
67
|
+
`;
|
|
68
|
+
/** The page's own look. Colours come from the tokens only. */
|
|
69
|
+
const MOVED_STYLE = String.raw `
|
|
70
|
+
.mv{max-width:620px;margin:18vh auto 0;text-align:center;display:flex;flex-direction:column;align-items:center}
|
|
71
|
+
.mv-eyebrow{margin:0 0 14px;font-size:15px;font-weight:600;color:var(--coral-text)}
|
|
72
|
+
.mv-title{margin:0 0 12px;font-size:36px;line-height:1.15;letter-spacing:-.02em;font-weight:650;text-wrap:balance}
|
|
73
|
+
.mv-title strong,.mv-text strong{color:var(--text);font-weight:650}
|
|
74
|
+
.mv-text{margin:0 0 26px;font-size:17px;line-height:1.55;color:var(--text-2);text-wrap:pretty}
|
|
75
|
+
.mv-go{display:flex;gap:12px;flex-wrap:wrap;justify-content:center}
|
|
76
|
+
.mv-go .pill{justify-content:center}
|
|
77
|
+
.mv-say{margin:16px 0 0;font-size:14px;color:var(--text-soft)}
|
|
78
|
+
.mv-say:empty{display:none}
|
|
79
|
+
.mv-noscript{margin:16px 0 0;font-size:13.5px;color:var(--text-3)}
|
|
80
|
+
.js .mv-noscript{display:none}
|
|
81
|
+
@media (max-width:640px){.mv{margin-top:10vh}.mv-title{font-size:28px}.mv-go{flex-direction:column;align-self:stretch}.mv-go .js-only .pill,.mv-go>.pill{width:100%}}
|
|
82
|
+
`;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { escapeHtml as e } from '../../render/html-report-components.js';
|
|
2
|
+
import { inLanguages, LANGS, translator } from '../../render/report-copy.js';
|
|
3
|
+
import { pill } from '../../render/ui/button.js';
|
|
4
|
+
/**
|
|
5
|
+
* Showing another project in this tab (`.ai/specs/2026-09-27-which-project.md` V12, V14, V15), shared by the two places
|
|
6
|
+
* that offer it: the window the sidebar's card opens, and the onboarding's project step (V11). A container carrying
|
|
7
|
+
* `switchArea(from)` holds the buttons: any `[data-switch-project]` in it asks this run's server to show that project,
|
|
8
|
+
* and **Choose a folder…** opens the computer's own folder window. The page names a project by the id the server listed
|
|
9
|
+
* it under, never by a path (V17), and says where it asked from, so the run it lands on opens the page it should (V16).
|
|
10
|
+
*/
|
|
11
|
+
/** The attributes of a container the script works in: its words, and - on the onboarding's step - where it asks from. */
|
|
12
|
+
export function switchArea(from) {
|
|
13
|
+
return ' data-switch-words="' + e(JSON.stringify(switchWords())) + '"' + (from === undefined ? '' : ' data-switch-from="' + from + '"');
|
|
14
|
+
}
|
|
15
|
+
/** A small folder, drawn: the guidelines have no emoji, not even in a button (§9.3). */
|
|
16
|
+
const FOLDER_SVG = '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true">' +
|
|
17
|
+
'<path d="M3 6.5A1.5 1.5 0 0 1 4.5 5h4.2l2 2.2h8.8A1.5 1.5 0 0 1 21 8.7v9.8a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 18.5z"/></svg>';
|
|
18
|
+
/**
|
|
19
|
+
* "Don't see your project?" (V12): where the computer has a folder window, **Choose a folder…** opens it, and what it came
|
|
20
|
+
* to is said under the box; where it has none, how to bring a project onto the list.
|
|
21
|
+
*/
|
|
22
|
+
export function chooseFolderBox(choosable) {
|
|
23
|
+
return '<div class="pjw-missing" data-choose-area><div class="pjw-missing-words">' +
|
|
24
|
+
'<p class="pjw-missing-title">' + inLanguages((t) => t('proj.missing.title')) + '</p>' +
|
|
25
|
+
'<p class="pjw-missing-text">' + inLanguages((t) => t('proj.missing.text')) +
|
|
26
|
+
(choosable ? '' : ' ' + inLanguages((t) => t('proj.missing.how'))) + '</p></div>' +
|
|
27
|
+
(choosable ? '<span class="js-only pjw-choose">' + pill({ label: FOLDER_SVG + inLanguages((t) => t('proj.choose')), tone: 'light', size: 'lg', button: true, attributes: ' data-choose-folder' }) + '</span>' : '') +
|
|
28
|
+
'</div>' +
|
|
29
|
+
(choosable ? '<div class="pjw-near" data-choose-area><p class="pjw-say" role="status" data-choose-say></p><div class="pjw-near-offers" data-choose-near></div></div>' : '');
|
|
30
|
+
}
|
|
31
|
+
/** Where a switch asked for over the list says what it came to. */
|
|
32
|
+
export const SWITCH_SAY = '<p class="pjw-say" role="status" data-switch-say></p>';
|
|
33
|
+
/** The script's own sentences, per language; `{name}` and `{reason}` are filled in where they are said. */
|
|
34
|
+
function switchWords() {
|
|
35
|
+
return Object.fromEntries(LANGS.map((lang) => {
|
|
36
|
+
const t = translator(lang);
|
|
37
|
+
return [lang, {
|
|
38
|
+
switching: t('proj.switching', { name: '{name}' }),
|
|
39
|
+
failed: t('proj.switchFailed', { reason: '{reason}' }),
|
|
40
|
+
unreachable: t('proj.unreachable'),
|
|
41
|
+
chooseHome: t('proj.chooseHome'),
|
|
42
|
+
chooseRoot: t('proj.chooseRoot'),
|
|
43
|
+
chooseHere: t('proj.chooseHere'),
|
|
44
|
+
nearAbove: t('proj.nearAbove', { name: '{name}' }),
|
|
45
|
+
nearInside: t('proj.nearInside', { name: '{name}' }),
|
|
46
|
+
openName: t('proj.openName', { name: '{name}' }),
|
|
47
|
+
useAnyway: t('proj.useAnyway', { name: '{name}' }),
|
|
48
|
+
}];
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* V14, V15: a click asks this run's server to show the project in its place; the answer is the address of the run that
|
|
53
|
+
* does, and this tab goes there. A refusal leaves the page as it was and says why. The page sends the project's id -
|
|
54
|
+
* the name the server listed it by - and never a path (V17).
|
|
55
|
+
*/
|
|
56
|
+
export const SWITCH_SCRIPT = String.raw `
|
|
57
|
+
(() => {
|
|
58
|
+
document.querySelectorAll('[data-switch-words]').forEach((root) => {
|
|
59
|
+
const words = JSON.parse(root.getAttribute('data-switch-words'));
|
|
60
|
+
const from = root.getAttribute('data-switch-from');
|
|
61
|
+
const say = root.querySelector('[data-switch-say]');
|
|
62
|
+
const chooseSay = root.querySelector('[data-choose-say]');
|
|
63
|
+
const near = root.querySelector('[data-choose-near]');
|
|
64
|
+
const word = (key) => (words[document.documentElement.dataset.lang] || words.en)[key];
|
|
65
|
+
const lock = (locked) => root.querySelectorAll('[data-switch-project], [data-choose-folder]').forEach((one) => { one.disabled = locked; });
|
|
66
|
+
// Said beside what was clicked: a row's switch over the list, a chosen folder's under its box.
|
|
67
|
+
const sayFor = (element) => (element.closest('[data-choose-area]') && chooseSay) || say;
|
|
68
|
+
const ask = (route, body) => fetch(route, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(from ? { ...body, from } : body) })
|
|
69
|
+
.then((response) => response.json().then((answer) => ({ ok: response.ok && answer.ok === true, answer })))
|
|
70
|
+
.catch(() => ({ ok: false, unreachable: true }));
|
|
71
|
+
const refused = (where, answer) => {
|
|
72
|
+
lock(false);
|
|
73
|
+
if (where) where.textContent = answer.unreachable ? word('unreachable') : word('failed').replace('{reason}', (answer.answer && answer.answer.message) || '');
|
|
74
|
+
};
|
|
75
|
+
const offer = (label, id, name, tone) => {
|
|
76
|
+
const button = document.createElement('button');
|
|
77
|
+
button.type = 'button';
|
|
78
|
+
button.className = 'pill pill-' + tone + ' pill-md';
|
|
79
|
+
button.setAttribute('data-switch-project', id);
|
|
80
|
+
button.setAttribute('data-switch-name', name);
|
|
81
|
+
button.textContent = label.replace('{name}', name);
|
|
82
|
+
return button;
|
|
83
|
+
};
|
|
84
|
+
root.addEventListener('click', (event) => {
|
|
85
|
+
const button = event.target.closest('[data-switch-project]');
|
|
86
|
+
if (button) {
|
|
87
|
+
const where = sayFor(button);
|
|
88
|
+
lock(true);
|
|
89
|
+
if (where) where.textContent = word('switching').replace('{name}', button.getAttribute('data-switch-name'));
|
|
90
|
+
ask('api/switch-project', { id: button.getAttribute('data-switch-project') }).then((answer) => {
|
|
91
|
+
if (answer.ok && typeof answer.answer.url === 'string') { location.assign(answer.answer.url); return; }
|
|
92
|
+
refused(where, answer);
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const choose = event.target.closest('[data-choose-folder]');
|
|
97
|
+
if (!choose || !chooseSay || !near) return;
|
|
98
|
+
lock(true);
|
|
99
|
+
near.textContent = '';
|
|
100
|
+
chooseSay.textContent = '';
|
|
101
|
+
// The maintainer, 2026-09-28: a loader, not a sentence, while the folder window opens and is open.
|
|
102
|
+
const busy = (on) => { choose.classList.toggle('pill-busy', on); if (on) choose.setAttribute('aria-busy', 'true'); else choose.removeAttribute('aria-busy'); };
|
|
103
|
+
busy(true);
|
|
104
|
+
ask('api/choose-folder', { lang: document.documentElement.dataset.lang || 'en' }).then((answer) => {
|
|
105
|
+
busy(false);
|
|
106
|
+
if (!answer.ok) { refused(chooseSay, answer); return; }
|
|
107
|
+
const said = answer.answer;
|
|
108
|
+
if (said.kind === 'switched' && typeof said.url === 'string') { location.assign(said.url); return; }
|
|
109
|
+
lock(false);
|
|
110
|
+
if (said.kind === 'cancelled') { chooseSay.textContent = ''; return; }
|
|
111
|
+
if (said.kind === 'refused') { chooseSay.textContent = word(said.not === 'home' ? 'chooseHome' : 'chooseRoot'); return; }
|
|
112
|
+
if (said.kind === 'here') { chooseSay.textContent = word('chooseHere'); return; }
|
|
113
|
+
if (said.kind === 'near') {
|
|
114
|
+
chooseSay.textContent = said.above ? word('nearAbove').replace('{name}', said.projects[0].name) : word('nearInside').replace('{name}', said.chosen.name);
|
|
115
|
+
// The project shown is offered only on the onboarding's step, where choosing it goes on in the page.
|
|
116
|
+
said.projects.filter((project) => !project.here).forEach((project) => near.append(offer(word('openName'), project.id, project.name, 'light')));
|
|
117
|
+
near.append(offer(word('useAnyway'), said.chosen.id, said.chosen.name, 'outline'));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
})();
|
|
123
|
+
`;
|
|
124
|
+
/** The box, and what is said under it and over the list. Colours come from the tokens only. */
|
|
125
|
+
export const SWITCH_STYLE = String.raw `
|
|
126
|
+
.pjw-say{margin:0;min-height:0;font-size:14px;color:var(--text-soft)}
|
|
127
|
+
.pjw-say:not(:empty){margin:10px 4px 0}
|
|
128
|
+
.pjw-missing{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:20px;padding:18px 20px;border:1px dashed var(--white-16);border-radius:16px}
|
|
129
|
+
.pjw-missing-words{min-width:0}
|
|
130
|
+
.pjw-choose{flex:none}
|
|
131
|
+
.pjw-choose .pill{display:inline-flex;align-items:center;gap:9px}
|
|
132
|
+
.pjw-near .pjw-say:not(:empty){margin:12px 4px 0}
|
|
133
|
+
.pjw-near-offers{display:flex;flex-wrap:wrap;gap:10px;margin:10px 4px 0}
|
|
134
|
+
.pjw-near-offers:empty{display:none}
|
|
135
|
+
.pjw-missing-title{margin:0;font-size:15.5px;font-weight:600}
|
|
136
|
+
.pjw-missing-text{margin:4px 0 0;font-size:14px;line-height:1.5;color:var(--text-2)}
|
|
137
|
+
@media (max-width:640px){.pjw-missing{flex-direction:column;align-items:stretch}.pjw-choose .pill{width:100%;justify-content:center}}
|
|
138
|
+
`;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { escapeHtml as e } from '../../render/html-report-components.js';
|
|
2
|
+
import { inLanguages, labelAttributes } from '../../render/report-copy.js';
|
|
3
|
+
import { BUTTON_STYLE, closeButton, pill } from '../../render/ui/button.js';
|
|
4
|
+
import { CLOSES, POPUP_SCRIPT, POPUP_STYLE, popup } from '../../render/ui/popup.js';
|
|
5
|
+
import { PROJECT_LIST_SCRIPT, PROJECT_LIST_STYLE, projectList } from '../../render/ui/project-list.js';
|
|
6
|
+
import { TAG_STYLE } from '../../render/ui/tag.js';
|
|
7
|
+
import { chooseFolderBox, SWITCH_SAY, SWITCH_SCRIPT, SWITCH_STYLE, switchArea } from './project-switch.js';
|
|
8
|
+
/** The window's id: the sidebar's card links to it (`which-project.md` V2). */
|
|
9
|
+
export const PROJECTS_WINDOW = 'projects';
|
|
10
|
+
/**
|
|
11
|
+
* The window that switches projects, over any page `start` serves (`.ai/specs/2026-09-27-which-project.md` V2, V10-V15),
|
|
12
|
+
* as the maintainer's design of 2026-09-28 draws it: opened from the sidebar's card, so the page stays where it is
|
|
13
|
+
* (guidelines §8). Every other project offers a way to it - **Open** (light, the maintainer's) where it is set up, **Set
|
|
14
|
+
* it up →** (coral: it starts the setup) where it is not - and either shows that project in this tab (V14, V15). Where
|
|
15
|
+
* the run cannot switch, nothing is offered; without a script nothing switches, and the window says so (V13). Nothing
|
|
16
|
+
* where the run is not served or shares its pages.
|
|
17
|
+
*/
|
|
18
|
+
export function projectsWindows(index) {
|
|
19
|
+
const projects = index.projects;
|
|
20
|
+
if (projects === undefined)
|
|
21
|
+
return [];
|
|
22
|
+
const title = PROJECTS_WINDOW + '-title';
|
|
23
|
+
const action = projects.switchable ? switchAction : undefined;
|
|
24
|
+
return [{
|
|
25
|
+
html: popup({
|
|
26
|
+
id: PROJECTS_WINDOW,
|
|
27
|
+
size: 'list',
|
|
28
|
+
labelledBy: title,
|
|
29
|
+
body: '<div class="pjw"' + switchArea() + '>' +
|
|
30
|
+
'<div class="pjw-head"><div><h2 class="pjw-title" id="' + title + '">' + inLanguages((t) => t('proj.title')) + '</h2>' +
|
|
31
|
+
'<p class="pjw-lead">' + inLanguages((t) => t('proj.lead')) + '</p></div>' +
|
|
32
|
+
closeButton(labelAttributes((t) => t('app.close')) + CLOSES) + '</div>' +
|
|
33
|
+
'<div class="pjw-body">' +
|
|
34
|
+
SWITCH_SAY +
|
|
35
|
+
projectList({ rows: projects.rows, unreadable: projects.unreadable, now: index.now, timeZone: index.timeZone ?? 'UTC', ...(action === undefined ? {} : { action }) }) +
|
|
36
|
+
(projects.switchable ? '<p class="pjw-noscript">' + inLanguages((t) => t('proj.noScript')) + '</p>' : '') +
|
|
37
|
+
chooseFolderBox(projects.choosable) +
|
|
38
|
+
'<p class="pjw-which">' + inLanguages((t) => t('proj.which')) + '</p>' +
|
|
39
|
+
'</div></div>',
|
|
40
|
+
}),
|
|
41
|
+
styles: [BUTTON_STYLE, POPUP_STYLE, TAG_STYLE, PROJECT_LIST_STYLE, SWITCH_STYLE, PROJECTS_WINDOW_STYLE],
|
|
42
|
+
scripts: [POPUP_SCRIPT, PROJECT_LIST_SCRIPT, SWITCH_SCRIPT],
|
|
43
|
+
}];
|
|
44
|
+
}
|
|
45
|
+
/** What another project offers: the way to it, which only a script can take. */
|
|
46
|
+
function switchAction(row) {
|
|
47
|
+
if (row.current)
|
|
48
|
+
return '';
|
|
49
|
+
const label = inLanguages((t) => t(row.setUp === false ? 'proj.setItUp' : 'proj.open'));
|
|
50
|
+
return '<span class="js-only">' + pill({
|
|
51
|
+
label,
|
|
52
|
+
tone: row.setUp === false ? 'primary' : 'light',
|
|
53
|
+
size: 'md',
|
|
54
|
+
button: true,
|
|
55
|
+
attributes: ' data-switch-project="' + e(row.id) + '" data-switch-name="' + e(row.name) + '"',
|
|
56
|
+
}) + '</span>';
|
|
57
|
+
}
|
|
58
|
+
const PROJECTS_WINDOW_STYLE = String.raw `
|
|
59
|
+
.pjw-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:30px 32px 0}
|
|
60
|
+
.pjw-title{margin:0;font-size:28px;line-height:1.2;font-weight:650;letter-spacing:-0.015em}
|
|
61
|
+
.pjw-lead{margin:8px 0 0;font-size:15px;line-height:1.5;color:var(--text-2)}
|
|
62
|
+
.pjw-body{padding:8px 32px 30px}
|
|
63
|
+
.pjw-noscript{display:none;margin:12px 4px 0;font-size:13px;color:var(--text-3)}
|
|
64
|
+
html:not(.js) .pjw-noscript{display:block}
|
|
65
|
+
.pjw-which{margin:16px 4px 0;font-size:12.5px;line-height:1.5;color:var(--text-3)}
|
|
66
|
+
@media (max-width:640px){.pjw-head{padding:22px 18px 0}.pjw-title{font-size:24px}.pjw-body{padding:6px 18px 22px}}
|
|
67
|
+
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isAbsolute, relative, sep } from 'node:path';
|
|
2
|
+
/** Where systems keep what is thrown away: `/tmp`, and on macOS the per-person folders `TMPDIR` points into. */
|
|
3
|
+
const SYSTEM_TEMPORARY = ['/tmp', '/private/tmp', '/var/tmp', '/private/var/tmp', '/var/folders', '/private/var/folders'];
|
|
4
|
+
/**
|
|
5
|
+
* Whether a folder lies in the computer's temporary space - where a quick try with the agent is started, and nobody keeps a
|
|
6
|
+
* project (`.ai/specs/2026-09-27-which-project.md` V10, the maintainer's design: "Hidden: 1 temporary folder"). The run's
|
|
7
|
+
* own temporary directory counts, reached through `/private` too, since macOS reports a folder's path with every link
|
|
8
|
+
* followed. A home directory that itself lies there - a test's, a sandbox's - keeps its folders: they are its projects.
|
|
9
|
+
*/
|
|
10
|
+
export function inTemporarySpace(path, temporaryDirectory, home) {
|
|
11
|
+
const roots = [temporaryDirectory, ...(temporaryDirectory.startsWith('/var/') ? [`/private${temporaryDirectory}`] : []), ...SYSTEM_TEMPORARY];
|
|
12
|
+
return roots.some((root) => within(root, path) && !(home !== undefined && home !== '' && within(root, home) && (within(home, path) || relative(home, path) === '')));
|
|
13
|
+
}
|
|
14
|
+
/** Whether `path` lies under `folder`, and is not `folder` itself. */
|
|
15
|
+
function within(folder, path) {
|
|
16
|
+
const between = relative(folder, path);
|
|
17
|
+
return between !== '' && between !== '..' && !between.startsWith(`..${sep}`) && !isAbsolute(between);
|
|
18
|
+
}
|