byterover-cli 0.3.4 → 0.4.0
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 +119 -63
- package/bin/dev.js +8 -1
- package/bin/run.js +7 -0
- package/dist/commands/cipher-agent/run.d.ts +30 -0
- package/dist/commands/cipher-agent/run.js +123 -61
- package/dist/commands/cipher-agent/set-prompt.d.ts +2 -0
- package/dist/commands/cipher-agent/set-prompt.js +13 -8
- package/dist/commands/cipher-agent/show-prompt.d.ts +2 -0
- package/dist/commands/cipher-agent/show-prompt.js +17 -12
- package/dist/commands/curate.d.ts +3 -60
- package/dist/commands/curate.js +45 -341
- package/dist/commands/foo.d.ts +4 -2
- package/dist/commands/foo.js +21 -16
- package/dist/commands/main.d.ts +9 -0
- package/dist/commands/main.js +34 -0
- package/dist/commands/query.d.ts +2 -48
- package/dist/commands/query.js +19 -287
- package/dist/commands/status.d.ts +2 -13
- package/dist/commands/status.js +12 -91
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +23 -19
- package/dist/config/environment.d.ts +1 -1
- package/dist/config/environment.js +2 -2
- package/dist/constants.d.ts +4 -5
- package/dist/constants.js +5 -5
- package/dist/core/domain/cipher/errors/storage-error.d.ts +89 -0
- package/dist/core/domain/cipher/errors/storage-error.js +130 -0
- package/dist/core/domain/cipher/queue/types.d.ts +71 -0
- package/dist/core/domain/cipher/queue/types.js +9 -0
- package/dist/core/domain/cipher/storage/message-storage-types.d.ts +218 -0
- package/dist/core/domain/cipher/storage/message-storage-types.js +18 -0
- package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
- package/dist/core/domain/cipher/tools/constants.js +1 -0
- package/dist/core/domain/entities/event.d.ts +1 -1
- package/dist/core/domain/entities/event.js +5 -0
- package/dist/core/domain/entities/global-config.d.ts +36 -0
- package/dist/core/domain/entities/global-config.js +66 -0
- package/dist/core/domain/knowledge/directory-manager.d.ts +10 -0
- package/dist/core/domain/knowledge/directory-manager.js +18 -0
- package/dist/core/domain/knowledge/markdown-writer.d.ts +9 -0
- package/dist/core/domain/knowledge/markdown-writer.js +51 -1
- package/dist/core/interfaces/cipher/i-agent-storage.d.ts +152 -0
- package/dist/core/interfaces/cipher/i-agent-storage.js +1 -0
- package/dist/core/interfaces/cipher/i-cipher-agent.d.ts +2 -0
- package/dist/core/interfaces/cipher/i-key-storage.d.ts +91 -0
- package/dist/core/interfaces/cipher/i-key-storage.js +1 -0
- package/dist/core/interfaces/i-global-config-store.d.ts +34 -0
- package/dist/core/interfaces/i-global-config-store.js +1 -0
- package/dist/core/interfaces/i-onboarding-preference-store.d.ts +20 -0
- package/dist/core/interfaces/i-onboarding-preference-store.js +1 -0
- package/dist/core/interfaces/i-terminal.d.ts +146 -0
- package/dist/core/interfaces/i-terminal.js +1 -0
- package/dist/core/interfaces/i-workspace-detector-service.d.ts +8 -0
- package/dist/core/interfaces/i-workspace-detector-service.js +1 -0
- package/dist/core/interfaces/usecase/i-clear-use-case.d.ts +6 -0
- package/dist/core/interfaces/usecase/i-clear-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-curate-use-case.d.ts +10 -0
- package/dist/core/interfaces/usecase/i-curate-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-init-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-init-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-login-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-login-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-logout-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-logout-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-pull-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-pull-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-push-use-case.d.ts +6 -0
- package/dist/core/interfaces/usecase/i-push-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-query-use-case.d.ts +9 -0
- package/dist/core/interfaces/usecase/i-query-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-space-list-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-space-list-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-space-switch-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-space-switch-use-case.js +1 -0
- package/dist/core/interfaces/usecase/i-status-use-case.d.ts +5 -0
- package/dist/core/interfaces/usecase/i-status-use-case.js +1 -0
- package/dist/hooks/init/update-notifier.js +1 -5
- package/dist/hooks/init/welcome.js +1 -2
- package/dist/infra/cipher/agent-service-factory.d.ts +13 -6
- package/dist/infra/cipher/agent-service-factory.js +40 -16
- package/dist/infra/cipher/cipher-agent.js +4 -4
- package/dist/infra/cipher/consumer/consumer-lock.d.ts +20 -0
- package/dist/infra/cipher/consumer/consumer-lock.js +40 -0
- package/dist/infra/cipher/consumer/consumer-service.d.ts +99 -0
- package/dist/infra/cipher/consumer/consumer-service.js +165 -0
- package/dist/infra/cipher/consumer/execution-consumer.d.ts +121 -0
- package/dist/infra/cipher/consumer/execution-consumer.js +523 -0
- package/dist/infra/cipher/consumer/index.d.ts +33 -0
- package/dist/infra/cipher/consumer/index.js +33 -0
- package/dist/infra/cipher/consumer/queue-polling-service.d.ts +120 -0
- package/dist/infra/cipher/consumer/queue-polling-service.js +248 -0
- package/dist/infra/cipher/http/internal-llm-http-service.d.ts +94 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +118 -0
- package/dist/infra/cipher/llm/context/compaction/compaction-service.d.ts +106 -0
- package/dist/infra/cipher/llm/context/compaction/compaction-service.js +132 -0
- package/dist/infra/cipher/llm/context/compaction/index.d.ts +9 -0
- package/dist/infra/cipher/llm/context/compaction/index.js +9 -0
- package/dist/infra/cipher/llm/context/context-manager.d.ts +46 -2
- package/dist/infra/cipher/llm/context/context-manager.js +68 -4
- package/dist/infra/cipher/llm/context/rw-lock.d.ts +72 -0
- package/dist/infra/cipher/llm/context/rw-lock.js +145 -0
- package/dist/infra/cipher/llm/generators/byterover-content-generator.d.ts +7 -7
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +8 -8
- package/dist/infra/cipher/llm/internal-llm-service.js +2 -0
- package/dist/infra/cipher/session/session-manager.d.ts +4 -4
- package/dist/infra/cipher/session/session-manager.js +5 -5
- package/dist/infra/cipher/storage/agent-storage.d.ts +246 -0
- package/dist/infra/cipher/storage/agent-storage.js +956 -0
- package/dist/infra/cipher/storage/dual-format-history-storage.d.ts +77 -0
- package/dist/infra/cipher/storage/dual-format-history-storage.js +149 -0
- package/dist/infra/cipher/storage/granular-history-storage.d.ts +65 -0
- package/dist/infra/cipher/storage/granular-history-storage.js +118 -0
- package/dist/infra/cipher/storage/message-storage-service.d.ts +108 -0
- package/dist/infra/cipher/storage/message-storage-service.js +529 -0
- package/dist/infra/cipher/storage/process-utils.d.ts +16 -0
- package/dist/infra/cipher/storage/process-utils.js +43 -0
- package/dist/infra/cipher/storage/sqlite-key-storage.d.ts +105 -0
- package/dist/infra/cipher/storage/sqlite-key-storage.js +404 -0
- package/dist/infra/cipher/system-prompt/simple-prompt-factory.d.ts +1 -0
- package/dist/infra/cipher/system-prompt/simple-prompt-factory.js +7 -0
- package/dist/infra/cipher/tools/default-policy-rules.js +1 -1
- package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +10 -0
- package/dist/infra/cipher/tools/implementations/curate-tool.js +371 -0
- package/dist/infra/cipher/tools/implementations/find-knowledge-topics-tool.js +11 -8
- package/dist/infra/cipher/tools/tool-manager.d.ts +8 -2
- package/dist/infra/cipher/tools/tool-manager.js +29 -2
- package/dist/infra/cipher/tools/tool-registry.js +7 -0
- package/dist/infra/http/authenticated-http-client.d.ts +21 -0
- package/dist/infra/http/authenticated-http-client.js +38 -0
- package/dist/infra/repl/commands/arg-parser.d.ts +97 -0
- package/dist/infra/repl/commands/arg-parser.js +129 -0
- package/dist/infra/repl/commands/clear-command.d.ts +5 -0
- package/dist/infra/repl/commands/clear-command.js +61 -0
- package/dist/infra/repl/commands/curate-command.d.ts +9 -0
- package/dist/infra/repl/commands/curate-command.js +88 -0
- package/dist/infra/repl/commands/gen-rules-command.d.ts +7 -0
- package/dist/infra/repl/commands/gen-rules-command.js +38 -0
- package/dist/infra/repl/commands/index.d.ts +8 -0
- package/dist/infra/repl/commands/index.js +36 -0
- package/dist/infra/repl/commands/init-command.d.ts +7 -0
- package/dist/infra/repl/commands/init-command.js +83 -0
- package/dist/infra/repl/commands/login-command.d.ts +7 -0
- package/dist/infra/repl/commands/login-command.js +50 -0
- package/dist/infra/repl/commands/logout-command.d.ts +5 -0
- package/dist/infra/repl/commands/logout-command.js +48 -0
- package/dist/infra/repl/commands/pull-command.d.ts +5 -0
- package/dist/infra/repl/commands/pull-command.js +61 -0
- package/dist/infra/repl/commands/push-command.d.ts +5 -0
- package/dist/infra/repl/commands/push-command.js +66 -0
- package/dist/infra/repl/commands/query-command.d.ts +5 -0
- package/dist/infra/repl/commands/query-command.js +66 -0
- package/dist/infra/repl/commands/space/index.d.ts +5 -0
- package/dist/infra/repl/commands/space/index.js +14 -0
- package/dist/infra/repl/commands/space/list-command.d.ts +5 -0
- package/dist/infra/repl/commands/space/list-command.js +70 -0
- package/dist/infra/repl/commands/space/switch-command.d.ts +5 -0
- package/dist/infra/repl/commands/space/switch-command.js +37 -0
- package/dist/infra/repl/commands/status-command.d.ts +5 -0
- package/dist/infra/repl/commands/status-command.js +39 -0
- package/dist/infra/repl/repl-startup.d.ts +18 -0
- package/dist/infra/repl/repl-startup.js +26 -0
- package/dist/infra/storage/file-global-config-store.d.ts +22 -0
- package/dist/infra/storage/file-global-config-store.js +65 -0
- package/dist/infra/storage/file-onboarding-preference-store.d.ts +10 -0
- package/dist/infra/storage/file-onboarding-preference-store.js +46 -0
- package/dist/infra/terminal/oclif-terminal.d.ts +19 -0
- package/dist/infra/terminal/oclif-terminal.js +60 -0
- package/dist/infra/terminal/repl-terminal.d.ts +31 -0
- package/dist/infra/terminal/repl-terminal.js +116 -0
- package/dist/infra/tracking/mixpanel-tracking-service.d.ts +11 -1
- package/dist/infra/tracking/mixpanel-tracking-service.js +18 -13
- package/dist/infra/usecase/clear-use-case.d.ts +20 -0
- package/dist/infra/usecase/clear-use-case.js +58 -0
- package/dist/infra/usecase/curate-use-case.d.ts +66 -0
- package/dist/infra/usecase/curate-use-case.js +283 -0
- package/dist/{commands/gen-rules.d.ts → infra/usecase/generate-rules-use-case.d.ts} +14 -20
- package/dist/{commands/gen-rules.js → infra/usecase/generate-rules-use-case.js} +59 -78
- package/dist/infra/usecase/init-use-case.d.ts +139 -0
- package/dist/{commands/init.js → infra/usecase/init-use-case.js} +184 -230
- package/dist/infra/usecase/login-use-case.d.ts +28 -0
- package/dist/infra/usecase/login-use-case.js +88 -0
- package/dist/infra/usecase/logout-use-case.d.ts +22 -0
- package/dist/infra/usecase/logout-use-case.js +51 -0
- package/dist/infra/usecase/pull-use-case.d.ts +35 -0
- package/dist/infra/usecase/pull-use-case.js +89 -0
- package/dist/infra/usecase/push-use-case.d.ts +37 -0
- package/dist/infra/usecase/push-use-case.js +124 -0
- package/dist/infra/usecase/query-use-case.d.ts +78 -0
- package/dist/infra/usecase/query-use-case.js +401 -0
- package/dist/infra/usecase/space-list-use-case.d.ts +27 -0
- package/dist/infra/usecase/space-list-use-case.js +64 -0
- package/dist/infra/usecase/space-switch-use-case.d.ts +36 -0
- package/dist/infra/usecase/space-switch-use-case.js +140 -0
- package/dist/infra/usecase/status-use-case.d.ts +27 -0
- package/dist/infra/usecase/status-use-case.js +97 -0
- package/dist/infra/workspace/workspace-detector-service.d.ts +3 -6
- package/dist/resources/prompts/curate-context-tree-curation.yml +23 -11
- package/dist/resources/prompts/query-context-tree-retrieval.yml +3 -4
- package/dist/resources/prompts/system-prompt.yml +1 -1
- package/dist/resources/prompts/tool-outputs.yml +4 -3
- package/dist/templates/sections/command-reference.md +12 -0
- package/dist/templates/sections/workflow.md +10 -1
- package/dist/tui/app.d.ts +9 -0
- package/dist/tui/app.js +26 -0
- package/dist/tui/components/enter-prompt.d.ts +13 -0
- package/dist/tui/components/enter-prompt.js +15 -0
- package/dist/tui/components/execution/execution-changes.d.ts +14 -0
- package/dist/tui/components/execution/execution-changes.js +15 -0
- package/dist/tui/components/execution/execution-content.d.ts +25 -0
- package/dist/tui/components/execution/execution-content.js +67 -0
- package/dist/tui/components/execution/execution-input.d.ts +12 -0
- package/dist/tui/components/execution/execution-input.js +16 -0
- package/dist/tui/components/execution/execution-progress.d.ts +21 -0
- package/dist/tui/components/execution/execution-progress.js +21 -0
- package/dist/tui/components/execution/execution-status.d.ts +13 -0
- package/dist/tui/components/execution/execution-status.js +19 -0
- package/dist/tui/components/execution/index.d.ts +11 -0
- package/dist/tui/components/execution/index.js +11 -0
- package/dist/tui/components/execution/log-item.d.ts +17 -0
- package/dist/tui/components/execution/log-item.js +25 -0
- package/dist/tui/components/footer.d.ts +5 -0
- package/dist/tui/components/footer.js +12 -0
- package/dist/tui/components/header.d.ts +18 -0
- package/dist/tui/components/header.js +18 -0
- package/dist/tui/components/index.d.ts +17 -0
- package/dist/tui/components/index.js +14 -0
- package/dist/tui/components/inline-prompts/index.d.ts +15 -0
- package/dist/tui/components/inline-prompts/index.js +10 -0
- package/dist/tui/components/inline-prompts/inline-confirm.d.ts +17 -0
- package/dist/tui/components/inline-prompts/inline-confirm.js +32 -0
- package/dist/tui/components/inline-prompts/inline-file-selector.d.ts +43 -0
- package/dist/tui/components/inline-prompts/inline-file-selector.js +185 -0
- package/dist/tui/components/inline-prompts/inline-input.d.ts +19 -0
- package/dist/tui/components/inline-prompts/inline-input.js +32 -0
- package/dist/tui/components/inline-prompts/inline-search.d.ts +20 -0
- package/dist/tui/components/inline-prompts/inline-search.js +50 -0
- package/dist/tui/components/inline-prompts/inline-select.d.ts +20 -0
- package/dist/tui/components/inline-prompts/inline-select.js +34 -0
- package/dist/tui/components/logo.d.ts +43 -0
- package/dist/tui/components/logo.js +103 -0
- package/dist/tui/components/message-item.d.ts +12 -0
- package/dist/tui/components/message-item.js +12 -0
- package/dist/tui/components/onboarding/copyable-prompt.d.ts +15 -0
- package/dist/tui/components/onboarding/copyable-prompt.js +65 -0
- package/dist/tui/components/onboarding/index.d.ts +7 -0
- package/dist/tui/components/onboarding/index.js +6 -0
- package/dist/tui/components/onboarding/onboarding-flow.d.ts +13 -0
- package/dist/tui/components/onboarding/onboarding-flow.js +304 -0
- package/dist/tui/components/onboarding/onboarding-step.d.ts +23 -0
- package/dist/tui/components/onboarding/onboarding-step.js +12 -0
- package/dist/tui/components/output-log.d.ts +14 -0
- package/dist/tui/components/output-log.js +13 -0
- package/dist/tui/components/scrollable-list.d.ts +30 -0
- package/dist/tui/components/scrollable-list.js +121 -0
- package/dist/tui/components/suggestions.d.ts +16 -0
- package/dist/tui/components/suggestions.js +162 -0
- package/dist/tui/components/tab-bar.d.ts +10 -0
- package/dist/tui/components/tab-bar.js +12 -0
- package/dist/tui/constants.d.ts +11 -0
- package/dist/tui/constants.js +13 -0
- package/dist/tui/contexts/auth-context.d.ts +30 -0
- package/dist/tui/contexts/auth-context.js +153 -0
- package/dist/tui/contexts/consumer.d.ts +31 -0
- package/dist/tui/contexts/consumer.js +56 -0
- package/dist/tui/contexts/index.d.ts +6 -0
- package/dist/tui/contexts/index.js +6 -0
- package/dist/tui/contexts/onboarding-context.d.ts +43 -0
- package/dist/tui/contexts/onboarding-context.js +181 -0
- package/dist/tui/contexts/services-context.d.ts +29 -0
- package/dist/tui/contexts/services-context.js +20 -0
- package/dist/tui/contexts/use-commands.d.ts +29 -0
- package/dist/tui/contexts/use-commands.js +53 -0
- package/dist/tui/contexts/use-mode.d.ts +43 -0
- package/dist/tui/contexts/use-mode.js +76 -0
- package/dist/tui/contexts/use-theme.d.ts +53 -0
- package/dist/tui/contexts/use-theme.js +60 -0
- package/dist/tui/hooks/index.d.ts +17 -0
- package/dist/tui/hooks/index.js +14 -0
- package/dist/tui/hooks/use-activity-logs.d.ts +26 -0
- package/dist/tui/hooks/use-activity-logs.js +90 -0
- package/dist/tui/hooks/use-consumer.d.ts +12 -0
- package/dist/tui/hooks/use-consumer.js +50 -0
- package/dist/tui/hooks/use-onboarding.d.ts +7 -0
- package/dist/tui/hooks/use-onboarding.js +6 -0
- package/dist/tui/hooks/use-queue-polling.d.ts +31 -0
- package/dist/tui/hooks/use-queue-polling.js +90 -0
- package/dist/tui/hooks/use-slash-command-processor.d.ts +16 -0
- package/dist/tui/hooks/use-slash-command-processor.js +132 -0
- package/dist/tui/hooks/use-slash-completion.d.ts +30 -0
- package/dist/tui/hooks/use-slash-completion.js +230 -0
- package/dist/tui/hooks/use-tab-navigation.d.ts +10 -0
- package/dist/tui/hooks/use-tab-navigation.js +35 -0
- package/dist/tui/hooks/use-visible-window.d.ts +22 -0
- package/dist/tui/hooks/use-visible-window.js +37 -0
- package/dist/tui/index.d.ts +1 -0
- package/dist/tui/index.js +1 -0
- package/dist/tui/providers/app-providers.d.ts +25 -0
- package/dist/tui/providers/app-providers.js +9 -0
- package/dist/tui/types/commands.d.ts +252 -0
- package/dist/tui/types/commands.js +16 -0
- package/dist/tui/types/dialogs.d.ts +37 -0
- package/dist/tui/types/dialogs.js +4 -0
- package/dist/tui/types/index.d.ts +11 -0
- package/dist/tui/types/index.js +7 -0
- package/dist/tui/types/messages.d.ts +55 -0
- package/dist/tui/types/messages.js +4 -0
- package/dist/tui/types/prompts.d.ts +100 -0
- package/dist/tui/types/prompts.js +4 -0
- package/dist/tui/types/ui.d.ts +14 -0
- package/dist/tui/types/ui.js +4 -0
- package/dist/tui/types.d.ts +1 -0
- package/dist/tui/types.js +1 -0
- package/dist/tui/views/command-view.d.ts +12 -0
- package/dist/tui/views/command-view.js +451 -0
- package/dist/tui/views/index.d.ts +6 -0
- package/dist/tui/views/index.js +6 -0
- package/dist/tui/views/login-view.d.ts +10 -0
- package/dist/tui/views/login-view.js +30 -0
- package/dist/tui/views/logs-view.d.ts +11 -0
- package/dist/tui/views/logs-view.js +73 -0
- package/dist/utils/file-validator.d.ts +16 -0
- package/dist/utils/file-validator.js +81 -0
- package/dist/utils/global-config-path.d.ts +15 -0
- package/dist/utils/global-config-path.js +38 -0
- package/oclif.manifest.json +29 -315
- package/package.json +11 -4
- package/dist/commands/clear.d.ts +0 -19
- package/dist/commands/clear.js +0 -78
- package/dist/commands/init.d.ts +0 -130
- package/dist/commands/login.d.ts +0 -22
- package/dist/commands/login.js +0 -108
- package/dist/commands/logout.d.ts +0 -16
- package/dist/commands/logout.js +0 -61
- package/dist/commands/pull.d.ts +0 -33
- package/dist/commands/pull.js +0 -115
- package/dist/commands/push.d.ts +0 -35
- package/dist/commands/push.js +0 -160
- package/dist/commands/space/list.d.ts +0 -25
- package/dist/commands/space/list.js +0 -114
- package/dist/commands/space/switch.d.ts +0 -36
- package/dist/commands/space/switch.js +0 -160
- package/dist/infra/cipher/grpc/internal-llm-grpc-service.d.ts +0 -149
- package/dist/infra/cipher/grpc/internal-llm-grpc-service.js +0 -364
- package/dist/infra/cipher/grpc/internal-llm-grpc.proto +0 -94
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Command View
|
|
4
|
+
*
|
|
5
|
+
* Main view with slash command input and streaming output support.
|
|
6
|
+
* Uses ScrollableList for message history with dynamic height calculation.
|
|
7
|
+
*/
|
|
8
|
+
import { Box, Spacer, Text, useApp } from 'ink';
|
|
9
|
+
import Spinner from 'ink-spinner';
|
|
10
|
+
import TextInput from 'ink-text-input';
|
|
11
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
12
|
+
import { stopConsumer } from '../../infra/cipher/consumer/execution-consumer.js';
|
|
13
|
+
import { stopQueuePollingService } from '../../infra/cipher/consumer/queue-polling-service.js';
|
|
14
|
+
import { MessageItem, ScrollableList, Suggestions } from '../components/index.js';
|
|
15
|
+
import { InlineConfirm, InlineFileSelector, InlineInput, InlineSearch, InlineSelect, } from '../components/inline-prompts/index.js';
|
|
16
|
+
import { useAuth } from '../contexts/auth-context.js';
|
|
17
|
+
import { useConsumer } from '../contexts/index.js';
|
|
18
|
+
import { useCommands, useMode, useTheme } from '../hooks/index.js';
|
|
19
|
+
/** Fixed height for bottom area (suggestions + input) */
|
|
20
|
+
const BOTTOM_AREA_HEIGHT = 4;
|
|
21
|
+
/** Max visible items in InlineSearch */
|
|
22
|
+
const INLINE_SEARCH_MAX_ITEMS = 7;
|
|
23
|
+
/** Minimum output lines to show before truncation */
|
|
24
|
+
const MIN_OUTPUT_LINES = 5;
|
|
25
|
+
/** Reserved lines for output box (command header + borders + hint line + margin) */
|
|
26
|
+
const OUTPUT_BOX_OVERHEAD = 5;
|
|
27
|
+
/**
|
|
28
|
+
* Count the total number of lines in streaming messages
|
|
29
|
+
* Each message can contain multiple lines (newlines in content)
|
|
30
|
+
*/
|
|
31
|
+
function countOutputLines(messages) {
|
|
32
|
+
let total = 0;
|
|
33
|
+
for (const msg of messages) {
|
|
34
|
+
total += msg.content.split('\n').length;
|
|
35
|
+
}
|
|
36
|
+
return total;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get messages from the end that fit within maxLines, truncating from the beginning (shows newest first)
|
|
40
|
+
* Used for both completed and live streaming output to always show the latest messages
|
|
41
|
+
*/
|
|
42
|
+
function getMessagesFromEnd(messages, maxLines) {
|
|
43
|
+
const totalLines = countOutputLines(messages);
|
|
44
|
+
if (totalLines <= maxLines) {
|
|
45
|
+
return { displayMessages: messages, skippedLines: 0, totalLines };
|
|
46
|
+
}
|
|
47
|
+
const displayMessages = [];
|
|
48
|
+
let lineCount = 0;
|
|
49
|
+
// Iterate from the end (newest messages first)
|
|
50
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
51
|
+
const msg = messages[i];
|
|
52
|
+
const msgLineArray = msg.content.split('\n');
|
|
53
|
+
const msgLineCount = msgLineArray.length;
|
|
54
|
+
if (lineCount + msgLineCount <= maxLines) {
|
|
55
|
+
// Message fits completely - prepend to maintain order
|
|
56
|
+
displayMessages.unshift(msg);
|
|
57
|
+
lineCount += msgLineCount;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Message needs to be truncated - show partial lines from the end
|
|
61
|
+
const remainingSpace = maxLines - lineCount;
|
|
62
|
+
if (remainingSpace > 0) {
|
|
63
|
+
// Take only the last lines that fit
|
|
64
|
+
const truncatedContent = msgLineArray.slice(-remainingSpace).join('\n');
|
|
65
|
+
displayMessages.unshift({
|
|
66
|
+
...msg,
|
|
67
|
+
content: truncatedContent,
|
|
68
|
+
});
|
|
69
|
+
lineCount += remainingSpace;
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Ensure we show at least one line
|
|
75
|
+
if (displayMessages.length === 0 && messages.length > 0) {
|
|
76
|
+
const lastMsg = messages.at(-1);
|
|
77
|
+
const lastLines = lastMsg.content.split('\n');
|
|
78
|
+
displayMessages.push({
|
|
79
|
+
...lastMsg,
|
|
80
|
+
content: lastLines.at(-1) ?? '',
|
|
81
|
+
});
|
|
82
|
+
lineCount = 1;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
displayMessages,
|
|
86
|
+
skippedLines: totalLines - lineCount,
|
|
87
|
+
totalLines,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Process streaming messages to handle action_start/action_stop pairs
|
|
92
|
+
* - action_start with matching action_stop: mark as completed with stop message
|
|
93
|
+
* - action_start without matching action_stop: mark as running (show spinner)
|
|
94
|
+
* - action_stop messages are filtered out (consumed by their action_start)
|
|
95
|
+
*/
|
|
96
|
+
function processMessagesForActions(messages) {
|
|
97
|
+
// Build a map of actionId -> action_stop message
|
|
98
|
+
const stopMessages = new Map();
|
|
99
|
+
for (const msg of messages) {
|
|
100
|
+
if (msg.type === 'action_stop' && msg.actionId) {
|
|
101
|
+
stopMessages.set(msg.actionId, msg.content);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Process messages, transforming action_start and filtering action_stop
|
|
105
|
+
const result = [];
|
|
106
|
+
for (const msg of messages) {
|
|
107
|
+
if (msg.type === 'action_stop') {
|
|
108
|
+
// Skip action_stop messages - they're consumed by action_start
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (msg.type === 'action_start' && msg.actionId) {
|
|
112
|
+
const stopMessage = stopMessages.get(msg.actionId);
|
|
113
|
+
result.push({
|
|
114
|
+
...msg,
|
|
115
|
+
isActionRunning: stopMessage === undefined,
|
|
116
|
+
stopMessage,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
result.push(msg);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
/** Default page size for file selector */
|
|
126
|
+
const INLINE_FILE_SELECTOR_PAGE_SIZE = 7;
|
|
127
|
+
/**
|
|
128
|
+
* Estimate height of an active prompt
|
|
129
|
+
*/
|
|
130
|
+
function estimatePromptHeight(prompt) {
|
|
131
|
+
if (!prompt)
|
|
132
|
+
return 0;
|
|
133
|
+
switch (prompt.type) {
|
|
134
|
+
case 'confirm': {
|
|
135
|
+
// Single line: "? message (Y/n) [input]"
|
|
136
|
+
return 3;
|
|
137
|
+
}
|
|
138
|
+
case 'file_selector': {
|
|
139
|
+
// Message + path + separator + items + scroll indicator + hint
|
|
140
|
+
const pageSize = prompt.pageSize ?? INLINE_FILE_SELECTOR_PAGE_SIZE;
|
|
141
|
+
return 5 + pageSize;
|
|
142
|
+
}
|
|
143
|
+
case 'input': {
|
|
144
|
+
// Message line + optional error line
|
|
145
|
+
return 3;
|
|
146
|
+
}
|
|
147
|
+
case 'search': {
|
|
148
|
+
// Message line + up to 7 visible choices (or 1 for "No results")
|
|
149
|
+
// We estimate 7 since we don't know actual results
|
|
150
|
+
return 3 + INLINE_SEARCH_MAX_ITEMS;
|
|
151
|
+
}
|
|
152
|
+
case 'select': {
|
|
153
|
+
// Message line + choices + optional description (with margin)
|
|
154
|
+
const hasDescription = prompt.choices.some((c) => c.description);
|
|
155
|
+
return 1 + prompt.choices.length + (hasDescription ? 2 : 0);
|
|
156
|
+
}
|
|
157
|
+
default: {
|
|
158
|
+
return 4;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Estimate the line height of a command message
|
|
164
|
+
*/
|
|
165
|
+
function estimateMessageHeight(msg, options) {
|
|
166
|
+
const { isLast = false, maxOutputLines, promptHeight = 0, streamingLines = 0 } = options;
|
|
167
|
+
let lines = 0;
|
|
168
|
+
if (msg.type === 'command') {
|
|
169
|
+
// Command header with left border
|
|
170
|
+
lines += 1;
|
|
171
|
+
// Output box if present (completed output)
|
|
172
|
+
if (msg.output && msg.output.length > 0) {
|
|
173
|
+
// Count actual lines in the output (each message can have multiple lines)
|
|
174
|
+
const totalOutputLines = countOutputLines(msg.output);
|
|
175
|
+
// Account for truncation: show max lines + 1 for hint
|
|
176
|
+
const displayedLines = totalOutputLines <= maxOutputLines ? totalOutputLines : maxOutputLines + 1;
|
|
177
|
+
// Border top + content + border bottom
|
|
178
|
+
lines += 2 + displayedLines;
|
|
179
|
+
}
|
|
180
|
+
// Live streaming output box (for last message while streaming)
|
|
181
|
+
if (isLast && (streamingLines > 0 || promptHeight > 0)) {
|
|
182
|
+
// Border top + streaming content + prompt + border bottom
|
|
183
|
+
lines += 2 + streamingLines + promptHeight;
|
|
184
|
+
}
|
|
185
|
+
// Top margin for non-first items
|
|
186
|
+
lines += 1;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
// Other message types (info, error, etc)
|
|
190
|
+
lines += 2;
|
|
191
|
+
}
|
|
192
|
+
return lines;
|
|
193
|
+
}
|
|
194
|
+
export const CommandView = ({ availableHeight }) => {
|
|
195
|
+
const { exit } = useApp();
|
|
196
|
+
const { reloadAuth, reloadBrvConfig } = useAuth();
|
|
197
|
+
const { restart } = useConsumer();
|
|
198
|
+
const [command, setCommand] = useState('');
|
|
199
|
+
const [inputKey, setInputKey] = useState(0);
|
|
200
|
+
const [messages, setMessages] = useState([]);
|
|
201
|
+
const [streamingMessages, setStreamingMessages] = useState([]);
|
|
202
|
+
const [activePrompt, setActivePrompt] = useState(null);
|
|
203
|
+
const [isStreaming, setIsStreaming] = useState(false);
|
|
204
|
+
const { theme: { colors }, } = useTheme();
|
|
205
|
+
// Process streaming messages to handle action_start/action_stop pairs
|
|
206
|
+
const processedStreamingMessages = useMemo(() => processMessagesForActions(streamingMessages), [streamingMessages]);
|
|
207
|
+
const { handleSlashCommand } = useCommands();
|
|
208
|
+
const { appendShortcuts, mode, removeShortcuts } = useMode();
|
|
209
|
+
// Append shortcuts for prompts
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (activePrompt?.type === 'search' || activePrompt?.type === 'select') {
|
|
212
|
+
appendShortcuts([{ description: 'select', key: 'enter' }]);
|
|
213
|
+
return () => {
|
|
214
|
+
removeShortcuts(['enter']);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}, [activePrompt?.type, appendShortcuts, removeShortcuts]);
|
|
218
|
+
const executeCommand = useCallback(async (value) => {
|
|
219
|
+
const trimmed = value.trim();
|
|
220
|
+
if (!trimmed)
|
|
221
|
+
return;
|
|
222
|
+
// Clear command input immediately
|
|
223
|
+
setCommand('');
|
|
224
|
+
setMessages((prev) => [
|
|
225
|
+
...prev,
|
|
226
|
+
{
|
|
227
|
+
content: '',
|
|
228
|
+
fromCommand: trimmed,
|
|
229
|
+
type: 'command',
|
|
230
|
+
},
|
|
231
|
+
]);
|
|
232
|
+
const result = await handleSlashCommand(trimmed);
|
|
233
|
+
if (result && result.type === 'message') {
|
|
234
|
+
setMessages((prev) => {
|
|
235
|
+
const last = prev.at(-1);
|
|
236
|
+
return [
|
|
237
|
+
...(last?.type === 'command' ? prev.slice(0, -1) : [...prev]),
|
|
238
|
+
{
|
|
239
|
+
content: result.content,
|
|
240
|
+
fromCommand: trimmed,
|
|
241
|
+
type: result.messageType === 'error' ? 'error' : 'info',
|
|
242
|
+
},
|
|
243
|
+
];
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (result && result.type === 'quit') {
|
|
247
|
+
stopQueuePollingService();
|
|
248
|
+
exit();
|
|
249
|
+
}
|
|
250
|
+
if (result && result.type === 'streaming') {
|
|
251
|
+
setIsStreaming(true);
|
|
252
|
+
setStreamingMessages([]);
|
|
253
|
+
const collectedMessages = [];
|
|
254
|
+
const onMessage = (msg) => {
|
|
255
|
+
collectedMessages.push(msg);
|
|
256
|
+
setStreamingMessages((prev) => [...prev, msg]);
|
|
257
|
+
};
|
|
258
|
+
const onPrompt = (prompt) => {
|
|
259
|
+
setActivePrompt(prompt);
|
|
260
|
+
};
|
|
261
|
+
try {
|
|
262
|
+
await result.execute(onMessage, onPrompt);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
266
|
+
const errorMsg = {
|
|
267
|
+
content: `Error: ${errorMessage}`,
|
|
268
|
+
id: `error-${Date.now()}`,
|
|
269
|
+
type: 'error',
|
|
270
|
+
};
|
|
271
|
+
collectedMessages.push(errorMsg);
|
|
272
|
+
setStreamingMessages((prev) => [...prev, errorMsg]);
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
// Store output with the command message
|
|
276
|
+
setMessages((prev) => {
|
|
277
|
+
const updated = [...prev];
|
|
278
|
+
const lastIndex = updated.length - 1;
|
|
279
|
+
if (lastIndex >= 0 && updated[lastIndex].type === 'command') {
|
|
280
|
+
updated[lastIndex] = { ...updated[lastIndex], output: collectedMessages };
|
|
281
|
+
}
|
|
282
|
+
return updated;
|
|
283
|
+
});
|
|
284
|
+
setStreamingMessages([]);
|
|
285
|
+
setIsStreaming(false);
|
|
286
|
+
setActivePrompt(null);
|
|
287
|
+
const needReloadAuth = trimmed.startsWith('/login') || trimmed.startsWith('/logout');
|
|
288
|
+
const needReloadBrvConfig = trimmed.startsWith('/space switch') || trimmed.startsWith('/init');
|
|
289
|
+
// Refresh state after commands that change auth or project state
|
|
290
|
+
if (needReloadAuth || needReloadBrvConfig) {
|
|
291
|
+
// Stop queue polling and consumer
|
|
292
|
+
stopQueuePollingService();
|
|
293
|
+
stopConsumer();
|
|
294
|
+
// Wait for consumer to stop
|
|
295
|
+
setTimeout(async () => {
|
|
296
|
+
if (needReloadAuth)
|
|
297
|
+
await reloadAuth();
|
|
298
|
+
if (needReloadBrvConfig)
|
|
299
|
+
await reloadBrvConfig();
|
|
300
|
+
await restart();
|
|
301
|
+
}, 1000);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}, [exit, handleSlashCommand, reloadAuth, restart]);
|
|
306
|
+
const handleSubmit = useCallback(async (value) => {
|
|
307
|
+
if (mode === 'console' && !isStreaming)
|
|
308
|
+
await executeCommand(value);
|
|
309
|
+
}, [executeCommand, isStreaming, mode]);
|
|
310
|
+
const handleSelect = useCallback(async (value) => {
|
|
311
|
+
if (!isStreaming)
|
|
312
|
+
await executeCommand(value);
|
|
313
|
+
}, [executeCommand, isStreaming]);
|
|
314
|
+
const handleInsert = useCallback((value) => {
|
|
315
|
+
// Don't add space after directories (ends with /) to allow continued navigation
|
|
316
|
+
const suffix = value.endsWith('/') ? '' : ' ';
|
|
317
|
+
setCommand(value + suffix);
|
|
318
|
+
// TRICK: Force TextInput to remount with cursor at the end
|
|
319
|
+
setInputKey((prev) => prev + 1);
|
|
320
|
+
}, []);
|
|
321
|
+
// Handle prompt response
|
|
322
|
+
const handleSearchResponse = useCallback((value) => {
|
|
323
|
+
if (activePrompt?.type === 'search') {
|
|
324
|
+
activePrompt.onResponse(value);
|
|
325
|
+
setActivePrompt(null);
|
|
326
|
+
}
|
|
327
|
+
}, [activePrompt]);
|
|
328
|
+
const handleConfirmResponse = useCallback((value) => {
|
|
329
|
+
if (activePrompt?.type === 'confirm') {
|
|
330
|
+
activePrompt.onResponse(value);
|
|
331
|
+
setActivePrompt(null);
|
|
332
|
+
}
|
|
333
|
+
}, [activePrompt]);
|
|
334
|
+
const handleSelectResponse = useCallback((value) => {
|
|
335
|
+
if (activePrompt?.type === 'select') {
|
|
336
|
+
activePrompt.onResponse(value);
|
|
337
|
+
setActivePrompt(null);
|
|
338
|
+
}
|
|
339
|
+
}, [activePrompt]);
|
|
340
|
+
const handleInputResponse = useCallback((value) => {
|
|
341
|
+
if (activePrompt?.type === 'input') {
|
|
342
|
+
activePrompt.onResponse(value);
|
|
343
|
+
setActivePrompt(null);
|
|
344
|
+
}
|
|
345
|
+
}, [activePrompt]);
|
|
346
|
+
const handleFileSelectorResponse = useCallback((value) => {
|
|
347
|
+
if (activePrompt?.type === 'file_selector') {
|
|
348
|
+
activePrompt.onResponse(value);
|
|
349
|
+
setActivePrompt(null);
|
|
350
|
+
}
|
|
351
|
+
}, [activePrompt]);
|
|
352
|
+
// Calculate available height for scrollable content
|
|
353
|
+
// Subtract bottom area (suggestions + input)
|
|
354
|
+
const scrollableHeight = Math.max(1, availableHeight - BOTTOM_AREA_HEIGHT);
|
|
355
|
+
// Calculate max output lines based on available height
|
|
356
|
+
// Reserve space for command header, borders, hint line, and margin
|
|
357
|
+
const maxOutputLines = Math.max(MIN_OUTPUT_LINES, scrollableHeight - OUTPUT_BOX_OVERHEAD);
|
|
358
|
+
// Render streaming message (handles ProcessedMessage for action types)
|
|
359
|
+
const renderStreamingMessage = useCallback((msg) => {
|
|
360
|
+
// Handle action messages with spinner
|
|
361
|
+
if (msg.type === 'action_start') {
|
|
362
|
+
if (msg.isActionRunning) {
|
|
363
|
+
// Action is still running - show spinner
|
|
364
|
+
return (_jsxs(Text, { color: colors.text, children: [_jsx(Spinner, { type: "dots" }), " ", msg.content] }, msg.id));
|
|
365
|
+
}
|
|
366
|
+
// Action completed - show with completion message
|
|
367
|
+
return (_jsxs(Text, { color: colors.text, children: [msg.content, msg.stopMessage ? `... ${msg.stopMessage}` : ''] }, msg.id));
|
|
368
|
+
}
|
|
369
|
+
// Regular messages
|
|
370
|
+
let color = colors.text;
|
|
371
|
+
if (msg.type === 'error')
|
|
372
|
+
color = colors.errorText;
|
|
373
|
+
if (msg.type === 'warning')
|
|
374
|
+
color = colors.warning;
|
|
375
|
+
return (_jsx(Text, { color: color, children: msg.content }, msg.id));
|
|
376
|
+
}, [colors]);
|
|
377
|
+
// Render active prompt component based on type
|
|
378
|
+
const renderActivePrompt = useCallback(() => {
|
|
379
|
+
if (!activePrompt)
|
|
380
|
+
return null;
|
|
381
|
+
switch (activePrompt.type) {
|
|
382
|
+
case 'confirm': {
|
|
383
|
+
return (_jsx(InlineConfirm, { default: activePrompt.default, message: activePrompt.message, onConfirm: handleConfirmResponse }));
|
|
384
|
+
}
|
|
385
|
+
case 'file_selector': {
|
|
386
|
+
return (_jsx(InlineFileSelector, { allowCancel: activePrompt.allowCancel, basePath: activePrompt.basePath, filter: activePrompt.filter, message: activePrompt.message, mode: activePrompt.mode, onSelect: handleFileSelectorResponse, pageSize: activePrompt.pageSize }));
|
|
387
|
+
}
|
|
388
|
+
case 'input': {
|
|
389
|
+
return (_jsx(InlineInput, { message: activePrompt.message, onSubmit: handleInputResponse, placeholder: activePrompt.placeholder, validate: activePrompt.validate }));
|
|
390
|
+
}
|
|
391
|
+
case 'search': {
|
|
392
|
+
return (_jsx(InlineSearch, { message: activePrompt.message, onSelect: handleSearchResponse, source: activePrompt.source }));
|
|
393
|
+
}
|
|
394
|
+
case 'select': {
|
|
395
|
+
return (_jsx(InlineSelect, { choices: activePrompt.choices, message: activePrompt.message, onSelect: handleSelectResponse }));
|
|
396
|
+
}
|
|
397
|
+
default: {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}, [
|
|
402
|
+
activePrompt,
|
|
403
|
+
handleConfirmResponse,
|
|
404
|
+
handleFileSelectorResponse,
|
|
405
|
+
handleInputResponse,
|
|
406
|
+
handleSearchResponse,
|
|
407
|
+
handleSelectResponse,
|
|
408
|
+
]);
|
|
409
|
+
// Render a single message item
|
|
410
|
+
// For the last command message during streaming, show live streaming output
|
|
411
|
+
const renderMessageItem = useCallback((msg, index) => {
|
|
412
|
+
if (msg.type === 'command') {
|
|
413
|
+
const hasOutput = msg.output && msg.output.length > 0;
|
|
414
|
+
const isLastMessage = index === messages.length - 1;
|
|
415
|
+
const showLiveOutput = isLastMessage && (isStreaming || activePrompt) && (streamingMessages.length > 0 || activePrompt);
|
|
416
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, width: "100%", children: [_jsx(Box, { borderBottom: false, borderLeftColor: colors.primary, borderRight: false, borderStyle: "bold", borderTop: false, paddingLeft: 1, children: _jsxs(Text, { color: colors.text, dimColor: true, children: [msg.fromCommand, " ", _jsx(Text, { wrap: "truncate-end", children: msg.content })] }) }), hasOutput &&
|
|
417
|
+
(() => {
|
|
418
|
+
const processedOutput = processMessagesForActions(msg.output);
|
|
419
|
+
const { displayMessages, skippedLines } = getMessagesFromEnd(processedOutput, maxOutputLines);
|
|
420
|
+
return (_jsxs(Box, { borderColor: colors.border, borderStyle: "round", flexDirection: "column", marginTop: 0, paddingX: 1, width: "100%", children: [skippedLines > 0 && (_jsxs(Text, { color: colors.secondary, dimColor: true, children: ["\u2191 ", skippedLines, " more lines above"] })), displayMessages.map((streamMsg) => renderStreamingMessage(streamMsg))] }));
|
|
421
|
+
})(), showLiveOutput &&
|
|
422
|
+
(() => {
|
|
423
|
+
const { displayMessages: liveMessages, skippedLines } = getMessagesFromEnd(processedStreamingMessages, maxOutputLines);
|
|
424
|
+
return (_jsxs(Box, { borderColor: colors.border, borderStyle: "round", flexDirection: "column", paddingX: 1, paddingY: 0, width: "100%", children: [skippedLines > 0 && (_jsxs(Text, { color: colors.secondary, dimColor: true, children: ["\u2191 ", skippedLines, " more lines above"] })), liveMessages.map((streamMsg) => renderStreamingMessage(streamMsg)), isStreaming && !activePrompt && msg.fromCommand.startsWith('/query') && (_jsxs(Text, { color: colors.dimText, children: [_jsx(Spinner, { type: "dots" }), " Processing..."] })), renderActivePrompt()] }));
|
|
425
|
+
})()] }));
|
|
426
|
+
}
|
|
427
|
+
return _jsx(MessageItem, { message: msg });
|
|
428
|
+
}, [
|
|
429
|
+
activePrompt,
|
|
430
|
+
colors,
|
|
431
|
+
isStreaming,
|
|
432
|
+
maxOutputLines,
|
|
433
|
+
messages.length,
|
|
434
|
+
processedStreamingMessages,
|
|
435
|
+
renderActivePrompt,
|
|
436
|
+
renderStreamingMessage,
|
|
437
|
+
]);
|
|
438
|
+
const keyExtractor = useCallback((_msg, index) => `msg-${index}`, []);
|
|
439
|
+
// Height estimator that accounts for live streaming output and prompts
|
|
440
|
+
const heightEstimator = useCallback((msg, index) => {
|
|
441
|
+
const isLast = index === messages.length - 1;
|
|
442
|
+
const isLive = isLast && (isStreaming || activePrompt);
|
|
443
|
+
return estimateMessageHeight(msg, {
|
|
444
|
+
isLast,
|
|
445
|
+
maxOutputLines,
|
|
446
|
+
promptHeight: isLive ? estimatePromptHeight(activePrompt) : 0,
|
|
447
|
+
streamingLines: isLive ? streamingMessages.length : 0,
|
|
448
|
+
});
|
|
449
|
+
}, [activePrompt, isStreaming, maxOutputLines, messages.length, streamingMessages.length]);
|
|
450
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", width: "100%", children: [messages.length > 0 ? (_jsx(Box, { flexDirection: "column", flexGrow: 1, paddingX: 2, children: _jsx(ScrollableList, { autoScrollToBottom: true, availableHeight: scrollableHeight, estimateItemHeight: heightEstimator, isActive: mode === 'console' && !activePrompt && !isStreaming, items: messages, keyExtractor: keyExtractor, renderItem: renderMessageItem }) })) : (_jsx(Spacer, {})), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [!isStreaming && !activePrompt && (_jsx(Suggestions, { input: command, onInsert: handleInsert, onSelect: handleSelect })), _jsxs(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", paddingX: 2, children: [_jsx(Text, { color: colors.primary, children: '> ' }), _jsx(TextInput, { focus: !activePrompt && (mode === 'console' || mode === 'suggestions'), onChange: setCommand, onSubmit: handleSubmit, placeholder: isStreaming ? 'Command running...' : 'Use / to view commands', value: command }, inputKey)] })] })] }));
|
|
451
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Login View
|
|
4
|
+
*
|
|
5
|
+
* Three states:
|
|
6
|
+
* 1. Idle: Prompt user to login
|
|
7
|
+
* 2. Loading: Show spinner and output
|
|
8
|
+
* 3. Completed: Show output and prompt to continue
|
|
9
|
+
*/
|
|
10
|
+
import { Box, Text } from 'ink';
|
|
11
|
+
import Spinner from 'ink-spinner';
|
|
12
|
+
import { useState } from 'react';
|
|
13
|
+
import { EnterPrompt, OutputLog } from '../components/index.js';
|
|
14
|
+
import { useAuth, useConsumer } from '../contexts/index.js';
|
|
15
|
+
import { useTheme } from '../contexts/use-theme.js';
|
|
16
|
+
export const LoginView = () => {
|
|
17
|
+
const { theme } = useTheme();
|
|
18
|
+
const { isLoggingIn, login, loginOutput, reloadAuth } = useAuth();
|
|
19
|
+
const { restart } = useConsumer();
|
|
20
|
+
const [hasStartedLogin, setHasStartedLogin] = useState(false);
|
|
21
|
+
// Derive state from props and local state
|
|
22
|
+
const state = isLoggingIn ? 'loading' : hasStartedLogin ? 'completed' : 'idle';
|
|
23
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingTop: 1, children: [state === 'idle' && _jsx(Text, { children: "You are not logged in." }), state === 'loading' && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: "Logging in..." })] })), loginOutput.length > 0 && _jsx(OutputLog, { lines: loginOutput, logColor: theme.colors.text }), state === 'idle' && (_jsx(EnterPrompt, { action: "login", onEnter: () => {
|
|
24
|
+
login();
|
|
25
|
+
setHasStartedLogin(true);
|
|
26
|
+
} })), state === 'completed' && _jsx(EnterPrompt, { action: "continue", onEnter: async () => {
|
|
27
|
+
await reloadAuth();
|
|
28
|
+
await restart();
|
|
29
|
+
} })] }));
|
|
30
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logs View
|
|
3
|
+
*
|
|
4
|
+
* Activity log display using ScrollableList with dynamic height calculation
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
interface LogsViewProps {
|
|
8
|
+
availableHeight: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const LogsView: React.FC<LogsViewProps>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Logs View
|
|
4
|
+
*
|
|
5
|
+
* Activity log display using ScrollableList with dynamic height calculation
|
|
6
|
+
*/
|
|
7
|
+
import { Box, Text } from 'ink';
|
|
8
|
+
import { useCallback } from 'react';
|
|
9
|
+
import { LogItem, OnboardingFlow, ScrollableList } from '../components/index.js';
|
|
10
|
+
import { useActivityLogs, useMode, useTheme } from '../hooks/index.js';
|
|
11
|
+
import { useOnboarding } from '../hooks/use-onboarding.js';
|
|
12
|
+
const MAX_PROGRESS_ITEMS = 3;
|
|
13
|
+
/** Minimum content lines to show before truncation */
|
|
14
|
+
const MIN_CONTENT_LINES = 5;
|
|
15
|
+
/** Reserved lines for log item (header + input box + progress + margins) */
|
|
16
|
+
const LOG_ITEM_OVERHEAD = 6;
|
|
17
|
+
/**
|
|
18
|
+
* Estimate the line height of a log item
|
|
19
|
+
*/
|
|
20
|
+
function estimateLogHeight(log, maxContentLines) {
|
|
21
|
+
let lines = 0;
|
|
22
|
+
// Header line: [type] @source [timestamp]
|
|
23
|
+
lines += 1;
|
|
24
|
+
// Input box with border (top border + content + bottom border)
|
|
25
|
+
lines += 3;
|
|
26
|
+
// Progress items (max MAX_PROGRESS_ITEMS shown + optional "more" line)
|
|
27
|
+
const progressCount = Math.min(log.progress?.length ?? 0, MAX_PROGRESS_ITEMS);
|
|
28
|
+
lines += progressCount;
|
|
29
|
+
if ((log.progress?.length ?? 0) > MAX_PROGRESS_ITEMS) {
|
|
30
|
+
lines += 1; // "... and X more" line
|
|
31
|
+
}
|
|
32
|
+
// Processing spinner (if running)
|
|
33
|
+
if (log.status === 'running') {
|
|
34
|
+
lines += 1;
|
|
35
|
+
}
|
|
36
|
+
// Content (if completed or failed)
|
|
37
|
+
if (log.status === 'completed' || log.status === 'failed') {
|
|
38
|
+
const contentLineCount = log.content?.split('\n').length ?? 0;
|
|
39
|
+
// Account for truncation: show max lines + 1 for hint if truncated
|
|
40
|
+
const displayedLines = contentLineCount <= maxContentLines ? contentLineCount : maxContentLines + 1;
|
|
41
|
+
lines += displayedLines;
|
|
42
|
+
}
|
|
43
|
+
// Changes sections
|
|
44
|
+
if (log.status === 'completed') {
|
|
45
|
+
if (log.changes.created.length > 0) {
|
|
46
|
+
lines += 1 + log.changes.created.length;
|
|
47
|
+
}
|
|
48
|
+
if (log.changes.updated.length > 0) {
|
|
49
|
+
lines += 1 + log.changes.updated.length;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Bottom margin
|
|
53
|
+
lines += 1;
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
|
56
|
+
export const LogsView = ({ availableHeight }) => {
|
|
57
|
+
const { theme: { colors }, } = useTheme();
|
|
58
|
+
const { mode } = useMode();
|
|
59
|
+
const { logs } = useActivityLogs();
|
|
60
|
+
const { shouldShowOnboarding } = useOnboarding();
|
|
61
|
+
// Calculate max content lines based on available height
|
|
62
|
+
const scrollableHeight = Math.max(1, availableHeight - 1);
|
|
63
|
+
const maxContentLines = Math.max(MIN_CONTENT_LINES, scrollableHeight - LOG_ITEM_OVERHEAD);
|
|
64
|
+
const renderLogItem = useCallback((log) => _jsx(LogItem, { log: log, maxContentLines: maxContentLines, maxProgressItems: MAX_PROGRESS_ITEMS }), [maxContentLines]);
|
|
65
|
+
const keyExtractor = useCallback((log) => log.id, []);
|
|
66
|
+
// Height estimator that accounts for content truncation
|
|
67
|
+
const heightEstimator = useCallback((log) => estimateLogHeight(log, maxContentLines), [maxContentLines]);
|
|
68
|
+
// Show onboarding when project is not initialized
|
|
69
|
+
if (shouldShowOnboarding) {
|
|
70
|
+
return _jsx(OnboardingFlow, { availableHeight: scrollableHeight });
|
|
71
|
+
}
|
|
72
|
+
return (_jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", borderTop: false, flexDirection: "column", height: "100%", width: "100%", children: logs.length > 0 ? (_jsx(Box, { flexDirection: "column", height: "100%", paddingX: 2, children: _jsx(ScrollableList, { autoScrollToBottom: true, availableHeight: scrollableHeight, estimateItemHeight: heightEstimator, isActive: mode === 'activity', items: logs, keyExtractor: keyExtractor, renderItem: renderLogItem }) })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.primary, children: "Welcome to ByteRover!" }), _jsx(Text, { color: colors.text, children: "Start by telling your AI Agent what to save or retrieve." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, dimColor: true, children: "Press [Tab] to switch to commands view" }) })] })) }));
|
|
73
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate file for --files flag in brv curate command
|
|
3
|
+
* Checks:
|
|
4
|
+
* 1. File exists
|
|
5
|
+
* 2. File is within project directory (where .brv exists)
|
|
6
|
+
* 3. File is text/code file (not binary)
|
|
7
|
+
*
|
|
8
|
+
* @param filePath - The file path to validate (can be relative, absolute, or tilde)
|
|
9
|
+
* @param projectRoot - The project root directory (current working directory)
|
|
10
|
+
* @returns Validation result with normalized path or error message
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateFileForCurate(filePath: string, projectRoot: string): {
|
|
13
|
+
error?: string;
|
|
14
|
+
normalizedPath?: string;
|
|
15
|
+
valid: boolean;
|
|
16
|
+
};
|