@pentoshi/clai 3.18.2 → 4.0.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 +44 -24
- package/bin/postinstall-policy.mjs +3 -0
- package/bin/postinstall.mjs +6 -3
- package/dist/agent/confirm-port.d.ts +5 -17
- package/dist/agent/confirm-port.js +8 -65
- package/dist/agent/confirm-port.js.map +1 -1
- package/dist/agent/evidence-governor.d.ts +1 -1
- package/dist/agent/evidence-governor.js +2 -2
- package/dist/agent/evidence-governor.js.map +1 -1
- package/dist/agent/loop-guard.d.ts +1 -0
- package/dist/agent/loop-guard.js +34 -1
- package/dist/agent/loop-guard.js.map +1 -1
- package/dist/agent/plan-decision.d.ts +0 -29
- package/dist/agent/plan-decision.js +0 -96
- package/dist/agent/plan-decision.js.map +1 -1
- package/dist/agent/plan-tool.js +19 -14
- package/dist/agent/plan-tool.js.map +1 -1
- package/dist/agent/progress-pause-policy.d.ts +0 -15
- package/dist/agent/progress-pause-policy.js +0 -15
- package/dist/agent/progress-pause-policy.js.map +1 -1
- package/dist/agent/runner.js +152 -392
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/task-evidence.d.ts +6 -0
- package/dist/agent/task-evidence.js +48 -3
- package/dist/agent/task-evidence.js.map +1 -1
- package/dist/agent/tool-history.js +11 -4
- package/dist/agent/tool-history.js.map +1 -1
- package/dist/app/commands/catalog.js +397 -0
- package/dist/app/commands/catalog.js.map +1 -0
- package/dist/app/commands/registry.js +1 -1
- package/dist/app/commands/registry.js.map +1 -1
- package/dist/app/confirm-prompt-text.d.ts +17 -0
- package/dist/app/confirm-prompt-text.js +34 -0
- package/dist/app/confirm-prompt-text.js.map +1 -0
- package/dist/app/controllers/session-controller.d.ts +1 -1
- package/dist/app/controllers/session-controller.js +3 -1
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/app/controllers/session-prompt-queue.d.ts +5 -1
- package/dist/app/controllers/session-prompt-queue.js +47 -30
- package/dist/app/controllers/session-prompt-queue.js.map +1 -1
- package/dist/app/controllers/turn-controller.js +41 -3
- package/dist/app/controllers/turn-controller.js.map +1 -1
- package/dist/app/ports/confirm-port.d.ts +0 -1
- package/dist/app/ports/persistence-port.d.ts +1 -1
- package/dist/app/ports/transcript-item.d.ts +63 -0
- package/dist/app/ports/transcript-item.js +2 -0
- package/dist/app/ports/transcript-item.js.map +1 -0
- package/dist/classic/app/ClassicApp.d.ts +12 -0
- package/dist/classic/app/ClassicApp.js +117 -0
- package/dist/classic/app/ClassicApp.js.map +1 -0
- package/dist/classic/app/action-handlers.d.ts +45 -0
- package/dist/classic/app/action-handlers.js +280 -0
- package/dist/classic/app/action-handlers.js.map +1 -0
- package/dist/classic/app/app-wiring.d.ts +133 -0
- package/dist/classic/app/app-wiring.js +383 -0
- package/dist/classic/app/app-wiring.js.map +1 -0
- package/dist/classic/app/use-feed.d.ts +40 -0
- package/dist/classic/app/use-feed.js +52 -0
- package/dist/classic/app/use-feed.js.map +1 -0
- package/dist/classic/app/wiring-interactions.d.ts +40 -0
- package/dist/classic/app/wiring-interactions.js +430 -0
- package/dist/classic/app/wiring-interactions.js.map +1 -0
- package/dist/classic/app/wiring-lifecycle.d.ts +10 -0
- package/dist/classic/app/wiring-lifecycle.js +240 -0
- package/dist/classic/app/wiring-lifecycle.js.map +1 -0
- package/dist/classic/app/wiring-selection.d.ts +3 -0
- package/dist/classic/app/wiring-selection.js +113 -0
- package/dist/classic/app/wiring-selection.js.map +1 -0
- package/dist/classic/app/wiring-types.d.ts +157 -0
- package/dist/classic/app/wiring-types.js +2 -0
- package/dist/classic/app/wiring-types.js.map +1 -0
- package/dist/classic/blocks/AssistantBlock.d.ts +3 -0
- package/dist/classic/blocks/AssistantBlock.js +6 -0
- package/dist/classic/blocks/AssistantBlock.js.map +1 -0
- package/dist/classic/blocks/BatchBlock.d.ts +3 -0
- package/dist/classic/blocks/BatchBlock.js +6 -0
- package/dist/classic/blocks/BatchBlock.js.map +1 -0
- package/dist/classic/blocks/CompactedBlock.d.ts +3 -0
- package/dist/classic/blocks/CompactedBlock.js +6 -0
- package/dist/classic/blocks/CompactedBlock.js.map +1 -0
- package/dist/classic/blocks/DiffBlock.d.ts +3 -0
- package/dist/classic/blocks/DiffBlock.js +6 -0
- package/dist/classic/blocks/DiffBlock.js.map +1 -0
- package/dist/classic/blocks/IntroBlock.d.ts +3 -0
- package/dist/classic/blocks/IntroBlock.js +6 -0
- package/dist/classic/blocks/IntroBlock.js.map +1 -0
- package/dist/classic/blocks/NoticeBlock.d.ts +3 -0
- package/dist/classic/blocks/NoticeBlock.js +6 -0
- package/dist/classic/blocks/NoticeBlock.js.map +1 -0
- package/dist/classic/blocks/ThinkingBlock.d.ts +3 -0
- package/dist/classic/blocks/ThinkingBlock.js +6 -0
- package/dist/classic/blocks/ThinkingBlock.js.map +1 -0
- package/dist/classic/blocks/ToolBlock.d.ts +3 -0
- package/dist/classic/blocks/ToolBlock.js +6 -0
- package/dist/classic/blocks/ToolBlock.js.map +1 -0
- package/dist/classic/blocks/UserBlock.d.ts +3 -0
- package/dist/classic/blocks/UserBlock.js +6 -0
- package/dist/classic/blocks/UserBlock.js.map +1 -0
- package/dist/classic/blocks/assistant-lines.d.ts +10 -0
- package/dist/classic/blocks/assistant-lines.js +43 -0
- package/dist/classic/blocks/assistant-lines.js.map +1 -0
- package/dist/classic/blocks/batch-lines.d.ts +7 -0
- package/dist/classic/blocks/batch-lines.js +79 -0
- package/dist/classic/blocks/batch-lines.js.map +1 -0
- package/dist/classic/blocks/block-context.d.ts +33 -0
- package/dist/classic/blocks/block-context.js +41 -0
- package/dist/classic/blocks/block-context.js.map +1 -0
- package/dist/classic/blocks/compacted-lines.d.ts +5 -0
- package/dist/classic/blocks/compacted-lines.js +54 -0
- package/dist/classic/blocks/compacted-lines.js.map +1 -0
- package/dist/classic/blocks/diff-lines.d.ts +13 -0
- package/dist/classic/blocks/diff-lines.js +116 -0
- package/dist/classic/blocks/diff-lines.js.map +1 -0
- package/dist/classic/blocks/intro-lines.d.ts +16 -0
- package/dist/classic/blocks/intro-lines.js +23 -0
- package/dist/classic/blocks/intro-lines.js.map +1 -0
- package/dist/classic/blocks/notice-lines.d.ts +7 -0
- package/dist/classic/blocks/notice-lines.js +28 -0
- package/dist/classic/blocks/notice-lines.js.map +1 -0
- package/dist/classic/blocks/thinking-lines.d.ts +5 -0
- package/dist/classic/blocks/thinking-lines.js +34 -0
- package/dist/classic/blocks/thinking-lines.js.map +1 -0
- package/dist/classic/blocks/tool-lines.d.ts +22 -0
- package/dist/classic/blocks/tool-lines.js +135 -0
- package/dist/classic/blocks/tool-lines.js.map +1 -0
- package/dist/classic/blocks/user-lines.d.ts +5 -0
- package/dist/classic/blocks/user-lines.js +26 -0
- package/dist/classic/blocks/user-lines.js.map +1 -0
- package/dist/classic/bootstrap/osc52-renderer.d.ts +10 -0
- package/dist/classic/bootstrap/osc52-renderer.js +38 -0
- package/dist/classic/bootstrap/osc52-renderer.js.map +1 -0
- package/dist/classic/bootstrap/renderer-handle.d.ts +15 -0
- package/dist/classic/bootstrap/renderer-handle.js +22 -0
- package/dist/classic/bootstrap/renderer-handle.js.map +1 -0
- package/dist/classic/bootstrap/start-classic.d.ts +8 -0
- package/dist/classic/bootstrap/start-classic.js +130 -0
- package/dist/classic/bootstrap/start-classic.js.map +1 -0
- package/dist/classic/bootstrap/suspend-port.d.ts +14 -0
- package/dist/classic/bootstrap/suspend-port.js +29 -0
- package/dist/classic/bootstrap/suspend-port.js.map +1 -0
- package/dist/classic/bootstrap/terminal-session.d.ts +58 -0
- package/dist/classic/bootstrap/terminal-session.js +136 -0
- package/dist/classic/bootstrap/terminal-session.js.map +1 -0
- package/dist/classic/chrome/Chrome.d.ts +19 -0
- package/dist/classic/chrome/Chrome.js +30 -0
- package/dist/classic/chrome/Chrome.js.map +1 -0
- package/dist/classic/chrome/Composer.d.ts +12 -0
- package/dist/classic/chrome/Composer.js +61 -0
- package/dist/classic/chrome/Composer.js.map +1 -0
- package/dist/classic/chrome/QueuePanel.d.ts +3 -0
- package/dist/classic/chrome/QueuePanel.js +10 -0
- package/dist/classic/chrome/QueuePanel.js.map +1 -0
- package/dist/classic/chrome/ResponderStrip.d.ts +3 -0
- package/dist/classic/chrome/ResponderStrip.js +9 -0
- package/dist/classic/chrome/ResponderStrip.js.map +1 -0
- package/dist/classic/chrome/StatusBar.d.ts +3 -0
- package/dist/classic/chrome/StatusBar.js +7 -0
- package/dist/classic/chrome/StatusBar.js.map +1 -0
- package/dist/classic/chrome/ToastRow.d.ts +3 -0
- package/dist/classic/chrome/ToastRow.js +10 -0
- package/dist/classic/chrome/ToastRow.js.map +1 -0
- package/dist/classic/chrome/composer-controller.d.ts +57 -0
- package/dist/classic/chrome/composer-controller.js +345 -0
- package/dist/classic/chrome/composer-controller.js.map +1 -0
- package/dist/classic/chrome/composer-frame.d.ts +35 -0
- package/dist/classic/chrome/composer-frame.js +46 -0
- package/dist/classic/chrome/composer-frame.js.map +1 -0
- package/dist/classic/chrome/composer-keys.d.ts +4 -0
- package/dist/classic/chrome/composer-keys.js +45 -0
- package/dist/classic/chrome/composer-keys.js.map +1 -0
- package/dist/classic/chrome/context-limit-editor.d.ts +7 -0
- package/dist/classic/chrome/context-limit-editor.js +13 -0
- package/dist/classic/chrome/context-limit-editor.js.map +1 -0
- package/dist/classic/chrome/directory-row.d.ts +14 -0
- package/dist/classic/chrome/directory-row.js +26 -0
- package/dist/classic/chrome/directory-row.js.map +1 -0
- package/dist/classic/chrome/editor-model.d.ts +37 -0
- package/dist/classic/chrome/editor-model.js +195 -0
- package/dist/classic/chrome/editor-model.js.map +1 -0
- package/dist/classic/chrome/editor-view.d.ts +34 -0
- package/dist/classic/chrome/editor-view.js +133 -0
- package/dist/classic/chrome/editor-view.js.map +1 -0
- package/dist/classic/chrome/queue-rows.d.ts +10 -0
- package/dist/classic/chrome/queue-rows.js +32 -0
- package/dist/classic/chrome/queue-rows.js.map +1 -0
- package/dist/classic/chrome/responder-row.d.ts +9 -0
- package/dist/classic/chrome/responder-row.js +17 -0
- package/dist/classic/chrome/responder-row.js.map +1 -0
- package/dist/classic/chrome/row-budget.d.ts +43 -0
- package/dist/classic/chrome/row-budget.js +78 -0
- package/dist/classic/chrome/row-budget.js.map +1 -0
- package/dist/classic/chrome/status-rows.d.ts +44 -0
- package/dist/classic/chrome/status-rows.js +165 -0
- package/dist/classic/chrome/status-rows.js.map +1 -0
- package/dist/classic/chrome/toast-rows.d.ts +14 -0
- package/dist/classic/chrome/toast-rows.js +82 -0
- package/dist/classic/chrome/toast-rows.js.map +1 -0
- package/dist/classic/chrome/use-terminal-size.d.ts +14 -0
- package/dist/classic/chrome/use-terminal-size.js +38 -0
- package/dist/classic/chrome/use-terminal-size.js.map +1 -0
- package/dist/classic/feed/Feed.d.ts +17 -0
- package/dist/classic/feed/Feed.js +37 -0
- package/dist/classic/feed/Feed.js.map +1 -0
- package/dist/classic/feed/FeedStatic.d.ts +13 -0
- package/dist/classic/feed/FeedStatic.js +15 -0
- package/dist/classic/feed/FeedStatic.js.map +1 -0
- package/dist/classic/feed/LiveTail.d.ts +10 -0
- package/dist/classic/feed/LiveTail.js +20 -0
- package/dist/classic/feed/LiveTail.js.map +1 -0
- package/dist/classic/feed/ScrollbarGutter.d.ts +9 -0
- package/dist/classic/feed/ScrollbarGutter.js +42 -0
- package/dist/classic/feed/ScrollbarGutter.js.map +1 -0
- package/dist/classic/feed/block-height.d.ts +12 -0
- package/dist/classic/feed/block-height.js +34 -0
- package/dist/classic/feed/block-height.js.map +1 -0
- package/dist/classic/feed/commit-ledger.d.ts +26 -0
- package/dist/classic/feed/commit-ledger.js +52 -0
- package/dist/classic/feed/commit-ledger.js.map +1 -0
- package/dist/classic/feed/feed-blocks.d.ts +39 -0
- package/dist/classic/feed/feed-blocks.js +124 -0
- package/dist/classic/feed/feed-blocks.js.map +1 -0
- package/dist/classic/feed/live-tail-policy.d.ts +35 -0
- package/dist/classic/feed/live-tail-policy.js +80 -0
- package/dist/classic/feed/live-tail-policy.js.map +1 -0
- package/dist/classic/feed/scrollbar-rows.d.ts +6 -0
- package/dist/classic/feed/scrollbar-rows.js +18 -0
- package/dist/classic/feed/scrollbar-rows.js.map +1 -0
- package/dist/classic/feed/transcript-selection.d.ts +16 -0
- package/dist/classic/feed/transcript-selection.js +92 -0
- package/dist/classic/feed/transcript-selection.js.map +1 -0
- package/dist/classic/feed/transcript-window.d.ts +33 -0
- package/dist/classic/feed/transcript-window.js +53 -0
- package/dist/classic/feed/transcript-window.js.map +1 -0
- package/dist/classic/input/cancel-ladder.d.ts +47 -0
- package/dist/classic/input/cancel-ladder.js +100 -0
- package/dist/classic/input/cancel-ladder.js.map +1 -0
- package/dist/classic/input/chord-from-key.d.ts +2 -0
- package/dist/classic/input/chord-from-key.js +25 -0
- package/dist/classic/input/chord-from-key.js.map +1 -0
- package/dist/classic/input/input-router.d.ts +33 -0
- package/dist/classic/input/input-router.js +146 -0
- package/dist/classic/input/input-router.js.map +1 -0
- package/dist/classic/input/key-event.d.ts +39 -0
- package/dist/classic/input/key-event.js +12 -0
- package/dist/classic/input/key-event.js.map +1 -0
- package/dist/classic/input/paste-decoder.d.ts +1 -0
- package/dist/classic/input/paste-decoder.js +9 -0
- package/dist/classic/input/paste-decoder.js.map +1 -0
- package/dist/classic/input/raw-decoder.d.ts +31 -0
- package/dist/classic/input/raw-decoder.js +325 -0
- package/dist/classic/input/raw-decoder.js.map +1 -0
- package/dist/classic/input/sgr-mouse.d.ts +2 -0
- package/dist/classic/input/sgr-mouse.js +37 -0
- package/dist/classic/input/sgr-mouse.js.map +1 -0
- package/dist/classic/input/terminal-sequences.d.ts +38 -0
- package/dist/classic/input/terminal-sequences.js +111 -0
- package/dist/classic/input/terminal-sequences.js.map +1 -0
- package/dist/classic/panels/CompletionPanel.d.ts +3 -0
- package/dist/classic/panels/CompletionPanel.js +10 -0
- package/dist/classic/panels/CompletionPanel.js.map +1 -0
- package/dist/classic/panels/ConfirmPanel.d.ts +3 -0
- package/dist/classic/panels/ConfirmPanel.js +7 -0
- package/dist/classic/panels/ConfirmPanel.js.map +1 -0
- package/dist/classic/panels/JobsPanel.d.ts +3 -0
- package/dist/classic/panels/JobsPanel.js +7 -0
- package/dist/classic/panels/JobsPanel.js.map +1 -0
- package/dist/classic/panels/KeysPanel.d.ts +3 -0
- package/dist/classic/panels/KeysPanel.js +7 -0
- package/dist/classic/panels/KeysPanel.js.map +1 -0
- package/dist/classic/panels/PagerPanel.d.ts +3 -0
- package/dist/classic/panels/PagerPanel.js +7 -0
- package/dist/classic/panels/PagerPanel.js.map +1 -0
- package/dist/classic/panels/PanelFrame.d.ts +3 -0
- package/dist/classic/panels/PanelFrame.js +8 -0
- package/dist/classic/panels/PanelFrame.js.map +1 -0
- package/dist/classic/panels/PickerPanel.d.ts +3 -0
- package/dist/classic/panels/PickerPanel.js +7 -0
- package/dist/classic/panels/PickerPanel.js.map +1 -0
- package/dist/classic/panels/PlanPanel.d.ts +3 -0
- package/dist/classic/panels/PlanPanel.js +7 -0
- package/dist/classic/panels/PlanPanel.js.map +1 -0
- package/dist/classic/panels/PromptActionsPanel.d.ts +3 -0
- package/dist/classic/panels/PromptActionsPanel.js +7 -0
- package/dist/classic/panels/PromptActionsPanel.js.map +1 -0
- package/dist/classic/panels/ScopePanel.d.ts +3 -0
- package/dist/classic/panels/ScopePanel.js +7 -0
- package/dist/classic/panels/ScopePanel.js.map +1 -0
- package/dist/classic/panels/SearchPanel.d.ts +3 -0
- package/dist/classic/panels/SearchPanel.js +7 -0
- package/dist/classic/panels/SearchPanel.js.map +1 -0
- package/dist/classic/panels/SecretPanel.d.ts +3 -0
- package/dist/classic/panels/SecretPanel.js +7 -0
- package/dist/classic/panels/SecretPanel.js.map +1 -0
- package/dist/classic/panels/completion-accept.d.ts +17 -0
- package/dist/classic/panels/completion-accept.js +58 -0
- package/dist/classic/panels/completion-accept.js.map +1 -0
- package/dist/classic/panels/completion-rows.d.ts +29 -0
- package/dist/classic/panels/completion-rows.js +135 -0
- package/dist/classic/panels/completion-rows.js.map +1 -0
- package/dist/classic/panels/confirm-panel.d.ts +24 -0
- package/dist/classic/panels/confirm-panel.js +115 -0
- package/dist/classic/panels/confirm-panel.js.map +1 -0
- package/dist/classic/panels/jobs-panel.d.ts +32 -0
- package/dist/classic/panels/jobs-panel.js +116 -0
- package/dist/classic/panels/jobs-panel.js.map +1 -0
- package/dist/classic/panels/keys-panel.d.ts +40 -0
- package/dist/classic/panels/keys-panel.js +174 -0
- package/dist/classic/panels/keys-panel.js.map +1 -0
- package/dist/classic/panels/list-rows.d.ts +27 -0
- package/dist/classic/panels/list-rows.js +42 -0
- package/dist/classic/panels/list-rows.js.map +1 -0
- package/dist/classic/panels/list-window.d.ts +15 -0
- package/dist/classic/panels/list-window.js +25 -0
- package/dist/classic/panels/list-window.js.map +1 -0
- package/dist/classic/panels/pager-panel.d.ts +50 -0
- package/dist/classic/panels/pager-panel.js +238 -0
- package/dist/classic/panels/pager-panel.js.map +1 -0
- package/dist/classic/panels/panel-controller.d.ts +29 -0
- package/dist/classic/panels/panel-controller.js +320 -0
- package/dist/classic/panels/panel-controller.js.map +1 -0
- package/dist/classic/panels/panel-effect.d.ts +74 -0
- package/dist/classic/panels/panel-effect.js +7 -0
- package/dist/classic/panels/panel-effect.js.map +1 -0
- package/dist/classic/panels/panel-effects.d.ts +11 -0
- package/dist/classic/panels/panel-effects.js +105 -0
- package/dist/classic/panels/panel-effects.js.map +1 -0
- package/dist/classic/panels/panel-frame.d.ts +20 -0
- package/dist/classic/panels/panel-frame.js +47 -0
- package/dist/classic/panels/panel-frame.js.map +1 -0
- package/dist/classic/panels/panel-host.d.ts +16 -0
- package/dist/classic/panels/panel-host.js +49 -0
- package/dist/classic/panels/panel-host.js.map +1 -0
- package/dist/classic/panels/panel-initial-states.d.ts +18 -0
- package/dist/classic/panels/panel-initial-states.js +33 -0
- package/dist/classic/panels/panel-initial-states.js.map +1 -0
- package/dist/classic/panels/panel-types.d.ts +47 -0
- package/dist/classic/panels/panel-types.js +2 -0
- package/dist/classic/panels/panel-types.js.map +1 -0
- package/dist/classic/panels/panel-wheel.d.ts +20 -0
- package/dist/classic/panels/panel-wheel.js +82 -0
- package/dist/classic/panels/panel-wheel.js.map +1 -0
- package/dist/classic/panels/picker-panel.d.ts +35 -0
- package/dist/classic/panels/picker-panel.js +150 -0
- package/dist/classic/panels/picker-panel.js.map +1 -0
- package/dist/classic/panels/plan-panel.d.ts +27 -0
- package/dist/classic/panels/plan-panel.js +98 -0
- package/dist/classic/panels/plan-panel.js.map +1 -0
- package/dist/classic/panels/prompt-actions-panel.d.ts +25 -0
- package/dist/classic/panels/prompt-actions-panel.js +57 -0
- package/dist/classic/panels/prompt-actions-panel.js.map +1 -0
- package/dist/classic/panels/scope-panel.d.ts +27 -0
- package/dist/classic/panels/scope-panel.js +122 -0
- package/dist/classic/panels/scope-panel.js.map +1 -0
- package/dist/classic/panels/search-panel.d.ts +32 -0
- package/dist/classic/panels/search-panel.js +126 -0
- package/dist/classic/panels/search-panel.js.map +1 -0
- package/dist/classic/panels/secret-panel.d.ts +28 -0
- package/dist/classic/panels/secret-panel.js +70 -0
- package/dist/classic/panels/secret-panel.js.map +1 -0
- package/dist/classic/render/ansi-text.d.ts +36 -0
- package/dist/classic/render/ansi-text.js +234 -0
- package/dist/classic/render/ansi-text.js.map +1 -0
- package/dist/classic/render/glyphs.d.ts +49 -0
- package/dist/classic/render/glyphs.js +111 -0
- package/dist/classic/render/glyphs.js.map +1 -0
- package/dist/classic/render/ink-theme.d.ts +49 -0
- package/dist/classic/render/ink-theme.js +73 -0
- package/dist/classic/render/ink-theme.js.map +1 -0
- package/dist/classic/render/measure.d.ts +17 -0
- package/dist/classic/render/measure.js +33 -0
- package/dist/classic/render/measure.js.map +1 -0
- package/dist/classic/render/shell-width.d.ts +12 -0
- package/dist/classic/render/shell-width.js +23 -0
- package/dist/classic/render/shell-width.js.map +1 -0
- package/dist/classic/render/wrap.d.ts +19 -0
- package/dist/classic/render/wrap.js +102 -0
- package/dist/classic/render/wrap.js.map +1 -0
- package/dist/commands/doctor.js +9 -8
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/providers.js +43 -50
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/search-providers.js +3 -5
- package/dist/commands/search-providers.js.map +1 -1
- package/dist/index.js +76 -84
- package/dist/index.js.map +1 -1
- package/dist/llm/capabilities.js +5 -2
- package/dist/llm/capabilities.js.map +1 -1
- package/dist/llm/custom-providers.js +16 -3
- package/dist/llm/custom-providers.js.map +1 -1
- package/dist/llm/http.d.ts +5 -0
- package/dist/llm/http.js +38 -8
- package/dist/llm/http.js.map +1 -1
- package/dist/llm/meta.js +56 -24
- package/dist/llm/meta.js.map +1 -1
- package/dist/llm/modal.js +3 -2
- package/dist/llm/modal.js.map +1 -1
- package/dist/llm/router.js +36 -23
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/system-messages.d.ts +1 -0
- package/dist/llm/system-messages.js +6 -0
- package/dist/llm/system-messages.js.map +1 -1
- package/dist/llm/token-usage.js +11 -1
- package/dist/llm/token-usage.js.map +1 -1
- package/dist/llm/tool-protocol.js +10 -3
- package/dist/llm/tool-protocol.js.map +1 -1
- package/dist/modes/agent.d.ts +3 -0
- package/dist/modes/agent.js +10 -3
- package/dist/modes/agent.js.map +1 -1
- package/dist/modes/ask.js +2 -2
- package/dist/modes/ask.js.map +1 -1
- package/dist/noninteractive/read-stdin.d.ts +1 -0
- package/dist/noninteractive/read-stdin.js +8 -0
- package/dist/noninteractive/read-stdin.js.map +1 -0
- package/dist/noninteractive/readline-prompts.d.ts +47 -0
- package/dist/noninteractive/readline-prompts.js +201 -0
- package/dist/noninteractive/readline-prompts.js.map +1 -0
- package/dist/noninteractive/start-noninteractive.d.ts +32 -0
- package/dist/noninteractive/start-noninteractive.js +100 -0
- package/dist/noninteractive/start-noninteractive.js.map +1 -0
- package/dist/noninteractive/stdio-confirm-port.d.ts +15 -0
- package/dist/noninteractive/stdio-confirm-port.js +45 -0
- package/dist/noninteractive/stdio-confirm-port.js.map +1 -0
- package/dist/noninteractive/stream-blocks.d.ts +117 -0
- package/dist/noninteractive/stream-blocks.js +445 -0
- package/dist/noninteractive/stream-blocks.js.map +1 -0
- package/dist/noninteractive/stream-renderer.d.ts +40 -0
- package/dist/noninteractive/stream-renderer.js +164 -0
- package/dist/noninteractive/stream-renderer.js.map +1 -0
- package/dist/noninteractive/stream-spinner.d.ts +28 -0
- package/dist/noninteractive/stream-spinner.js +45 -0
- package/dist/noninteractive/stream-spinner.js.map +1 -0
- package/dist/os/bun-runtime.js +207 -0
- package/dist/os/bun-runtime.js.map +1 -0
- package/dist/prompts/embedded.js +1 -1
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/system.agent.md +11 -7
- package/dist/store/config.d.ts +3 -1
- package/dist/store/config.js +37 -5
- package/dist/store/config.js.map +1 -1
- package/dist/store/history.d.ts +1 -1
- package/dist/store/history.js +3 -0
- package/dist/store/history.js.map +1 -1
- package/dist/store/keys.d.ts +5 -2
- package/dist/store/keys.js +97 -14
- package/dist/store/keys.js.map +1 -1
- package/dist/tools/definitions.js +1 -1
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/registry.js +27 -4
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/web/providers/duckduckgo.js +1 -1
- package/dist/tools/web/providers/duckduckgo.js.map +1 -1
- package/dist/tools/web/readable.js +1 -1
- package/dist/tools/web/readable.js.map +1 -1
- package/dist/tui-v2/app/App.js +20 -28
- package/dist/tui-v2/app/App.js.map +1 -1
- package/dist/tui-v2/bootstrap/pager-export.d.ts +9 -18
- package/dist/tui-v2/bootstrap/pager-export.js +15 -72
- package/dist/tui-v2/bootstrap/pager-export.js.map +1 -1
- package/dist/tui-v2/bootstrap/start-tui-v2.js +8 -8
- package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
- package/dist/tui-v2/components/completion/completion-menu.d.ts +2 -2
- package/dist/tui-v2/components/composer/composer-input-box.d.ts +1 -1
- package/dist/tui-v2/components/composer/paste-chip.d.ts +2 -2
- package/dist/tui-v2/components/composer/paste-chip.js +1 -1
- package/dist/tui-v2/components/composer/paste-chip.js.map +1 -1
- package/dist/tui-v2/components/jobs/jobs-panel.d.ts +2 -2
- package/dist/tui-v2/components/jobs/jobs-panel.js +4 -4
- package/dist/tui-v2/components/jobs/jobs-panel.js.map +1 -1
- package/dist/tui-v2/components/modal/confirm-modal.d.ts +3 -3
- package/dist/tui-v2/components/modal/confirm-modal.js +6 -6
- package/dist/tui-v2/components/modal/confirm-modal.js.map +1 -1
- package/dist/tui-v2/components/modal/keys-modal.d.ts +3 -3
- package/dist/tui-v2/components/modal/keys-modal.js +24 -9
- package/dist/tui-v2/components/modal/keys-modal.js.map +1 -1
- package/dist/tui-v2/components/modal/prompt-actions-modal.d.ts +3 -3
- package/dist/tui-v2/components/modal/prompt-actions-modal.js +2 -2
- package/dist/tui-v2/components/modal/prompt-actions-modal.js.map +1 -1
- package/dist/tui-v2/components/modal/scope-modal.d.ts +3 -3
- package/dist/tui-v2/components/modal/scope-modal.js +1 -1
- package/dist/tui-v2/components/modal/scope-modal.js.map +1 -1
- package/dist/tui-v2/components/modal/secret-modal.d.ts +3 -3
- package/dist/tui-v2/components/modal/secret-modal.js +2 -2
- package/dist/tui-v2/components/modal/secret-modal.js.map +1 -1
- package/dist/tui-v2/components/overlay/overlay-host.d.ts +2 -2
- package/dist/tui-v2/components/overlay/overlay-host.js +1 -1
- package/dist/tui-v2/components/overlay/overlay-host.js.map +1 -1
- package/dist/tui-v2/components/pager/pager-line.d.ts +3 -3
- package/dist/tui-v2/components/pager/pager-line.js +3 -3
- package/dist/tui-v2/components/pager/pager-line.js.map +1 -1
- package/dist/tui-v2/components/pager/pager.d.ts +3 -3
- package/dist/tui-v2/components/pager/pager.js +6 -5
- package/dist/tui-v2/components/pager/pager.js.map +1 -1
- package/dist/tui-v2/components/picker/picker.d.ts +3 -3
- package/dist/tui-v2/components/picker/picker.js +2 -2
- package/dist/tui-v2/components/picker/picker.js.map +1 -1
- package/dist/tui-v2/components/plan/plan-pane-anim.js +1 -1
- package/dist/tui-v2/components/plan/plan-pane-anim.js.map +1 -1
- package/dist/tui-v2/components/plan/plan-view.d.ts +2 -2
- package/dist/tui-v2/components/plan/plan-view.js +2 -2
- package/dist/tui-v2/components/plan/plan-view.js.map +1 -1
- package/dist/tui-v2/components/queue/queue-panel.d.ts +2 -2
- package/dist/tui-v2/components/queue/queue-panel.js +3 -3
- package/dist/tui-v2/components/queue/queue-panel.js.map +1 -1
- package/dist/tui-v2/components/status/context-limit-chip.d.ts +1 -5
- package/dist/tui-v2/components/status/context-limit-chip.js +1 -38
- package/dist/tui-v2/components/status/context-limit-chip.js.map +1 -1
- package/dist/tui-v2/components/status/status-line.d.ts +5 -34
- package/dist/tui-v2/components/status/status-line.js +7 -99
- package/dist/tui-v2/components/status/status-line.js.map +1 -1
- package/dist/tui-v2/components/toast/toast-anim.d.ts +0 -1
- package/dist/tui-v2/components/toast/toast-anim.js +2 -3
- package/dist/tui-v2/components/toast/toast-anim.js.map +1 -1
- package/dist/tui-v2/components/toast/toast-host.d.ts +2 -2
- package/dist/tui-v2/components/toast/toast-host.js +2 -2
- package/dist/tui-v2/components/toast/toast-host.js.map +1 -1
- package/dist/tui-v2/components/transcript/assistant-message.d.ts +2 -2
- package/dist/tui-v2/components/transcript/assistant-message.js +3 -2
- package/dist/tui-v2/components/transcript/assistant-message.js.map +1 -1
- package/dist/tui-v2/components/transcript/compacted-row.d.ts +3 -3
- package/dist/tui-v2/components/transcript/compacted-row.js +6 -5
- package/dist/tui-v2/components/transcript/compacted-row.js.map +1 -1
- package/dist/tui-v2/components/transcript/file-diff-card.d.ts +1 -1
- package/dist/tui-v2/components/transcript/file-diff-card.js +1 -1
- package/dist/tui-v2/components/transcript/file-diff-card.js.map +1 -1
- package/dist/tui-v2/components/transcript/intro-card.d.ts +2 -2
- package/dist/tui-v2/components/transcript/intro-card.js +1 -1
- package/dist/tui-v2/components/transcript/intro-card.js.map +1 -1
- package/dist/tui-v2/components/transcript/linkable-text.d.ts +1 -1
- package/dist/tui-v2/components/transcript/linkable-text.js +1 -1
- package/dist/tui-v2/components/transcript/linkable-text.js.map +1 -1
- package/dist/tui-v2/components/transcript/notice-row.d.ts +2 -2
- package/dist/tui-v2/components/transcript/search-bar.d.ts +1 -1
- package/dist/tui-v2/components/transcript/thinking-block.d.ts +2 -2
- package/dist/tui-v2/components/transcript/thinking-block.js +3 -3
- package/dist/tui-v2/components/transcript/thinking-block.js.map +1 -1
- package/dist/tui-v2/components/transcript/tool-card.d.ts +3 -3
- package/dist/tui-v2/components/transcript/tool-card.js +7 -7
- package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
- package/dist/tui-v2/components/transcript/transcript-row.d.ts +4 -4
- package/dist/tui-v2/components/transcript/transcript-scrollbar.d.ts +1 -1
- package/dist/tui-v2/components/transcript/transcript-scrollbar.js +42 -9
- package/dist/tui-v2/components/transcript/transcript-scrollbar.js.map +1 -1
- package/dist/tui-v2/components/transcript/transcript-view.d.ts +3 -3
- package/dist/tui-v2/components/transcript/transcript-view.js +6 -6
- package/dist/tui-v2/components/transcript/transcript-view.js.map +1 -1
- package/dist/tui-v2/components/transcript/use-native-selection-copy.d.ts +1 -1
- package/dist/tui-v2/components/transcript/use-transcript-selection.d.ts +2 -2
- package/dist/tui-v2/components/transcript/use-transcript-selection.js +3 -3
- package/dist/tui-v2/components/transcript/use-transcript-selection.js.map +1 -1
- package/dist/tui-v2/components/transcript/user-message.d.ts +2 -2
- package/dist/tui-v2/components/transcript/user-message.js +1 -1
- package/dist/tui-v2/components/transcript/user-message.js.map +1 -1
- package/dist/tui-v2/composer/composer-editor.d.ts +2 -2
- package/dist/tui-v2/composer/composer-editor.js +21 -20
- package/dist/tui-v2/composer/composer-editor.js.map +1 -1
- package/dist/tui-v2/composer/composer-image-paste.d.ts +1 -1
- package/dist/tui-v2/composer/composer-image-paste.js +1 -1
- package/dist/tui-v2/composer/composer-image-paste.js.map +1 -1
- package/dist/tui-v2/composer/use-draft-actions.d.ts +2 -2
- package/dist/tui-v2/composer/use-draft-actions.js +2 -2
- package/dist/tui-v2/composer/use-draft-actions.js.map +1 -1
- package/dist/tui-v2/input/chord-from-opentui-key.d.ts +20 -0
- package/dist/tui-v2/input/chord-from-opentui-key.js +51 -0
- package/dist/tui-v2/input/chord-from-opentui-key.js.map +1 -0
- package/dist/tui-v2/rendering/pager-markdown.d.ts +2 -22
- package/dist/tui-v2/rendering/pager-markdown.js +4 -103
- package/dist/tui-v2/rendering/pager-markdown.js.map +1 -1
- package/dist/tui-v2/rendering/styled-markdown.d.ts +20 -0
- package/dist/tui-v2/rendering/styled-markdown.js +35 -0
- package/dist/tui-v2/rendering/styled-markdown.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/ui/mentions.d.ts +5 -8
- package/dist/ui/mentions.js +26 -8
- package/dist/ui/mentions.js.map +1 -1
- package/dist/ui/warn-once.d.ts +2 -0
- package/dist/ui/warn-once.js +13 -0
- package/dist/ui/warn-once.js.map +1 -0
- package/dist/ui-core/actions/action-id.d.ts +14 -0
- package/dist/ui-core/actions/action-id.js +123 -0
- package/dist/ui-core/actions/action-id.js.map +1 -0
- package/dist/ui-core/actions/action-router.js.map +1 -0
- package/dist/ui-core/actions/chord.d.ts +20 -0
- package/dist/ui-core/actions/chord.js +28 -0
- package/dist/ui-core/actions/chord.js.map +1 -0
- package/dist/ui-core/actions/format-shortcuts.js +267 -0
- package/dist/ui-core/actions/format-shortcuts.js.map +1 -0
- package/dist/ui-core/actions/keymap.d.ts +23 -0
- package/dist/ui-core/actions/keymap.js +147 -0
- package/dist/ui-core/actions/keymap.js.map +1 -0
- package/dist/ui-core/actions/mode-cycle.js.map +1 -0
- package/dist/ui-core/bootstrap/can-use-tui.js.map +1 -0
- package/dist/ui-core/bootstrap/capabilities.js +96 -0
- package/dist/ui-core/bootstrap/capabilities.js.map +1 -0
- package/dist/ui-core/bootstrap/composition-root.d.ts +92 -0
- package/dist/ui-core/bootstrap/composition-root.js.map +1 -0
- package/dist/ui-core/bootstrap/console-guard.js.map +1 -0
- package/dist/ui-core/bootstrap/console-suppress.js.map +1 -0
- package/dist/ui-core/bootstrap/lifecycle.js.map +1 -0
- package/dist/ui-core/bootstrap/overlay-ports.js +92 -0
- package/dist/ui-core/bootstrap/overlay-ports.js.map +1 -0
- package/dist/ui-core/bootstrap/ui-selection.d.ts +27 -0
- package/dist/ui-core/bootstrap/ui-selection.js +109 -0
- package/dist/ui-core/bootstrap/ui-selection.js.map +1 -0
- package/dist/ui-core/commands/command-handlers.js +86 -0
- package/dist/ui-core/commands/command-handlers.js.map +1 -0
- package/dist/ui-core/commands/config-commands.d.ts +14 -0
- package/dist/ui-core/commands/config-commands.js +315 -0
- package/dist/ui-core/commands/config-commands.js.map +1 -0
- package/dist/ui-core/commands/key-commands.d.ts +9 -0
- package/dist/ui-core/commands/key-commands.js +532 -0
- package/dist/ui-core/commands/key-commands.js.map +1 -0
- package/dist/ui-core/commands/picker-commands.d.ts +25 -0
- package/dist/ui-core/commands/picker-commands.js +1045 -0
- package/dist/ui-core/commands/picker-commands.js.map +1 -0
- package/dist/ui-core/commands/session-commands.d.ts +19 -0
- package/dist/ui-core/commands/session-commands.js +183 -0
- package/dist/ui-core/commands/session-commands.js.map +1 -0
- package/dist/ui-core/commands/startup-update.js.map +1 -0
- package/dist/ui-core/composer/arrow-intent.js.map +1 -0
- package/dist/ui-core/composer/completion.js.map +1 -0
- package/dist/ui-core/composer/composer-action-port.js.map +1 -0
- package/dist/ui-core/composer/composer-height.js +29 -0
- package/dist/ui-core/composer/composer-height.js.map +1 -0
- package/dist/ui-core/composer/composer-image-paste.d.ts +20 -0
- package/dist/ui-core/composer/composer-image-paste.js +77 -0
- package/dist/ui-core/composer/composer-image-paste.js.map +1 -0
- package/dist/ui-core/composer/composer-meta.d.ts +2 -0
- package/dist/ui-core/composer/composer-meta.js +31 -0
- package/dist/ui-core/composer/composer-meta.js.map +1 -0
- package/dist/ui-core/composer/composer-wheel.d.ts +58 -0
- package/dist/ui-core/composer/composer-wheel.js +108 -0
- package/dist/ui-core/composer/composer-wheel.js.map +1 -0
- package/dist/ui-core/composer/draft-actions.d.ts +31 -0
- package/dist/ui-core/composer/draft-actions.js +45 -0
- package/dist/ui-core/composer/draft-actions.js.map +1 -0
- package/dist/ui-core/composer/history-nav.js.map +1 -0
- package/dist/ui-core/composer/input-history.js +9 -0
- package/dist/ui-core/composer/input-history.js.map +1 -0
- package/dist/ui-core/composer/newline-hint.d.ts +13 -0
- package/dist/ui-core/composer/newline-hint.js.map +1 -0
- package/dist/ui-core/composer/paste-placeholder.js.map +1 -0
- package/dist/ui-core/composer/prompt-history.js.map +1 -0
- package/dist/ui-core/composer/secret-buffer.js.map +1 -0
- package/dist/ui-core/composer/textarea-keybindings.d.ts +25 -0
- package/dist/ui-core/composer/textarea-keybindings.js +84 -0
- package/dist/ui-core/composer/textarea-keybindings.js.map +1 -0
- package/dist/ui-core/controllers/focus-controller.js.map +1 -0
- package/dist/ui-core/controllers/interruptible-controller.js.map +1 -0
- package/dist/ui-core/controllers/overlay-controller.d.ts +211 -0
- package/dist/ui-core/controllers/overlay-controller.js.map +1 -0
- package/dist/ui-core/controllers/selection-controller.js.map +1 -0
- package/dist/ui-core/controllers/toast-controller.js.map +1 -0
- package/dist/ui-core/layout/compute-layout.js.map +1 -0
- package/dist/ui-core/motion/ease.js.map +1 -0
- package/dist/ui-core/notify.js.map +1 -0
- package/dist/ui-core/plan/plan-lifecycle.js.map +1 -0
- package/dist/ui-core/ports/clipboard-osc52.js +44 -0
- package/dist/ui-core/ports/clipboard-osc52.js.map +1 -0
- package/dist/ui-core/ports/pager-export-port.d.ts +16 -0
- package/dist/ui-core/ports/pager-export-port.js +70 -0
- package/dist/ui-core/ports/pager-export-port.js.map +1 -0
- package/dist/ui-core/ports/renderer-port.d.ts +10 -0
- package/dist/ui-core/ports/renderer-port.js +2 -0
- package/dist/ui-core/ports/renderer-port.js.map +1 -0
- package/dist/ui-core/react/providers.d.ts +16 -0
- package/dist/ui-core/react/providers.js +31 -0
- package/dist/ui-core/react/providers.js.map +1 -0
- package/dist/ui-core/react/use-has-draft.js.map +1 -0
- package/dist/ui-core/react/use-overlay.js.map +1 -0
- package/dist/ui-core/react/use-plan.js.map +1 -0
- package/dist/ui-core/react/use-session-state.js.map +1 -0
- package/dist/ui-core/react/use-toast.js.map +1 -0
- package/dist/ui-core/react/use-transcript-store.d.ts +10 -0
- package/dist/ui-core/react/use-transcript-store.js.map +1 -0
- package/dist/ui-core/rendering/artifact-pager-source.js.map +1 -0
- package/dist/ui-core/rendering/batch-sections.js.map +1 -0
- package/dist/ui-core/rendering/code-block.d.ts +48 -0
- package/dist/ui-core/rendering/code-block.js +407 -0
- package/dist/ui-core/rendering/code-block.js.map +1 -0
- package/dist/ui-core/rendering/context-limit.d.ts +8 -0
- package/dist/ui-core/rendering/context-limit.js +49 -0
- package/dist/ui-core/rendering/context-limit.js.map +1 -0
- package/dist/ui-core/rendering/file-diff-view.js.map +1 -0
- package/dist/ui-core/rendering/format-help.js.map +1 -0
- package/dist/ui-core/rendering/format-keys.d.ts +14 -0
- package/dist/ui-core/rendering/format-keys.js +78 -0
- package/dist/ui-core/rendering/format-keys.js.map +1 -0
- package/dist/ui-core/rendering/incremental-strip.js.map +1 -0
- package/dist/ui-core/rendering/intro-header.d.ts +11 -0
- package/dist/ui-core/rendering/intro-header.js +208 -0
- package/dist/ui-core/rendering/intro-header.js.map +1 -0
- package/dist/ui-core/rendering/job-tail-source.js.map +1 -0
- package/dist/ui-core/rendering/link-detector.js.map +1 -0
- package/dist/ui-core/rendering/markdown.js.map +1 -0
- package/dist/ui-core/rendering/open-tool-output.js +346 -0
- package/dist/ui-core/rendering/open-tool-output.js.map +1 -0
- package/dist/ui-core/rendering/pager-chrome.js +119 -0
- package/dist/ui-core/rendering/pager-chrome.js.map +1 -0
- package/dist/ui-core/rendering/pager-source.d.ts +20 -0
- package/dist/ui-core/rendering/pager-source.js +101 -0
- package/dist/ui-core/rendering/pager-source.js.map +1 -0
- package/dist/ui-core/rendering/pager-text.d.ts +24 -0
- package/dist/ui-core/rendering/pager-text.js +105 -0
- package/dist/ui-core/rendering/pager-text.js.map +1 -0
- package/dist/ui-core/rendering/pager-view-policy.js +98 -0
- package/dist/ui-core/rendering/pager-view-policy.js.map +1 -0
- package/dist/ui-core/rendering/picker-filter.js.map +1 -0
- package/dist/ui-core/rendering/plan-view.js.map +1 -0
- package/dist/ui-core/rendering/prompt-preview.js.map +1 -0
- package/dist/ui-core/rendering/render-markdown-lines.d.ts +7 -0
- package/dist/ui-core/rendering/render-markdown-lines.js +26 -0
- package/dist/ui-core/rendering/render-markdown-lines.js.map +1 -0
- package/dist/ui-core/rendering/sanitize-display.js.map +1 -0
- package/dist/ui-core/rendering/status-segments.d.ts +25 -0
- package/dist/ui-core/rendering/status-segments.js +88 -0
- package/dist/ui-core/rendering/status-segments.js.map +1 -0
- package/dist/ui-core/rendering/streaming-markdown.d.ts +23 -0
- package/dist/ui-core/rendering/streaming-markdown.js +118 -0
- package/dist/ui-core/rendering/streaming-markdown.js.map +1 -0
- package/dist/ui-core/rendering/strip-tool-surfaces.js.map +1 -0
- package/dist/ui-core/rendering/syntax-highlight.js.map +1 -0
- package/dist/ui-core/rendering/text-format.js.map +1 -0
- package/dist/ui-core/rendering/text-width.js.map +1 -0
- package/dist/ui-core/rendering/theme.js.map +1 -0
- package/dist/ui-core/rendering/thinking-tail.js.map +1 -0
- package/dist/ui-core/rendering/tool-presenter.d.ts +68 -0
- package/dist/ui-core/rendering/tool-presenter.js +371 -0
- package/dist/ui-core/rendering/tool-presenter.js.map +1 -0
- package/dist/ui-core/rendering/transcript-export.d.ts +11 -0
- package/dist/ui-core/rendering/transcript-export.js +18 -0
- package/dist/ui-core/rendering/transcript-export.js.map +1 -0
- package/dist/ui-core/rendering/transcript-semantic.js.map +1 -0
- package/dist/ui-core/rendering/user-message-wrap.d.ts +18 -0
- package/dist/ui-core/rendering/user-message-wrap.js +23 -0
- package/dist/ui-core/rendering/user-message-wrap.js.map +1 -0
- package/dist/ui-core/rendering/wordmark.js.map +1 -0
- package/dist/ui-core/state/pager-search.js.map +1 -0
- package/dist/ui-core/state/semantic-document.js.map +1 -0
- package/dist/ui-core/state/transcript-compaction.js.map +1 -0
- package/dist/ui-core/state/transcript-hydrate.d.ts +67 -0
- package/dist/ui-core/state/transcript-hydrate.js +665 -0
- package/dist/ui-core/state/transcript-hydrate.js.map +1 -0
- package/dist/ui-core/state/transcript-reducer.js +484 -0
- package/dist/ui-core/state/transcript-reducer.js.map +1 -0
- package/dist/ui-core/state/transcript-search.js.map +1 -0
- package/dist/ui-core/state/transcript-store.js.map +1 -0
- package/dist/ui-core/state/transcript-struct.js.map +1 -0
- package/dist/ui-core/state/transcript-types.d.ts +103 -0
- package/dist/ui-core/state/transcript-types.js.map +1 -0
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/dist/version.generated.js.map +1 -1
- package/package.json +17 -13
- package/dist/agent/classic-renderer.d.ts +0 -5
- package/dist/agent/classic-renderer.js +0 -136
- package/dist/agent/classic-renderer.js.map +0 -1
- package/dist/app/adapters/current-terminal-adapter.d.ts +0 -2
- package/dist/app/adapters/current-terminal-adapter.js +0 -24
- package/dist/app/adapters/current-terminal-adapter.js.map +0 -1
- package/dist/app/controllers/job-controller.d.ts +0 -28
- package/dist/app/controllers/job-controller.js +0 -70
- package/dist/app/controllers/job-controller.js.map +0 -1
- package/dist/repl/prompt-line.d.ts +0 -24
- package/dist/repl/prompt-line.js +0 -558
- package/dist/repl/prompt-line.js.map +0 -1
- package/dist/repl/slash-commands.js +0 -392
- package/dist/repl/slash-commands.js.map +0 -1
- package/dist/repl.d.ts +0 -18
- package/dist/repl.js +0 -1868
- package/dist/repl.js.map +0 -1
- package/dist/safety/path-permissions.d.ts +0 -14
- package/dist/safety/path-permissions.js +0 -43
- package/dist/safety/path-permissions.js.map +0 -1
- package/dist/tools/reducers/generic.d.ts +0 -14
- package/dist/tools/reducers/generic.js +0 -5
- package/dist/tools/reducers/generic.js.map +0 -1
- package/dist/tui/can-use-tui.js.map +0 -1
- package/dist/tui/format-keys.d.ts +0 -13
- package/dist/tui/format-keys.js +0 -78
- package/dist/tui/format-keys.js.map +0 -1
- package/dist/tui/input-history.js +0 -9
- package/dist/tui/input-history.js.map +0 -1
- package/dist/tui/runtime.js +0 -207
- package/dist/tui/runtime.js.map +0 -1
- package/dist/tui/state.d.ts +0 -124
- package/dist/tui/state.js +0 -496
- package/dist/tui/state.js.map +0 -1
- package/dist/tui/text-format.js.map +0 -1
- package/dist/tui-v2/actions/action-id.d.ts +0 -14
- package/dist/tui-v2/actions/action-id.js +0 -117
- package/dist/tui-v2/actions/action-id.js.map +0 -1
- package/dist/tui-v2/actions/action-router.js.map +0 -1
- package/dist/tui-v2/actions/chord-from-key.d.ts +0 -26
- package/dist/tui-v2/actions/chord-from-key.js +0 -47
- package/dist/tui-v2/actions/chord-from-key.js.map +0 -1
- package/dist/tui-v2/actions/format-shortcuts.js +0 -267
- package/dist/tui-v2/actions/format-shortcuts.js.map +0 -1
- package/dist/tui-v2/actions/keymap.d.ts +0 -24
- package/dist/tui-v2/actions/keymap.js +0 -151
- package/dist/tui-v2/actions/keymap.js.map +0 -1
- package/dist/tui-v2/app/command-handlers.js +0 -86
- package/dist/tui-v2/app/command-handlers.js.map +0 -1
- package/dist/tui-v2/app/commands/config-commands.d.ts +0 -14
- package/dist/tui-v2/app/commands/config-commands.js +0 -315
- package/dist/tui-v2/app/commands/config-commands.js.map +0 -1
- package/dist/tui-v2/app/commands/key-commands.d.ts +0 -9
- package/dist/tui-v2/app/commands/key-commands.js +0 -496
- package/dist/tui-v2/app/commands/key-commands.js.map +0 -1
- package/dist/tui-v2/app/commands/picker-commands.d.ts +0 -25
- package/dist/tui-v2/app/commands/picker-commands.js +0 -1006
- package/dist/tui-v2/app/commands/picker-commands.js.map +0 -1
- package/dist/tui-v2/app/commands/session-commands.d.ts +0 -19
- package/dist/tui-v2/app/commands/session-commands.js +0 -183
- package/dist/tui-v2/app/commands/session-commands.js.map +0 -1
- package/dist/tui-v2/app/mode-cycle.js.map +0 -1
- package/dist/tui-v2/app/plan-lifecycle.js.map +0 -1
- package/dist/tui-v2/app/providers.d.ts +0 -17
- package/dist/tui-v2/app/providers.js +0 -32
- package/dist/tui-v2/app/providers.js.map +0 -1
- package/dist/tui-v2/app/startup-update.js.map +0 -1
- package/dist/tui-v2/bootstrap/capabilities.js +0 -93
- package/dist/tui-v2/bootstrap/capabilities.js.map +0 -1
- package/dist/tui-v2/bootstrap/composition-root.d.ts +0 -92
- package/dist/tui-v2/bootstrap/composition-root.js.map +0 -1
- package/dist/tui-v2/bootstrap/console-guard.js.map +0 -1
- package/dist/tui-v2/bootstrap/console-suppress.js.map +0 -1
- package/dist/tui-v2/bootstrap/disable-native-selection.d.ts +0 -11
- package/dist/tui-v2/bootstrap/disable-native-selection.js +0 -13
- package/dist/tui-v2/bootstrap/disable-native-selection.js.map +0 -1
- package/dist/tui-v2/bootstrap/lifecycle.js.map +0 -1
- package/dist/tui-v2/bootstrap/osc52-clipboard.js +0 -44
- package/dist/tui-v2/bootstrap/osc52-clipboard.js.map +0 -1
- package/dist/tui-v2/bootstrap/overlay-ports.js +0 -114
- package/dist/tui-v2/bootstrap/overlay-ports.js.map +0 -1
- package/dist/tui-v2/bootstrap/ui-selection.d.ts +0 -30
- package/dist/tui-v2/bootstrap/ui-selection.js +0 -59
- package/dist/tui-v2/bootstrap/ui-selection.js.map +0 -1
- package/dist/tui-v2/composer/arrow-intent.js.map +0 -1
- package/dist/tui-v2/composer/completion.js.map +0 -1
- package/dist/tui-v2/composer/composer-action-port.js.map +0 -1
- package/dist/tui-v2/composer/composer-height.js +0 -29
- package/dist/tui-v2/composer/composer-height.js.map +0 -1
- package/dist/tui-v2/composer/composer-meta.d.ts +0 -9
- package/dist/tui-v2/composer/composer-meta.js +0 -38
- package/dist/tui-v2/composer/composer-meta.js.map +0 -1
- package/dist/tui-v2/composer/draft-actions.d.ts +0 -31
- package/dist/tui-v2/composer/draft-actions.js +0 -45
- package/dist/tui-v2/composer/draft-actions.js.map +0 -1
- package/dist/tui-v2/composer/history-nav.js.map +0 -1
- package/dist/tui-v2/composer/newline-hint.d.ts +0 -13
- package/dist/tui-v2/composer/newline-hint.js.map +0 -1
- package/dist/tui-v2/composer/paste-placeholder.js.map +0 -1
- package/dist/tui-v2/composer/prompt-history.js.map +0 -1
- package/dist/tui-v2/composer/secret-buffer.js.map +0 -1
- package/dist/tui-v2/controllers/focus-controller.js.map +0 -1
- package/dist/tui-v2/controllers/interruptible-controller.js.map +0 -1
- package/dist/tui-v2/controllers/overlay-controller.d.ts +0 -209
- package/dist/tui-v2/controllers/overlay-controller.js.map +0 -1
- package/dist/tui-v2/controllers/selection-controller.js.map +0 -1
- package/dist/tui-v2/controllers/toast-controller.js.map +0 -1
- package/dist/tui-v2/layout/compute-layout.js.map +0 -1
- package/dist/tui-v2/motion/ease.js.map +0 -1
- package/dist/tui-v2/notify.js.map +0 -1
- package/dist/tui-v2/rendering/artifact-pager-source.js.map +0 -1
- package/dist/tui-v2/rendering/batch-sections.js.map +0 -1
- package/dist/tui-v2/rendering/file-diff-view.js.map +0 -1
- package/dist/tui-v2/rendering/format-help.js.map +0 -1
- package/dist/tui-v2/rendering/incremental-strip.js.map +0 -1
- package/dist/tui-v2/rendering/job-tail-source.js.map +0 -1
- package/dist/tui-v2/rendering/link-detector.js.map +0 -1
- package/dist/tui-v2/rendering/open-tool-output.js +0 -346
- package/dist/tui-v2/rendering/open-tool-output.js.map +0 -1
- package/dist/tui-v2/rendering/pager-chrome.js +0 -119
- package/dist/tui-v2/rendering/pager-chrome.js.map +0 -1
- package/dist/tui-v2/rendering/pager-view-policy.js +0 -98
- package/dist/tui-v2/rendering/pager-view-policy.js.map +0 -1
- package/dist/tui-v2/rendering/picker-filter.js.map +0 -1
- package/dist/tui-v2/rendering/plan-view.js.map +0 -1
- package/dist/tui-v2/rendering/prompt-preview.js.map +0 -1
- package/dist/tui-v2/rendering/render-markdown-lines.d.ts +0 -29
- package/dist/tui-v2/rendering/render-markdown-lines.js +0 -46
- package/dist/tui-v2/rendering/render-markdown-lines.js.map +0 -1
- package/dist/tui-v2/rendering/sanitize-display.js.map +0 -1
- package/dist/tui-v2/rendering/streaming-markdown.d.ts +0 -25
- package/dist/tui-v2/rendering/streaming-markdown.js +0 -117
- package/dist/tui-v2/rendering/streaming-markdown.js.map +0 -1
- package/dist/tui-v2/rendering/strip-tool-surfaces.js.map +0 -1
- package/dist/tui-v2/rendering/syntax-highlight.js.map +0 -1
- package/dist/tui-v2/rendering/theme.js.map +0 -1
- package/dist/tui-v2/rendering/thinking-tail.js.map +0 -1
- package/dist/tui-v2/rendering/tool-presenter.d.ts +0 -63
- package/dist/tui-v2/rendering/tool-presenter.js +0 -376
- package/dist/tui-v2/rendering/tool-presenter.js.map +0 -1
- package/dist/tui-v2/rendering/transcript-export.d.ts +0 -11
- package/dist/tui-v2/rendering/transcript-export.js +0 -18
- package/dist/tui-v2/rendering/transcript-export.js.map +0 -1
- package/dist/tui-v2/rendering/transcript-semantic.js.map +0 -1
- package/dist/tui-v2/rendering/user-message-wrap.d.ts +0 -16
- package/dist/tui-v2/rendering/user-message-wrap.js +0 -21
- package/dist/tui-v2/rendering/user-message-wrap.js.map +0 -1
- package/dist/tui-v2/state/pager-search.js.map +0 -1
- package/dist/tui-v2/state/semantic-document.js.map +0 -1
- package/dist/tui-v2/state/transcript-compaction.js.map +0 -1
- package/dist/tui-v2/state/transcript-hydrate.d.ts +0 -67
- package/dist/tui-v2/state/transcript-hydrate.js +0 -648
- package/dist/tui-v2/state/transcript-hydrate.js.map +0 -1
- package/dist/tui-v2/state/transcript-reducer.js +0 -482
- package/dist/tui-v2/state/transcript-reducer.js.map +0 -1
- package/dist/tui-v2/state/transcript-search.js.map +0 -1
- package/dist/tui-v2/state/transcript-store.js.map +0 -1
- package/dist/tui-v2/state/transcript-struct.js.map +0 -1
- package/dist/tui-v2/state/transcript-types.d.ts +0 -101
- package/dist/tui-v2/state/transcript-types.js.map +0 -1
- package/dist/tui-v2/state/use-has-draft.js.map +0 -1
- package/dist/tui-v2/state/use-overlay.js.map +0 -1
- package/dist/tui-v2/state/use-plan.js.map +0 -1
- package/dist/tui-v2/state/use-session-state.js.map +0 -1
- package/dist/tui-v2/state/use-toast.js.map +0 -1
- package/dist/tui-v2/state/use-transcript-store.d.ts +0 -10
- package/dist/tui-v2/state/use-transcript-store.js.map +0 -1
- package/dist/ui/ansi-box.d.ts +0 -9
- package/dist/ui/ansi-box.js +0 -25
- package/dist/ui/ansi-box.js.map +0 -1
- package/dist/ui/banner.d.ts +0 -3
- package/dist/ui/banner.js +0 -13
- package/dist/ui/banner.js.map +0 -1
- package/dist/ui/code-block.d.ts +0 -48
- package/dist/ui/code-block.js +0 -407
- package/dist/ui/code-block.js.map +0 -1
- package/dist/ui/intro-card.d.ts +0 -11
- package/dist/ui/intro-card.js +0 -74
- package/dist/ui/intro-card.js.map +0 -1
- package/dist/ui/intro-header.d.ts +0 -24
- package/dist/ui/intro-header.js +0 -233
- package/dist/ui/intro-header.js.map +0 -1
- package/dist/ui/keys.d.ts +0 -23
- package/dist/ui/keys.js +0 -23
- package/dist/ui/keys.js.map +0 -1
- package/dist/ui/markdown.js.map +0 -1
- package/dist/ui/output-pane.d.ts +0 -59
- package/dist/ui/output-pane.js +0 -398
- package/dist/ui/output-pane.js.map +0 -1
- package/dist/ui/spinner.d.ts +0 -8
- package/dist/ui/spinner.js +0 -185
- package/dist/ui/spinner.js.map +0 -1
- package/dist/ui/task-pane.d.ts +0 -17
- package/dist/ui/task-pane.js +0 -80
- package/dist/ui/task-pane.js.map +0 -1
- package/dist/ui/text-width.js.map +0 -1
- package/dist/ui/wordmark.js.map +0 -1
- /package/dist/{repl/slash-commands.d.ts → app/commands/catalog.d.ts} +0 -0
- /package/dist/{tui/runtime.d.ts → os/bun-runtime.d.ts} +0 -0
- /package/dist/{tui-v2 → ui-core}/actions/action-router.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/actions/action-router.js +0 -0
- /package/dist/{tui-v2 → ui-core}/actions/format-shortcuts.d.ts +0 -0
- /package/dist/{tui-v2/app → ui-core/actions}/mode-cycle.d.ts +0 -0
- /package/dist/{tui-v2/app → ui-core/actions}/mode-cycle.js +0 -0
- /package/dist/{tui → ui-core/bootstrap}/can-use-tui.d.ts +0 -0
- /package/dist/{tui → ui-core/bootstrap}/can-use-tui.js +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/capabilities.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/composition-root.js +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/console-guard.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/console-guard.js +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/console-suppress.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/console-suppress.js +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/lifecycle.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/lifecycle.js +0 -0
- /package/dist/{tui-v2 → ui-core}/bootstrap/overlay-ports.d.ts +0 -0
- /package/dist/{tui-v2/app → ui-core/commands}/command-handlers.d.ts +0 -0
- /package/dist/{tui-v2/app → ui-core/commands}/startup-update.d.ts +0 -0
- /package/dist/{tui-v2/app → ui-core/commands}/startup-update.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/arrow-intent.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/arrow-intent.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/completion.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/completion.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/composer-action-port.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/composer-action-port.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/composer-height.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/history-nav.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/history-nav.js +0 -0
- /package/dist/{tui → ui-core/composer}/input-history.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/newline-hint.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/paste-placeholder.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/paste-placeholder.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/prompt-history.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/prompt-history.js +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/secret-buffer.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/composer/secret-buffer.js +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/focus-controller.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/focus-controller.js +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/interruptible-controller.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/interruptible-controller.js +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/overlay-controller.js +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/selection-controller.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/selection-controller.js +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/toast-controller.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/controllers/toast-controller.js +0 -0
- /package/dist/{tui-v2 → ui-core}/layout/compute-layout.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/layout/compute-layout.js +0 -0
- /package/dist/{tui-v2 → ui-core}/motion/ease.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/motion/ease.js +0 -0
- /package/dist/{tui-v2 → ui-core}/notify.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/notify.js +0 -0
- /package/dist/{tui-v2/app → ui-core/plan}/plan-lifecycle.d.ts +0 -0
- /package/dist/{tui-v2/app → ui-core/plan}/plan-lifecycle.js +0 -0
- /package/dist/{tui-v2/bootstrap/osc52-clipboard.d.ts → ui-core/ports/clipboard-osc52.d.ts} +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-has-draft.d.ts +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-has-draft.js +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-overlay.d.ts +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-overlay.js +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-plan.d.ts +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-plan.js +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-session-state.d.ts +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-session-state.js +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-toast.d.ts +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-toast.js +0 -0
- /package/dist/{tui-v2/state → ui-core/react}/use-transcript-store.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/artifact-pager-source.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/artifact-pager-source.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/batch-sections.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/batch-sections.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/file-diff-view.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/file-diff-view.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/format-help.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/format-help.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/incremental-strip.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/incremental-strip.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/job-tail-source.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/job-tail-source.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/link-detector.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/link-detector.js +0 -0
- /package/dist/{ui → ui-core/rendering}/markdown.d.ts +0 -0
- /package/dist/{ui → ui-core/rendering}/markdown.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/open-tool-output.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/pager-chrome.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/pager-view-policy.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/picker-filter.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/picker-filter.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/plan-view.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/plan-view.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/prompt-preview.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/prompt-preview.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/sanitize-display.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/sanitize-display.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/strip-tool-surfaces.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/strip-tool-surfaces.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/syntax-highlight.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/syntax-highlight.js +0 -0
- /package/dist/{tui → ui-core/rendering}/text-format.d.ts +0 -0
- /package/dist/{tui → ui-core/rendering}/text-format.js +0 -0
- /package/dist/{ui → ui-core/rendering}/text-width.d.ts +0 -0
- /package/dist/{ui → ui-core/rendering}/text-width.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/theme.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/theme.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/thinking-tail.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/thinking-tail.js +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/transcript-semantic.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/rendering/transcript-semantic.js +0 -0
- /package/dist/{ui → ui-core/rendering}/wordmark.d.ts +0 -0
- /package/dist/{ui → ui-core/rendering}/wordmark.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/pager-search.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/pager-search.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/semantic-document.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/semantic-document.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-compaction.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-compaction.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-reducer.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-search.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-search.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-store.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-store.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-struct.d.ts +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-struct.js +0 -0
- /package/dist/{tui-v2 → ui-core}/state/transcript-types.js +0 -0
package/dist/repl.js
DELETED
|
@@ -1,1868 +0,0 @@
|
|
|
1
|
-
import { clearLine, cursorTo, emitKeypressEvents, moveCursor } from "node:readline";
|
|
2
|
-
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
import { confirm } from "@inquirer/prompts";
|
|
5
|
-
import { runAskStream } from "./modes/ask.js";
|
|
6
|
-
import { resolveTurnInput, shouldRunImageOcr } from "./attachments/service.js";
|
|
7
|
-
import { runAgent, createSessionPolicy, } from "./modes/agent.js";
|
|
8
|
-
import { attachClassicRenderer } from "./agent/classic-renderer.js";
|
|
9
|
-
import { clearActiveProjectRoot } from "./agent/project-root.js";
|
|
10
|
-
import { providerSwitcher, printProviderKeys, setKeyPicker, unsetKeyPicker, } from "./commands/providers.js";
|
|
11
|
-
import { printSearchProviderKeys, useSearchProvider, } from "./commands/search-providers.js";
|
|
12
|
-
import { getActiveProviderEndpoint, getConfig, getCustomProviders, getProviderModel, setDefaultMode, setProviderModel, setThinking, updateConfig, } from "./store/config.js";
|
|
13
|
-
import { envValue, getProviderSecret } from "./store/keys.js";
|
|
14
|
-
import { listSessionSummaries, upsertSession, clearAllHistory, deleteSession, getSession, } from "./store/history.js";
|
|
15
|
-
import { beginSessionWorkspace } from "./store/session-workspace.js";
|
|
16
|
-
import { assertProvider, defaultModels, getProviderInfoText, redactSecrets, } from "./llm/provider.js";
|
|
17
|
-
import { getProvider, providerAuth } from "./llm/router.js";
|
|
18
|
-
import { clearTextOnlyModels } from "./llm/tool-protocol.js";
|
|
19
|
-
import { providerIds } from "./types.js";
|
|
20
|
-
import { runUpdate, checkForUpdateSilent, getCurrentVersion, } from "./commands/update.js";
|
|
21
|
-
import { renderModeSwitch, renderProviderSwitch, PROMPT, } from "./ui/banner.js";
|
|
22
|
-
import { renderIntroCard } from "./ui/intro-card.js";
|
|
23
|
-
import { clearThinking, createThinkingStreamParser, getLastThinking, rememberThinkingFromText, renderAllThinking, renderThinkingSummary, renderThinkingToggleMessage, } from "./ui/thinking.js";
|
|
24
|
-
import { createMarkdownStreamWriter, renderMarkdown } from "./ui/markdown.js";
|
|
25
|
-
import { startThinkingSpinner } from "./ui/spinner.js";
|
|
26
|
-
import { modelSupportsThinking } from "./llm/capabilities.js";
|
|
27
|
-
import { clearViewports, getLastViewport, getViewport, isPagerActive, listViewports, openPager, openViewportPager, toggleViewport, } from "./ui/output-pane.js";
|
|
28
|
-
import { loadPlan, mutatePlan, deletePlan, isPlanSuccessful, } from "./store/plan.js";
|
|
29
|
-
import { renderPlanDocument, renderPlanChecklist } from "./ui/plan-pane.js";
|
|
30
|
-
import { safeCwd, cwdIsBroken, recoverCwd } from "./os/cwd.js";
|
|
31
|
-
import { compactMessages, estimateMessagesTokens, } from "./agent/context-manager.js";
|
|
32
|
-
import { isCtrlC, isCtrlO, isCtrlP, isCtrlT, isEscape } from "./ui/keys.js";
|
|
33
|
-
import { imageAttachmentPaths } from "./ui/mentions.js";
|
|
34
|
-
import { meaningfulCharCount, runOcr } from "./tools/ocr.js";
|
|
35
|
-
import { jobManager } from "./tools/jobs.js";
|
|
36
|
-
import { interactiveSessionManager } from "./interactive-session/manager.js";
|
|
37
|
-
import { handleDraftPlanDecision, IMPLEMENT_DIRECTIVE, } from "./agent/plan-decision.js";
|
|
38
|
-
import { readPromptLine, PROMPT_EXIT_INTENT, stripAnsi, isPrintableSequence, } from "./repl/prompt-line.js";
|
|
39
|
-
import { slashCommands, knownModels, looksLikeSlashCommand, slashCommandLabel, } from "./repl/slash-commands.js";
|
|
40
|
-
// Re-exported so existing imports of these names from "./repl.js" keep
|
|
41
|
-
// working unchanged — the command catalogue, model lists, and the line
|
|
42
|
-
// editor now live in their own modules under "./repl/".
|
|
43
|
-
export { slashCommands, knownModels, getKnownModels, inferProviderForModel, looksLikeSlashCommand, getSlashCommandSuggestions, isKnownSlashCommand, } from "./repl/slash-commands.js";
|
|
44
|
-
export { renderSlashCommandMenu, renderFileMentionMenu, } from "./repl/prompt-line.js";
|
|
45
|
-
/** Reset process-global project routing and rotate/adopt classic session identity together. */
|
|
46
|
-
export function resetClassicSessionContext(state, sessionId, workspace) {
|
|
47
|
-
const previousSessionId = state.session.sessionId;
|
|
48
|
-
if (!sessionId || sessionId !== previousSessionId) {
|
|
49
|
-
void interactiveSessionManager
|
|
50
|
-
.beginCloseOwner(previousSessionId, "conversation-teardown")
|
|
51
|
-
.catch(() => undefined);
|
|
52
|
-
}
|
|
53
|
-
clearActiveProjectRoot();
|
|
54
|
-
state.session = createSessionPolicy(sessionId);
|
|
55
|
-
void interactiveSessionManager.activateOwner(state.session.sessionId).catch(() => undefined);
|
|
56
|
-
// Isolate scratch + tool outputs per session (restore on history resume).
|
|
57
|
-
beginSessionWorkspace(workspace?.folderName
|
|
58
|
-
? { folderName: workspace.folderName, code: workspace.code }
|
|
59
|
-
: undefined);
|
|
60
|
-
}
|
|
61
|
-
function splitCommand(line) {
|
|
62
|
-
return (line
|
|
63
|
-
.match(/(?:[^\s"]+|"[^"]*")+/g)
|
|
64
|
-
?.map((part) => part.replace(/^"|"$/g, "")) ?? []);
|
|
65
|
-
}
|
|
66
|
-
function isAbortLikeError(error) {
|
|
67
|
-
if (!error)
|
|
68
|
-
return false;
|
|
69
|
-
if (typeof error === "object") {
|
|
70
|
-
const err = error;
|
|
71
|
-
if (err.name === "AbortError")
|
|
72
|
-
return true;
|
|
73
|
-
if (err.code === "ABORT_ERR")
|
|
74
|
-
return true;
|
|
75
|
-
if (typeof err.message === "string" && /abort/i.test(err.message))
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
async function buildImageOcrGrounding(line, baseDir) {
|
|
81
|
-
const paths = imageAttachmentPaths(line, baseDir);
|
|
82
|
-
if (paths.length === 0)
|
|
83
|
-
return "";
|
|
84
|
-
const sections = [];
|
|
85
|
-
for (const path of paths) {
|
|
86
|
-
try {
|
|
87
|
-
const result = await runOcr({
|
|
88
|
-
path,
|
|
89
|
-
lang: "eng",
|
|
90
|
-
psmCandidates: [6, 3, 11],
|
|
91
|
-
timeoutMs: 60_000,
|
|
92
|
-
});
|
|
93
|
-
const text = result.text.trim();
|
|
94
|
-
if (result.ok && result.reliable && meaningfulCharCount(text) >= 8) {
|
|
95
|
-
sections.push(`----- OCR of ${path} -----\n${text}\n----- end OCR -----`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
// tesseract missing or failed — skip silently; vision bytes still sent.
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (sections.length === 0)
|
|
103
|
-
return "";
|
|
104
|
-
return ('<image-ocr note="Text extracted locally from the attached image(s) via OCR, in case the model cannot see the image bytes directly. Use it to ground your answer; if you CAN see the image, prefer your own visual reading and use this only to confirm text.">\n' +
|
|
105
|
-
sections.join("\n\n") +
|
|
106
|
-
"\n</image-ocr>");
|
|
107
|
-
}
|
|
108
|
-
// Abort controller for streaming cancellation
|
|
109
|
-
let currentAbortController = null;
|
|
110
|
-
class AbortRunError extends Error {
|
|
111
|
-
constructor() {
|
|
112
|
-
super("Aborted.");
|
|
113
|
-
this.name = "AbortRunError";
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// Build a concise, width-bounded spinner label for ask-mode research so the
|
|
117
|
-
// user sees what's being searched/fetched without the line overflowing.
|
|
118
|
-
function askResearchLabel(name, argsDisplay) {
|
|
119
|
-
const verb = name === "web.search"
|
|
120
|
-
? "searching the web"
|
|
121
|
-
: name === "web.fetch"
|
|
122
|
-
? "fetching"
|
|
123
|
-
: name === "tool.batch"
|
|
124
|
-
? "researching"
|
|
125
|
-
: name === "fs.read"
|
|
126
|
-
? "reading"
|
|
127
|
-
: name === "fs.list"
|
|
128
|
-
? "listing"
|
|
129
|
-
: name === "fs.search"
|
|
130
|
-
? "searching files"
|
|
131
|
-
: name;
|
|
132
|
-
const detail = argsDisplay.trim().replace(/\s+/g, " ");
|
|
133
|
-
const full = detail ? `${verb}: ${detail}` : verb;
|
|
134
|
-
// Leave room for the spinner frame, elapsed time, and padding so the head
|
|
135
|
-
// line never wraps. Columns can be undefined on non-TTY; default to 80.
|
|
136
|
-
const cols = Math.max(40, process.stdout.columns ?? 80);
|
|
137
|
-
const max = Math.max(20, cols - 18);
|
|
138
|
-
return full.length > max ? `${full.slice(0, max - 1)}…` : full;
|
|
139
|
-
}
|
|
140
|
-
// Stream a response while hiding <think> blocks and handling ESC abort
|
|
141
|
-
async function streamWithAbort(run, signal) {
|
|
142
|
-
let sawToken = false;
|
|
143
|
-
const spinner = startThinkingSpinner("waiting for model", signal);
|
|
144
|
-
// Lets the run function surface progress (e.g. ask-mode research activity)
|
|
145
|
-
// on the spinner's label line. Ignored once visible tokens have started
|
|
146
|
-
// since the spinner is stopped at that point.
|
|
147
|
-
const setStatus = (label) => {
|
|
148
|
-
if (!sawToken && label)
|
|
149
|
-
spinner.setLabel(label);
|
|
150
|
-
};
|
|
151
|
-
const markdown = createMarkdownStreamWriter((chunk) => process.stdout.write(chunk));
|
|
152
|
-
const parser = createThinkingStreamParser((text) => {
|
|
153
|
-
// Visible content arriving — drop the spinner so output isn't stomped on.
|
|
154
|
-
spinner.stop();
|
|
155
|
-
markdown.push(text);
|
|
156
|
-
}, (reasoning) => {
|
|
157
|
-
// Reasoning tokens are hidden by default; show progress so users see
|
|
158
|
-
// something is happening even when the model spends a minute thinking.
|
|
159
|
-
spinner.setLabel("thinking");
|
|
160
|
-
spinner.pushPreview(reasoning);
|
|
161
|
-
const approx = reasoning.split(/\s+/).filter(Boolean).length;
|
|
162
|
-
if (approx > 0)
|
|
163
|
-
spinner.bumpReasoning(approx);
|
|
164
|
-
});
|
|
165
|
-
const onToken = (token) => {
|
|
166
|
-
if (!sawToken) {
|
|
167
|
-
// First raw token from the provider — still hold the spinner if it
|
|
168
|
-
// turns out to be a reasoning token; the parser's callbacks above
|
|
169
|
-
// decide which label to show.
|
|
170
|
-
sawToken = true;
|
|
171
|
-
}
|
|
172
|
-
parser.push(token);
|
|
173
|
-
};
|
|
174
|
-
try {
|
|
175
|
-
const raw = await run(signal, onToken, setStatus);
|
|
176
|
-
spinner.stop();
|
|
177
|
-
parser.finish();
|
|
178
|
-
const result = rememberThinkingFromText(raw);
|
|
179
|
-
if (sawToken) {
|
|
180
|
-
markdown.finish();
|
|
181
|
-
}
|
|
182
|
-
else if (result.visible) {
|
|
183
|
-
process.stdout.write(renderMarkdown(result.visible));
|
|
184
|
-
}
|
|
185
|
-
if (result.hasThinking) {
|
|
186
|
-
const prefix = result.visible && !result.visible.endsWith("\n") ? "\n" : "";
|
|
187
|
-
process.stdout.write(`${prefix}${renderThinkingSummary(result.thinkContent)}\n`);
|
|
188
|
-
}
|
|
189
|
-
return result.visible;
|
|
190
|
-
}
|
|
191
|
-
catch (err) {
|
|
192
|
-
const result = parser.finish();
|
|
193
|
-
if (sawToken)
|
|
194
|
-
markdown.finish();
|
|
195
|
-
if (signal.aborted) {
|
|
196
|
-
spinner.stop();
|
|
197
|
-
process.stdout.write(chalk.yellow("\n ⏹ Aborted.\n"));
|
|
198
|
-
return result.visible;
|
|
199
|
-
}
|
|
200
|
-
throw err;
|
|
201
|
-
}
|
|
202
|
-
finally {
|
|
203
|
-
// Belt and braces: spinner already self-stops on abort, but make sure
|
|
204
|
-
// any other exit path (success, network error) clears it too.
|
|
205
|
-
spinner.stop();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
async function withAbortableInput(run) {
|
|
209
|
-
const ac = new AbortController();
|
|
210
|
-
const abortFromRawData = (chunk) => {
|
|
211
|
-
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
212
|
-
if (text.includes("\x03"))
|
|
213
|
-
ac.abort();
|
|
214
|
-
};
|
|
215
|
-
currentAbortController = ac;
|
|
216
|
-
if (input.isTTY)
|
|
217
|
-
input.setRawMode(true);
|
|
218
|
-
input.resume();
|
|
219
|
-
// Raw Ctrl+C fallback for terminals whose keypress normalization is
|
|
220
|
-
// unreliable. Escape is handled by the REPL keypress double-press gate.
|
|
221
|
-
input.on("data", abortFromRawData);
|
|
222
|
-
try {
|
|
223
|
-
return await run(ac.signal);
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
if (ac.signal.aborted ||
|
|
227
|
-
(error instanceof Error && error.name === "AbortError")) {
|
|
228
|
-
throw new AbortRunError();
|
|
229
|
-
}
|
|
230
|
-
throw error;
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
input.off("data", abortFromRawData);
|
|
234
|
-
currentAbortController = null;
|
|
235
|
-
if (input.isTTY)
|
|
236
|
-
input.setRawMode(true);
|
|
237
|
-
input.resume();
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function help() {
|
|
241
|
-
const maxCmd = Math.max(...slashCommands.map((command) => slashCommandLabel(command).length));
|
|
242
|
-
const lines = slashCommands
|
|
243
|
-
.map((command) => {
|
|
244
|
-
const label = slashCommandLabel(command).padEnd(maxCmd + 2);
|
|
245
|
-
return ` ${chalk.cyan(label)}${chalk.dim(command.description)}`;
|
|
246
|
-
})
|
|
247
|
-
.join("\n");
|
|
248
|
-
return (lines +
|
|
249
|
-
chalk.dim("\n\n ESC twice cancels turn + jobs │ Ctrl+C clears input (twice to exit) │ Ctrl+T toggle thinking │ Ctrl+O opens tool output pager (q to close)"));
|
|
250
|
-
}
|
|
251
|
-
async function pickInline(options) {
|
|
252
|
-
const { items, header, pageSize = 15 } = options;
|
|
253
|
-
if (items.length === 0)
|
|
254
|
-
return undefined;
|
|
255
|
-
return new Promise((resolve) => {
|
|
256
|
-
let filter = "";
|
|
257
|
-
let filterCursor = 0;
|
|
258
|
-
let selectedIndex = 0;
|
|
259
|
-
let renderedLines = 0;
|
|
260
|
-
const promptLabel = chalk.dim(" filter: ");
|
|
261
|
-
const promptLabelLen = stripAnsi(promptLabel).length;
|
|
262
|
-
const getFiltered = () => {
|
|
263
|
-
const needle = filter.trim().toLowerCase();
|
|
264
|
-
if (!needle)
|
|
265
|
-
return items;
|
|
266
|
-
return items.filter((item) => item.filterText.toLowerCase().includes(needle));
|
|
267
|
-
};
|
|
268
|
-
const renderMenu = () => {
|
|
269
|
-
const filtered = getFiltered();
|
|
270
|
-
if (selectedIndex >= filtered.length)
|
|
271
|
-
selectedIndex = Math.max(0, filtered.length - 1);
|
|
272
|
-
const cols = process.stdout.columns || 80;
|
|
273
|
-
// Scroll the visible window so the selected item is always shown
|
|
274
|
-
let start = 0;
|
|
275
|
-
if (filtered.length > pageSize) {
|
|
276
|
-
start = Math.max(0, selectedIndex - Math.floor(pageSize / 2));
|
|
277
|
-
start = Math.min(start, filtered.length - pageSize);
|
|
278
|
-
}
|
|
279
|
-
const visible = filtered.slice(start, start + pageSize);
|
|
280
|
-
const lines = [];
|
|
281
|
-
for (let i = 0; i < visible.length; i++) {
|
|
282
|
-
const marker = start + i === selectedIndex ? chalk.magenta("›") : " ";
|
|
283
|
-
let full = ` ${marker} ${visible[i].label}`;
|
|
284
|
-
const stripped = stripAnsi(full);
|
|
285
|
-
if (stripped.length > cols - 1) {
|
|
286
|
-
full = full.slice(0, cols - 1) + chalk.dim("…");
|
|
287
|
-
}
|
|
288
|
-
lines.push(full);
|
|
289
|
-
}
|
|
290
|
-
if (filtered.length > pageSize) {
|
|
291
|
-
const prefix = start > 0 ? `… ${start} above · ` : "";
|
|
292
|
-
const suffix = start + pageSize < filtered.length
|
|
293
|
-
? ` · ${filtered.length - start - pageSize} below`
|
|
294
|
-
: "";
|
|
295
|
-
lines.push(chalk.dim(` ${prefix}${visible.length} shown${suffix}`));
|
|
296
|
-
}
|
|
297
|
-
if (filtered.length === 0) {
|
|
298
|
-
lines.push(chalk.dim(" no matches"));
|
|
299
|
-
}
|
|
300
|
-
// Clear old menu
|
|
301
|
-
const toClear = Math.max(renderedLines, lines.length + 1); // +1 for filter line
|
|
302
|
-
cursorTo(output, 0);
|
|
303
|
-
clearLine(output, 0);
|
|
304
|
-
output.write(`${promptLabel}${filter}`);
|
|
305
|
-
if (toClear > 0) {
|
|
306
|
-
// Reserve space
|
|
307
|
-
if (renderedLines === 0 && lines.length > 0) {
|
|
308
|
-
output.write("\n".repeat(lines.length));
|
|
309
|
-
moveCursor(output, 0, -lines.length);
|
|
310
|
-
}
|
|
311
|
-
for (let i = 0; i < toClear; i++) {
|
|
312
|
-
output.write("\x1b[B");
|
|
313
|
-
cursorTo(output, 0);
|
|
314
|
-
clearLine(output, 0);
|
|
315
|
-
if (i < lines.length)
|
|
316
|
-
output.write(lines[i]);
|
|
317
|
-
}
|
|
318
|
-
moveCursor(output, 0, -toClear);
|
|
319
|
-
}
|
|
320
|
-
cursorTo(output, promptLabelLen + filterCursor);
|
|
321
|
-
renderedLines = lines.length;
|
|
322
|
-
};
|
|
323
|
-
const cleanup = () => {
|
|
324
|
-
input.off("keypress", handleKeypress);
|
|
325
|
-
// Clear menu from screen
|
|
326
|
-
cursorTo(output, 0);
|
|
327
|
-
clearLine(output, 0);
|
|
328
|
-
for (let i = 0; i < renderedLines; i++) {
|
|
329
|
-
output.write("\x1b[B");
|
|
330
|
-
cursorTo(output, 0);
|
|
331
|
-
clearLine(output, 0);
|
|
332
|
-
}
|
|
333
|
-
if (renderedLines > 0)
|
|
334
|
-
moveCursor(output, 0, -renderedLines);
|
|
335
|
-
renderedLines = 0;
|
|
336
|
-
// Restore terminal state to what readPromptLine expects:
|
|
337
|
-
// raw mode off, input paused.
|
|
338
|
-
input.pause();
|
|
339
|
-
if (input.isTTY)
|
|
340
|
-
input.setRawMode(false);
|
|
341
|
-
output.write("\n");
|
|
342
|
-
};
|
|
343
|
-
function handleKeypress(sequence, key) {
|
|
344
|
-
if (isPagerActive())
|
|
345
|
-
return;
|
|
346
|
-
if (isCtrlC(key) || isEscape(key)) {
|
|
347
|
-
cleanup();
|
|
348
|
-
resolve(undefined);
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
if (key.name === "return" || key.name === "enter") {
|
|
352
|
-
const filtered = getFiltered();
|
|
353
|
-
const picked = filtered[selectedIndex];
|
|
354
|
-
cleanup();
|
|
355
|
-
resolve(picked?.value);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
if (key.name === "tab") {
|
|
359
|
-
const filtered = getFiltered();
|
|
360
|
-
const picked = filtered[selectedIndex];
|
|
361
|
-
if (picked) {
|
|
362
|
-
filter = picked.filterText;
|
|
363
|
-
filterCursor = filter.length;
|
|
364
|
-
selectedIndex = 0;
|
|
365
|
-
renderMenu();
|
|
366
|
-
}
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (key.name === "up") {
|
|
370
|
-
const filtered = getFiltered();
|
|
371
|
-
if (filtered.length > 0) {
|
|
372
|
-
selectedIndex =
|
|
373
|
-
(selectedIndex - 1 + filtered.length) % filtered.length;
|
|
374
|
-
renderMenu();
|
|
375
|
-
}
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
if (key.name === "down") {
|
|
379
|
-
const filtered = getFiltered();
|
|
380
|
-
if (filtered.length > 0) {
|
|
381
|
-
selectedIndex = (selectedIndex + 1) % filtered.length;
|
|
382
|
-
renderMenu();
|
|
383
|
-
}
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
if (key.name === "backspace") {
|
|
387
|
-
if (filterCursor > 0) {
|
|
388
|
-
filter =
|
|
389
|
-
filter.slice(0, filterCursor - 1) + filter.slice(filterCursor);
|
|
390
|
-
filterCursor -= 1;
|
|
391
|
-
selectedIndex = 0;
|
|
392
|
-
renderMenu();
|
|
393
|
-
}
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
if (key.name === "delete") {
|
|
397
|
-
if (filterCursor < filter.length) {
|
|
398
|
-
filter =
|
|
399
|
-
filter.slice(0, filterCursor) + filter.slice(filterCursor + 1);
|
|
400
|
-
selectedIndex = 0;
|
|
401
|
-
renderMenu();
|
|
402
|
-
}
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
if (key.name === "left") {
|
|
406
|
-
if (filterCursor > 0) {
|
|
407
|
-
filterCursor -= 1;
|
|
408
|
-
renderMenu();
|
|
409
|
-
}
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
if (key.name === "right") {
|
|
413
|
-
if (filterCursor < filter.length) {
|
|
414
|
-
filterCursor += 1;
|
|
415
|
-
renderMenu();
|
|
416
|
-
}
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
if (isPrintableSequence(sequence) && !key.ctrl && !key.meta) {
|
|
420
|
-
filter =
|
|
421
|
-
filter.slice(0, filterCursor) + sequence + filter.slice(filterCursor);
|
|
422
|
-
filterCursor += sequence.length;
|
|
423
|
-
selectedIndex = 0;
|
|
424
|
-
renderMenu();
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
if (header) {
|
|
428
|
-
console.log(header);
|
|
429
|
-
}
|
|
430
|
-
if (input.isTTY)
|
|
431
|
-
input.setRawMode(true);
|
|
432
|
-
input.resume();
|
|
433
|
-
input.on("keypress", handleKeypress);
|
|
434
|
-
renderMenu();
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
async function pickModelInteractively(provider, currentModel) {
|
|
438
|
-
let models = [];
|
|
439
|
-
let fetchedDynamically = false;
|
|
440
|
-
const def = defaultModels[provider] ?? "";
|
|
441
|
-
const providerImpl = getProvider(provider);
|
|
442
|
-
if (providerImpl.listModels) {
|
|
443
|
-
try {
|
|
444
|
-
const auth = await providerAuth(provider);
|
|
445
|
-
models = await providerImpl.listModels(auth);
|
|
446
|
-
fetchedDynamically = models.length > 0;
|
|
447
|
-
}
|
|
448
|
-
catch (error) {
|
|
449
|
-
console.warn(chalk.yellow(` Warning: Could not fetch models from ${provider}: ${error instanceof Error ? error.message : String(error)}`));
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if (models.length === 0) {
|
|
453
|
-
models = knownModels[provider] ?? [];
|
|
454
|
-
if (models.length > 0 && providerImpl.listModels) {
|
|
455
|
-
console.warn(chalk.yellow(` Warning: ${provider} model list could not be refreshed; showing known models.`));
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
if (models.length === 0) {
|
|
459
|
-
console.log(chalk.dim(" No known models for this provider. Type /model <name> to set manually."));
|
|
460
|
-
return undefined;
|
|
461
|
-
}
|
|
462
|
-
const header = fetchedDynamically
|
|
463
|
-
? chalk.dim(` ↑↓:navigate · type:filter · tab:fill · enter:select · esc:cancel`)
|
|
464
|
-
: chalk.dim(` ↑↓:navigate · type:filter · tab:fill · enter:select · esc:cancel`);
|
|
465
|
-
const items = models.map((model, index) => {
|
|
466
|
-
const tags = [];
|
|
467
|
-
if (model === currentModel)
|
|
468
|
-
tags.push(chalk.green("active"));
|
|
469
|
-
if (model === def)
|
|
470
|
-
tags.push(chalk.yellow("default"));
|
|
471
|
-
const suffix = tags.length > 0 ? ` ${chalk.dim(tags.join(" · "))}` : "";
|
|
472
|
-
const label = `${chalk.dim(`${(index + 1).toString().padStart(2)}.`)} ${model}${suffix}`;
|
|
473
|
-
return { label, value: model, filterText: model };
|
|
474
|
-
});
|
|
475
|
-
return pickInline({
|
|
476
|
-
items,
|
|
477
|
-
header,
|
|
478
|
-
pageSize: Math.min(15, models.length),
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
async function showModelList(provider, currentModel) {
|
|
482
|
-
let models = [];
|
|
483
|
-
let fetchedDynamically = false;
|
|
484
|
-
const def = defaultModels[provider] ?? "";
|
|
485
|
-
const providerImpl = getProvider(provider);
|
|
486
|
-
if (providerImpl.listModels) {
|
|
487
|
-
try {
|
|
488
|
-
const auth = await providerAuth(provider);
|
|
489
|
-
models = await providerImpl.listModels(auth);
|
|
490
|
-
fetchedDynamically = models.length > 0;
|
|
491
|
-
}
|
|
492
|
-
catch {
|
|
493
|
-
// Silently fall back to empty array on error
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
if (models.length === 0) {
|
|
497
|
-
models = knownModels[provider] ?? [];
|
|
498
|
-
if (models.length > 0 && providerImpl.listModels) {
|
|
499
|
-
console.warn(chalk.yellow(` Warning: ${provider} model list could not be refreshed; showing known models.`));
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
if (models.length === 0) {
|
|
503
|
-
console.log(chalk.dim(" No known models for this provider. Type /model <name> to set manually."));
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
const sourceSuffix = fetchedDynamically
|
|
507
|
-
? chalk.dim(" (fetched live)")
|
|
508
|
-
: chalk.dim(" (known models)");
|
|
509
|
-
console.log(chalk.dim(` Available models for ${chalk.cyan(provider)}:${sourceSuffix}`));
|
|
510
|
-
models.forEach((m, i) => {
|
|
511
|
-
const tags = [];
|
|
512
|
-
if (m === currentModel)
|
|
513
|
-
tags.push("active");
|
|
514
|
-
if (m === def)
|
|
515
|
-
tags.push("default");
|
|
516
|
-
const tag = tags.length > 0 ? chalk.dim(` (${tags.join(" · ")})`) : "";
|
|
517
|
-
console.log(` ${chalk.dim(`${i + 1}.`)} ${chalk.white(m)}${tag}`);
|
|
518
|
-
});
|
|
519
|
-
console.log(chalk.dim(" Use /model <name> or /model <#> to select."));
|
|
520
|
-
}
|
|
521
|
-
async function showAllProviderModels(filter) {
|
|
522
|
-
const custom = getCustomProviders().map((def) => def.id);
|
|
523
|
-
const candidates = [...providerIds, ...custom];
|
|
524
|
-
const configured = [];
|
|
525
|
-
for (const provider of candidates) {
|
|
526
|
-
if (provider === "ollama" || provider === "free") {
|
|
527
|
-
configured.push(provider);
|
|
528
|
-
continue;
|
|
529
|
-
}
|
|
530
|
-
const hasKey = Boolean(envValue(provider)) ||
|
|
531
|
-
Boolean((await getProviderSecret(provider)).value);
|
|
532
|
-
if (!hasKey)
|
|
533
|
-
continue;
|
|
534
|
-
if (provider === "modal" && !getActiveProviderEndpoint("modal"))
|
|
535
|
-
continue;
|
|
536
|
-
configured.push(provider);
|
|
537
|
-
}
|
|
538
|
-
if (configured.length === 0) {
|
|
539
|
-
console.log(chalk.dim(" No providers configured. Add a key with: clai set <provider>"));
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
const needle = filter.trim().toLowerCase().replace(/\s+/g, "/");
|
|
543
|
-
const results = await Promise.all(configured.map(async (provider) => {
|
|
544
|
-
const providerImpl = getProvider(provider);
|
|
545
|
-
let models = [];
|
|
546
|
-
let live = false;
|
|
547
|
-
if (providerImpl.listModels) {
|
|
548
|
-
try {
|
|
549
|
-
models = await providerImpl.listModels(await providerAuth(provider));
|
|
550
|
-
live = models.length > 0;
|
|
551
|
-
}
|
|
552
|
-
catch {
|
|
553
|
-
models = [];
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
if (models.length === 0)
|
|
557
|
-
models = knownModels[provider] ?? [];
|
|
558
|
-
return { provider, models, live };
|
|
559
|
-
}));
|
|
560
|
-
let total = 0;
|
|
561
|
-
const lines = [];
|
|
562
|
-
for (const { provider, models, live } of results) {
|
|
563
|
-
const matching = needle
|
|
564
|
-
? models.filter((model) => `${provider}/${model}`.toLowerCase().includes(needle))
|
|
565
|
-
: models;
|
|
566
|
-
if (matching.length === 0)
|
|
567
|
-
continue;
|
|
568
|
-
lines.push(` ${chalk.cyan(provider)}${chalk.dim(live ? " · live" : " · known")}`);
|
|
569
|
-
for (const model of matching) {
|
|
570
|
-
total += 1;
|
|
571
|
-
lines.push(` ${chalk.white(`${provider}/${model}`)}`);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
if (total === 0) {
|
|
575
|
-
console.log(chalk.dim(` No model matches "${filter.trim()}".`));
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
console.log(chalk.dim(` ${total} models across ${configured.length} configured provider${configured.length === 1 ? "" : "s"}:`));
|
|
579
|
-
for (const line of lines)
|
|
580
|
-
console.log(line);
|
|
581
|
-
console.log(chalk.dim(" Use /provider <name> then /model <name> to switch."));
|
|
582
|
-
}
|
|
583
|
-
function maybePrintThinkingTip(provider, model) {
|
|
584
|
-
if (getConfig().thinking.enabled)
|
|
585
|
-
return;
|
|
586
|
-
if (!modelSupportsThinking(provider, model))
|
|
587
|
-
return;
|
|
588
|
-
console.log(chalk.dim(" 💡 ") +
|
|
589
|
-
chalk.dim(`${model} supports reasoning. Run `) +
|
|
590
|
-
chalk.cyan("/effort") +
|
|
591
|
-
chalk.dim(" to pick an effort level or ") +
|
|
592
|
-
chalk.cyan("/effort high") +
|
|
593
|
-
chalk.dim(" to enable directly."));
|
|
594
|
-
}
|
|
595
|
-
async function offerAgentSwitch(info, state, requestModel, images) {
|
|
596
|
-
if (info.preamble) {
|
|
597
|
-
process.stdout.write(`${renderMarkdown(info.preamble)}\n`);
|
|
598
|
-
}
|
|
599
|
-
const toolsNote = info.tools.length > 0 ? ` (${info.tools.join(", ")})` : "";
|
|
600
|
-
console.log(chalk.yellow(` This needs to take actions${toolsNote}, which ask mode can't do — it's read-only.`));
|
|
601
|
-
// inquirer expects cooked mode; mirror the slash-command path so the
|
|
602
|
-
// prompt reads cleanly, then restore raw mode for the next run/prompt.
|
|
603
|
-
input.pause();
|
|
604
|
-
if (input.isTTY)
|
|
605
|
-
input.setRawMode(false);
|
|
606
|
-
let switchNow = false;
|
|
607
|
-
try {
|
|
608
|
-
switchNow = await confirm({
|
|
609
|
-
message: chalk.yellow("switch to agent mode and run it?"),
|
|
610
|
-
default: true,
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
catch {
|
|
614
|
-
// Treat an aborted/failed prompt as "no".
|
|
615
|
-
switchNow = false;
|
|
616
|
-
}
|
|
617
|
-
finally {
|
|
618
|
-
if (input.isTTY)
|
|
619
|
-
input.setRawMode(true);
|
|
620
|
-
input.resume();
|
|
621
|
-
}
|
|
622
|
-
if (!switchNow) {
|
|
623
|
-
console.log(chalk.dim(" Staying in ask mode."));
|
|
624
|
-
return "Staying in ask mode; the task wasn't run. Use /agent to run tasks that take actions.";
|
|
625
|
-
}
|
|
626
|
-
state.mode = "agent";
|
|
627
|
-
setDefaultMode("agent");
|
|
628
|
-
console.log(renderModeSwitch("agent"));
|
|
629
|
-
const classicRenderer = attachClassicRenderer();
|
|
630
|
-
return withAbortableInput(async (signal) => runAgent(info.prompt, {
|
|
631
|
-
provider: state.provider,
|
|
632
|
-
model: requestModel,
|
|
633
|
-
history: state.messages,
|
|
634
|
-
signal,
|
|
635
|
-
session: state.session,
|
|
636
|
-
images,
|
|
637
|
-
onEvent: classicRenderer.onEvent,
|
|
638
|
-
}));
|
|
639
|
-
}
|
|
640
|
-
async function persistSession(state, name) {
|
|
641
|
-
const id = state.historyId ?? state.session.sessionId;
|
|
642
|
-
const record = await upsertSession(id, state.messages, name);
|
|
643
|
-
state.historyId = record.id;
|
|
644
|
-
}
|
|
645
|
-
async function handleSlash(line, state) {
|
|
646
|
-
const [command, ...args] = splitCommand(line);
|
|
647
|
-
switch (command) {
|
|
648
|
-
case "/ask":
|
|
649
|
-
state.mode = "ask";
|
|
650
|
-
setDefaultMode("ask");
|
|
651
|
-
console.log(renderModeSwitch("ask"));
|
|
652
|
-
return true;
|
|
653
|
-
case "/agent":
|
|
654
|
-
state.mode = "agent";
|
|
655
|
-
setDefaultMode("agent");
|
|
656
|
-
console.log(renderModeSwitch("agent"));
|
|
657
|
-
return true;
|
|
658
|
-
case "/model": {
|
|
659
|
-
const arg = args.join(" ").trim();
|
|
660
|
-
if (!arg) {
|
|
661
|
-
// No arg → interactive picker (up/down, Enter to select)
|
|
662
|
-
const picked = await pickModelInteractively(state.provider, state.model);
|
|
663
|
-
if (!picked) {
|
|
664
|
-
console.log(chalk.dim(" model unchanged"));
|
|
665
|
-
return true;
|
|
666
|
-
}
|
|
667
|
-
state.model = picked;
|
|
668
|
-
setProviderModel(state.provider, picked);
|
|
669
|
-
// Sticky text-only from a prior tools-unsupported 400 should not
|
|
670
|
-
// permanently disable native tools after the user switches models.
|
|
671
|
-
clearTextOnlyModels();
|
|
672
|
-
console.log(renderProviderSwitch(state.provider, picked));
|
|
673
|
-
maybePrintThinkingTip(state.provider, picked);
|
|
674
|
-
return true;
|
|
675
|
-
}
|
|
676
|
-
if (arg === "list" || arg === "ls") {
|
|
677
|
-
await showModelList(state.provider, state.model);
|
|
678
|
-
return true;
|
|
679
|
-
}
|
|
680
|
-
// Numeric → pick from known list
|
|
681
|
-
const num = parseInt(arg, 10);
|
|
682
|
-
const models = knownModels[state.provider] ?? [];
|
|
683
|
-
if (!isNaN(num) && num >= 1 && num <= models.length) {
|
|
684
|
-
const picked = models[num - 1];
|
|
685
|
-
state.model = picked;
|
|
686
|
-
setProviderModel(state.provider, picked);
|
|
687
|
-
clearTextOnlyModels();
|
|
688
|
-
console.log(renderProviderSwitch(state.provider, picked));
|
|
689
|
-
maybePrintThinkingTip(state.provider, picked);
|
|
690
|
-
}
|
|
691
|
-
else {
|
|
692
|
-
// Name → set directly
|
|
693
|
-
state.model = arg;
|
|
694
|
-
setProviderModel(state.provider, arg);
|
|
695
|
-
clearTextOnlyModels();
|
|
696
|
-
console.log(renderProviderSwitch(state.provider, arg));
|
|
697
|
-
maybePrintThinkingTip(state.provider, arg);
|
|
698
|
-
}
|
|
699
|
-
return true;
|
|
700
|
-
}
|
|
701
|
-
case "/models": {
|
|
702
|
-
await showAllProviderModels(args.join(" ").trim());
|
|
703
|
-
return true;
|
|
704
|
-
}
|
|
705
|
-
case "/provider":
|
|
706
|
-
case "/use": {
|
|
707
|
-
await providerSwitcher(args[0]);
|
|
708
|
-
const config = getConfig();
|
|
709
|
-
state.provider = config.defaultProvider;
|
|
710
|
-
state.model = getProviderModel(state.provider);
|
|
711
|
-
clearTextOnlyModels();
|
|
712
|
-
console.log(renderProviderSwitch(state.provider, state.model));
|
|
713
|
-
maybePrintThinkingTip(state.provider, state.model);
|
|
714
|
-
return true;
|
|
715
|
-
}
|
|
716
|
-
case "/set": {
|
|
717
|
-
await setKeyPicker(args[0], args[1]);
|
|
718
|
-
return true;
|
|
719
|
-
}
|
|
720
|
-
case "/unset": {
|
|
721
|
-
await unsetKeyPicker(args[0]);
|
|
722
|
-
return true;
|
|
723
|
-
}
|
|
724
|
-
case "/keys":
|
|
725
|
-
await printProviderKeys();
|
|
726
|
-
return true;
|
|
727
|
-
case "/info": {
|
|
728
|
-
const providerVal = args.join(" ").trim().toLowerCase();
|
|
729
|
-
let targetProvider = state.provider;
|
|
730
|
-
if (providerVal) {
|
|
731
|
-
try {
|
|
732
|
-
targetProvider = assertProvider(providerVal);
|
|
733
|
-
}
|
|
734
|
-
catch {
|
|
735
|
-
console.log(chalk.dim(` unknown provider: ${providerVal}`));
|
|
736
|
-
return true;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
const infoText = getProviderInfoText(targetProvider);
|
|
740
|
-
console.log(infoText);
|
|
741
|
-
return true;
|
|
742
|
-
}
|
|
743
|
-
case "/search":
|
|
744
|
-
case "/search-provider":
|
|
745
|
-
if (!args[0] || args[0] === "list" || args[0] === "ls") {
|
|
746
|
-
await printSearchProviderKeys();
|
|
747
|
-
}
|
|
748
|
-
else {
|
|
749
|
-
await useSearchProvider(args[0]);
|
|
750
|
-
}
|
|
751
|
-
return true;
|
|
752
|
-
case "/effort":
|
|
753
|
-
case "/reasoning": {
|
|
754
|
-
const arg = (args[0] ?? "").toLowerCase().trim();
|
|
755
|
-
const current = getConfig().thinking;
|
|
756
|
-
const supported = modelSupportsThinking(state.provider, state.model);
|
|
757
|
-
const allEfforts = [
|
|
758
|
-
{ value: "off", label: "off", desc: "disable thinking entirely" },
|
|
759
|
-
{ value: "none", label: "none", desc: "no reasoning tokens (fastest)" },
|
|
760
|
-
{ value: "minimal", label: "minimal", desc: "bare minimum reasoning" },
|
|
761
|
-
{ value: "low", label: "low", desc: "modest reasoning, optimized for speed" },
|
|
762
|
-
{ value: "medium", label: "medium", desc: "balanced quality and latency (default)" },
|
|
763
|
-
{ value: "high", label: "high", desc: "deep reasoning for complex tasks" },
|
|
764
|
-
{ value: "xhigh", label: "xhigh", desc: "maximum reasoning depth (highest latency)" },
|
|
765
|
-
{ value: "max", label: "max", desc: "highest supported reasoning depth for the model" },
|
|
766
|
-
];
|
|
767
|
-
if (!arg) {
|
|
768
|
-
// Show current status
|
|
769
|
-
const status = current.enabled ? chalk.green("on") : chalk.dim("off");
|
|
770
|
-
const support = supported
|
|
771
|
-
? chalk.green("supported")
|
|
772
|
-
: chalk.yellow("not advertised by this model");
|
|
773
|
-
console.log(chalk.dim(" thinking: ") +
|
|
774
|
-
status +
|
|
775
|
-
chalk.dim(" effort: ") +
|
|
776
|
-
chalk.cyan(current.effort) +
|
|
777
|
-
chalk.dim(" · ") +
|
|
778
|
-
support);
|
|
779
|
-
// Interactive picker for effort levels
|
|
780
|
-
const items = allEfforts.map((e) => {
|
|
781
|
-
const isActive = (e.value === "off" && !current.enabled) ||
|
|
782
|
-
(e.value !== "off" && current.enabled && current.effort === e.value);
|
|
783
|
-
const activeTag = isActive ? chalk.green(" (active)") : "";
|
|
784
|
-
const label = ` ${chalk.cyan(e.label.padEnd(8))} ${chalk.dim(e.desc)}${activeTag}`;
|
|
785
|
-
return { label, value: e.value, filterText: e.label };
|
|
786
|
-
});
|
|
787
|
-
const picked = await pickInline({
|
|
788
|
-
items,
|
|
789
|
-
header: chalk.dim(" ↑↓:navigate · enter:select · esc:cancel"),
|
|
790
|
-
pageSize: allEfforts.length,
|
|
791
|
-
});
|
|
792
|
-
if (!picked) {
|
|
793
|
-
console.log(chalk.dim(" effort unchanged"));
|
|
794
|
-
return true;
|
|
795
|
-
}
|
|
796
|
-
if (picked === "off") {
|
|
797
|
-
setThinking({ enabled: false });
|
|
798
|
-
console.log(chalk.dim(` thinking: ${chalk.dim("off")}`));
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
setThinking({ enabled: true, effort: picked });
|
|
802
|
-
console.log(chalk.dim(` thinking: ${chalk.green("on")} effort: ${chalk.cyan(picked)}`));
|
|
803
|
-
if (!supported) {
|
|
804
|
-
console.log(chalk.yellow(" note: current model may ignore the thinking flag."));
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
return true;
|
|
808
|
-
}
|
|
809
|
-
if (arg === "on" || arg === "enable" || arg === "true") {
|
|
810
|
-
setThinking({ enabled: true });
|
|
811
|
-
console.log(chalk.dim(` thinking: ${chalk.green("on")} (effort=${getConfig().thinking.effort})`));
|
|
812
|
-
if (!supported) {
|
|
813
|
-
console.log(chalk.yellow(" note: current model may ignore the thinking flag."));
|
|
814
|
-
}
|
|
815
|
-
return true;
|
|
816
|
-
}
|
|
817
|
-
if (arg === "off" || arg === "disable" || arg === "false" || arg === "none") {
|
|
818
|
-
setThinking({ enabled: false });
|
|
819
|
-
console.log(chalk.dim(` thinking: ${chalk.dim("off")}`));
|
|
820
|
-
return true;
|
|
821
|
-
}
|
|
822
|
-
const validEfforts = ["minimal", "low", "medium", "high", "xhigh", "max"];
|
|
823
|
-
if (validEfforts.includes(arg)) {
|
|
824
|
-
setThinking({ enabled: true, effort: arg });
|
|
825
|
-
console.log(chalk.dim(` thinking: ${chalk.green("on")} effort: ${chalk.cyan(arg)}`));
|
|
826
|
-
if (!supported) {
|
|
827
|
-
console.log(chalk.yellow(" note: current model may ignore the thinking flag."));
|
|
828
|
-
}
|
|
829
|
-
return true;
|
|
830
|
-
}
|
|
831
|
-
console.log(chalk.dim(" usage: /effort on|off|none|minimal|low|medium|high|xhigh|max"));
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
case "/clear":
|
|
835
|
-
state.messages.length = 0;
|
|
836
|
-
state.resumedMessageCount = 0;
|
|
837
|
-
state.session.planApproved.value = false;
|
|
838
|
-
console.log(chalk.dim(" context cleared"));
|
|
839
|
-
return true;
|
|
840
|
-
case "/new": {
|
|
841
|
-
// Save the current session if it has new messages, then start fresh
|
|
842
|
-
if (state.messages.length > state.resumedMessageCount) {
|
|
843
|
-
if (!getConfig().privateMode) {
|
|
844
|
-
await persistSession(state);
|
|
845
|
-
console.log(chalk.dim(" current session saved"));
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
resetClassicSessionContext(state);
|
|
849
|
-
state.messages.length = 0;
|
|
850
|
-
state.resumedMessageCount = 0;
|
|
851
|
-
state.historyId = undefined;
|
|
852
|
-
console.log(chalk.dim(" ✦ fresh session started"));
|
|
853
|
-
return true;
|
|
854
|
-
}
|
|
855
|
-
case "/history": {
|
|
856
|
-
if (args[0] && ["delete", "remove", "rm", "del"].includes(args[0].toLowerCase())) {
|
|
857
|
-
const targetId = args[1]?.trim() ?? args.slice(1).join(" ").trim();
|
|
858
|
-
if (!targetId) {
|
|
859
|
-
console.log(chalk.dim(" usage: /history delete <session-id>"));
|
|
860
|
-
return true;
|
|
861
|
-
}
|
|
862
|
-
const result = await deleteSession(targetId);
|
|
863
|
-
console.log(result.deleted ? chalk.green(` deleted ${targetId}`) : chalk.yellow(` ${result.detail}`));
|
|
864
|
-
return true;
|
|
865
|
-
}
|
|
866
|
-
const sessions = await listSessionSummaries(50, { recovery: "background" });
|
|
867
|
-
if (sessions.length === 0) {
|
|
868
|
-
console.log(chalk.dim(" no saved sessions"));
|
|
869
|
-
return true;
|
|
870
|
-
}
|
|
871
|
-
const sessionLabel = (s) => s.name?.trim() || s.id;
|
|
872
|
-
const items = sessions.map((s) => {
|
|
873
|
-
const name = sessionLabel(s);
|
|
874
|
-
const date = chalk.dim(s.createdAt.replace("T", " ").slice(0, 19));
|
|
875
|
-
const msgs = chalk.dim(`(${s.messageCount} msgs)`);
|
|
876
|
-
const label = `${date} ${chalk.white(name)} ${msgs}`;
|
|
877
|
-
return { label, value: s.id, filterText: `${name} ${s.createdAt}` };
|
|
878
|
-
});
|
|
879
|
-
const selectedId = await pickInline({
|
|
880
|
-
items,
|
|
881
|
-
header: chalk.dim(` ↑↓:navigate · type:filter · enter:resume · esc:cancel`),
|
|
882
|
-
pageSize: Math.min(15, items.length),
|
|
883
|
-
});
|
|
884
|
-
if (!selectedId) {
|
|
885
|
-
console.log(chalk.dim(" cancelled"));
|
|
886
|
-
return true;
|
|
887
|
-
}
|
|
888
|
-
clearActiveProjectRoot();
|
|
889
|
-
// Load the selected session
|
|
890
|
-
const session = await getSession(selectedId);
|
|
891
|
-
if (!session) {
|
|
892
|
-
console.log(chalk.red(" session not found"));
|
|
893
|
-
return true;
|
|
894
|
-
}
|
|
895
|
-
// Save current session first if it has new messages
|
|
896
|
-
if (state.messages.length > state.resumedMessageCount) {
|
|
897
|
-
if (!getConfig().privateMode) {
|
|
898
|
-
await persistSession(state);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
resetClassicSessionContext(state, session.id, {
|
|
902
|
-
folderName: session.workspaceFolder,
|
|
903
|
-
code: session.workspaceCode,
|
|
904
|
-
});
|
|
905
|
-
// Replay only a recent visual tail; the full path-backed model history is
|
|
906
|
-
// installed below for an explicit continuation.
|
|
907
|
-
console.log(chalk.dim(`\n ── resuming session ──\n`));
|
|
908
|
-
const displayMessages = session.messages
|
|
909
|
-
.filter((message) => message.role === "user" || message.role === "assistant")
|
|
910
|
-
.slice(-120);
|
|
911
|
-
const omittedDisplay = session.messages.filter((message) => message.role === "user" || message.role === "assistant").length - displayMessages.length;
|
|
912
|
-
if (omittedDisplay > 0) {
|
|
913
|
-
console.log(chalk.dim(` … ${omittedDisplay} older messages omitted from initial display …\n`));
|
|
914
|
-
}
|
|
915
|
-
for (const msg of displayMessages) {
|
|
916
|
-
if (msg.role === "user") {
|
|
917
|
-
console.log(`${PROMPT}${chalk.white(msg.content)}`);
|
|
918
|
-
}
|
|
919
|
-
else if (msg.role === "assistant") {
|
|
920
|
-
process.stdout.write(renderMarkdown(msg.content));
|
|
921
|
-
process.stdout.write("\n");
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
console.log(chalk.dim(` ── end of history · continue below ──\n`));
|
|
925
|
-
// Load into state so the user can continue. Adopt the resumed record's
|
|
926
|
-
// id so continuing the conversation updates that same row instead of
|
|
927
|
-
// minting a new, unnamed duplicate on the next save/exit.
|
|
928
|
-
state.messages.splice(0, state.messages.length, ...session.messages);
|
|
929
|
-
state.resumedMessageCount = session.messages.length;
|
|
930
|
-
state.historyId = session.id;
|
|
931
|
-
return true;
|
|
932
|
-
}
|
|
933
|
-
case "/save": {
|
|
934
|
-
const name = args.join(" ") || undefined;
|
|
935
|
-
if (state.historyId) {
|
|
936
|
-
await upsertSession(state.historyId, state.messages, name);
|
|
937
|
-
console.log(chalk.dim(` saved session ${state.historyId}`));
|
|
938
|
-
return true;
|
|
939
|
-
}
|
|
940
|
-
const record = await upsertSession(state.session.sessionId, state.messages, name);
|
|
941
|
-
state.historyId = record.id;
|
|
942
|
-
console.log(chalk.dim(` saved session ${record.id}`));
|
|
943
|
-
return true;
|
|
944
|
-
}
|
|
945
|
-
case "/reset": {
|
|
946
|
-
const result = await clearAllHistory();
|
|
947
|
-
console.log(chalk.dim(` all history cleared (${result.detail || "ok"})`));
|
|
948
|
-
return true;
|
|
949
|
-
}
|
|
950
|
-
case "/cwd": {
|
|
951
|
-
const dir = args.join(" ");
|
|
952
|
-
if (!dir) {
|
|
953
|
-
if (cwdIsBroken()) {
|
|
954
|
-
const recovered = recoverCwd();
|
|
955
|
-
console.log(chalk.yellow(` ⚠ the previous working directory no longer exists — moved to ${recovered}`));
|
|
956
|
-
}
|
|
957
|
-
else {
|
|
958
|
-
console.log(chalk.dim(` ${safeCwd()}`));
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
else {
|
|
962
|
-
try {
|
|
963
|
-
process.chdir(dir);
|
|
964
|
-
}
|
|
965
|
-
catch (error) {
|
|
966
|
-
console.log(chalk.red(` ✗ cannot change to ${dir}: ${error instanceof Error ? error.message : String(error)}`));
|
|
967
|
-
return true;
|
|
968
|
-
}
|
|
969
|
-
const config = getConfig();
|
|
970
|
-
updateConfig({
|
|
971
|
-
sandboxRoots: Array.from(new Set([...config.sandboxRoots, safeCwd()])),
|
|
972
|
-
});
|
|
973
|
-
console.log(chalk.dim(` cwd → ${safeCwd()}`));
|
|
974
|
-
}
|
|
975
|
-
return true;
|
|
976
|
-
}
|
|
977
|
-
case "/allow": {
|
|
978
|
-
const tool = args[0];
|
|
979
|
-
if (!tool) {
|
|
980
|
-
console.log(chalk.dim("usage: /allow <tool>|list"));
|
|
981
|
-
return true;
|
|
982
|
-
}
|
|
983
|
-
if (tool === "list" || tool === "ls") {
|
|
984
|
-
if (state.session.allow.size === 0) {
|
|
985
|
-
console.log(chalk.dim(" no session allows"));
|
|
986
|
-
}
|
|
987
|
-
else {
|
|
988
|
-
for (const allowed of state.session.allow) {
|
|
989
|
-
console.log(chalk.dim(` ✓ ${allowed}`));
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
return true;
|
|
993
|
-
}
|
|
994
|
-
state.session.allow.add(tool);
|
|
995
|
-
console.log(chalk.dim(` allowed ${tool} for this session only ✓`));
|
|
996
|
-
return true;
|
|
997
|
-
}
|
|
998
|
-
case "/disallow": {
|
|
999
|
-
const tool = args[0];
|
|
1000
|
-
if (!tool) {
|
|
1001
|
-
console.log(chalk.dim("usage: /disallow <tool>"));
|
|
1002
|
-
return true;
|
|
1003
|
-
}
|
|
1004
|
-
if (state.session.allow.delete(tool)) {
|
|
1005
|
-
console.log(chalk.dim(` revoked ${tool} ✓`));
|
|
1006
|
-
}
|
|
1007
|
-
else {
|
|
1008
|
-
console.log(chalk.dim(` ${tool} was not in the session allow list`));
|
|
1009
|
-
}
|
|
1010
|
-
return true;
|
|
1011
|
-
}
|
|
1012
|
-
case "/context": {
|
|
1013
|
-
const tokens = estimateMessagesTokens(state.messages);
|
|
1014
|
-
console.log(chalk.dim(` ${state.messages.length} message(s), ~${tokens.toLocaleString()} tokens estimated`));
|
|
1015
|
-
return true;
|
|
1016
|
-
}
|
|
1017
|
-
case "/plan": {
|
|
1018
|
-
const sub = (args[0] ?? "").toLowerCase();
|
|
1019
|
-
if (!sub || sub === "mode" || sub === "on" || sub === "enter") {
|
|
1020
|
-
state.mode = "plan";
|
|
1021
|
-
setDefaultMode("plan");
|
|
1022
|
-
console.log(renderModeSwitch("plan"));
|
|
1023
|
-
console.log(chalk.dim(" plan mode on — describe the multi-step task you want to plan.\n"));
|
|
1024
|
-
return true;
|
|
1025
|
-
}
|
|
1026
|
-
if (sub === "off" || sub === "agent") {
|
|
1027
|
-
state.mode = "agent";
|
|
1028
|
-
setDefaultMode("agent");
|
|
1029
|
-
console.log(renderModeSwitch("agent"));
|
|
1030
|
-
return true;
|
|
1031
|
-
}
|
|
1032
|
-
if (sub === "view" || sub === "show") {
|
|
1033
|
-
const plan = await loadPlan(state.session.sessionId).catch(() => undefined);
|
|
1034
|
-
if (!plan) {
|
|
1035
|
-
console.log(chalk.dim(" no active plan to view"));
|
|
1036
|
-
return true;
|
|
1037
|
-
}
|
|
1038
|
-
if (process.stdout.isTTY && input.isTTY) {
|
|
1039
|
-
await openPager({
|
|
1040
|
-
title: `plan · ${plan.goal}`,
|
|
1041
|
-
body: renderPlanDocument(plan),
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
else {
|
|
1045
|
-
console.log(renderPlanDocument(plan));
|
|
1046
|
-
}
|
|
1047
|
-
return true;
|
|
1048
|
-
}
|
|
1049
|
-
console.log(chalk.dim(" usage: /plan [view|off]"));
|
|
1050
|
-
return true;
|
|
1051
|
-
}
|
|
1052
|
-
case "/discard": {
|
|
1053
|
-
const plan = await loadPlan(state.session.sessionId).catch(() => undefined);
|
|
1054
|
-
if (!plan) {
|
|
1055
|
-
console.log(chalk.dim(" no active plan to discard"));
|
|
1056
|
-
return true;
|
|
1057
|
-
}
|
|
1058
|
-
await deletePlan(state.session.sessionId).catch(() => undefined);
|
|
1059
|
-
state.session.planApproved.value = false;
|
|
1060
|
-
console.log(chalk.yellow(` ✗ plan discarded — "${plan.goal}"`) +
|
|
1061
|
-
chalk.dim("\n later messages are now independent of it.\n"));
|
|
1062
|
-
return true;
|
|
1063
|
-
}
|
|
1064
|
-
case "/compact": {
|
|
1065
|
-
const before = state.messages.length;
|
|
1066
|
-
const compacted = compactMessages(state.messages, { budgetTokens: 0 });
|
|
1067
|
-
state.messages.splice(0, state.messages.length, ...compacted);
|
|
1068
|
-
console.log(chalk.dim(` compacted ${before} → ${state.messages.length} messages (~${estimateMessagesTokens(state.messages).toLocaleString()} tokens)`));
|
|
1069
|
-
return true;
|
|
1070
|
-
}
|
|
1071
|
-
case "/scope": {
|
|
1072
|
-
const sub = (args[0] ?? "show").toLowerCase();
|
|
1073
|
-
const { loadScope, saveScope, addScopeTargets, clearScope, isScopeActive, getScopePath, resetScopeCache, } = await import("./store/scope.js");
|
|
1074
|
-
if (sub === "show" || sub === "ls" || sub === "list") {
|
|
1075
|
-
resetScopeCache();
|
|
1076
|
-
const scope = await loadScope();
|
|
1077
|
-
if (!scope) {
|
|
1078
|
-
console.log(chalk.dim(" no engagement scope configured"));
|
|
1079
|
-
console.log(chalk.dim(` expected at: ${getScopePath()}`));
|
|
1080
|
-
console.log(chalk.dim(" create one with: /scope add domain1,domain2 or `clai scope add --targets ...`"));
|
|
1081
|
-
return true;
|
|
1082
|
-
}
|
|
1083
|
-
const status = isScopeActive(scope)
|
|
1084
|
-
? chalk.green("active")
|
|
1085
|
-
: chalk.yellow("expired or empty");
|
|
1086
|
-
console.log(chalk.dim(` scope: ${scope.name ?? "(unnamed)"} [${status}]`));
|
|
1087
|
-
console.log(chalk.dim(` authorized: ${scope.authorizedTargets.join(", ")}`));
|
|
1088
|
-
if (scope.excludedTargets && scope.excludedTargets.length > 0) {
|
|
1089
|
-
console.log(chalk.dim(` excluded: ${scope.excludedTargets.join(", ")}`));
|
|
1090
|
-
}
|
|
1091
|
-
if (scope.expiresAt) {
|
|
1092
|
-
console.log(chalk.dim(` expires: ${scope.expiresAt}`));
|
|
1093
|
-
}
|
|
1094
|
-
return true;
|
|
1095
|
-
}
|
|
1096
|
-
if (sub === "clear" || sub === "reset" || sub === "off") {
|
|
1097
|
-
await clearScope();
|
|
1098
|
-
console.log(chalk.dim(" engagement scope cleared"));
|
|
1099
|
-
return true;
|
|
1100
|
-
}
|
|
1101
|
-
if (sub === "add") {
|
|
1102
|
-
const rest = args.slice(1).join(" ").trim();
|
|
1103
|
-
if (!rest) {
|
|
1104
|
-
console.log(chalk.dim(" usage: /scope add <target1,target2,...>"));
|
|
1105
|
-
return true;
|
|
1106
|
-
}
|
|
1107
|
-
const targets = rest
|
|
1108
|
-
.split(/\s+/)[0]
|
|
1109
|
-
.split(",")
|
|
1110
|
-
.map((t) => t.trim())
|
|
1111
|
-
.filter(Boolean);
|
|
1112
|
-
if (targets.length === 0) {
|
|
1113
|
-
console.log(chalk.dim(" no targets parsed"));
|
|
1114
|
-
return true;
|
|
1115
|
-
}
|
|
1116
|
-
const scope = await addScopeTargets(targets);
|
|
1117
|
-
console.log(chalk.dim(` added ${targets.length} target(s); scope now has ${scope.authorizedTargets.length}`));
|
|
1118
|
-
return true;
|
|
1119
|
-
}
|
|
1120
|
-
if (sub === "new" || sub === "set") {
|
|
1121
|
-
const rest = args.slice(1).join(" ").trim();
|
|
1122
|
-
if (!rest) {
|
|
1123
|
-
console.log(chalk.dim(" usage: /scope new <target1,target2,...> [name=<engagement>] [expires=<iso>]"));
|
|
1124
|
-
return true;
|
|
1125
|
-
}
|
|
1126
|
-
// Parse: first whitespace-delimited token is the targets list,
|
|
1127
|
-
// remaining `key=value` pairs configure name/expires/note.
|
|
1128
|
-
const tokens = rest.split(/\s+/);
|
|
1129
|
-
const targetsRaw = tokens[0] ?? "";
|
|
1130
|
-
const targets = targetsRaw
|
|
1131
|
-
.split(",")
|
|
1132
|
-
.map((t) => t.trim())
|
|
1133
|
-
.filter(Boolean);
|
|
1134
|
-
if (targets.length === 0) {
|
|
1135
|
-
console.log(chalk.dim(" no targets parsed"));
|
|
1136
|
-
return true;
|
|
1137
|
-
}
|
|
1138
|
-
let name;
|
|
1139
|
-
let expires;
|
|
1140
|
-
let note;
|
|
1141
|
-
let exclude;
|
|
1142
|
-
for (const token of tokens.slice(1)) {
|
|
1143
|
-
const eq = token.indexOf("=");
|
|
1144
|
-
if (eq < 0)
|
|
1145
|
-
continue;
|
|
1146
|
-
const key = token.slice(0, eq).toLowerCase();
|
|
1147
|
-
const value = token.slice(eq + 1);
|
|
1148
|
-
if (key === "name")
|
|
1149
|
-
name = value;
|
|
1150
|
-
else if (key === "expires")
|
|
1151
|
-
expires = value;
|
|
1152
|
-
else if (key === "note")
|
|
1153
|
-
note = value;
|
|
1154
|
-
else if (key === "exclude")
|
|
1155
|
-
exclude = value
|
|
1156
|
-
.split(",")
|
|
1157
|
-
.map((t) => t.trim())
|
|
1158
|
-
.filter(Boolean);
|
|
1159
|
-
}
|
|
1160
|
-
const scope = {
|
|
1161
|
-
name,
|
|
1162
|
-
authorizedTargets: targets,
|
|
1163
|
-
excludedTargets: exclude,
|
|
1164
|
-
authorizationNote: note,
|
|
1165
|
-
createdAt: new Date().toISOString(),
|
|
1166
|
-
expiresAt: expires,
|
|
1167
|
-
};
|
|
1168
|
-
await saveScope(scope);
|
|
1169
|
-
console.log(chalk.dim(` saved scope${name ? ` "${name}"` : ""} with ${targets.length} target(s)`));
|
|
1170
|
-
return true;
|
|
1171
|
-
}
|
|
1172
|
-
console.log(chalk.dim(" usage: /scope [show|clear|new <targets>|add <targets> [key=value]...]"));
|
|
1173
|
-
return true;
|
|
1174
|
-
}
|
|
1175
|
-
case "/privacy": {
|
|
1176
|
-
const sub = (args[0] ?? "status").toLowerCase();
|
|
1177
|
-
if (sub === "on" || sub === "enable") {
|
|
1178
|
-
updateConfig({ privateMode: true });
|
|
1179
|
-
console.log(chalk.dim(" privateMode: " +
|
|
1180
|
-
chalk.green("on") +
|
|
1181
|
-
" (history not written; in-memory only)"));
|
|
1182
|
-
return true;
|
|
1183
|
-
}
|
|
1184
|
-
if (sub === "off" || sub === "disable") {
|
|
1185
|
-
updateConfig({ privateMode: false });
|
|
1186
|
-
console.log(chalk.dim(" privateMode: " + chalk.dim("off")));
|
|
1187
|
-
return true;
|
|
1188
|
-
}
|
|
1189
|
-
if (sub === "status" || sub === "show") {
|
|
1190
|
-
const cfg = getConfig();
|
|
1191
|
-
console.log(chalk.dim(` privateMode: ${cfg.privateMode ? chalk.green("on") : chalk.dim("off")} retention: ${cfg.historyRetentionLimit || "unlimited"}`));
|
|
1192
|
-
return true;
|
|
1193
|
-
}
|
|
1194
|
-
const { clearAllHistory } = await import("./store/history.js");
|
|
1195
|
-
const { clearAuditLogs, clearArtifacts } = await import("./store/logs.js");
|
|
1196
|
-
if (sub === "clear-history") {
|
|
1197
|
-
const r = await clearAllHistory();
|
|
1198
|
-
console.log(chalk.dim(` history cleared (${r.detail || "ok"})`));
|
|
1199
|
-
return true;
|
|
1200
|
-
}
|
|
1201
|
-
if (sub === "clear-logs") {
|
|
1202
|
-
const r = await clearAuditLogs();
|
|
1203
|
-
console.log(chalk.dim(` audit logs cleared (${r.removed} files)`));
|
|
1204
|
-
return true;
|
|
1205
|
-
}
|
|
1206
|
-
if (sub === "clear-artifacts") {
|
|
1207
|
-
const r = await clearArtifacts();
|
|
1208
|
-
console.log(chalk.dim(` artifacts cleared (${r.removed} files)`));
|
|
1209
|
-
return true;
|
|
1210
|
-
}
|
|
1211
|
-
if (sub === "clear-all") {
|
|
1212
|
-
const a = await clearAllHistory();
|
|
1213
|
-
const b = await clearAuditLogs();
|
|
1214
|
-
const c = await clearArtifacts();
|
|
1215
|
-
console.log(chalk.dim(` history (${a.detail || "ok"}); logs (${b.removed}); artifacts (${c.removed})`));
|
|
1216
|
-
return true;
|
|
1217
|
-
}
|
|
1218
|
-
console.log(chalk.dim(" usage: /privacy [status|on|off|clear-history|clear-logs|clear-artifacts|clear-all]"));
|
|
1219
|
-
return true;
|
|
1220
|
-
}
|
|
1221
|
-
case "/freeonly": {
|
|
1222
|
-
const arg = (args[0] ?? "").toLowerCase().trim();
|
|
1223
|
-
if (!arg) {
|
|
1224
|
-
const value = getConfig().freeOnly;
|
|
1225
|
-
console.log(chalk.dim(` freeOnly: ${value ? chalk.green("on") : chalk.dim("off")} (applies when /fallback is on)`));
|
|
1226
|
-
return true;
|
|
1227
|
-
}
|
|
1228
|
-
if (arg === "on" || arg === "true" || arg === "enable") {
|
|
1229
|
-
updateConfig({ freeOnly: true });
|
|
1230
|
-
console.log(chalk.dim(" freeOnly: " + chalk.green("on")));
|
|
1231
|
-
return true;
|
|
1232
|
-
}
|
|
1233
|
-
if (arg === "off" || arg === "false" || arg === "disable") {
|
|
1234
|
-
updateConfig({ freeOnly: false });
|
|
1235
|
-
console.log(chalk.dim(" freeOnly: " + chalk.dim("off")));
|
|
1236
|
-
return true;
|
|
1237
|
-
}
|
|
1238
|
-
console.log(chalk.dim(" usage: /freeonly [on|off]"));
|
|
1239
|
-
return true;
|
|
1240
|
-
}
|
|
1241
|
-
case "/fallback": {
|
|
1242
|
-
const arg = (args[0] ?? "").toLowerCase().trim();
|
|
1243
|
-
if (!arg) {
|
|
1244
|
-
const value = getConfig().providerFallback;
|
|
1245
|
-
console.log(chalk.dim(` fallback: ${value ? chalk.green("on") : chalk.dim("off")} (selected provider/model only when off)`));
|
|
1246
|
-
return true;
|
|
1247
|
-
}
|
|
1248
|
-
if (arg === "on" || arg === "true" || arg === "enable") {
|
|
1249
|
-
updateConfig({ providerFallback: true });
|
|
1250
|
-
console.log(chalk.dim(" fallback: " + chalk.green("on")));
|
|
1251
|
-
return true;
|
|
1252
|
-
}
|
|
1253
|
-
if (arg === "off" || arg === "false" || arg === "disable") {
|
|
1254
|
-
updateConfig({ providerFallback: false });
|
|
1255
|
-
console.log(chalk.dim(" fallback: " + chalk.dim("off")));
|
|
1256
|
-
return true;
|
|
1257
|
-
}
|
|
1258
|
-
console.log(chalk.dim(" usage: /fallback [on|off]"));
|
|
1259
|
-
return true;
|
|
1260
|
-
}
|
|
1261
|
-
case "/output": {
|
|
1262
|
-
const target = args[0] ?? "last";
|
|
1263
|
-
if (target === "list" || target === "ls") {
|
|
1264
|
-
const all = listViewports();
|
|
1265
|
-
if (all.length === 0) {
|
|
1266
|
-
console.log(chalk.dim(" no tool outputs recorded yet"));
|
|
1267
|
-
}
|
|
1268
|
-
else {
|
|
1269
|
-
for (const v of all) {
|
|
1270
|
-
console.log(chalk.dim(` ${v.id} — ${v.toolName} ${v.argsDisplay}${v.artifactPath ? ` (${v.artifactPath})` : ""}`));
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
return true;
|
|
1274
|
-
}
|
|
1275
|
-
const viewport = target === "last" ? getLastViewport() : getViewport(target);
|
|
1276
|
-
if (!viewport) {
|
|
1277
|
-
console.log(chalk.dim(` no viewport: ${target}`));
|
|
1278
|
-
return true;
|
|
1279
|
-
}
|
|
1280
|
-
// On a TTY, open the alternate-screen pager so users can scroll long
|
|
1281
|
-
// outputs and close with q / Ctrl+O. In non-TTY contexts (piped or
|
|
1282
|
-
// CI), fall back to the inline toggle so the artifact still lands in
|
|
1283
|
-
// the captured stdout.
|
|
1284
|
-
if (process.stdout.isTTY) {
|
|
1285
|
-
await openViewportPager(viewport.id);
|
|
1286
|
-
}
|
|
1287
|
-
else {
|
|
1288
|
-
await toggleViewport(viewport.id);
|
|
1289
|
-
}
|
|
1290
|
-
return true;
|
|
1291
|
-
}
|
|
1292
|
-
case "/think":
|
|
1293
|
-
case "/thinking": {
|
|
1294
|
-
if (getLastThinking()) {
|
|
1295
|
-
console.log(renderAllThinking());
|
|
1296
|
-
}
|
|
1297
|
-
else {
|
|
1298
|
-
console.log(chalk.dim(" No thinking from last response."));
|
|
1299
|
-
}
|
|
1300
|
-
return true;
|
|
1301
|
-
}
|
|
1302
|
-
case "/exit":
|
|
1303
|
-
case "/quit":
|
|
1304
|
-
process.stdout.write(chalk.dim(" Goodbye!\n"));
|
|
1305
|
-
return false;
|
|
1306
|
-
case "/clean": {
|
|
1307
|
-
// A fresh session, not just a cleared screen: new identity, policy,
|
|
1308
|
-
// plan approval, allowances and scratch workspace.
|
|
1309
|
-
state.messages.length = 0;
|
|
1310
|
-
state.resumedMessageCount = 0;
|
|
1311
|
-
state.historyId = undefined;
|
|
1312
|
-
resetClassicSessionContext(state);
|
|
1313
|
-
clearViewports();
|
|
1314
|
-
clearThinking();
|
|
1315
|
-
// Clear the entire screen and move cursor to top
|
|
1316
|
-
process.stdout.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1317
|
-
// Re-render the startup intro card
|
|
1318
|
-
console.log(renderIntroCard({
|
|
1319
|
-
version: getCurrentVersion(),
|
|
1320
|
-
workdir: safeCwd(),
|
|
1321
|
-
model: state.model,
|
|
1322
|
-
provider: state.provider,
|
|
1323
|
-
mode: state.mode,
|
|
1324
|
-
permissions: getConfig().permissions ?? "default",
|
|
1325
|
-
}));
|
|
1326
|
-
console.log();
|
|
1327
|
-
return true;
|
|
1328
|
-
}
|
|
1329
|
-
case "/update":
|
|
1330
|
-
await runUpdate();
|
|
1331
|
-
return true;
|
|
1332
|
-
case "/help":
|
|
1333
|
-
console.log(help());
|
|
1334
|
-
return true;
|
|
1335
|
-
default:
|
|
1336
|
-
console.log(chalk.dim(` unknown command: ${command}. Try /help`));
|
|
1337
|
-
return true;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
export async function startRepl(options = {}) {
|
|
1341
|
-
const config = getConfig();
|
|
1342
|
-
const provider = options.provider
|
|
1343
|
-
? assertProvider(options.provider)
|
|
1344
|
-
: config.defaultProvider;
|
|
1345
|
-
// Bind a unique session workspace before any tool/scratch path is resolved.
|
|
1346
|
-
beginSessionWorkspace();
|
|
1347
|
-
const state = {
|
|
1348
|
-
mode: options.mode ?? config.defaultMode,
|
|
1349
|
-
provider,
|
|
1350
|
-
model: options.model ?? getProviderModel(provider),
|
|
1351
|
-
messages: [],
|
|
1352
|
-
session: createSessionPolicy(),
|
|
1353
|
-
resumedMessageCount: 0,
|
|
1354
|
-
historyId: undefined,
|
|
1355
|
-
};
|
|
1356
|
-
const promptHistory = [];
|
|
1357
|
-
let isReadingPrompt = false;
|
|
1358
|
-
let outputShortcutBusy = false;
|
|
1359
|
-
let lastOutputShortcutAt = 0;
|
|
1360
|
-
let abortPressCount = 0;
|
|
1361
|
-
let lastEscapeAt = 0;
|
|
1362
|
-
emitKeypressEvents(input);
|
|
1363
|
-
// Only expected abort noise is tolerated. Anything else leaves the process
|
|
1364
|
-
// with unknown invariants, so it shuts down through the same boundary.
|
|
1365
|
-
const handleFatal = (kind, cause) => {
|
|
1366
|
-
if (isAbortLikeError(cause))
|
|
1367
|
-
return;
|
|
1368
|
-
const message = cause instanceof Error ? cause.message : String(cause);
|
|
1369
|
-
process.stderr.write(chalk.red(`\n ✖ fatal ${kind}: ${redactSecrets(message)}\n`));
|
|
1370
|
-
void shutdown(1);
|
|
1371
|
-
};
|
|
1372
|
-
const handleUnhandledRejection = (reason) => handleFatal("rejection", reason);
|
|
1373
|
-
const handleUncaughtException = (error) => handleFatal("exception", error);
|
|
1374
|
-
process.on("unhandledRejection", handleUnhandledRejection);
|
|
1375
|
-
process.on("uncaughtException", handleUncaughtException);
|
|
1376
|
-
// Double Esc cancels the turn + jobs; Ctrl+C aborts; Ctrl+T toggles thinking
|
|
1377
|
-
if (process.stdin.isTTY) {
|
|
1378
|
-
process.stdin.setRawMode(false);
|
|
1379
|
-
}
|
|
1380
|
-
const handleThinkingShortcut = () => {
|
|
1381
|
-
process.stdout.write(`\n${renderThinkingToggleMessage()}\n`);
|
|
1382
|
-
};
|
|
1383
|
-
const handleOutputShortcut = async () => {
|
|
1384
|
-
if (outputShortcutBusy)
|
|
1385
|
-
return;
|
|
1386
|
-
const now = Date.now();
|
|
1387
|
-
if (now - lastOutputShortcutAt < 400)
|
|
1388
|
-
return;
|
|
1389
|
-
lastOutputShortcutAt = now;
|
|
1390
|
-
outputShortcutBusy = true;
|
|
1391
|
-
try {
|
|
1392
|
-
const v = getLastViewport();
|
|
1393
|
-
if (!v) {
|
|
1394
|
-
process.stdout.write(chalk.dim("\n (no tool output to expand yet)\n"));
|
|
1395
|
-
return;
|
|
1396
|
-
}
|
|
1397
|
-
if (currentAbortController || !isReadingPrompt) {
|
|
1398
|
-
if (v.artifactPath) {
|
|
1399
|
-
process.stdout.write(chalk.dim(`\n ▸ ${v.toolName} output is streaming to:\n ${v.artifactPath}\n` +
|
|
1400
|
-
` (tail it in another terminal now, or press Ctrl+O here after it finishes)\n`));
|
|
1401
|
-
}
|
|
1402
|
-
else {
|
|
1403
|
-
process.stdout.write(chalk.dim(`\n (press Ctrl+O at the prompt after it finishes to open ${v.toolName})\n`));
|
|
1404
|
-
}
|
|
1405
|
-
return;
|
|
1406
|
-
}
|
|
1407
|
-
// Idle path: open the full output in the alternate-screen pager.
|
|
1408
|
-
// Keys (q / ESC / Ctrl+O) inside the pager close it and return here.
|
|
1409
|
-
await openViewportPager(v.id);
|
|
1410
|
-
}
|
|
1411
|
-
finally {
|
|
1412
|
-
outputShortcutBusy = false;
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1415
|
-
let planShortcutBusy = false;
|
|
1416
|
-
const handlePlanShortcut = async () => {
|
|
1417
|
-
if (planShortcutBusy)
|
|
1418
|
-
return;
|
|
1419
|
-
planShortcutBusy = true;
|
|
1420
|
-
try {
|
|
1421
|
-
// Only open the pager when idle (same reasoning as Ctrl+O).
|
|
1422
|
-
if (currentAbortController || !isReadingPrompt) {
|
|
1423
|
-
process.stdout.write(chalk.dim("\n (press Ctrl+P at the prompt when idle to view the plan)\n"));
|
|
1424
|
-
return;
|
|
1425
|
-
}
|
|
1426
|
-
const plan = await loadPlan(state.session.sessionId).catch(() => undefined);
|
|
1427
|
-
if (!plan) {
|
|
1428
|
-
process.stdout.write(chalk.dim('\n (no plan yet — ask clai to plan a multi-step task, e.g. "build a react blog app")\n'));
|
|
1429
|
-
return;
|
|
1430
|
-
}
|
|
1431
|
-
await openPager({
|
|
1432
|
-
title: `plan · ${plan.goal}`,
|
|
1433
|
-
body: renderPlanDocument(plan),
|
|
1434
|
-
});
|
|
1435
|
-
}
|
|
1436
|
-
finally {
|
|
1437
|
-
planShortcutBusy = false;
|
|
1438
|
-
}
|
|
1439
|
-
};
|
|
1440
|
-
const handleKeypress = (_sequence, key) => {
|
|
1441
|
-
if (isPagerActive())
|
|
1442
|
-
return;
|
|
1443
|
-
if (isCtrlT(key) && !isReadingPrompt)
|
|
1444
|
-
handleThinkingShortcut();
|
|
1445
|
-
if (isCtrlO(key) && !isReadingPrompt) {
|
|
1446
|
-
void handleOutputShortcut();
|
|
1447
|
-
}
|
|
1448
|
-
if (isCtrlP(key) && !isReadingPrompt) {
|
|
1449
|
-
void handlePlanShortcut();
|
|
1450
|
-
}
|
|
1451
|
-
if (isEscape(key) && currentAbortController) {
|
|
1452
|
-
const now = Date.now();
|
|
1453
|
-
if (lastEscapeAt > 0 && now - lastEscapeAt < 1_500) {
|
|
1454
|
-
lastEscapeAt = 0;
|
|
1455
|
-
abortPressCount += 1;
|
|
1456
|
-
currentAbortController.abort();
|
|
1457
|
-
void jobManager.cancelAll(state.session.sessionId);
|
|
1458
|
-
void interactiveSessionManager.cancelOwner(state.session.sessionId);
|
|
1459
|
-
process.stdout.write(chalk.yellow("\n ⏹ cancelling turn + session processes…\n"));
|
|
1460
|
-
}
|
|
1461
|
-
else {
|
|
1462
|
-
lastEscapeAt = now;
|
|
1463
|
-
process.stdout.write(chalk.dim("\n (press Esc again to cancel turn + session jobs)\n"));
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
else if (isCtrlC(key) && currentAbortController) {
|
|
1467
|
-
abortPressCount += 1;
|
|
1468
|
-
currentAbortController.abort();
|
|
1469
|
-
if (abortPressCount === 1) {
|
|
1470
|
-
process.stdout.write(chalk.yellow("\n ⏹ aborting…\n"));
|
|
1471
|
-
}
|
|
1472
|
-
else {
|
|
1473
|
-
process.stdout.write(chalk.yellow(" ⏹ force-killing…\n"));
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
};
|
|
1477
|
-
input.on("keypress", handleKeypress);
|
|
1478
|
-
const siginfo = "SIGINFO";
|
|
1479
|
-
let siginfoRegistered = false;
|
|
1480
|
-
try {
|
|
1481
|
-
process.on(siginfo, handleThinkingShortcut);
|
|
1482
|
-
siginfoRegistered = true;
|
|
1483
|
-
}
|
|
1484
|
-
catch {
|
|
1485
|
-
// SIGINFO is macOS/BSD-specific; other platforms can still use /think.
|
|
1486
|
-
}
|
|
1487
|
-
let lastSigintAt = 0;
|
|
1488
|
-
let shuttingDown = false;
|
|
1489
|
-
/** One shutdown boundary for normal quit, signals and fatal errors. */
|
|
1490
|
-
const finalizeSession = async () => {
|
|
1491
|
-
isReadingPrompt = false;
|
|
1492
|
-
input.off("keypress", handleKeypress);
|
|
1493
|
-
process.off("SIGINT", handleSigint);
|
|
1494
|
-
process.off("unhandledRejection", handleUnhandledRejection);
|
|
1495
|
-
process.off("uncaughtException", handleUncaughtException);
|
|
1496
|
-
if (siginfoRegistered)
|
|
1497
|
-
process.off(siginfo, handleThinkingShortcut);
|
|
1498
|
-
if (state.messages.length > state.resumedMessageCount) {
|
|
1499
|
-
// Honor `--no-history` and the persistent privateMode setting.
|
|
1500
|
-
if (!options.noHistory && !getConfig().privateMode) {
|
|
1501
|
-
await persistSession(state).catch(() => undefined);
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
if (input.isTTY)
|
|
1505
|
-
input.setRawMode(false);
|
|
1506
|
-
};
|
|
1507
|
-
const shutdown = async (code) => {
|
|
1508
|
-
if (shuttingDown)
|
|
1509
|
-
return;
|
|
1510
|
-
shuttingDown = true;
|
|
1511
|
-
const cleanup = await interactiveSessionManager
|
|
1512
|
-
.closeAll("app-shutdown")
|
|
1513
|
-
.catch(() => undefined);
|
|
1514
|
-
for (const failure of cleanup?.failures ?? []) {
|
|
1515
|
-
process.stderr.write(`clai interactive-session cleanup: [${failure.code}] ${failure.message}\n`);
|
|
1516
|
-
}
|
|
1517
|
-
await finalizeSession();
|
|
1518
|
-
// Force exit so lingering handles (timers, watchers, bg jobs) don't keep
|
|
1519
|
-
// the process alive after the user chose to quit.
|
|
1520
|
-
process.exitCode = code;
|
|
1521
|
-
process.exit(code);
|
|
1522
|
-
};
|
|
1523
|
-
const handleSigint = () => {
|
|
1524
|
-
if (currentAbortController) {
|
|
1525
|
-
currentAbortController.abort();
|
|
1526
|
-
return;
|
|
1527
|
-
}
|
|
1528
|
-
if (isReadingPrompt) {
|
|
1529
|
-
// readPromptLine's keypress handler owns the prompt-level Ctrl+C
|
|
1530
|
-
// semantics; do nothing here so the two paths never fight.
|
|
1531
|
-
return;
|
|
1532
|
-
}
|
|
1533
|
-
const now = Date.now();
|
|
1534
|
-
if (now - lastSigintAt < 1_000) {
|
|
1535
|
-
console.log();
|
|
1536
|
-
void shutdown(0);
|
|
1537
|
-
return;
|
|
1538
|
-
}
|
|
1539
|
-
lastSigintAt = now;
|
|
1540
|
-
console.log(chalk.dim("\n (press Ctrl+C again to exit)"));
|
|
1541
|
-
};
|
|
1542
|
-
process.on("SIGINT", handleSigint);
|
|
1543
|
-
// Startup intro card
|
|
1544
|
-
console.log(renderIntroCard({
|
|
1545
|
-
version: getCurrentVersion(),
|
|
1546
|
-
workdir: safeCwd(),
|
|
1547
|
-
model: state.model,
|
|
1548
|
-
provider: state.provider,
|
|
1549
|
-
mode: state.mode,
|
|
1550
|
-
permissions: getConfig().permissions ?? "default",
|
|
1551
|
-
}));
|
|
1552
|
-
console.log();
|
|
1553
|
-
// Hint thinking-capable users that the toggle exists. We default it to
|
|
1554
|
-
// off for speed, since on NIM many models route through a much slower
|
|
1555
|
-
// chat-template path when reasoning is enabled.
|
|
1556
|
-
if (!getConfig().thinking.enabled &&
|
|
1557
|
-
modelSupportsThinking(state.provider, state.model)) {
|
|
1558
|
-
console.log(chalk.dim(" 💡 ") +
|
|
1559
|
-
chalk.dim(`${state.model} supports reasoning. Run `) +
|
|
1560
|
-
chalk.cyan("/effort") +
|
|
1561
|
-
chalk.dim(" to pick an effort level or ") +
|
|
1562
|
-
chalk.cyan("/effort high") +
|
|
1563
|
-
chalk.dim(" to enable directly.\n"));
|
|
1564
|
-
}
|
|
1565
|
-
// Non-blocking update check
|
|
1566
|
-
checkForUpdateSilent();
|
|
1567
|
-
try {
|
|
1568
|
-
while (true) {
|
|
1569
|
-
isReadingPrompt = true;
|
|
1570
|
-
const rawLine = await readPromptLine({
|
|
1571
|
-
history: promptHistory,
|
|
1572
|
-
onThinkingShortcut: handleThinkingShortcut,
|
|
1573
|
-
onOutputShortcut: handleOutputShortcut,
|
|
1574
|
-
onPlanShortcut: handlePlanShortcut,
|
|
1575
|
-
});
|
|
1576
|
-
isReadingPrompt = false;
|
|
1577
|
-
if (rawLine === PROMPT_EXIT_INTENT)
|
|
1578
|
-
break;
|
|
1579
|
-
const line = rawLine.trim();
|
|
1580
|
-
if (!line)
|
|
1581
|
-
continue;
|
|
1582
|
-
// /implement — approve the active plan and execute it
|
|
1583
|
-
// Handled here (not in handleSlash) because it must trigger a full
|
|
1584
|
-
// agent run with the plan marked approved, not just print something.
|
|
1585
|
-
let implementApproved = false;
|
|
1586
|
-
let effectiveLine = line;
|
|
1587
|
-
if (line === "/implement" || line.startsWith("/implement ")) {
|
|
1588
|
-
const plan = await loadPlan(state.session.sessionId).catch(() => undefined);
|
|
1589
|
-
if (!plan) {
|
|
1590
|
-
console.log(chalk.dim(" no plan to implement — ask clai to plan a multi-step task first"));
|
|
1591
|
-
continue;
|
|
1592
|
-
}
|
|
1593
|
-
if (isPlanSuccessful(plan)) {
|
|
1594
|
-
console.log(chalk.dim(" this plan is already complete ✓"));
|
|
1595
|
-
continue;
|
|
1596
|
-
}
|
|
1597
|
-
plan.status = "approved";
|
|
1598
|
-
await mutatePlan(plan.sessionId, (draft) => {
|
|
1599
|
-
if (draft.status === "approved")
|
|
1600
|
-
return false;
|
|
1601
|
-
draft.status = "approved";
|
|
1602
|
-
return true;
|
|
1603
|
-
}).catch(() => undefined);
|
|
1604
|
-
state.session.planApproved.value = true;
|
|
1605
|
-
console.log(chalk.cyan(" ✦ plan approved — clai will now execute it\n"));
|
|
1606
|
-
console.log(renderPlanChecklist(plan) + "\n");
|
|
1607
|
-
implementApproved = true;
|
|
1608
|
-
if (state.mode === "plan") {
|
|
1609
|
-
state.mode = "agent";
|
|
1610
|
-
setDefaultMode("agent");
|
|
1611
|
-
console.log(renderModeSwitch("agent"));
|
|
1612
|
-
}
|
|
1613
|
-
effectiveLine = IMPLEMENT_DIRECTIVE;
|
|
1614
|
-
}
|
|
1615
|
-
// Continuation detection
|
|
1616
|
-
// When the user types "continue" after the agent paused at a step limit,
|
|
1617
|
-
// augment the bare keyword with rich resumption context so the model
|
|
1618
|
-
// picks up from where it left off instead of restarting.
|
|
1619
|
-
const REPL_CONTINUE_RE = /^(?:continue|proceed|keep\s+going|go\s+on|resume|finish\s+it|next)$/i;
|
|
1620
|
-
if (REPL_CONTINUE_RE.test(line) &&
|
|
1621
|
-
!implementApproved &&
|
|
1622
|
-
state.messages.length > 0) {
|
|
1623
|
-
const lastMsg = state.messages[state.messages.length - 1];
|
|
1624
|
-
if (lastMsg?.role === "assistant" &&
|
|
1625
|
-
/Session paused after \d+ steps/i.test(lastMsg.content)) {
|
|
1626
|
-
effectiveLine =
|
|
1627
|
-
"Continue from where you left off. " +
|
|
1628
|
-
"The previous session summary is in your context — review it and resume the next pending task. " +
|
|
1629
|
-
"Do NOT repeat completed work. Do NOT re-fetch data you already have.";
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
// Only remember real prompts in the history ring. Slash commands
|
|
1633
|
-
// are operational toggles (eg /model, /provider) and surfacing them
|
|
1634
|
-
// when the user presses ↑ to recall a past prompt is just noise.
|
|
1635
|
-
if (!looksLikeSlashCommand(line) &&
|
|
1636
|
-
promptHistory[promptHistory.length - 1] !== line) {
|
|
1637
|
-
promptHistory.push(line);
|
|
1638
|
-
}
|
|
1639
|
-
if (looksLikeSlashCommand(line) && !implementApproved) {
|
|
1640
|
-
// Slash commands may call inquirer/password prompts, which expect the
|
|
1641
|
-
// terminal in cooked mode. Normal model runs keep raw mode enabled so
|
|
1642
|
-
// ESC/Ctrl+C can abort while streaming.
|
|
1643
|
-
input.pause();
|
|
1644
|
-
if (input.isTTY)
|
|
1645
|
-
input.setRawMode(false);
|
|
1646
|
-
const shouldContinue = await handleSlash(line, state);
|
|
1647
|
-
if (!shouldContinue)
|
|
1648
|
-
break;
|
|
1649
|
-
continue;
|
|
1650
|
-
}
|
|
1651
|
-
try {
|
|
1652
|
-
clearThinking();
|
|
1653
|
-
abortPressCount = 0;
|
|
1654
|
-
lastEscapeAt = 0;
|
|
1655
|
-
let assistantContent = "";
|
|
1656
|
-
// Expand @file mentions and drag-and-dropped paths into real context.
|
|
1657
|
-
// The user-visible `line` stays readable in history; the model gets
|
|
1658
|
-
// the line plus an appended block of file contents / path notes.
|
|
1659
|
-
const resolvedInput = resolveTurnInput({
|
|
1660
|
-
prompt: effectiveLine,
|
|
1661
|
-
mode: state.mode,
|
|
1662
|
-
provider: state.provider,
|
|
1663
|
-
model: state.model,
|
|
1664
|
-
baseDir: safeCwd(),
|
|
1665
|
-
});
|
|
1666
|
-
const requestModel = resolvedInput.model;
|
|
1667
|
-
const visionCapable = resolvedInput.capability.vision;
|
|
1668
|
-
const expansion = { attachments: resolvedInput.attachments };
|
|
1669
|
-
const images = [...resolvedInput.images];
|
|
1670
|
-
if (resolvedInput.fallbackReason) {
|
|
1671
|
-
console.log(chalk.dim(` ↳ ${resolvedInput.fallbackReason}`));
|
|
1672
|
-
}
|
|
1673
|
-
for (const issue of resolvedInput.imageIssues) {
|
|
1674
|
-
console.log(chalk.yellow(` ↳ ${issue}`));
|
|
1675
|
-
}
|
|
1676
|
-
const hasImageAttachment = expansion.attachments.some((att) => att.kind === "image" && att.sendable !== false);
|
|
1677
|
-
const sentImagePaths = new Set(images.map((img) => img.path).filter((p) => Boolean(p)));
|
|
1678
|
-
// Vision is authoritative for visual analysis. OCR is opt-in when the
|
|
1679
|
-
// selected model cannot see the image and the user specifically asks
|
|
1680
|
-
// for text extraction; it is never treated as layout/color evidence.
|
|
1681
|
-
const shouldOcr = shouldRunImageOcr({
|
|
1682
|
-
hasImage: hasImageAttachment,
|
|
1683
|
-
visionCapable,
|
|
1684
|
-
support: resolvedInput.capability.support,
|
|
1685
|
-
prompt: effectiveLine,
|
|
1686
|
-
});
|
|
1687
|
-
const ocrGrounding = shouldOcr
|
|
1688
|
-
? await buildImageOcrGrounding(effectiveLine, safeCwd())
|
|
1689
|
-
: "";
|
|
1690
|
-
const modelInput = ocrGrounding
|
|
1691
|
-
? `${resolvedInput.prompt}\n\n${ocrGrounding}`
|
|
1692
|
-
: resolvedInput.prompt;
|
|
1693
|
-
if (expansion.attachments.length > 0) {
|
|
1694
|
-
for (const att of expansion.attachments) {
|
|
1695
|
-
const tag = att.kind === "text"
|
|
1696
|
-
? chalk.green("attached")
|
|
1697
|
-
: att.kind === "missing"
|
|
1698
|
-
? chalk.red("not found")
|
|
1699
|
-
: att.kind === "image" && sentImagePaths.has(att.path)
|
|
1700
|
-
? chalk.green("image (sent to model)")
|
|
1701
|
-
: att.kind === "image" && visionCapable
|
|
1702
|
-
? chalk.yellow("image (not sent)")
|
|
1703
|
-
: chalk.yellow(att.kind);
|
|
1704
|
-
console.log(chalk.dim(` ↳ ${tag}: `) + chalk.dim(att.path));
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
if (state.mode === "ask") {
|
|
1708
|
-
let actionRequired;
|
|
1709
|
-
assistantContent = await withAbortableInput(async (signal) => streamWithAbort(async (runSignal, onToken, setStatus) => {
|
|
1710
|
-
return await runAskStream(modelInput, onToken, {
|
|
1711
|
-
provider: state.provider,
|
|
1712
|
-
model: requestModel,
|
|
1713
|
-
history: state.messages,
|
|
1714
|
-
signal: runSignal,
|
|
1715
|
-
images,
|
|
1716
|
-
onActionRequired: (info) => {
|
|
1717
|
-
actionRequired = info;
|
|
1718
|
-
},
|
|
1719
|
-
onEvent: (event) => {
|
|
1720
|
-
if (event.type === "tool-call") {
|
|
1721
|
-
setStatus(askResearchLabel(event.name, event.argsDisplay));
|
|
1722
|
-
}
|
|
1723
|
-
},
|
|
1724
|
-
});
|
|
1725
|
-
}, signal));
|
|
1726
|
-
process.stdout.write("\n");
|
|
1727
|
-
if (actionRequired) {
|
|
1728
|
-
// The task needs actions ask mode can't perform. Offer to switch
|
|
1729
|
-
// into agent mode and run it there. On confirm, state.mode flips
|
|
1730
|
-
// so subsequent turns stay in agent mode too.
|
|
1731
|
-
assistantContent = await offerAgentSwitch(actionRequired, state, requestModel, images);
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
else {
|
|
1735
|
-
const classicRenderer = attachClassicRenderer();
|
|
1736
|
-
assistantContent = await withAbortableInput(async (signal) => runAgent(modelInput, {
|
|
1737
|
-
provider: state.provider,
|
|
1738
|
-
model: requestModel,
|
|
1739
|
-
history: state.messages,
|
|
1740
|
-
signal,
|
|
1741
|
-
session: state.session,
|
|
1742
|
-
images,
|
|
1743
|
-
visionProven: resolvedInput.capability.support === "yes",
|
|
1744
|
-
mode: state.mode === "plan" ? "plan" : "agent",
|
|
1745
|
-
onEvent: classicRenderer.onEvent,
|
|
1746
|
-
}));
|
|
1747
|
-
}
|
|
1748
|
-
console.log();
|
|
1749
|
-
const userHistoryMessage = {
|
|
1750
|
-
role: "user",
|
|
1751
|
-
content: modelInput,
|
|
1752
|
-
};
|
|
1753
|
-
if (images.length > 0)
|
|
1754
|
-
userHistoryMessage.images = images;
|
|
1755
|
-
state.messages.push(userHistoryMessage, {
|
|
1756
|
-
role: "assistant",
|
|
1757
|
-
content: assistantContent,
|
|
1758
|
-
});
|
|
1759
|
-
// After plan mode (or any draft plan), offer Accept / Discard / View / Suggest.
|
|
1760
|
-
if (state.mode === "plan" || !state.session.planApproved.value) {
|
|
1761
|
-
const draft = await loadPlan(state.session.sessionId).catch(() => undefined);
|
|
1762
|
-
if (draft && draft.status === "draft") {
|
|
1763
|
-
input.pause();
|
|
1764
|
-
if (input.isTTY)
|
|
1765
|
-
input.setRawMode(false);
|
|
1766
|
-
try {
|
|
1767
|
-
const decision = await handleDraftPlanDecision({
|
|
1768
|
-
plan: draft,
|
|
1769
|
-
sessionId: state.session.sessionId,
|
|
1770
|
-
planApproved: state.session.planApproved,
|
|
1771
|
-
setModeAgent: () => {
|
|
1772
|
-
state.mode = "agent";
|
|
1773
|
-
setDefaultMode("agent");
|
|
1774
|
-
console.log(renderModeSwitch("agent"));
|
|
1775
|
-
},
|
|
1776
|
-
});
|
|
1777
|
-
if (decision.action === "implement") {
|
|
1778
|
-
const classicRenderer = attachClassicRenderer();
|
|
1779
|
-
const followUp = await withAbortableInput(async (signal) => runAgent(decision.line, {
|
|
1780
|
-
provider: state.provider,
|
|
1781
|
-
model: requestModel,
|
|
1782
|
-
history: state.messages,
|
|
1783
|
-
signal,
|
|
1784
|
-
session: state.session,
|
|
1785
|
-
mode: "agent",
|
|
1786
|
-
onEvent: classicRenderer.onEvent,
|
|
1787
|
-
}));
|
|
1788
|
-
console.log();
|
|
1789
|
-
state.messages.push({ role: "user", content: decision.line }, { role: "assistant", content: followUp });
|
|
1790
|
-
}
|
|
1791
|
-
else if (decision.action === "suggest") {
|
|
1792
|
-
state.mode = "plan";
|
|
1793
|
-
setDefaultMode("plan");
|
|
1794
|
-
const classicRenderer = attachClassicRenderer();
|
|
1795
|
-
const revision = await withAbortableInput(async (signal) => runAgent(decision.line, {
|
|
1796
|
-
provider: state.provider,
|
|
1797
|
-
model: requestModel,
|
|
1798
|
-
history: state.messages,
|
|
1799
|
-
signal,
|
|
1800
|
-
session: state.session,
|
|
1801
|
-
mode: "plan",
|
|
1802
|
-
onEvent: classicRenderer.onEvent,
|
|
1803
|
-
}));
|
|
1804
|
-
console.log();
|
|
1805
|
-
state.messages.push({ role: "user", content: decision.line }, { role: "assistant", content: revision });
|
|
1806
|
-
// Re-prompt if a new draft exists
|
|
1807
|
-
const revised = await loadPlan(state.session.sessionId).catch(() => undefined);
|
|
1808
|
-
if (revised && revised.status === "draft") {
|
|
1809
|
-
const again = await handleDraftPlanDecision({
|
|
1810
|
-
plan: revised,
|
|
1811
|
-
sessionId: state.session.sessionId,
|
|
1812
|
-
planApproved: state.session.planApproved,
|
|
1813
|
-
setModeAgent: () => {
|
|
1814
|
-
state.mode = "agent";
|
|
1815
|
-
setDefaultMode("agent");
|
|
1816
|
-
console.log(renderModeSwitch("agent"));
|
|
1817
|
-
},
|
|
1818
|
-
});
|
|
1819
|
-
if (again.action === "implement") {
|
|
1820
|
-
const classicRenderer2 = attachClassicRenderer();
|
|
1821
|
-
const followUp2 = await withAbortableInput(async (signal) => runAgent(again.line, {
|
|
1822
|
-
provider: state.provider,
|
|
1823
|
-
model: requestModel,
|
|
1824
|
-
history: state.messages,
|
|
1825
|
-
signal,
|
|
1826
|
-
session: state.session,
|
|
1827
|
-
mode: "agent",
|
|
1828
|
-
onEvent: classicRenderer2.onEvent,
|
|
1829
|
-
}));
|
|
1830
|
-
console.log();
|
|
1831
|
-
state.messages.push({ role: "user", content: again.line }, { role: "assistant", content: followUp2 });
|
|
1832
|
-
}
|
|
1833
|
-
else if (again.action === "suggest") {
|
|
1834
|
-
// Leave feedback in chat; next turn continues plan mode
|
|
1835
|
-
state.messages.push({
|
|
1836
|
-
role: "user",
|
|
1837
|
-
content: again.line,
|
|
1838
|
-
});
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
finally {
|
|
1844
|
-
if (input.isTTY)
|
|
1845
|
-
input.setRawMode(true);
|
|
1846
|
-
input.resume();
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
catch (error) {
|
|
1852
|
-
if (error instanceof AbortRunError) {
|
|
1853
|
-
process.stdout.write(chalk.yellow("\n ⏹ Aborted.\n"));
|
|
1854
|
-
continue;
|
|
1855
|
-
}
|
|
1856
|
-
// Still save the exchange so /history doesn't lose conversations
|
|
1857
|
-
// that hit transient errors (e.g. "requires root", network timeout).
|
|
1858
|
-
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1859
|
-
state.messages.push({ role: "user", content: line }, { role: "assistant", content: `[error: ${errMsg}]` });
|
|
1860
|
-
console.error(chalk.red(errMsg));
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
finally {
|
|
1865
|
-
await shutdown(0);
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
//# sourceMappingURL=repl.js.map
|