@alumbwe/anvil 1.0.19 → 1.0.20
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/README.md +28 -30
- package/bin/anvil +2 -0
- package/package.json +71 -75
- package/src/agents/bundled-agents.generated.d.ts +14 -0
- package/src/agents/bundled-agents.generated.ts +3760 -0
- package/src/app.tsx +399 -0
- package/src/chat.tsx +1698 -0
- package/src/cli-args.ts +134 -0
- package/src/commands/__tests__/anvil-command-aliases.test.ts +56 -0
- package/src/commands/__tests__/bash-command.test.ts +445 -0
- package/src/commands/__tests__/command-args.test.ts +349 -0
- package/src/commands/__tests__/copy-conversation-clipboard.test.ts +108 -0
- package/src/commands/__tests__/copy-conversation.test.ts +226 -0
- package/src/commands/__tests__/diagnostics-command.test.ts +35 -0
- package/src/commands/__tests__/image.test.ts +99 -0
- package/src/commands/__tests__/init.test.ts +456 -0
- package/src/commands/__tests__/new-command-rotates-chat.test.ts +91 -0
- package/src/commands/__tests__/process-diagnostics.test.ts +54 -0
- package/src/commands/__tests__/prompt-builders.test.ts +28 -0
- package/src/commands/__tests__/router-input.test.ts +298 -0
- package/src/commands/ads.ts +44 -0
- package/src/commands/command-registry.ts +726 -0
- package/src/commands/copy-conversation.ts +392 -0
- package/src/commands/help.ts +13 -0
- package/src/commands/image.ts +20 -0
- package/src/commands/init.ts +121 -0
- package/src/commands/process-diagnostics.ts +120 -0
- package/src/commands/prompt-builders.ts +100 -0
- package/src/commands/publish.ts +203 -0
- package/src/commands/router-utils.ts +80 -0
- package/src/commands/router.ts +487 -0
- package/src/commands/usage.ts +26 -0
- package/src/components/__tests__/ad-banner.test.tsx +83 -0
- package/src/components/__tests__/anvil-landing-heading-row.test.tsx +91 -0
- package/src/components/__tests__/anvil-model-selector.test.tsx +519 -0
- package/src/components/__tests__/anvil-offer-invariants.test.ts +54 -0
- package/src/components/__tests__/anvil-takeover-prompt.test.tsx +212 -0
- package/src/components/__tests__/chat-input-bar.test.tsx +130 -0
- package/src/components/__tests__/grid-layout.integration.test.tsx +304 -0
- package/src/components/__tests__/grid-layout.test.tsx +1051 -0
- package/src/components/__tests__/message-block.completion.test.tsx +104 -0
- package/src/components/__tests__/message-block.streaming.test.tsx +78 -0
- package/src/components/__tests__/message-with-agents.test.tsx +579 -0
- package/src/components/__tests__/multiline-input.test.tsx +1132 -0
- package/src/components/__tests__/queue-panel.test.tsx +398 -0
- package/src/components/__tests__/selectable-list.test.ts +232 -0
- package/src/components/__tests__/status-bar.test.tsx +44 -0
- package/src/components/__tests__/status-indicator.test.tsx +204 -0
- package/src/components/__tests__/terminal-thinking-orb.test.tsx +111 -0
- package/src/components/__tests__/user-error-banner.test.tsx +102 -0
- package/src/components/ad-banner.tsx +323 -0
- package/src/components/agent-checklist.tsx +350 -0
- package/src/components/agent-mode-toggle.tsx +241 -0
- package/src/components/anvil-active-session-summary.tsx +63 -0
- package/src/components/anvil-landing-screen.tsx +865 -0
- package/src/components/anvil-mark.tsx +52 -0
- package/src/components/anvil-model-selector.tsx +1087 -0
- package/src/components/anvil-referral-banner.tsx +604 -0
- package/src/components/anvil-superseded-screen.tsx +62 -0
- package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +432 -0
- package/src/components/ask-user/__tests__/validation.test.ts +213 -0
- package/src/components/ask-user/components/accordion-question.tsx +153 -0
- package/src/components/ask-user/components/custom-answer-input.tsx +66 -0
- package/src/components/ask-user/components/options-list.tsx +133 -0
- package/src/components/ask-user/components/question-header.tsx +80 -0
- package/src/components/ask-user/components/question-option.tsx +89 -0
- package/src/components/ask-user/constants.ts +38 -0
- package/src/components/ask-user/index.tsx +631 -0
- package/src/components/ask-user/utils/validation.ts +85 -0
- package/src/components/attachment-card.tsx +65 -0
- package/src/components/blocks/agent-block-grid.tsx +58 -0
- package/src/components/blocks/agent-branch-item.tsx +311 -0
- package/src/components/blocks/agent-branch-wrapper.tsx +493 -0
- package/src/components/blocks/agent-list-branch.tsx +77 -0
- package/src/components/blocks/ask-user-branch.tsx +81 -0
- package/src/components/blocks/block-helpers.ts +11 -0
- package/src/components/blocks/blocks-renderer.tsx +289 -0
- package/src/components/blocks/content-with-markdown.tsx +33 -0
- package/src/components/blocks/image-block.tsx +128 -0
- package/src/components/blocks/implementor-row.tsx +429 -0
- package/src/components/blocks/single-block.tsx +201 -0
- package/src/components/blocks/thinking-block.tsx +67 -0
- package/src/components/blocks/tool-block-group.tsx +57 -0
- package/src/components/blocks/tool-branch.tsx +206 -0
- package/src/components/blocks/user-content-copy.tsx +157 -0
- package/src/components/bottom-banner.tsx +136 -0
- package/src/components/build-mode-buttons.tsx +110 -0
- package/src/components/button.tsx +66 -0
- package/src/components/chat-header.tsx +77 -0
- package/src/components/chat-history-screen.tsx +413 -0
- package/src/components/chat-input-bar.tsx +523 -0
- package/src/components/clickable-title-box.tsx +66 -0
- package/src/components/clickable.tsx +119 -0
- package/src/components/collapse-button.tsx +34 -0
- package/src/components/copy-button.tsx +203 -0
- package/src/components/elapsed-timer.tsx +55 -0
- package/src/components/error-boundary.tsx +55 -0
- package/src/components/feedback-container.tsx +181 -0
- package/src/components/feedback-icon-button.tsx +89 -0
- package/src/components/feedback-input-mode.tsx +344 -0
- package/src/components/file-attachment-card.tsx +98 -0
- package/src/components/grid-layout.tsx +80 -0
- package/src/components/help-banner.tsx +127 -0
- package/src/components/highlighted-text.tsx +52 -0
- package/src/components/image-card.tsx +167 -0
- package/src/components/image-thumbnail.tsx +114 -0
- package/src/components/input-cursor.tsx +77 -0
- package/src/components/input-mode-banner.tsx +57 -0
- package/src/components/load-previous-button.tsx +48 -0
- package/src/components/login-modal.tsx +475 -0
- package/src/components/message-block.tsx +346 -0
- package/src/components/message-footer.tsx +258 -0
- package/src/components/message-with-agents.tsx +506 -0
- package/src/components/mode-divider.tsx +43 -0
- package/src/components/multiline-input.tsx +1241 -0
- package/src/components/out-of-credits-banner.tsx +160 -0
- package/src/components/pending-attachments-banner.tsx +116 -0
- package/src/components/pending-bash-message.tsx +59 -0
- package/src/components/progress-bar.tsx +81 -0
- package/src/components/project-picker-screen.tsx +485 -0
- package/src/components/publish-confirmation.tsx +493 -0
- package/src/components/publish-container.tsx +687 -0
- package/src/components/queue-panel.tsx +310 -0
- package/src/components/raised-pill.tsx +95 -0
- package/src/components/renderers/plan-box.tsx +59 -0
- package/src/components/review-screen.tsx +112 -0
- package/src/components/scroll-to-bottom-button.tsx +34 -0
- package/src/components/segmented-control.tsx +187 -0
- package/src/components/selectable-list.tsx +248 -0
- package/src/components/selected-chips.tsx +80 -0
- package/src/components/separator.tsx +40 -0
- package/src/components/session-ended-banner.tsx +226 -0
- package/src/components/shimmer-text.tsx +248 -0
- package/src/components/status-bar.tsx +313 -0
- package/src/components/subscription-limit-banner.tsx +206 -0
- package/src/components/suggested-prompts.tsx +184 -0
- package/src/components/suggestion-menu.tsx +215 -0
- package/src/components/terminal-command-display.tsx +151 -0
- package/src/components/terminal-link.tsx +97 -0
- package/src/components/terminal-thinking-orb.tsx +95 -0
- package/src/components/text-attachment-card.tsx +77 -0
- package/src/components/thinking.tsx +109 -0
- package/src/components/tools/__tests__/apply-patch.test.tsx +84 -0
- package/src/components/tools/__tests__/code-search.test.tsx +45 -0
- package/src/components/tools/__tests__/gravity-index.test.ts +76 -0
- package/src/components/tools/__tests__/render-ui.test.tsx +92 -0
- package/src/components/tools/__tests__/run-terminal-command.test.ts +233 -0
- package/src/components/tools/apply-patch.tsx +95 -0
- package/src/components/tools/code-search.tsx +45 -0
- package/src/components/tools/composio.tsx +152 -0
- package/src/components/tools/diff-viewer.tsx +76 -0
- package/src/components/tools/glob.tsx +61 -0
- package/src/components/tools/gravity-index.tsx +77 -0
- package/src/components/tools/list-directory.tsx +61 -0
- package/src/components/tools/read-docs.tsx +37 -0
- package/src/components/tools/read-files.tsx +91 -0
- package/src/components/tools/read-subtree.tsx +44 -0
- package/src/components/tools/read-url.tsx +33 -0
- package/src/components/tools/registry.ts +126 -0
- package/src/components/tools/render-ui.tsx +122 -0
- package/src/components/tools/run-terminal-command.tsx +79 -0
- package/src/components/tools/skill.tsx +29 -0
- package/src/components/tools/str-replace.tsx +77 -0
- package/src/components/tools/suggest-followups.tsx +369 -0
- package/src/components/tools/task-completed.tsx +16 -0
- package/src/components/tools/tool-call-item.tsx +273 -0
- package/src/components/tools/types.ts +55 -0
- package/src/components/tools/web-search.tsx +33 -0
- package/src/components/tools/write-file.tsx +16 -0
- package/src/components/tools/write-todos.tsx +95 -0
- package/src/components/top-banner.tsx +178 -0
- package/src/components/usage-banner.tsx +244 -0
- package/src/components/user-error-banner.tsx +56 -0
- package/src/components/validation-error-popover.tsx +192 -0
- package/src/contexts/__tests__/chat-runtime-context.test.tsx +149 -0
- package/src/contexts/chat-runtime-context.tsx +240 -0
- package/src/data/slash-commands.test.ts +24 -0
- package/src/data/slash-commands.ts +249 -0
- package/src/entry.ts +12 -0
- package/src/hooks/__tests__/holds-live-anvil-slot.test.ts +49 -0
- package/src/hooks/__tests__/session-fetch-signal.test.ts +63 -0
- package/src/hooks/__tests__/use-activity-query.test.ts +1000 -0
- package/src/hooks/__tests__/use-ask-user-bridge.test.ts +176 -0
- package/src/hooks/__tests__/use-auth-query.test.ts +21 -0
- package/src/hooks/__tests__/use-chat-input-remount.test.tsx +66 -0
- package/src/hooks/__tests__/use-clipboard.test.tsx +73 -0
- package/src/hooks/__tests__/use-connection-status.test.ts +112 -0
- package/src/hooks/__tests__/use-directory-browser.test.ts +203 -0
- package/src/hooks/__tests__/use-gravity-ad.test.ts +73 -0
- package/src/hooks/__tests__/use-grid-layout.test.ts +349 -0
- package/src/hooks/__tests__/use-input-history.test.ts +699 -0
- package/src/hooks/__tests__/use-message-queue-editing.test.tsx +209 -0
- package/src/hooks/__tests__/use-message-queue.test.tsx +71 -0
- package/src/hooks/__tests__/use-path-tab-completion.test.ts +315 -0
- package/src/hooks/__tests__/use-queue-controls.test.ts +68 -0
- package/src/hooks/__tests__/use-queue-ui.test.tsx +83 -0
- package/src/hooks/__tests__/use-searchable-list.test.ts +359 -0
- package/src/hooks/__tests__/use-send-message-timer.test.ts +146 -0
- package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +361 -0
- package/src/hooks/__tests__/use-suggestion-engine.test.ts +485 -0
- package/src/hooks/__tests__/use-terminal-focus.test.ts +66 -0
- package/src/hooks/__tests__/use-terminal-layout.test.ts +675 -0
- package/src/hooks/__tests__/use-timeout.test.ts +330 -0
- package/src/hooks/__tests__/use-usage-query.test.ts +502 -0
- package/src/hooks/__tests__/use-user-details-query.test.ts +195 -0
- package/src/hooks/helpers/__tests__/send-message.test.ts +1918 -0
- package/src/hooks/helpers/send-message.ts +654 -0
- package/src/hooks/stream-state.ts +83 -0
- package/src/hooks/use-activity-query.ts +668 -0
- package/src/hooks/use-agent-validation.ts +74 -0
- package/src/hooks/use-anvil-ctrl-c-exit.ts +23 -0
- package/src/hooks/use-anvil-session-progress.ts +34 -0
- package/src/hooks/use-anvil-session.ts +806 -0
- package/src/hooks/use-anvil-streak-query.ts +61 -0
- package/src/hooks/use-ask-user-bridge.ts +92 -0
- package/src/hooks/use-auth-query.ts +262 -0
- package/src/hooks/use-auth-state.ts +152 -0
- package/src/hooks/use-chat-input.ts +105 -0
- package/src/hooks/use-chat-keyboard.ts +321 -0
- package/src/hooks/use-chat-messages.ts +254 -0
- package/src/hooks/use-chat-state.ts +165 -0
- package/src/hooks/use-chat-streaming.ts +180 -0
- package/src/hooks/use-chat-ui.ts +131 -0
- package/src/hooks/use-clipboard.ts +151 -0
- package/src/hooks/use-connection-status.ts +142 -0
- package/src/hooks/use-directory-browser.ts +88 -0
- package/src/hooks/use-elapsed-time.ts +116 -0
- package/src/hooks/use-event.ts +34 -0
- package/src/hooks/use-exit-handler.ts +66 -0
- package/src/hooks/use-fetch-login-url.ts +65 -0
- package/src/hooks/use-fingerprint.ts +61 -0
- package/src/hooks/use-gravity-ad.ts +656 -0
- package/src/hooks/use-grid-layout.ts +73 -0
- package/src/hooks/use-input-history.ts +173 -0
- package/src/hooks/use-login-keyboard-handlers.ts +75 -0
- package/src/hooks/use-login-polling.ts +114 -0
- package/src/hooks/use-logo.tsx +161 -0
- package/src/hooks/use-message-queue.ts +387 -0
- package/src/hooks/use-now.ts +20 -0
- package/src/hooks/use-path-tab-completion.ts +88 -0
- package/src/hooks/use-publish-mutation.ts +60 -0
- package/src/hooks/use-queue-controls.ts +68 -0
- package/src/hooks/use-queue-ui.ts +74 -0
- package/src/hooks/use-scroll-management.ts +184 -0
- package/src/hooks/use-searchable-list.ts +98 -0
- package/src/hooks/use-send-message.ts +773 -0
- package/src/hooks/use-sheen-animation.tsx +88 -0
- package/src/hooks/use-subscription-query.ts +71 -0
- package/src/hooks/use-suggestion-engine.ts +783 -0
- package/src/hooks/use-terminal-breakpoints.ts +77 -0
- package/src/hooks/use-terminal-dimensions.ts +46 -0
- package/src/hooks/use-terminal-focus.ts +29 -0
- package/src/hooks/use-terminal-layout.ts +189 -0
- package/src/hooks/use-theme.tsx +140 -0
- package/src/hooks/use-timeout.ts +85 -0
- package/src/hooks/use-update-preference.ts +66 -0
- package/src/hooks/use-usage-monitor.ts +55 -0
- package/src/hooks/use-usage-query.ts +124 -0
- package/src/hooks/use-user-details-query.ts +93 -0
- package/src/hooks/use-why-did-you-update.ts +177 -0
- package/src/index.tsx +473 -0
- package/src/init/__tests__/init-direnv.test.ts +526 -0
- package/src/init/init-app.ts +34 -0
- package/src/init/init-direnv.ts +133 -0
- package/src/login/constants.ts +60 -0
- package/src/login/login-flow.ts +204 -0
- package/src/login/plain-login.ts +108 -0
- package/src/login/utils.ts +183 -0
- package/src/native/ripgrep.ts +80 -0
- package/src/polyfills/bun-strip-ansi.ts +12 -0
- package/src/pre-init/tree-sitter-wasm.ts +96 -0
- package/src/project-files.ts +102 -0
- package/src/setup/key-setup.ts +295 -0
- package/src/setup/load-env.ts +106 -0
- package/src/state/__tests__/chat-store-focus.test.ts +22 -0
- package/src/state/__tests__/feedback-store.test.ts +257 -0
- package/src/state/anvil-model-store.ts +42 -0
- package/src/state/anvil-session-store.ts +53 -0
- package/src/state/chat-history-store.ts +30 -0
- package/src/state/chat-store.ts +544 -0
- package/src/state/feedback-store.ts +160 -0
- package/src/state/login-store.ts +150 -0
- package/src/state/message-block-store.ts +149 -0
- package/src/state/publish-store.ts +146 -0
- package/src/state/queue-panel-store.ts +26 -0
- package/src/state/review-store.ts +24 -0
- package/src/types/anvil-session.ts +17 -0
- package/src/types/chat-state.ts +15 -0
- package/src/types/chat.ts +241 -0
- package/src/types/contracts/send-message.ts +12 -0
- package/src/types/env.ts +98 -0
- package/src/types/function-params.ts +28 -0
- package/src/types/react19-compat.d.ts +19 -0
- package/src/types/store.ts +111 -0
- package/src/types/theme-system.ts +144 -0
- package/src/types/utils.ts +3 -0
- package/src/utils/__tests__/active-run.test.ts +140 -0
- package/src/utils/__tests__/activity-tracker.test.ts +440 -0
- package/src/utils/__tests__/agent-display.test.ts +139 -0
- package/src/utils/__tests__/analytics-client.test.ts +262 -0
- package/src/utils/__tests__/anonymous-id.test.ts +74 -0
- package/src/utils/__tests__/anvil-api.test.ts +578 -0
- package/src/utils/__tests__/anvil-instance-owner.test.ts +69 -0
- package/src/utils/__tests__/anvil-model-navigation.test.ts +102 -0
- package/src/utils/__tests__/anvil-premium-reset.test.ts +79 -0
- package/src/utils/__tests__/anvil-referral-cache.test.ts +116 -0
- package/src/utils/__tests__/anvil-session-api.test.ts +215 -0
- package/src/utils/__tests__/anvil-session-display.test.ts +23 -0
- package/src/utils/__tests__/anvil-streak-line.test.ts +217 -0
- package/src/utils/__tests__/arrays.test.ts +264 -0
- package/src/utils/__tests__/bash-context-processor.test.ts +38 -0
- package/src/utils/__tests__/block-processor.test.ts +810 -0
- package/src/utils/__tests__/chat-history.test.ts +203 -0
- package/src/utils/__tests__/chat-input-key-intercept.test.ts +40 -0
- package/src/utils/__tests__/chat-meta.test.ts +130 -0
- package/src/utils/__tests__/clipboard-linux.test.ts +359 -0
- package/src/utils/__tests__/clipboard.test.ts +910 -0
- package/src/utils/__tests__/code-search-summary.test.ts +84 -0
- package/src/utils/__tests__/collapse-helpers.test.ts +1109 -0
- package/src/utils/__tests__/error-handling.test.ts +658 -0
- package/src/utils/__tests__/exit-cleanly.test.ts +110 -0
- package/src/utils/__tests__/feedback-helpers.test.ts +444 -0
- package/src/utils/__tests__/feedback-submission.test.ts +26 -0
- package/src/utils/__tests__/fetch-usage.test.ts +355 -0
- package/src/utils/__tests__/fingerprint.test.ts +144 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +7 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +3 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +33 -0
- package/src/utils/__tests__/format-elapsed-time.test.ts +103 -0
- package/src/utils/__tests__/format-timeout.test.ts +87 -0
- package/src/utils/__tests__/image-dimensions.test.ts +249 -0
- package/src/utils/__tests__/image-processor.test.ts +209 -0
- package/src/utils/__tests__/implementor-helpers.test.ts +1458 -0
- package/src/utils/__tests__/keyboard-actions.test.ts +660 -0
- package/src/utils/__tests__/layout-helpers.test.ts +36 -0
- package/src/utils/__tests__/lazy-response-ads.test.ts +167 -0
- package/src/utils/__tests__/log-shipper.test.ts +53 -0
- package/src/utils/__tests__/markdown-renderer.test.tsx +421 -0
- package/src/utils/__tests__/message-block-helpers.test.ts +1263 -0
- package/src/utils/__tests__/message-updater.test.ts +645 -0
- package/src/utils/__tests__/osc-timeout-scenarios.test.ts +172 -0
- package/src/utils/__tests__/pending-attachments.test.ts +277 -0
- package/src/utils/__tests__/polling-backoff.test.ts +51 -0
- package/src/utils/__tests__/queue-panel-actions.test.ts +148 -0
- package/src/utils/__tests__/run-state-storage.test.ts +919 -0
- package/src/utils/__tests__/safe-json.test.ts +77 -0
- package/src/utils/__tests__/sdk-event-handlers.test.ts +685 -0
- package/src/utils/__tests__/send-message-helpers.test.ts +1898 -0
- package/src/utils/__tests__/send-message-timer.test.ts +64 -0
- package/src/utils/__tests__/settings.test.ts +99 -0
- package/src/utils/__tests__/strings.test.ts +192 -0
- package/src/utils/__tests__/terminal-color-detection.test.ts +425 -0
- package/src/utils/__tests__/terminal-command-broker.test.ts +528 -0
- package/src/utils/__tests__/terminal-enter-detection.test.ts +77 -0
- package/src/utils/__tests__/terminal-io.test.ts +137 -0
- package/src/utils/__tests__/terminal-protocol-controller.test.ts +119 -0
- package/src/utils/__tests__/text-layout.test.ts +82 -0
- package/src/utils/__tests__/theme-platform-detection.test.ts +125 -0
- package/src/utils/__tests__/think-tag-parser.test.ts +159 -0
- package/src/utils/__tests__/trace-writer.test.ts +223 -0
- package/src/utils/__tests__/trim-chat-logs.test.ts +78 -0
- package/src/utils/__tests__/usage-banner-state.test.ts +298 -0
- package/src/utils/__tests__/validation-error-formatting.test.ts +126 -0
- package/src/utils/__tests__/windows-terminal-health.test.ts +101 -0
- package/src/utils/__tests__/write-file-atomic.test.ts +127 -0
- package/src/utils/active-run.ts +131 -0
- package/src/utils/activity-tracker.ts +66 -0
- package/src/utils/agent-display.ts +87 -0
- package/src/utils/agent-helpers.ts +27 -0
- package/src/utils/agent-id-utils.ts +14 -0
- package/src/utils/ai-message-id.ts +12 -0
- package/src/utils/analytics.ts +361 -0
- package/src/utils/anonymous-id.ts +87 -0
- package/src/utils/anvil-agent-selection.ts +33 -0
- package/src/utils/anvil-api.ts +612 -0
- package/src/utils/anvil-client.ts +217 -0
- package/src/utils/anvil-instance-owner.ts +66 -0
- package/src/utils/anvil-model-navigation.ts +50 -0
- package/src/utils/anvil-premium-reset.ts +55 -0
- package/src/utils/anvil-referral-cache.ts +51 -0
- package/src/utils/anvil-session-api.ts +243 -0
- package/src/utils/anvil-session-display.ts +21 -0
- package/src/utils/anvil-streak-line.ts +93 -0
- package/src/utils/arrays.ts +117 -0
- package/src/utils/auth.ts +258 -0
- package/src/utils/bash-context-processor.ts +47 -0
- package/src/utils/bash-messages.ts +108 -0
- package/src/utils/block-margins.ts +35 -0
- package/src/utils/block-operations.ts +523 -0
- package/src/utils/block-processor.ts +213 -0
- package/src/utils/chat-history.ts +253 -0
- package/src/utils/chat-input-key-intercept.ts +52 -0
- package/src/utils/chat-meta.ts +90 -0
- package/src/utils/chat-scroll-accel.ts +151 -0
- package/src/utils/clipboard-image.ts +588 -0
- package/src/utils/clipboard.ts +397 -0
- package/src/utils/code-search-summary.ts +70 -0
- package/src/utils/collapse-helpers.ts +256 -0
- package/src/utils/config-dir.ts +24 -0
- package/src/utils/constants.ts +190 -0
- package/src/utils/create-event-handler-state.ts +73 -0
- package/src/utils/create-run-config.ts +121 -0
- package/src/utils/detect-shell.ts +116 -0
- package/src/utils/directory-browser.ts +72 -0
- package/src/utils/engagement.ts +55 -0
- package/src/utils/env.ts +92 -0
- package/src/utils/error-handling.ts +255 -0
- package/src/utils/error-messages.ts +62 -0
- package/src/utils/exit-cleanly.ts +86 -0
- package/src/utils/feedback-helpers.ts +103 -0
- package/src/utils/feedback-submission.ts +22 -0
- package/src/utils/fetch-usage.ts +76 -0
- package/src/utils/fingerprint.ts +245 -0
- package/src/utils/format-elapsed-time.ts +33 -0
- package/src/utils/format-session-units.ts +6 -0
- package/src/utils/format-timeout.ts +28 -0
- package/src/utils/format-validation-errors-for-message.ts +84 -0
- package/src/utils/git.ts +17 -0
- package/src/utils/helpers.ts +47 -0
- package/src/utils/image-display.ts +67 -0
- package/src/utils/image-handler.ts +325 -0
- package/src/utils/image-processor.ts +133 -0
- package/src/utils/image-thumbnail.ts +78 -0
- package/src/utils/implementor-helpers.ts +787 -0
- package/src/utils/input-modes.ts +174 -0
- package/src/utils/keyboard-actions.ts +392 -0
- package/src/utils/keypad-keys.ts +47 -0
- package/src/utils/layout-helpers.ts +33 -0
- package/src/utils/lazy-response-ads.ts +1 -0
- package/src/utils/local-agent-registry.ts +474 -0
- package/src/utils/log-shipper.ts +126 -0
- package/src/utils/logger.ts +327 -0
- package/src/utils/markdown-renderer.tsx +1090 -0
- package/src/utils/math.ts +3 -0
- package/src/utils/message-block-helpers.ts +684 -0
- package/src/utils/message-history.ts +143 -0
- package/src/utils/message-tree-utils.ts +67 -0
- package/src/utils/message-updater.ts +279 -0
- package/src/utils/open-file.ts +146 -0
- package/src/utils/open-url.ts +63 -0
- package/src/utils/path-completion.ts +111 -0
- package/src/utils/path-helpers.ts +38 -0
- package/src/utils/pending-attachments.ts +360 -0
- package/src/utils/polling-backoff.ts +59 -0
- package/src/utils/project-picker.ts +12 -0
- package/src/utils/queue-panel-actions.ts +70 -0
- package/src/utils/recent-projects.ts +156 -0
- package/src/utils/renderer-cleanup.ts +233 -0
- package/src/utils/response-ad-positions.ts +1 -0
- package/src/utils/run-state-storage.ts +603 -0
- package/src/utils/safe-json.ts +153 -0
- package/src/utils/sdk-event-handlers.ts +571 -0
- package/src/utils/send-message-helpers.ts +210 -0
- package/src/utils/send-message-timer.ts +110 -0
- package/src/utils/settings.ts +226 -0
- package/src/utils/skill-registry.ts +94 -0
- package/src/utils/spawn-agent-matcher.ts +55 -0
- package/src/utils/status-indicator-state.ts +107 -0
- package/src/utils/stream-chunk-processor.ts +57 -0
- package/src/utils/strings.ts +271 -0
- package/src/utils/subscription.ts +31 -0
- package/src/utils/syntax-highlighter.tsx +19 -0
- package/src/utils/terminal-color-detection.ts +474 -0
- package/src/utils/terminal-command-broker.ts +467 -0
- package/src/utils/terminal-enter-detection.ts +63 -0
- package/src/utils/terminal-images.ts +229 -0
- package/src/utils/terminal-io.ts +85 -0
- package/src/utils/terminal-protocol-controller.ts +173 -0
- package/src/utils/terminal-reset-sequences.ts +33 -0
- package/src/utils/terminal-title.ts +84 -0
- package/src/utils/terminal-watchdog.ts +366 -0
- package/src/utils/text-layout.ts +149 -0
- package/src/utils/theme-config.ts +141 -0
- package/src/utils/theme-system.ts +1206 -0
- package/src/utils/think-tag-parser.ts +95 -0
- package/src/utils/time-format.ts +21 -0
- package/src/utils/trace-writer.ts +162 -0
- package/src/utils/ui-constants.ts +69 -0
- package/src/utils/update-checker.ts +110 -0
- package/src/utils/usage-banner-state.ts +146 -0
- package/src/utils/validation-error-formatting.ts +86 -0
- package/src/utils/validation-error-helpers.ts +19 -0
- package/src/utils/windows-terminal-health.ts +124 -0
- package/src/utils/word-wrap-utils.ts +53 -0
- package/src/utils/write-file-atomic.ts +54 -0
- package/src/utils/yield-to-event-loop.ts +9 -0
- package/bin/anvil.cjs +0 -40
- package/dist/bundled.js +0 -123508
- package/dist/bundled.js.map +0 -828
- package/dist/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/dist/vendor/ripgrep/arm64-linux/rg +0 -0
- package/dist/vendor/ripgrep/x64-darwin/rg +0 -0
- package/dist/vendor/ripgrep/x64-linux/rg +0 -0
- package/dist/vendor/ripgrep/x64-win32/rg.exe +0 -0
package/src/cli-args.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createRequire } from 'module'
|
|
2
|
+
|
|
3
|
+
import { Argument, Command } from 'commander'
|
|
4
|
+
|
|
5
|
+
import { IS_ANVIL, type AgentMode } from './utils/constants'
|
|
6
|
+
import { getCliEnv } from './utils/env'
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url)
|
|
9
|
+
|
|
10
|
+
export type ParsedArgs = {
|
|
11
|
+
initialPrompt: string | null
|
|
12
|
+
command?: string
|
|
13
|
+
agent?: string
|
|
14
|
+
clearLogs: boolean
|
|
15
|
+
continue: boolean
|
|
16
|
+
continueId?: string | null
|
|
17
|
+
cwd?: string
|
|
18
|
+
initialMode?: AgentMode
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadPackageVersion(): string {
|
|
22
|
+
const env = getCliEnv()
|
|
23
|
+
if (env.ANVIL_CLI_VERSION) {
|
|
24
|
+
return env.ANVIL_CLI_VERSION
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const pkg = require('../package.json') as { version?: string }
|
|
29
|
+
if (pkg.version) {
|
|
30
|
+
return pkg.version
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// Continue to dev fallback
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 'dev'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function parseArgs({
|
|
40
|
+
argv = process.argv,
|
|
41
|
+
isAnvil = IS_ANVIL,
|
|
42
|
+
version = loadPackageVersion(),
|
|
43
|
+
}: {
|
|
44
|
+
argv?: string[]
|
|
45
|
+
isAnvil?: boolean
|
|
46
|
+
version?: string
|
|
47
|
+
} = {}): ParsedArgs {
|
|
48
|
+
const program = new Command()
|
|
49
|
+
|
|
50
|
+
if (isAnvil) {
|
|
51
|
+
// Anvil: simplified CLI - no prompt args, no agent override, no clear-logs
|
|
52
|
+
program
|
|
53
|
+
.name('anvil')
|
|
54
|
+
.description('Anvil - Free AI coding assistant')
|
|
55
|
+
.version(version, '-v, --version', 'Print the CLI version')
|
|
56
|
+
.option(
|
|
57
|
+
'--continue [conversation-id]',
|
|
58
|
+
'Continue from a previous conversation (optionally specify a conversation id)',
|
|
59
|
+
)
|
|
60
|
+
.option(
|
|
61
|
+
'--cwd <directory>',
|
|
62
|
+
'Set the working directory (default: current directory)',
|
|
63
|
+
)
|
|
64
|
+
.addArgument(
|
|
65
|
+
new Argument('[command]', 'Command to run').choices(['login', 'setup']),
|
|
66
|
+
)
|
|
67
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
68
|
+
} else {
|
|
69
|
+
// Anvil: full CLI with all options
|
|
70
|
+
program
|
|
71
|
+
.name('anvil')
|
|
72
|
+
.description('Anvil CLI - AI-powered coding assistant')
|
|
73
|
+
.version(version, '-v, --version', 'Print the CLI version')
|
|
74
|
+
.option(
|
|
75
|
+
'--agent <agent-id>',
|
|
76
|
+
'Run a specific agent id (skips loading local .agents overrides)',
|
|
77
|
+
)
|
|
78
|
+
.option(
|
|
79
|
+
'--clear-logs',
|
|
80
|
+
'Remove any existing CLI log files before starting',
|
|
81
|
+
)
|
|
82
|
+
.option(
|
|
83
|
+
'--continue [conversation-id]',
|
|
84
|
+
'Continue from a previous conversation (optionally specify a conversation id)',
|
|
85
|
+
)
|
|
86
|
+
.option(
|
|
87
|
+
'--cwd <directory>',
|
|
88
|
+
'Set the working directory (default: current directory)',
|
|
89
|
+
)
|
|
90
|
+
.option('--lite', 'Start in LITE mode')
|
|
91
|
+
.option('--free', 'Start in LITE mode (deprecated alias)')
|
|
92
|
+
.option('--max', 'Start in MAX mode')
|
|
93
|
+
.option('--plan', 'Start in PLAN mode')
|
|
94
|
+
.addHelpText(
|
|
95
|
+
'after',
|
|
96
|
+
'\nCommands:\n login Log in to your account\n publish Publish agents to the registry',
|
|
97
|
+
)
|
|
98
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
99
|
+
.argument('[prompt...]', 'Initial prompt to send to the agent')
|
|
100
|
+
.allowExcessArguments(true)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
program.parse(argv)
|
|
104
|
+
|
|
105
|
+
const options = program.opts()
|
|
106
|
+
const args = program.args
|
|
107
|
+
|
|
108
|
+
const continueFlag = options.continue
|
|
109
|
+
|
|
110
|
+
// Determine initial mode from flags (last flag wins if multiple specified)
|
|
111
|
+
// Anvil always uses LITE mode
|
|
112
|
+
let initialMode: AgentMode | undefined
|
|
113
|
+
if (isAnvil) {
|
|
114
|
+
initialMode = 'LITE'
|
|
115
|
+
} else {
|
|
116
|
+
if (options.free || options.lite) initialMode = 'LITE'
|
|
117
|
+
if (options.max) initialMode = 'MAX'
|
|
118
|
+
if (options.plan) initialMode = 'PLAN'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
initialPrompt: !isAnvil && args.length > 0 ? args.join(' ') : null,
|
|
123
|
+
command: args[0],
|
|
124
|
+
agent: options.agent,
|
|
125
|
+
clearLogs: options.clearLogs || false,
|
|
126
|
+
continue: Boolean(continueFlag),
|
|
127
|
+
continueId:
|
|
128
|
+
typeof continueFlag === 'string' && continueFlag.trim().length > 0
|
|
129
|
+
? continueFlag.trim()
|
|
130
|
+
: null,
|
|
131
|
+
cwd: options.cwd,
|
|
132
|
+
initialMode,
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
describe('anvil command aliases', () => {
|
|
4
|
+
test('/model aliases /end-session in anvil', () => {
|
|
5
|
+
const slashCommandsUrl = new URL(
|
|
6
|
+
'../../data/slash-commands.ts',
|
|
7
|
+
import.meta.url,
|
|
8
|
+
).href
|
|
9
|
+
const commandRegistryUrl = new URL(
|
|
10
|
+
'../command-registry.ts',
|
|
11
|
+
import.meta.url,
|
|
12
|
+
).href
|
|
13
|
+
|
|
14
|
+
const result = Bun.spawnSync({
|
|
15
|
+
cmd: [
|
|
16
|
+
'bun',
|
|
17
|
+
'--eval',
|
|
18
|
+
`
|
|
19
|
+
import { SLASH_COMMANDS } from ${JSON.stringify(slashCommandsUrl)}
|
|
20
|
+
import { findCommand } from ${JSON.stringify(commandRegistryUrl)}
|
|
21
|
+
|
|
22
|
+
const endSession = SLASH_COMMANDS.find((cmd) => cmd.id === 'end-session')
|
|
23
|
+
if (!endSession) throw new Error('end-session slash command missing')
|
|
24
|
+
if (!endSession.aliases?.includes('model')) {
|
|
25
|
+
throw new Error('end-session slash command is missing model alias')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const modelCommand = findCommand('model')
|
|
29
|
+
if (!modelCommand) throw new Error('model command alias missing')
|
|
30
|
+
if (modelCommand.name !== 'end-session') {
|
|
31
|
+
throw new Error('model alias did not resolve to end-session')
|
|
32
|
+
}
|
|
33
|
+
`,
|
|
34
|
+
],
|
|
35
|
+
cwd: process.cwd(),
|
|
36
|
+
env: {
|
|
37
|
+
...process.env,
|
|
38
|
+
ANVIL_MODE: 'true',
|
|
39
|
+
NODE_ENV: 'test',
|
|
40
|
+
NEXT_PUBLIC_CB_ENVIRONMENT: 'test',
|
|
41
|
+
NEXT_PUBLIC_ANVIL_APP_URL: 'https://app.codebuff.test',
|
|
42
|
+
NEXT_PUBLIC_SUPPORT_EMAIL: 'support@codebuff.test',
|
|
43
|
+
NEXT_PUBLIC_POSTHOG_API_KEY: 'phc_test_key',
|
|
44
|
+
NEXT_PUBLIC_POSTHOG_HOST_URL: 'https://posthog.codebuff.test',
|
|
45
|
+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: 'pk_test_123',
|
|
46
|
+
NEXT_PUBLIC_STRIPE_CUSTOMER_PORTAL: 'https://stripe.codebuff.test',
|
|
47
|
+
NEXT_PUBLIC_WEB_PORT: '3000',
|
|
48
|
+
},
|
|
49
|
+
stderr: 'pipe',
|
|
50
|
+
stdout: 'pipe',
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const stderr = new TextDecoder().decode(result.stderr)
|
|
54
|
+
expect(result.exitCode, stderr).toBe(0)
|
|
55
|
+
}, 15_000)
|
|
56
|
+
})
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { describe, test, expect, mock, beforeEach } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { useChatStore } from '../../state/chat-store'
|
|
4
|
+
import { INPUT_MODE_CONFIGS, getInputModeConfig } from '../../utils/input-modes'
|
|
5
|
+
import { findCommand } from '../command-registry'
|
|
6
|
+
|
|
7
|
+
import type { RouterParams } from '../command-registry'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tests for bash command execution logic.
|
|
11
|
+
*
|
|
12
|
+
* These tests cover:
|
|
13
|
+
* 1. runBashCommand - ghost vs direct mode selection based on store state
|
|
14
|
+
* 2. /bash slash command handler - immediate execution vs entering bash mode
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
describe('bash command', () => {
|
|
18
|
+
// Reset store state before each test
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
useChatStore.getState().reset()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('/bash slash command handler', () => {
|
|
24
|
+
const createMockParams = (
|
|
25
|
+
overrides: Partial<RouterParams> = {},
|
|
26
|
+
): RouterParams => ({
|
|
27
|
+
agentMode: 'DEFAULT',
|
|
28
|
+
inputRef: { current: null },
|
|
29
|
+
inputValue: '/bash',
|
|
30
|
+
isChainInProgressRef: { current: false },
|
|
31
|
+
isStreaming: false,
|
|
32
|
+
logoutMutation: {} as RouterParams['logoutMutation'],
|
|
33
|
+
streamMessageIdRef: { current: null },
|
|
34
|
+
addToQueue: mock(() => {}),
|
|
35
|
+
clearMessages: mock(() => {}),
|
|
36
|
+
saveToHistory: mock(() => {}),
|
|
37
|
+
scrollToLatest: mock(() => {}),
|
|
38
|
+
sendMessage: mock(async () => {}),
|
|
39
|
+
setCanProcessQueue: mock(() => {}),
|
|
40
|
+
setInputFocused: mock(() => {}),
|
|
41
|
+
setInputValue: mock(() => {}),
|
|
42
|
+
setIsAuthenticated: mock(() => {}),
|
|
43
|
+
setMessages: mock(() => {}),
|
|
44
|
+
setUser: mock(() => {}),
|
|
45
|
+
...overrides,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('/bash command exists in registry', () => {
|
|
49
|
+
const bashCommand = findCommand('bash')
|
|
50
|
+
expect(bashCommand).toBeDefined()
|
|
51
|
+
expect(bashCommand?.name).toBe('bash')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('/bash with no args enters bash mode', () => {
|
|
55
|
+
const bashCommand = findCommand('bash')
|
|
56
|
+
const params = createMockParams()
|
|
57
|
+
|
|
58
|
+
// Execute with empty args
|
|
59
|
+
bashCommand?.handler(params, '')
|
|
60
|
+
|
|
61
|
+
// Should enter bash mode
|
|
62
|
+
expect(useChatStore.getState().inputMode).toBe('bash')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('/bash with args does NOT enter bash mode', () => {
|
|
66
|
+
const bashCommand = findCommand('bash')
|
|
67
|
+
const params = createMockParams()
|
|
68
|
+
|
|
69
|
+
// Execute with args - this will call runBashCommand which tries to run a real command
|
|
70
|
+
// We just verify it doesn't enter bash mode
|
|
71
|
+
bashCommand?.handler(params, 'echo test')
|
|
72
|
+
|
|
73
|
+
// Should NOT enter bash mode - command is executed immediately
|
|
74
|
+
expect(useChatStore.getState().inputMode).toBe('default')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('/bash with args saves command WITH ! prefix to history', () => {
|
|
78
|
+
const saveToHistory = mock(() => {})
|
|
79
|
+
const bashCommand = findCommand('bash')
|
|
80
|
+
const params = createMockParams({ saveToHistory })
|
|
81
|
+
|
|
82
|
+
bashCommand?.handler(params, 'ls -la')
|
|
83
|
+
|
|
84
|
+
// Should save with ! prefix
|
|
85
|
+
expect(saveToHistory).toHaveBeenCalledWith('!ls -la')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('/bash with no args saves original input to history', () => {
|
|
89
|
+
const saveToHistory = mock(() => {})
|
|
90
|
+
const bashCommand = findCommand('bash')
|
|
91
|
+
const params = createMockParams({
|
|
92
|
+
inputValue: '/bash',
|
|
93
|
+
saveToHistory,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
bashCommand?.handler(params, '')
|
|
97
|
+
|
|
98
|
+
// Should save the original input
|
|
99
|
+
expect(saveToHistory).toHaveBeenCalledWith('/bash')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('/bash with args clears input', () => {
|
|
103
|
+
const setInputValue = mock(() => {})
|
|
104
|
+
const bashCommand = findCommand('bash')
|
|
105
|
+
const params = createMockParams({ setInputValue })
|
|
106
|
+
|
|
107
|
+
bashCommand?.handler(params, 'pwd')
|
|
108
|
+
|
|
109
|
+
expect(setInputValue).toHaveBeenCalledWith({
|
|
110
|
+
text: '',
|
|
111
|
+
cursorPosition: 0,
|
|
112
|
+
lastEditDueToNav: false,
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('/bash with whitespace-only args enters bash mode', () => {
|
|
117
|
+
const bashCommand = findCommand('bash')
|
|
118
|
+
const params = createMockParams()
|
|
119
|
+
|
|
120
|
+
bashCommand?.handler(params, ' ')
|
|
121
|
+
|
|
122
|
+
// Whitespace-only should be treated as no args
|
|
123
|
+
expect(useChatStore.getState().inputMode).toBe('bash')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('"!" is an alias for /bash', () => {
|
|
127
|
+
const bangCommand = findCommand('!')
|
|
128
|
+
expect(bangCommand).toBeDefined()
|
|
129
|
+
expect(bangCommand?.name).toBe('bash')
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
describe('runBashCommand mode selection', () => {
|
|
134
|
+
test('uses direct mode when not busy (no streaming agents, no chain in progress)', () => {
|
|
135
|
+
// Ensure store is in non-busy state
|
|
136
|
+
const state = useChatStore.getState()
|
|
137
|
+
expect(state.streamingAgents.size).toBe(0)
|
|
138
|
+
expect(state.isChainInProgress).toBe(false)
|
|
139
|
+
|
|
140
|
+
// The isBusy calculation should be false
|
|
141
|
+
const isBusy = state.streamingAgents.size > 0 || state.isChainInProgress
|
|
142
|
+
expect(isBusy).toBe(false)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('uses ghost mode when streaming agents present', () => {
|
|
146
|
+
// Set up streaming agents
|
|
147
|
+
useChatStore.getState().setStreamingAgents(new Set(['agent-1']))
|
|
148
|
+
|
|
149
|
+
const state = useChatStore.getState()
|
|
150
|
+
const isBusy = state.streamingAgents.size > 0 || state.isChainInProgress
|
|
151
|
+
expect(isBusy).toBe(true)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('uses ghost mode when chain in progress', () => {
|
|
155
|
+
// Set chain in progress
|
|
156
|
+
useChatStore.getState().setIsChainInProgress(true)
|
|
157
|
+
|
|
158
|
+
const state = useChatStore.getState()
|
|
159
|
+
const isBusy = state.streamingAgents.size > 0 || state.isChainInProgress
|
|
160
|
+
expect(isBusy).toBe(true)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test('uses ghost mode when both streaming and chain in progress', () => {
|
|
164
|
+
useChatStore.getState().setStreamingAgents(new Set(['agent-1']))
|
|
165
|
+
useChatStore.getState().setIsChainInProgress(true)
|
|
166
|
+
|
|
167
|
+
const state = useChatStore.getState()
|
|
168
|
+
const isBusy = state.streamingAgents.size > 0 || state.isChainInProgress
|
|
169
|
+
expect(isBusy).toBe(true)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('pending bash messages', () => {
|
|
174
|
+
test('addPendingBashMessage adds message to store', () => {
|
|
175
|
+
const { addPendingBashMessage } = useChatStore.getState()
|
|
176
|
+
|
|
177
|
+
addPendingBashMessage({
|
|
178
|
+
id: 'test-id',
|
|
179
|
+
command: 'ls -la',
|
|
180
|
+
stdout: '',
|
|
181
|
+
stderr: '',
|
|
182
|
+
exitCode: 0,
|
|
183
|
+
isRunning: true,
|
|
184
|
+
cwd: '/test',
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const messages = useChatStore.getState().pendingBashMessages
|
|
188
|
+
expect(messages.length).toBe(1)
|
|
189
|
+
expect(messages[0].command).toBe('ls -la')
|
|
190
|
+
expect(messages[0].isRunning).toBe(true)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('updatePendingBashMessage updates existing message', () => {
|
|
194
|
+
const { addPendingBashMessage, updatePendingBashMessage } =
|
|
195
|
+
useChatStore.getState()
|
|
196
|
+
|
|
197
|
+
addPendingBashMessage({
|
|
198
|
+
id: 'test-id',
|
|
199
|
+
command: 'ls -la',
|
|
200
|
+
stdout: '',
|
|
201
|
+
stderr: '',
|
|
202
|
+
exitCode: 0,
|
|
203
|
+
isRunning: true,
|
|
204
|
+
cwd: '/test',
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
updatePendingBashMessage('test-id', {
|
|
208
|
+
stdout: 'file1.txt\nfile2.txt',
|
|
209
|
+
exitCode: 0,
|
|
210
|
+
isRunning: false,
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
const messages = useChatStore.getState().pendingBashMessages
|
|
214
|
+
expect(messages[0].stdout).toBe('file1.txt\nfile2.txt')
|
|
215
|
+
expect(messages[0].isRunning).toBe(false)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test('removePendingBashMessage removes message from store', () => {
|
|
219
|
+
const { addPendingBashMessage, removePendingBashMessage } =
|
|
220
|
+
useChatStore.getState()
|
|
221
|
+
|
|
222
|
+
addPendingBashMessage({
|
|
223
|
+
id: 'test-id',
|
|
224
|
+
command: 'ls',
|
|
225
|
+
stdout: '',
|
|
226
|
+
stderr: '',
|
|
227
|
+
exitCode: 0,
|
|
228
|
+
isRunning: false,
|
|
229
|
+
cwd: '/test',
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
expect(useChatStore.getState().pendingBashMessages.length).toBe(1)
|
|
233
|
+
|
|
234
|
+
removePendingBashMessage('test-id')
|
|
235
|
+
|
|
236
|
+
expect(useChatStore.getState().pendingBashMessages.length).toBe(0)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
test('clearPendingBashMessages removes all messages', () => {
|
|
240
|
+
const { addPendingBashMessage, clearPendingBashMessages } =
|
|
241
|
+
useChatStore.getState()
|
|
242
|
+
|
|
243
|
+
addPendingBashMessage({
|
|
244
|
+
id: 'test-1',
|
|
245
|
+
command: 'ls',
|
|
246
|
+
stdout: '',
|
|
247
|
+
stderr: '',
|
|
248
|
+
exitCode: 0,
|
|
249
|
+
isRunning: false,
|
|
250
|
+
cwd: '/test',
|
|
251
|
+
})
|
|
252
|
+
addPendingBashMessage({
|
|
253
|
+
id: 'test-2',
|
|
254
|
+
command: 'pwd',
|
|
255
|
+
stdout: '',
|
|
256
|
+
stderr: '',
|
|
257
|
+
exitCode: 0,
|
|
258
|
+
isRunning: false,
|
|
259
|
+
cwd: '/test',
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
expect(useChatStore.getState().pendingBashMessages.length).toBe(2)
|
|
263
|
+
|
|
264
|
+
clearPendingBashMessages()
|
|
265
|
+
|
|
266
|
+
expect(useChatStore.getState().pendingBashMessages.length).toBe(0)
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('bash mode state transitions', () => {
|
|
271
|
+
test('entering bash mode sets inputMode to bash', () => {
|
|
272
|
+
useChatStore.getState().setInputMode('bash')
|
|
273
|
+
expect(useChatStore.getState().inputMode).toBe('bash')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
test('exiting bash mode sets inputMode to default', () => {
|
|
277
|
+
useChatStore.getState().setInputMode('bash')
|
|
278
|
+
useChatStore.getState().setInputMode('default')
|
|
279
|
+
expect(useChatStore.getState().inputMode).toBe('default')
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test('reset clears inputMode to default', () => {
|
|
283
|
+
useChatStore.getState().setInputMode('bash')
|
|
284
|
+
useChatStore.getState().reset()
|
|
285
|
+
expect(useChatStore.getState().inputMode).toBe('default')
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
describe('/bash with special characters in args', () => {
|
|
290
|
+
const createMockParams = (
|
|
291
|
+
overrides: Partial<RouterParams> = {},
|
|
292
|
+
): RouterParams => ({
|
|
293
|
+
agentMode: 'DEFAULT',
|
|
294
|
+
inputRef: { current: null },
|
|
295
|
+
inputValue: '/bash',
|
|
296
|
+
isChainInProgressRef: { current: false },
|
|
297
|
+
isStreaming: false,
|
|
298
|
+
logoutMutation: {} as RouterParams['logoutMutation'],
|
|
299
|
+
streamMessageIdRef: { current: null },
|
|
300
|
+
addToQueue: mock(() => {}),
|
|
301
|
+
clearMessages: mock(() => {}),
|
|
302
|
+
saveToHistory: mock(() => {}),
|
|
303
|
+
scrollToLatest: mock(() => {}),
|
|
304
|
+
sendMessage: mock(async () => {}),
|
|
305
|
+
setCanProcessQueue: mock(() => {}),
|
|
306
|
+
setInputFocused: mock(() => {}),
|
|
307
|
+
setInputValue: mock(() => {}),
|
|
308
|
+
setIsAuthenticated: mock(() => {}),
|
|
309
|
+
setMessages: mock(() => {}),
|
|
310
|
+
setUser: mock(() => {}),
|
|
311
|
+
...overrides,
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('/bash with pipe characters preserves them', () => {
|
|
315
|
+
const saveToHistory = mock(() => {})
|
|
316
|
+
const bashCommand = findCommand('bash')
|
|
317
|
+
const params = createMockParams({ saveToHistory })
|
|
318
|
+
|
|
319
|
+
bashCommand?.handler(params, 'ls | grep foo')
|
|
320
|
+
|
|
321
|
+
expect(saveToHistory).toHaveBeenCalledWith('!ls | grep foo')
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
test('/bash with quoted arguments preserves them', () => {
|
|
325
|
+
const saveToHistory = mock(() => {})
|
|
326
|
+
const bashCommand = findCommand('bash')
|
|
327
|
+
const params = createMockParams({ saveToHistory })
|
|
328
|
+
|
|
329
|
+
bashCommand?.handler(params, 'echo "hello world"')
|
|
330
|
+
|
|
331
|
+
expect(saveToHistory).toHaveBeenCalledWith('!echo "hello world"')
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
test('/bash with redirection operators preserves them', () => {
|
|
335
|
+
const saveToHistory = mock(() => {})
|
|
336
|
+
const bashCommand = findCommand('bash')
|
|
337
|
+
const params = createMockParams({ saveToHistory })
|
|
338
|
+
|
|
339
|
+
bashCommand?.handler(params, 'echo test > debug/output.txt')
|
|
340
|
+
|
|
341
|
+
expect(saveToHistory).toHaveBeenCalledWith(
|
|
342
|
+
'!echo test > debug/output.txt',
|
|
343
|
+
)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
test('/bash with environment variables preserves them', () => {
|
|
347
|
+
const saveToHistory = mock(() => {})
|
|
348
|
+
const bashCommand = findCommand('bash')
|
|
349
|
+
const params = createMockParams({ saveToHistory })
|
|
350
|
+
|
|
351
|
+
bashCommand?.handler(params, 'echo $HOME')
|
|
352
|
+
|
|
353
|
+
expect(saveToHistory).toHaveBeenCalledWith('!echo $HOME')
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
test('/bash with semicolon command chaining preserves it', () => {
|
|
357
|
+
const saveToHistory = mock(() => {})
|
|
358
|
+
const bashCommand = findCommand('bash')
|
|
359
|
+
const params = createMockParams({ saveToHistory })
|
|
360
|
+
|
|
361
|
+
bashCommand?.handler(params, 'cd /tmp; ls')
|
|
362
|
+
|
|
363
|
+
expect(saveToHistory).toHaveBeenCalledWith('!cd /tmp; ls')
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test('/bash with && command chaining preserves it', () => {
|
|
367
|
+
const saveToHistory = mock(() => {})
|
|
368
|
+
const bashCommand = findCommand('bash')
|
|
369
|
+
const params = createMockParams({ saveToHistory })
|
|
370
|
+
|
|
371
|
+
bashCommand?.handler(params, 'mkdir test && cd test')
|
|
372
|
+
|
|
373
|
+
expect(saveToHistory).toHaveBeenCalledWith('!mkdir test && cd test')
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
describe('bang prefix handling in queue', () => {
|
|
378
|
+
test('command starting with ! and length > 1 is recognized as bash command', () => {
|
|
379
|
+
const input = '!ls -la'
|
|
380
|
+
const isBashFromQueue = input.startsWith('!') && input.length > 1
|
|
381
|
+
expect(isBashFromQueue).toBe(true)
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
test('single ! character is NOT recognized as bash command from queue', () => {
|
|
385
|
+
const input = '!'
|
|
386
|
+
const isBashFromQueue = input.startsWith('!') && input.length > 1
|
|
387
|
+
expect(isBashFromQueue).toBe(false)
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
test('command extracts correctly without ! prefix', () => {
|
|
391
|
+
const input = '!git status'
|
|
392
|
+
const command = input.slice(1)
|
|
393
|
+
expect(command).toBe('git status')
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
test('empty string is not a bash command from queue', () => {
|
|
397
|
+
const input = ''
|
|
398
|
+
const isBashFromQueue = input.startsWith('!') && input.length > 1
|
|
399
|
+
expect(isBashFromQueue).toBe(false)
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
test('regular text without ! is not a bash command from queue', () => {
|
|
403
|
+
const input = 'help me with this'
|
|
404
|
+
const isBashFromQueue = input.startsWith('!') && input.length > 1
|
|
405
|
+
expect(isBashFromQueue).toBe(false)
|
|
406
|
+
})
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
describe('bash mode configuration', () => {
|
|
410
|
+
test('bash mode has correct label', () => {
|
|
411
|
+
const config = getInputModeConfig('bash')
|
|
412
|
+
expect(config.icon).toBe(null)
|
|
413
|
+
expect(config.label).toBe('!')
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
test('bash mode uses info color', () => {
|
|
417
|
+
const config = getInputModeConfig('bash')
|
|
418
|
+
expect(config.color).toBe('info')
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
test('bash mode has correct placeholder', () => {
|
|
422
|
+
const config = getInputModeConfig('bash')
|
|
423
|
+
expect(config.placeholder).toBe('enter bash command...')
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
test('bash mode has width adjustment of 4', () => {
|
|
427
|
+
const config = getInputModeConfig('bash')
|
|
428
|
+
expect(config.widthAdjustment).toBe(4)
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
test('bash mode hides agent mode toggle', () => {
|
|
432
|
+
const config = getInputModeConfig('bash')
|
|
433
|
+
expect(config.showAgentModeToggle).toBe(false)
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
test('bash mode disables slash command suggestions', () => {
|
|
437
|
+
const config = getInputModeConfig('bash')
|
|
438
|
+
expect(config.disableSlashSuggestions).toBe(true)
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
test('bash mode config exists in INPUT_MODE_CONFIGS', () => {
|
|
442
|
+
expect(INPUT_MODE_CONFIGS.bash).toBeDefined()
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
})
|