byterover-cli 0.3.5 → 0.4.1
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 +8 -1
- 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/welcome.js +10 -26
- 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 +28 -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 +288 -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} +197 -233
- package/dist/infra/usecase/login-use-case.d.ts +28 -0
- package/dist/infra/usecase/login-use-case.js +94 -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 +402 -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 +54 -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,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution Components
|
|
3
|
+
*
|
|
4
|
+
* Reusable components for displaying execution state (tool calls, progress, content).
|
|
5
|
+
*/
|
|
6
|
+
export { ExecutionChanges } from './execution-changes.js';
|
|
7
|
+
export { ExecutionContent, truncateContent } from './execution-content.js';
|
|
8
|
+
export { ExecutionInput } from './execution-input.js';
|
|
9
|
+
export { ExecutionProgress } from './execution-progress.js';
|
|
10
|
+
export { ExecutionStatus } from './execution-status.js';
|
|
11
|
+
export { LogItem } from './log-item.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Item Component
|
|
3
|
+
*
|
|
4
|
+
* Displays a single activity log entry with header, input, progress, content, and changes.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { ActivityLog } from '../../types.js';
|
|
8
|
+
interface LogItemProps {
|
|
9
|
+
/** The activity log to display */
|
|
10
|
+
log: ActivityLog;
|
|
11
|
+
/** Maximum number of content lines before truncation */
|
|
12
|
+
maxContentLines: number;
|
|
13
|
+
/** Maximum number of progress items to show (default: 3) */
|
|
14
|
+
maxProgressItems?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const LogItem: React.FC<LogItemProps>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Log Item Component
|
|
4
|
+
*
|
|
5
|
+
* Displays a single activity log entry with header, input, progress, content, and changes.
|
|
6
|
+
*/
|
|
7
|
+
import { Box, Spacer, Text } from 'ink';
|
|
8
|
+
import { useTheme } from '../../hooks/index.js';
|
|
9
|
+
import { ExecutionChanges } from './execution-changes.js';
|
|
10
|
+
import { ExecutionContent } from './execution-content.js';
|
|
11
|
+
import { ExecutionInput } from './execution-input.js';
|
|
12
|
+
import { ExecutionProgress } from './execution-progress.js';
|
|
13
|
+
import { ExecutionStatus } from './execution-status.js';
|
|
14
|
+
/** Default maximum number of visible progress items */
|
|
15
|
+
const DEFAULT_MAX_PROGRESS_ITEMS = 3;
|
|
16
|
+
export const LogItem = ({ log, maxContentLines, maxProgressItems = DEFAULT_MAX_PROGRESS_ITEMS, }) => {
|
|
17
|
+
const { theme: { colors }, } = useTheme();
|
|
18
|
+
// Format timestamp as local time HH:MM:SS
|
|
19
|
+
const hours = log.timestamp.getHours().toString().padStart(2, '0');
|
|
20
|
+
const minutes = log.timestamp.getMinutes().toString().padStart(2, '0');
|
|
21
|
+
const seconds = log.timestamp.getSeconds().toString().padStart(2, '0');
|
|
22
|
+
const displayTime = `${hours}:${minutes}:${seconds}`;
|
|
23
|
+
const displayTools = Math.min(log.progress?.length ?? 0, maxProgressItems) + 1;
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: [_jsxs(Box, { children: [_jsxs(Text, { color: log.type === 'curate' ? colors.curateCommand : colors.queryCommand, children: ["[", log.type, "] "] }), _jsxs(Text, { color: colors.dimText, children: ["@", log.source ?? 'system'] }), _jsx(Spacer, {}), _jsxs(Text, { color: colors.dimText, children: ["[", displayTime, "]"] })] }), _jsx(ExecutionInput, { input: log.input }), _jsxs(Box, { flexDirection: "column", children: [log.progress && _jsx(ExecutionProgress, { maxItems: maxProgressItems, progress: log.progress }), _jsx(ExecutionStatus, { status: log.status })] }), (log.status === 'failed' || log.status === 'completed') && (_jsx(ExecutionContent, { content: log.content ?? '', isError: log.status === 'failed', maxLines: maxContentLines - displayTools })), log.status === 'completed' && _jsx(ExecutionChanges, { created: log.changes.created, updated: log.changes.updated })] }));
|
|
25
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Footer Component - Dynamic based on active tab
|
|
4
|
+
*/
|
|
5
|
+
import { Box, Text } from 'ink';
|
|
6
|
+
import { useMode } from '../contexts/use-mode.js';
|
|
7
|
+
import { useTheme } from '../contexts/use-theme.js';
|
|
8
|
+
export const Footer = () => {
|
|
9
|
+
const { shortcuts } = useMode();
|
|
10
|
+
const { theme: { colors } } = useTheme();
|
|
11
|
+
return (_jsx(Box, { gap: 1, paddingX: 1, width: "100%", children: shortcuts.map((shortcut, index) => (_jsxs(Box, { children: [index > 0 && _jsx(Text, { color: colors.dimText, children: " \u2022 " }), _jsx(Text, { color: colors.text, children: shortcut.key }), _jsxs(Text, { color: colors.dimText, children: [" ", shortcut.description] })] }, shortcut.key))) }));
|
|
12
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Component
|
|
3
|
+
*
|
|
4
|
+
* Sticky header with:
|
|
5
|
+
* - Adaptive ASCII/Text Logo (based on terminal size)
|
|
6
|
+
* - Connected agent status
|
|
7
|
+
* - Queue stats (pending/processing)
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import type { QueueStats } from '../types.js';
|
|
11
|
+
interface HeaderProps {
|
|
12
|
+
compact?: boolean;
|
|
13
|
+
connectedAgent?: string;
|
|
14
|
+
queueStats?: QueueStats;
|
|
15
|
+
showQueueStats?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const Header: React.FC<HeaderProps>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Header Component
|
|
4
|
+
*
|
|
5
|
+
* Sticky header with:
|
|
6
|
+
* - Adaptive ASCII/Text Logo (based on terminal size)
|
|
7
|
+
* - Connected agent status
|
|
8
|
+
* - Queue stats (pending/processing)
|
|
9
|
+
*/
|
|
10
|
+
import { Box, Spacer, Text } from 'ink';
|
|
11
|
+
import { useServices } from '../contexts/index.js';
|
|
12
|
+
import { useTheme } from '../contexts/use-theme.js';
|
|
13
|
+
import { Logo } from './logo.js';
|
|
14
|
+
export const Header = ({ compact, connectedAgent, queueStats, showQueueStats }) => {
|
|
15
|
+
const { version } = useServices();
|
|
16
|
+
const { theme } = useTheme();
|
|
17
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [_jsx(Logo, { compact: compact, version: version }), _jsxs(Box, { justifyContent: "space-between", children: [_jsx(Box, { gap: 2, children: connectedAgent && (_jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u25CF " }), _jsx(Text, { color: "gray", children: connectedAgent })] })) }), showQueueStats && (_jsxs(Box, { paddingRight: 1, children: [_jsx(Spacer, {}), _jsx(Text, { children: "~ in queue: " }), _jsx(Text, { color: theme.colors.warning, children: queueStats?.pending ?? 0 }), _jsx(Text, { children: " | running: " }), _jsx(Text, { color: theme.colors.primary, children: queueStats?.processing ?? 0 })] }))] })] }));
|
|
18
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Components
|
|
3
|
+
*/
|
|
4
|
+
export { EnterPrompt } from './enter-prompt.js';
|
|
5
|
+
export { ExecutionChanges, ExecutionContent, ExecutionInput, ExecutionProgress, ExecutionStatus, LogItem, truncateContent, } from './execution/index.js';
|
|
6
|
+
export { Footer } from './footer.js';
|
|
7
|
+
export { Header } from './header.js';
|
|
8
|
+
export { Logo } from './logo.js';
|
|
9
|
+
export type { LogoVariant } from './logo.js';
|
|
10
|
+
export { MessageItem } from './message-item.js';
|
|
11
|
+
export { CopyablePrompt, OnboardingFlow, OnboardingStep } from './onboarding/index.js';
|
|
12
|
+
export type { OnboardingStepType } from './onboarding/index.js';
|
|
13
|
+
export { OutputLog } from './output-log.js';
|
|
14
|
+
export { ScrollableList } from './scrollable-list.js';
|
|
15
|
+
export type { ScrollableListProps } from './scrollable-list.js';
|
|
16
|
+
export { Suggestions } from './suggestions.js';
|
|
17
|
+
export { TabBar } from './tab-bar.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Components
|
|
3
|
+
*/
|
|
4
|
+
export { EnterPrompt } from './enter-prompt.js';
|
|
5
|
+
export { ExecutionChanges, ExecutionContent, ExecutionInput, ExecutionProgress, ExecutionStatus, LogItem, truncateContent, } from './execution/index.js';
|
|
6
|
+
export { Footer } from './footer.js';
|
|
7
|
+
export { Header } from './header.js';
|
|
8
|
+
export { Logo } from './logo.js';
|
|
9
|
+
export { MessageItem } from './message-item.js';
|
|
10
|
+
export { CopyablePrompt, OnboardingFlow, OnboardingStep } from './onboarding/index.js';
|
|
11
|
+
export { OutputLog } from './output-log.js';
|
|
12
|
+
export { ScrollableList } from './scrollable-list.js';
|
|
13
|
+
export { Suggestions } from './suggestions.js';
|
|
14
|
+
export { TabBar } from './tab-bar.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline Prompt Components
|
|
3
|
+
*
|
|
4
|
+
* Generic reusable prompt components for TUI streaming commands.
|
|
5
|
+
*/
|
|
6
|
+
export { InlineConfirm } from './inline-confirm.js';
|
|
7
|
+
export type { InlineConfirmProps } from './inline-confirm.js';
|
|
8
|
+
export { InlineFileSelector } from './inline-file-selector.js';
|
|
9
|
+
export type { InlineFileSelectorProps } from './inline-file-selector.js';
|
|
10
|
+
export { InlineInput } from './inline-input.js';
|
|
11
|
+
export type { InlineInputProps } from './inline-input.js';
|
|
12
|
+
export { InlineSearch } from './inline-search.js';
|
|
13
|
+
export type { InlineSearchProps } from './inline-search.js';
|
|
14
|
+
export { InlineSelect } from './inline-select.js';
|
|
15
|
+
export type { InlineSelectProps } from './inline-select.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline Prompt Components
|
|
3
|
+
*
|
|
4
|
+
* Generic reusable prompt components for TUI streaming commands.
|
|
5
|
+
*/
|
|
6
|
+
export { InlineConfirm } from './inline-confirm.js';
|
|
7
|
+
export { InlineFileSelector } from './inline-file-selector.js';
|
|
8
|
+
export { InlineInput } from './inline-input.js';
|
|
9
|
+
export { InlineSearch } from './inline-search.js';
|
|
10
|
+
export { InlineSelect } from './inline-select.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineConfirm Component
|
|
3
|
+
*
|
|
4
|
+
* Generic inline yes/no confirmation prompt.
|
|
5
|
+
* Shows (Y/n) or (N/y) based on default value.
|
|
6
|
+
* User types y/n and presses Enter to submit.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
export interface InlineConfirmProps {
|
|
10
|
+
/** Default value (default: true = Yes) */
|
|
11
|
+
default?: boolean;
|
|
12
|
+
/** The prompt message */
|
|
13
|
+
message: string;
|
|
14
|
+
/** Callback when user confirms or cancels */
|
|
15
|
+
onConfirm: (value: boolean) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function InlineConfirm({ default: defaultValue, message, onConfirm, }: InlineConfirmProps): React.ReactElement;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* InlineConfirm Component
|
|
4
|
+
*
|
|
5
|
+
* Generic inline yes/no confirmation prompt.
|
|
6
|
+
* Shows (Y/n) or (N/y) based on default value.
|
|
7
|
+
* User types y/n and presses Enter to submit.
|
|
8
|
+
*/
|
|
9
|
+
import { Box, Text } from 'ink';
|
|
10
|
+
import TextInput from 'ink-text-input';
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { useTheme } from '../../hooks/index.js';
|
|
13
|
+
export function InlineConfirm({ default: defaultValue = true, message, onConfirm, }) {
|
|
14
|
+
const { theme: { colors }, } = useTheme();
|
|
15
|
+
const [input, setInput] = useState('');
|
|
16
|
+
// Format hint based on default: (Y/n) or (N/y)
|
|
17
|
+
const hint = defaultValue ? '(Y/n)' : '(N/y)';
|
|
18
|
+
const handleSubmit = (value) => {
|
|
19
|
+
const trimmed = value.trim().toLowerCase();
|
|
20
|
+
if (trimmed === 'y' || trimmed === 'yes') {
|
|
21
|
+
onConfirm(true);
|
|
22
|
+
}
|
|
23
|
+
else if (trimmed === 'n' || trimmed === 'no') {
|
|
24
|
+
onConfirm(false);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Empty or invalid input uses default
|
|
28
|
+
onConfirm(defaultValue);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "? " }), _jsxs(Text, { color: colors.text, children: [message, " "] }), _jsxs(Text, { color: colors.secondary, children: [hint, " "] }), _jsx(TextInput, { onChange: setInput, onSubmit: handleSubmit, value: input })] }));
|
|
32
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineFileSelector Component
|
|
3
|
+
*
|
|
4
|
+
* Tree-view file/directory selector similar to inquirer-file-selector.
|
|
5
|
+
*
|
|
6
|
+
* Two modes:
|
|
7
|
+
* - 'file': Select files only, folders are for navigation
|
|
8
|
+
* - 'directory': Select and list folders only
|
|
9
|
+
*
|
|
10
|
+
* Navigation:
|
|
11
|
+
* - Up/Down arrows: Move selection
|
|
12
|
+
* - Right arrow: Navigate into folder
|
|
13
|
+
* - Enter: Select current item (only works when item matches mode)
|
|
14
|
+
* - Left arrow/Backspace: Go up one directory level
|
|
15
|
+
* - Escape: Cancel selection (if allowCancel is true)
|
|
16
|
+
*
|
|
17
|
+
* Features:
|
|
18
|
+
* - Path breadcrumb showing current location
|
|
19
|
+
* - Sliding window for long lists
|
|
20
|
+
* - Filter support
|
|
21
|
+
* - Cannot navigate above basePath
|
|
22
|
+
*/
|
|
23
|
+
import React from 'react';
|
|
24
|
+
import type { FileSelectorItemResult } from '../../types.js';
|
|
25
|
+
/** Selection mode for the file selector */
|
|
26
|
+
export type FileSelectorMode = 'directory' | 'file';
|
|
27
|
+
export interface InlineFileSelectorProps {
|
|
28
|
+
/** Allow user to cancel selection */
|
|
29
|
+
allowCancel?: boolean;
|
|
30
|
+
/** Base path to start from (cannot navigate above this) */
|
|
31
|
+
basePath: string;
|
|
32
|
+
/** Filter function to show/hide items (applied after mode filter) */
|
|
33
|
+
filter?: (item: FileSelectorItemResult) => boolean;
|
|
34
|
+
/** The prompt message */
|
|
35
|
+
message: string;
|
|
36
|
+
/** Selection mode: 'file' or 'directory' (default: 'file') */
|
|
37
|
+
mode?: FileSelectorMode;
|
|
38
|
+
/** Callback when user selects or cancels */
|
|
39
|
+
onSelect: (item: FileSelectorItemResult | null) => void;
|
|
40
|
+
/** Number of items visible at once */
|
|
41
|
+
pageSize?: number;
|
|
42
|
+
}
|
|
43
|
+
export declare function InlineFileSelector({ allowCancel, basePath, filter, message, mode, onSelect, pageSize, }: InlineFileSelectorProps): React.ReactElement;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* InlineFileSelector Component
|
|
4
|
+
*
|
|
5
|
+
* Tree-view file/directory selector similar to inquirer-file-selector.
|
|
6
|
+
*
|
|
7
|
+
* Two modes:
|
|
8
|
+
* - 'file': Select files only, folders are for navigation
|
|
9
|
+
* - 'directory': Select and list folders only
|
|
10
|
+
*
|
|
11
|
+
* Navigation:
|
|
12
|
+
* - Up/Down arrows: Move selection
|
|
13
|
+
* - Right arrow: Navigate into folder
|
|
14
|
+
* - Enter: Select current item (only works when item matches mode)
|
|
15
|
+
* - Left arrow/Backspace: Go up one directory level
|
|
16
|
+
* - Escape: Cancel selection (if allowCancel is true)
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Path breadcrumb showing current location
|
|
20
|
+
* - Sliding window for long lists
|
|
21
|
+
* - Filter support
|
|
22
|
+
* - Cannot navigate above basePath
|
|
23
|
+
*/
|
|
24
|
+
import { Box, Text, useInput } from 'ink';
|
|
25
|
+
import fs from 'node:fs';
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
28
|
+
import { useTheme } from '../../hooks/index.js';
|
|
29
|
+
const DEFAULT_PAGE_SIZE = 7;
|
|
30
|
+
export function InlineFileSelector({ allowCancel = false, basePath, filter, message, mode = 'file', onSelect, pageSize = DEFAULT_PAGE_SIZE, }) {
|
|
31
|
+
const { theme: { colors }, } = useTheme();
|
|
32
|
+
const [currentPath, setCurrentPath] = useState(basePath);
|
|
33
|
+
const [items, setItems] = useState([]);
|
|
34
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
35
|
+
const [windowStart, setWindowStart] = useState(0);
|
|
36
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
37
|
+
// Normalize paths for comparison
|
|
38
|
+
const normalizedBasePath = useMemo(() => path.resolve(basePath), [basePath]);
|
|
39
|
+
const normalizedCurrentPath = useMemo(() => path.resolve(currentPath), [currentPath]);
|
|
40
|
+
// Check if we can go up (not at basePath)
|
|
41
|
+
const canGoUp = normalizedCurrentPath !== normalizedBasePath;
|
|
42
|
+
// Load directory contents
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const loadItems = async () => {
|
|
45
|
+
setIsLoading(true);
|
|
46
|
+
try {
|
|
47
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
48
|
+
let fileItems = entries.map((entry) => ({
|
|
49
|
+
isDirectory: entry.isDirectory(),
|
|
50
|
+
name: entry.name,
|
|
51
|
+
path: path.join(currentPath, entry.name),
|
|
52
|
+
}));
|
|
53
|
+
// In directory mode, only show directories
|
|
54
|
+
if (mode === 'directory') {
|
|
55
|
+
fileItems = fileItems.filter((item) => item.isDirectory);
|
|
56
|
+
}
|
|
57
|
+
// Apply custom filter
|
|
58
|
+
if (filter) {
|
|
59
|
+
fileItems = fileItems.filter((item) => filter(item));
|
|
60
|
+
}
|
|
61
|
+
// Sort: directories first, then alphabetically
|
|
62
|
+
fileItems.sort((a, b) => {
|
|
63
|
+
if (a.isDirectory !== b.isDirectory) {
|
|
64
|
+
return a.isDirectory ? -1 : 1;
|
|
65
|
+
}
|
|
66
|
+
return a.name.localeCompare(b.name);
|
|
67
|
+
});
|
|
68
|
+
// Add "." for selecting current folder (in directory mode)
|
|
69
|
+
if (mode === 'directory') {
|
|
70
|
+
fileItems.unshift({
|
|
71
|
+
isDirectory: true,
|
|
72
|
+
name: '.',
|
|
73
|
+
path: currentPath,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
setItems(fileItems);
|
|
77
|
+
setSelectedIndex(0);
|
|
78
|
+
setWindowStart(0);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Handle permission errors or other issues
|
|
82
|
+
setItems([]);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
setIsLoading(false);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
loadItems();
|
|
89
|
+
}, [currentPath, filter, mode]);
|
|
90
|
+
// Calculate visible items (sliding window)
|
|
91
|
+
const visibleItems = useMemo(() => items.slice(windowStart, windowStart + pageSize), [items, windowStart, pageSize]);
|
|
92
|
+
// Check if selected item can be selected based on mode
|
|
93
|
+
const canSelectItem = (item) => {
|
|
94
|
+
if (!item)
|
|
95
|
+
return false;
|
|
96
|
+
if (mode === 'file')
|
|
97
|
+
return !item.isDirectory;
|
|
98
|
+
if (mode === 'directory')
|
|
99
|
+
return item.isDirectory;
|
|
100
|
+
return false;
|
|
101
|
+
};
|
|
102
|
+
// Handle Enter key selection
|
|
103
|
+
const handleEnterKey = () => {
|
|
104
|
+
const selected = items[selectedIndex];
|
|
105
|
+
// In directory mode with empty folder, select current path
|
|
106
|
+
if (mode === 'directory' && items.length === 0) {
|
|
107
|
+
onSelect({
|
|
108
|
+
isDirectory: true,
|
|
109
|
+
name: path.basename(currentPath),
|
|
110
|
+
path: currentPath,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Select current folder on "." selection (directory mode)
|
|
115
|
+
if (selected?.name === '.' && mode === 'directory') {
|
|
116
|
+
onSelect(selected);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Navigate into folder (same as right arrow)
|
|
120
|
+
if (selected?.isDirectory) {
|
|
121
|
+
setCurrentPath(selected.path);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Select file (in file mode)
|
|
125
|
+
if (canSelectItem(selected)) {
|
|
126
|
+
onSelect(selected);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
// Handle keyboard input
|
|
130
|
+
useInput((_input, key) => {
|
|
131
|
+
if (key.upArrow) {
|
|
132
|
+
setSelectedIndex((prev) => {
|
|
133
|
+
const newIndex = Math.max(0, prev - 1);
|
|
134
|
+
// Adjust window if selection goes above visible area
|
|
135
|
+
if (newIndex < windowStart) {
|
|
136
|
+
setWindowStart(newIndex);
|
|
137
|
+
}
|
|
138
|
+
return newIndex;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else if (key.downArrow) {
|
|
142
|
+
setSelectedIndex((prev) => {
|
|
143
|
+
const newIndex = Math.min(items.length - 1, prev + 1);
|
|
144
|
+
// Adjust window if selection goes below visible area
|
|
145
|
+
if (newIndex >= windowStart + pageSize) {
|
|
146
|
+
setWindowStart(newIndex - pageSize + 1);
|
|
147
|
+
}
|
|
148
|
+
return newIndex;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else if (key.rightArrow) {
|
|
152
|
+
// Navigate into directory
|
|
153
|
+
const selected = items[selectedIndex];
|
|
154
|
+
if (selected?.isDirectory && selected.name !== '.') {
|
|
155
|
+
setCurrentPath(selected.path);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else if (key.return) {
|
|
159
|
+
handleEnterKey();
|
|
160
|
+
}
|
|
161
|
+
else if (key.backspace || key.leftArrow) {
|
|
162
|
+
// Go up one level (if not at basePath)
|
|
163
|
+
if (canGoUp) {
|
|
164
|
+
setCurrentPath(path.dirname(currentPath));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (key.escape && allowCancel) {
|
|
168
|
+
onSelect(null);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
// Get relative path for display
|
|
172
|
+
const displayPath = normalizedCurrentPath.replace(process.cwd(), '') || '.';
|
|
173
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: colors.text, children: [_jsx(Text, { color: colors.secondary, children: "? " }), message] }), _jsx(Text, { color: colors.primary, children: displayPath }), _jsx(Box, { borderBottom: false, borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", borderTop: true, flexDirection: "column", children: isLoading ? (_jsx(Text, { color: colors.dimText, children: "Loading..." })) : visibleItems.length === 0 ? (_jsx(Text, { color: colors.warning, children: mode === 'directory' ? '(empty folder) Press Enter to select this location' : '(no files)' })) : (visibleItems.map((item, index) => {
|
|
174
|
+
const actualIndex = windowStart + index;
|
|
175
|
+
const isSelected = actualIndex === selectedIndex;
|
|
176
|
+
const icon = index === visibleItems.length - 1 ? '└──' : '├──';
|
|
177
|
+
const suffix = item.isDirectory && item.name !== '.' ? '/' : '';
|
|
178
|
+
const isSelectable = canSelectItem(item) || item.name === '.';
|
|
179
|
+
// Dim items that can't be selected (except "." for folder selection)
|
|
180
|
+
const itemColor = isSelected ? colors.primary : isSelectable ? colors.text : colors.dimText;
|
|
181
|
+
// Display label for "."
|
|
182
|
+
const displayName = item.name === '.' ? '. (select this folder)' : item.name;
|
|
183
|
+
return (_jsxs(Text, { color: itemColor, children: [isSelected ? '❯ ' : ' ', icon, " ", displayName, suffix] }, item.path));
|
|
184
|
+
})) }), items.length > pageSize && (_jsxs(Text, { color: colors.dimText, dimColor: true, children: [windowStart > 0 ? '↑ ' : ' ', selectedIndex + 1, "/", items.length, windowStart + pageSize < items.length ? ' ↓' : ' '] })), _jsxs(Text, { dimColor: true, children: ['[↑↓] navigate [→] open [←] up [Enter] select', mode === 'file' ? ' file' : ' folder', allowCancel ? ' [Esc] cancel' : ''] })] }));
|
|
185
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineInput Component
|
|
3
|
+
*
|
|
4
|
+
* Generic inline text input prompt with validation.
|
|
5
|
+
* Shows validation errors below input.
|
|
6
|
+
* User types and presses Enter to submit.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
export interface InlineInputProps {
|
|
10
|
+
/** The prompt message */
|
|
11
|
+
message: string;
|
|
12
|
+
/** Callback when user submits valid input */
|
|
13
|
+
onSubmit: (value: string) => void;
|
|
14
|
+
/** Placeholder text */
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
/** Validation function - return true if valid, or error message string */
|
|
17
|
+
validate?: (value: string) => boolean | string;
|
|
18
|
+
}
|
|
19
|
+
export declare function InlineInput({ message, onSubmit, placeholder, validate, }: InlineInputProps): React.ReactElement;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* InlineInput Component
|
|
4
|
+
*
|
|
5
|
+
* Generic inline text input prompt with validation.
|
|
6
|
+
* Shows validation errors below input.
|
|
7
|
+
* User types and presses Enter to submit.
|
|
8
|
+
*/
|
|
9
|
+
import { Box, Text } from 'ink';
|
|
10
|
+
import TextInput from 'ink-text-input';
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { useTheme } from '../../hooks/index.js';
|
|
13
|
+
export function InlineInput({ message, onSubmit, placeholder, validate, }) {
|
|
14
|
+
const { theme: { colors }, } = useTheme();
|
|
15
|
+
const [value, setValue] = useState('');
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
const handleSubmit = (inputValue) => {
|
|
18
|
+
// Clear previous error
|
|
19
|
+
setError(null);
|
|
20
|
+
// Run validation if provided
|
|
21
|
+
if (validate) {
|
|
22
|
+
const result = validate(inputValue);
|
|
23
|
+
if (result !== true) {
|
|
24
|
+
setError(typeof result === 'string' ? result : 'Invalid input');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Submit valid input
|
|
29
|
+
onSubmit(inputValue);
|
|
30
|
+
};
|
|
31
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "? " }), _jsxs(Text, { color: colors.text, children: [message, " "] }), _jsx(TextInput, { onChange: setValue, onSubmit: handleSubmit, placeholder: placeholder, value: value })] }), error && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: colors.errorText, children: error }) }))] }));
|
|
32
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineSearch Component
|
|
3
|
+
*
|
|
4
|
+
* Generic inline searchable selection prompt.
|
|
5
|
+
* Renders a search input with filterable list of options.
|
|
6
|
+
* Shows max 7 items with a sliding window that follows selection.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { PromptChoice } from '../../types.js';
|
|
10
|
+
export interface InlineSearchProps<T = unknown> {
|
|
11
|
+
/** Maximum number of visible items in the dropdown (default: 7) */
|
|
12
|
+
maxVisibleItems?: number;
|
|
13
|
+
/** The prompt message */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Callback when user selects a value */
|
|
16
|
+
onSelect: (value: T) => void;
|
|
17
|
+
/** Function that returns choices based on search input */
|
|
18
|
+
source: (input: string | undefined) => Array<PromptChoice<T>> | Promise<Array<PromptChoice<T>>>;
|
|
19
|
+
}
|
|
20
|
+
export declare function InlineSearch<T>({ maxVisibleItems, message, onSelect, source }: InlineSearchProps<T>): React.ReactElement;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* InlineSearch Component
|
|
4
|
+
*
|
|
5
|
+
* Generic inline searchable selection prompt.
|
|
6
|
+
* Renders a search input with filterable list of options.
|
|
7
|
+
* Shows max 7 items with a sliding window that follows selection.
|
|
8
|
+
*/
|
|
9
|
+
import { Box, Text, useInput } from 'ink';
|
|
10
|
+
import TextInput from 'ink-text-input';
|
|
11
|
+
import { useEffect, useState } from 'react';
|
|
12
|
+
import { useTheme, useVisibleWindow } from '../../hooks/index.js';
|
|
13
|
+
const MAX_VISIBLE_ITEMS = 7;
|
|
14
|
+
export function InlineSearch({ maxVisibleItems = MAX_VISIBLE_ITEMS, message, onSelect, source }) {
|
|
15
|
+
const { theme: { colors }, } = useTheme();
|
|
16
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
17
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
18
|
+
const [choices, setChoices] = useState([]);
|
|
19
|
+
// Load choices based on search query
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const loadChoices = async () => {
|
|
22
|
+
const result = source(searchQuery || undefined);
|
|
23
|
+
const resolvedChoices = result instanceof Promise ? await result : result;
|
|
24
|
+
setChoices(resolvedChoices);
|
|
25
|
+
};
|
|
26
|
+
loadChoices();
|
|
27
|
+
}, [searchQuery, source]);
|
|
28
|
+
// Reset selected index when choices change
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setSelectedIndex(0);
|
|
31
|
+
}, [choices.length]);
|
|
32
|
+
// Calculate visible window based on selected index
|
|
33
|
+
const { visibleItems: visibleChoices, windowStart } = useVisibleWindow(choices, selectedIndex, maxVisibleItems);
|
|
34
|
+
// Handle keyboard input
|
|
35
|
+
useInput((_input, key) => {
|
|
36
|
+
if (key.upArrow) {
|
|
37
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
38
|
+
}
|
|
39
|
+
else if (key.downArrow) {
|
|
40
|
+
setSelectedIndex((prev) => Math.min(choices.length - 1, prev + 1));
|
|
41
|
+
}
|
|
42
|
+
else if (key.return && choices[selectedIndex]) {
|
|
43
|
+
onSelect(choices[selectedIndex].value);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: colors.text, children: [_jsx(Text, { color: colors.secondary, children: "? " }), message, " ", _jsx(TextInput, { onChange: setSearchQuery, value: searchQuery })] }), _jsxs(Box, { flexDirection: "column", children: [visibleChoices.map((choice, index) => {
|
|
47
|
+
const actualIndex = windowStart + index;
|
|
48
|
+
return (_jsxs(Text, { color: actualIndex === selectedIndex ? colors.primary : colors.text, children: [actualIndex === selectedIndex ? '❯ ' : ' ', choice.name] }, choice.name));
|
|
49
|
+
}), choices.length === 0 && _jsxs(Text, { color: colors.errorText, children: ['> ', "No results found"] })] })] }));
|
|
50
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineSelect Component
|
|
3
|
+
*
|
|
4
|
+
* Generic inline selection prompt with multiple choices.
|
|
5
|
+
* Shows description of selected item below the list.
|
|
6
|
+
* Shows max items with a sliding window that follows selection.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { PromptChoice } from '../../types.js';
|
|
10
|
+
export interface InlineSelectProps<T = unknown> {
|
|
11
|
+
/** Available choices */
|
|
12
|
+
choices: Array<PromptChoice<T>>;
|
|
13
|
+
/** Maximum number of visible items in the list (default: 7) */
|
|
14
|
+
maxVisibleItems?: number;
|
|
15
|
+
/** The prompt message */
|
|
16
|
+
message: string;
|
|
17
|
+
/** Callback when user selects a value */
|
|
18
|
+
onSelect: (value: T) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function InlineSelect<T>({ choices, maxVisibleItems, message, onSelect, }: InlineSelectProps<T>): React.ReactElement;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* InlineSelect Component
|
|
4
|
+
*
|
|
5
|
+
* Generic inline selection prompt with multiple choices.
|
|
6
|
+
* Shows description of selected item below the list.
|
|
7
|
+
* Shows max items with a sliding window that follows selection.
|
|
8
|
+
*/
|
|
9
|
+
import { Box, Text, useInput } from 'ink';
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
import { useTheme, useVisibleWindow } from '../../hooks/index.js';
|
|
12
|
+
export function InlineSelect({ choices, maxVisibleItems = 7, message, onSelect, }) {
|
|
13
|
+
const { theme: { colors }, } = useTheme();
|
|
14
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
15
|
+
const selectedChoice = choices[selectedIndex];
|
|
16
|
+
// Calculate visible window based on selected index
|
|
17
|
+
const { visibleItems: visibleChoices, windowStart } = useVisibleWindow(choices, selectedIndex, maxVisibleItems);
|
|
18
|
+
// Handle keyboard input
|
|
19
|
+
useInput((_input, key) => {
|
|
20
|
+
if (key.upArrow) {
|
|
21
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
22
|
+
}
|
|
23
|
+
else if (key.downArrow) {
|
|
24
|
+
setSelectedIndex((prev) => Math.min(choices.length - 1, prev + 1));
|
|
25
|
+
}
|
|
26
|
+
else if (key.return && selectedChoice) {
|
|
27
|
+
onSelect(selectedChoice.value);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: colors.text, children: [_jsx(Text, { color: colors.secondary, children: "? " }), message] }), _jsx(Box, { flexDirection: "column", children: visibleChoices.map((choice, index) => {
|
|
31
|
+
const actualIndex = windowStart + index;
|
|
32
|
+
return (_jsxs(Text, { color: actualIndex === selectedIndex ? colors.primary : colors.text, children: [actualIndex === selectedIndex ? '❯ ' : ' ', choice.name] }, choice.name));
|
|
33
|
+
}) }), selectedChoice?.description && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.secondary, children: selectedChoice.description }) }))] }));
|
|
34
|
+
}
|