praxis-agent 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/application/background-agent-manager.d.ts +5 -0
- package/dist/application/background-agent-manager.js +21 -0
- package/dist/application/background-bash-manager.d.ts +14 -0
- package/dist/application/background-bash-manager.js +42 -4
- package/dist/application/background-task-runtime.d.ts +34 -0
- package/dist/application/background-task-runtime.js +59 -0
- package/dist/application/session-service.d.ts +21 -1
- package/dist/application/session-service.js +188 -17
- package/dist/application/session-worktree.d.ts +1 -0
- package/dist/application/session-worktree.js +13 -2
- package/dist/application/subagent-service.d.ts +3 -0
- package/dist/application/subagent-service.js +6 -0
- package/dist/application/workflow-manager.d.ts +70 -2
- package/dist/application/workflow-manager.js +21 -4
- package/dist/cli/agents-dashboard.d.ts +9 -0
- package/dist/cli/agents-dashboard.js +16 -5
- package/dist/cli/interactive.d.ts +41 -3
- package/dist/cli/interactive.js +1281 -183
- package/dist/cli/tui/claude-style.d.ts +31 -3
- package/dist/cli/tui/claude-style.js +171 -54
- package/dist/cli/tui/config-dashboard.d.ts +63 -0
- package/dist/cli/tui/config-dashboard.js +150 -0
- package/dist/cli/tui/config-settings.d.ts +32 -0
- package/dist/cli/tui/config-settings.js +276 -0
- package/dist/cli/tui/custom-themes.d.ts +21 -0
- package/dist/cli/tui/custom-themes.js +284 -0
- package/dist/cli/tui/file-picker.d.ts +3 -1
- package/dist/cli/tui/file-picker.js +8 -2
- package/dist/cli/tui/keybindings.js +0 -1
- package/dist/cli/tui/mcp-panel-controller.d.ts +32 -0
- package/dist/cli/tui/mcp-panel-controller.js +80 -0
- package/dist/cli/tui/mcp-panel-projector.d.ts +79 -0
- package/dist/cli/tui/mcp-panel-projector.js +181 -0
- package/dist/cli/tui/mcp-panel.d.ts +8 -0
- package/dist/cli/tui/mcp-panel.js +99 -0
- package/dist/cli/tui/recently-denied.d.ts +15 -0
- package/dist/cli/tui/recently-denied.js +19 -0
- package/dist/cli/tui/runtime-interactions.d.ts +17 -0
- package/dist/cli/tui/runtime-interactions.js +100 -0
- package/dist/cli/tui/runtime-settings.d.ts +40 -0
- package/dist/cli/tui/runtime-settings.js +84 -0
- package/dist/cli/tui/slash-commands.js +10 -0
- package/dist/cli/tui/task-panel.d.ts +53 -0
- package/dist/cli/tui/task-panel.js +204 -0
- package/dist/cli/tui/terminal-notifications.d.ts +10 -0
- package/dist/cli/tui/terminal-notifications.js +48 -0
- package/dist/cli/tui/terminal-setup.d.ts +24 -0
- package/dist/cli/tui/terminal-setup.js +618 -0
- package/dist/cli/tui/theme-settings.d.ts +8 -0
- package/dist/cli/tui/theme-settings.js +132 -0
- package/dist/cli/tui/theme.d.ts +53 -0
- package/dist/cli/tui/theme.js +265 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +97 -17
- package/dist/compatibility/claude/schema.js +2 -0
- package/dist/core/runtime.d.ts +12 -0
- package/dist/core/runtime.js +24 -0
- package/dist/maintenance/self-update.js +1 -1
- package/dist/mcp/claude-mcp-management.d.ts +6 -1
- package/dist/mcp/claude-mcp-management.js +191 -44
- package/dist/mcp/claude-mcp-oauth.js +2 -0
- package/dist/mcp/claude-mcp-tools.d.ts +31 -3
- package/dist/mcp/claude-mcp-tools.js +116 -34
- package/dist/permissions/claude-permission-resolver.d.ts +3 -0
- package/dist/permissions/claude-permission-resolver.js +42 -20
- package/dist/platform/claude-version.js +2 -1
- package/dist/plugins/claude-plugin-runtime.js +11 -3
- package/dist/tools/claude-interactive-tools.d.ts +7 -0
- package/dist/tools/claude-interactive-tools.js +21 -8
- package/dist/tools/claude-task-tools.d.ts +4 -0
- package/dist/tools/claude-task-tools.js +7 -0
- package/package.json +2 -1
- package/dist/application/background-agent-manager.d.ts.map +0 -1
- package/dist/application/background-agent-manager.js.map +0 -1
- package/dist/application/background-bash-manager.d.ts.map +0 -1
- package/dist/application/background-bash-manager.js.map +0 -1
- package/dist/application/background-task-id.d.ts.map +0 -1
- package/dist/application/background-task-id.js.map +0 -1
- package/dist/application/claude-project-purge.d.ts.map +0 -1
- package/dist/application/claude-project-purge.js.map +0 -1
- package/dist/application/managed-worktree.d.ts.map +0 -1
- package/dist/application/managed-worktree.js.map +0 -1
- package/dist/application/model-compactor.d.ts.map +0 -1
- package/dist/application/model-compactor.js.map +0 -1
- package/dist/application/scheduled-prompt-manager.d.ts.map +0 -1
- package/dist/application/scheduled-prompt-manager.js.map +0 -1
- package/dist/application/session-service.d.ts.map +0 -1
- package/dist/application/session-service.js.map +0 -1
- package/dist/application/session-worktree.d.ts.map +0 -1
- package/dist/application/session-worktree.js.map +0 -1
- package/dist/application/subagent-service.d.ts.map +0 -1
- package/dist/application/subagent-service.js.map +0 -1
- package/dist/application/tool-use-summary.d.ts.map +0 -1
- package/dist/application/tool-use-summary.js.map +0 -1
- package/dist/application/top-level-agent-manager.d.ts.map +0 -1
- package/dist/application/top-level-agent-manager.js.map +0 -1
- package/dist/application/workflow-manager.d.ts.map +0 -1
- package/dist/application/workflow-manager.js.map +0 -1
- package/dist/application/workflow-meta.d.ts.map +0 -1
- package/dist/application/workflow-meta.js.map +0 -1
- package/dist/application/workflow-runtime.d.ts.map +0 -1
- package/dist/application/workflow-runtime.js.map +0 -1
- package/dist/application/workflow-worktree.d.ts.map +0 -1
- package/dist/application/workflow-worktree.js.map +0 -1
- package/dist/cli/agents-dashboard.d.ts.map +0 -1
- package/dist/cli/agents-dashboard.js.map +0 -1
- package/dist/cli/controls.d.ts.map +0 -1
- package/dist/cli/controls.js.map +0 -1
- package/dist/cli/debug.d.ts.map +0 -1
- package/dist/cli/debug.js.map +0 -1
- package/dist/cli/interactive.d.ts.map +0 -1
- package/dist/cli/interactive.js.map +0 -1
- package/dist/cli/protocol.d.ts.map +0 -1
- package/dist/cli/protocol.js.map +0 -1
- package/dist/cli/tui/claude-style.d.ts.map +0 -1
- package/dist/cli/tui/claude-style.js.map +0 -1
- package/dist/cli/tui/clipboard.d.ts.map +0 -1
- package/dist/cli/tui/clipboard.js.map +0 -1
- package/dist/cli/tui/composer-editor.d.ts.map +0 -1
- package/dist/cli/tui/composer-editor.js.map +0 -1
- package/dist/cli/tui/composer-images.d.ts.map +0 -1
- package/dist/cli/tui/composer-images.js.map +0 -1
- package/dist/cli/tui/conversation-export.d.ts.map +0 -1
- package/dist/cli/tui/conversation-export.js.map +0 -1
- package/dist/cli/tui/external-editor.d.ts.map +0 -1
- package/dist/cli/tui/external-editor.js.map +0 -1
- package/dist/cli/tui/file-picker.d.ts.map +0 -1
- package/dist/cli/tui/file-picker.js.map +0 -1
- package/dist/cli/tui/git-diff.d.ts.map +0 -1
- package/dist/cli/tui/git-diff.js.map +0 -1
- package/dist/cli/tui/hook-settings.d.ts.map +0 -1
- package/dist/cli/tui/hook-settings.js.map +0 -1
- package/dist/cli/tui/keybindings.d.ts.map +0 -1
- package/dist/cli/tui/keybindings.js.map +0 -1
- package/dist/cli/tui/memory-files.d.ts.map +0 -1
- package/dist/cli/tui/memory-files.js.map +0 -1
- package/dist/cli/tui/permission-settings.d.ts.map +0 -1
- package/dist/cli/tui/permission-settings.js.map +0 -1
- package/dist/cli/tui/slash-commands.d.ts.map +0 -1
- package/dist/cli/tui/slash-commands.js.map +0 -1
- package/dist/cli/tui/terminal-suspend.d.ts.map +0 -1
- package/dist/cli/tui/terminal-suspend.js.map +0 -1
- package/dist/cli/tui/workspace-directories.d.ts.map +0 -1
- package/dist/cli/tui/workspace-directories.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/compatibility/claude/compaction.d.ts.map +0 -1
- package/dist/compatibility/claude/compaction.js.map +0 -1
- package/dist/compatibility/claude/context.d.ts.map +0 -1
- package/dist/compatibility/claude/context.js.map +0 -1
- package/dist/compatibility/claude/dynamic-context.d.ts.map +0 -1
- package/dist/compatibility/claude/dynamic-context.js.map +0 -1
- package/dist/compatibility/claude/file-history.d.ts.map +0 -1
- package/dist/compatibility/claude/file-history.js.map +0 -1
- package/dist/compatibility/claude/file-resources.d.ts.map +0 -1
- package/dist/compatibility/claude/file-resources.js.map +0 -1
- package/dist/compatibility/claude/fork.d.ts.map +0 -1
- package/dist/compatibility/claude/fork.js.map +0 -1
- package/dist/compatibility/claude/history.d.ts.map +0 -1
- package/dist/compatibility/claude/history.js.map +0 -1
- package/dist/compatibility/claude/ownership.d.ts.map +0 -1
- package/dist/compatibility/claude/ownership.js.map +0 -1
- package/dist/compatibility/claude/paths.d.ts.map +0 -1
- package/dist/compatibility/claude/paths.js.map +0 -1
- package/dist/compatibility/claude/pr-links.d.ts.map +0 -1
- package/dist/compatibility/claude/pr-links.js.map +0 -1
- package/dist/compatibility/claude/projection.d.ts.map +0 -1
- package/dist/compatibility/claude/projection.js.map +0 -1
- package/dist/compatibility/claude/schema.d.ts.map +0 -1
- package/dist/compatibility/claude/schema.js.map +0 -1
- package/dist/compatibility/claude/shared-resources.d.ts.map +0 -1
- package/dist/compatibility/claude/shared-resources.js.map +0 -1
- package/dist/compatibility/claude/sidechain.d.ts.map +0 -1
- package/dist/compatibility/claude/sidechain.js.map +0 -1
- package/dist/compatibility/claude/tool-links.d.ts.map +0 -1
- package/dist/compatibility/claude/tool-links.js.map +0 -1
- package/dist/compatibility/claude/translation.d.ts.map +0 -1
- package/dist/compatibility/claude/translation.js.map +0 -1
- package/dist/compatibility/claude/workflow-replay.d.ts.map +0 -1
- package/dist/compatibility/claude/workflow-replay.js.map +0 -1
- package/dist/compatibility/claude/workflow.d.ts.map +0 -1
- package/dist/compatibility/claude/workflow.js.map +0 -1
- package/dist/core/compaction.d.ts.map +0 -1
- package/dist/core/compaction.js.map +0 -1
- package/dist/core/context-budget.d.ts.map +0 -1
- package/dist/core/context-budget.js.map +0 -1
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js.map +0 -1
- package/dist/core/runtime.d.ts.map +0 -1
- package/dist/core/runtime.js.map +0 -1
- package/dist/core/usage.d.ts.map +0 -1
- package/dist/core/usage.js.map +0 -1
- package/dist/extensions/claude-extension-tools.d.ts.map +0 -1
- package/dist/extensions/claude-extension-tools.js.map +0 -1
- package/dist/extensions/claude-extensions.d.ts.map +0 -1
- package/dist/extensions/claude-extensions.js.map +0 -1
- package/dist/hooks/claude-hook-tools.d.ts.map +0 -1
- package/dist/hooks/claude-hook-tools.js.map +0 -1
- package/dist/hooks/claude-hooks.d.ts.map +0 -1
- package/dist/hooks/claude-hooks.js.map +0 -1
- package/dist/maintenance/doctor.d.ts.map +0 -1
- package/dist/maintenance/doctor.js.map +0 -1
- package/dist/maintenance/self-update.d.ts.map +0 -1
- package/dist/maintenance/self-update.js.map +0 -1
- package/dist/mcp/claude-mcp-management.d.ts.map +0 -1
- package/dist/mcp/claude-mcp-management.js.map +0 -1
- package/dist/mcp/claude-mcp-oauth.d.ts.map +0 -1
- package/dist/mcp/claude-mcp-oauth.js.map +0 -1
- package/dist/mcp/claude-mcp-tools.d.ts.map +0 -1
- package/dist/mcp/claude-mcp-tools.js.map +0 -1
- package/dist/mcp/praxis-mcp-server.d.ts.map +0 -1
- package/dist/mcp/praxis-mcp-server.js.map +0 -1
- package/dist/permissions/claude-auto-classifier.d.ts.map +0 -1
- package/dist/permissions/claude-auto-classifier.js.map +0 -1
- package/dist/permissions/claude-permission-resolver.d.ts.map +0 -1
- package/dist/permissions/claude-permission-resolver.js.map +0 -1
- package/dist/persistence/claude-job-store.d.ts.map +0 -1
- package/dist/persistence/claude-job-store.js.map +0 -1
- package/dist/persistence/claude-scheduled-task-store.d.ts.map +0 -1
- package/dist/persistence/claude-scheduled-task-store.js.map +0 -1
- package/dist/persistence/claude-sidechain-store.d.ts.map +0 -1
- package/dist/persistence/claude-sidechain-store.js.map +0 -1
- package/dist/persistence/claude-task-store.d.ts.map +0 -1
- package/dist/persistence/claude-task-store.js.map +0 -1
- package/dist/persistence/claude-transcript-store.d.ts.map +0 -1
- package/dist/persistence/claude-transcript-store.js.map +0 -1
- package/dist/persistence/claude-workflow-store.d.ts.map +0 -1
- package/dist/persistence/claude-workflow-store.js.map +0 -1
- package/dist/persistence/in-memory-sidechain-store.d.ts.map +0 -1
- package/dist/persistence/in-memory-sidechain-store.js.map +0 -1
- package/dist/persistence/in-memory-transcript-store.d.ts.map +0 -1
- package/dist/persistence/in-memory-transcript-store.js.map +0 -1
- package/dist/platform/atomic-write.d.ts.map +0 -1
- package/dist/platform/atomic-write.js.map +0 -1
- package/dist/platform/bounded-process-runner.d.ts.map +0 -1
- package/dist/platform/bounded-process-runner.js.map +0 -1
- package/dist/platform/claude-version.d.ts.map +0 -1
- package/dist/platform/claude-version.js.map +0 -1
- package/dist/platform/command-shell.d.ts.map +0 -1
- package/dist/platform/command-shell.js.map +0 -1
- package/dist/platform/exclusive-file-lease.d.ts.map +0 -1
- package/dist/platform/exclusive-file-lease.js.map +0 -1
- package/dist/platform/sensitive-data.d.ts.map +0 -1
- package/dist/platform/sensitive-data.js.map +0 -1
- package/dist/platform/tmux-worktree.d.ts.map +0 -1
- package/dist/platform/tmux-worktree.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-discovery.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-discovery.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-graders.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-graders.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-runner.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-runner.js.map +0 -1
- package/dist/plugins/claude-plugin-eval-schema.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval-schema.js.map +0 -1
- package/dist/plugins/claude-plugin-eval.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-eval.js.map +0 -1
- package/dist/plugins/claude-plugin-maintenance.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-maintenance.js.map +0 -1
- package/dist/plugins/claude-plugin-marketplace.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-marketplace.js.map +0 -1
- package/dist/plugins/claude-plugin-mcpb.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-mcpb.js.map +0 -1
- package/dist/plugins/claude-plugin-runtime.d.ts.map +0 -1
- package/dist/plugins/claude-plugin-runtime.js.map +0 -1
- package/dist/providers/anthropic-compatible.d.ts.map +0 -1
- package/dist/providers/anthropic-compatible.js.map +0 -1
- package/dist/providers/environment.d.ts.map +0 -1
- package/dist/providers/environment.js.map +0 -1
- package/dist/providers/fallback-provider.d.ts.map +0 -1
- package/dist/providers/fallback-provider.js.map +0 -1
- package/dist/providers/openai-compatible.d.ts.map +0 -1
- package/dist/providers/openai-compatible.js.map +0 -1
- package/dist/tools/claude-interactive-tools.d.ts.map +0 -1
- package/dist/tools/claude-interactive-tools.js.map +0 -1
- package/dist/tools/claude-lsp-formatters.d.ts.map +0 -1
- package/dist/tools/claude-lsp-formatters.js.map +0 -1
- package/dist/tools/claude-lsp-tool.d.ts.map +0 -1
- package/dist/tools/claude-lsp-tool.js.map +0 -1
- package/dist/tools/claude-scheduled-tools.d.ts.map +0 -1
- package/dist/tools/claude-scheduled-tools.js.map +0 -1
- package/dist/tools/claude-task-tools.d.ts.map +0 -1
- package/dist/tools/claude-task-tools.js.map +0 -1
- package/dist/tools/claude-user-message.d.ts.map +0 -1
- package/dist/tools/claude-user-message.js.map +0 -1
- package/dist/tools/claude-workflow-tools.d.ts.map +0 -1
- package/dist/tools/claude-workflow-tools.js.map +0 -1
- package/dist/tools/claude-worktree-tools.d.ts.map +0 -1
- package/dist/tools/claude-worktree-tools.js.map +0 -1
- package/dist/tools/filtered-tool-registry.d.ts.map +0 -1
- package/dist/tools/filtered-tool-registry.js.map +0 -1
- package/dist/tools/glob.d.ts.map +0 -1
- package/dist/tools/glob.js.map +0 -1
- package/dist/tools/local-tools.d.ts.map +0 -1
- package/dist/tools/local-tools.js.map +0 -1
- package/dist/tools/notebook.d.ts.map +0 -1
- package/dist/tools/notebook.js.map +0 -1
- package/dist/tools/pdf.d.ts.map +0 -1
- package/dist/tools/pdf.js.map +0 -1
- package/dist/tools/web.d.ts.map +0 -1
- package/dist/tools/web.js.map +0 -1
package/dist/cli/interactive.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
4
|
import { resolve } from 'node:path';
|
|
4
5
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
5
6
|
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
6
7
|
import { AgentRunCancelledError } from '../core/runtime.js';
|
|
8
|
+
import { claudePermissionActionKey, } from '../permissions/claude-permission-resolver.js';
|
|
7
9
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
8
|
-
import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, PermissionDashboard, SelectionMenu, SessionPicker, StatusDashboard, Transcript, WelcomePanel, useTerminalWidth, } from './tui/claude-style.js';
|
|
10
|
+
import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, PermissionDashboard, SelectionMenu, SessionPicker, StatusDashboard, ThemePicker, CustomThemeEditor, Transcript, WelcomePanel, useTerminalWidth, } from './tui/claude-style.js';
|
|
9
11
|
import { loadTuiMemoryFiles, openTuiMemoryFolder, } from './tui/memory-files.js';
|
|
10
12
|
import { loadGitDiff, visiblePatchLines, } from './tui/git-diff.js';
|
|
11
13
|
import { addTuiPermissionRule, loadTuiPermissionRules, removeTuiPermissionRule, } from './tui/permission-settings.js';
|
|
14
|
+
import { createRecentlyDeniedStore, } from './tui/recently-denied.js';
|
|
12
15
|
import { filterTuiSlashCommands, mergeTuiSlashCommands, slashCommandQuery, } from './tui/slash-commands.js';
|
|
13
16
|
import { createComposerEditor, deleteComposerToEnd, deleteComposerToStart, deleteComposerWordBackward, insertComposerText, moveComposerCursorByWord, } from './tui/composer-editor.js';
|
|
14
17
|
import { applyMentionReference, fileReferenceAtCursor, filterTuiMentionEntries, loadTuiFileEntries, } from './tui/file-picker.js';
|
|
@@ -19,6 +22,20 @@ import { composerImageIds, deleteComposerImageBackward, deleteComposerImageForwa
|
|
|
19
22
|
import { defaultTuiKeybindings, ensureTuiKeybindingsFile, hasTuiKeybindingPrefix, loadTuiKeybindings, resolveTuiKeybinding, tuiKeyChord, } from './tui/keybindings.js';
|
|
20
23
|
import { completeTuiWorkspaceDirectory, resolveTuiWorkspaceDirectory, } from './tui/workspace-directories.js';
|
|
21
24
|
import { conversationExportPath, conversationExportText, defaultConversationExportFilename, writeConversationExport, } from './tui/conversation-export.js';
|
|
25
|
+
import { loadTuiThemeSettings, saveTuiThemeSettings, themeSettingsWithCustomTheme, } from './tui/theme-settings.js';
|
|
26
|
+
import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider, tuiPalette, } from './tui/theme.js';
|
|
27
|
+
import { CUSTOM_THEME_TOKENS, createTuiCustomTheme, deleteTuiCustomTheme, loadTuiCustomThemes, updateTuiCustomTheme, } from './tui/custom-themes.js';
|
|
28
|
+
import { setupTuiTerminal, terminalSetupTuiSlashCommand, } from './tui/terminal-setup.js';
|
|
29
|
+
import { ConfigDashboard, projectConfigRows } from './tui/config-dashboard.js';
|
|
30
|
+
import { loadConfigSettings, saveConfigSetting, configSettingDefinition, resolveConfigSettingsLocation, } from './tui/config-settings.js';
|
|
31
|
+
import { projectRuntimeSettings, loadRuntimeSettings, } from './tui/runtime-settings.js';
|
|
32
|
+
import { autoUpdateTarget, copyCandidates, externalEditorInitialContent, formatTurnDuration, questionTimeoutMilliseconds, sessionRecap, shouldShowCopyPicker, spinnerTip, } from './tui/runtime-interactions.js';
|
|
33
|
+
import { notifyTerminal, } from './tui/terminal-notifications.js';
|
|
34
|
+
import { McpPanel } from './tui/mcp-panel.js';
|
|
35
|
+
import { McpPanelController, mcpRuntimeFromSession, } from './tui/mcp-panel-controller.js';
|
|
36
|
+
import { initialTuiMcpPanelState, } from './tui/mcp-panel-projector.js';
|
|
37
|
+
import { TaskPanel, initialTuiTaskPanelState, projectTuiTasks, reconcileTuiTaskPanelState, updateTuiTaskPanelState, } from './tui/task-panel.js';
|
|
38
|
+
import { ClaudeMcpManagement } from '../mcp/claude-mcp-management.js';
|
|
22
39
|
const EMPTY_SLASH_COMMANDS = [];
|
|
23
40
|
const EMPTY_AGENTS = [];
|
|
24
41
|
const EFFORT_OPTIONS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
@@ -54,6 +71,11 @@ const PERMISSION_OPTIONS = [
|
|
|
54
71
|
description: 'Restrict changes to the plan file until approval.',
|
|
55
72
|
},
|
|
56
73
|
];
|
|
74
|
+
function selectionPrefix(selected, screenReader) {
|
|
75
|
+
if (selected)
|
|
76
|
+
return screenReader ? 'Selected: ' : '❯ ';
|
|
77
|
+
return screenReader ? '' : ' ';
|
|
78
|
+
}
|
|
57
79
|
function rewindActions(point) {
|
|
58
80
|
const shared = [
|
|
59
81
|
{ action: 'summarize-from', label: 'Summarize from here' },
|
|
@@ -143,6 +165,12 @@ function describeTool(call, sensitiveValues) {
|
|
|
143
165
|
const name = redactSensitiveText(call.name, sensitiveValues);
|
|
144
166
|
return `${name} ${describeToolInput(call, sensitiveValues)}`;
|
|
145
167
|
}
|
|
168
|
+
function describeRecentlyDeniedAction(call, sensitiveValues) {
|
|
169
|
+
const description = call.input.description;
|
|
170
|
+
return typeof description === 'string' && description.trim()
|
|
171
|
+
? redactSensitiveText(description.trim(), sensitiveValues)
|
|
172
|
+
: describeTool(call, sensitiveValues);
|
|
173
|
+
}
|
|
146
174
|
function redactToolCall(call, sensitiveValues) {
|
|
147
175
|
return {
|
|
148
176
|
...call,
|
|
@@ -187,22 +215,47 @@ function ordinal(value) {
|
|
|
187
215
|
return `${value}th`;
|
|
188
216
|
}
|
|
189
217
|
}
|
|
190
|
-
export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], signal, onCancel, onTurnChange, onCleanup, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, }) {
|
|
218
|
+
export function InteractiveApp({ factory, initialSessions, initialPrompt, initialHistory = [], signal, onCancel, onTurnChange, onCleanup, onRendererChange, onBackground, axScreenReader = false, allowNewSession = true, resume, display = { version: 'dev', cwd: process.cwd() }, terminalWidth, slashCommands = EMPTY_SLASH_COMMANDS, agents = EMPTY_AGENTS, allowDangerouslySkipPermissions = false, additionalDirectories = [], diffLoader, fileLoader, externalEditor = editTuiPrompt, keybindingsConfigRoot, keybindingsFile = ensureTuiKeybindingsFile, keybindingsLoader = loadTuiKeybindings, keybindingsEditor = openTuiEditorFile, memoryFilesLoader = (configRoot, cwd) => loadTuiMemoryFiles({ configRoot, cwd }), memoryEditor = openTuiEditorFile, memoryFolderOpener = openTuiMemoryFolder, suspendProcess = suspendTuiProcess, clipboardReader = readTuiClipboard, clipboardWriter = writeTuiClipboard, sideQuestionClipboardWriter = writeTuiOsc52Clipboard, exportWriter = writeConversationExport, permissionRuleStore, recentlyDeniedStore: suppliedRecentlyDeniedStore, terminalSetup: terminalSetupOverride, themeStore, initialThemeSettings, initialThemeLoadError, workspaceDirectoryResolver = resolveTuiWorkspaceDirectory, workspaceDirectoryCompleter = completeTuiWorkspaceDirectory, runtimeSettings: suppliedRuntimeSettings, runtimeSettingsTarget, notificationWriter, }) {
|
|
191
219
|
const { exit, suspendTerminal, waitUntilRenderFlush } = useApp();
|
|
192
220
|
const width = useTerminalWidth(terminalWidth);
|
|
193
221
|
const keybindingsRoot = useMemo(() => resolve(keybindingsConfigRoot ??
|
|
194
222
|
process.env.CLAUDE_CONFIG_DIR ??
|
|
195
223
|
resolve(homedir(), '.claude')), [keybindingsConfigRoot]);
|
|
224
|
+
const configTarget = useMemo(() => resolveConfigSettingsLocation(runtimeSettingsTarget), [runtimeSettingsTarget]);
|
|
196
225
|
const sensitiveValues = useMemo(() => sensitiveEnvironmentValues(process.env), []);
|
|
197
226
|
const [runtimeCwd, setRuntimeCwd] = useState(display.cwd);
|
|
198
227
|
const runtimeCwdRef = useRef(display.cwd);
|
|
228
|
+
const runtimeGitignoreRef = useRef(true);
|
|
199
229
|
const loadDiffSnapshot = useMemo(() => diffLoader ?? (() => loadGitDiff(runtimeCwd)), [diffLoader, runtimeCwd]);
|
|
200
|
-
const loadFiles = useMemo(() => fileLoader ??
|
|
230
|
+
const loadFiles = useMemo(() => fileLoader ??
|
|
231
|
+
(() => loadTuiFileEntries(runtimeCwd, {
|
|
232
|
+
respectGitignore: runtimeGitignoreRef.current,
|
|
233
|
+
})), [fileLoader, runtimeCwd]);
|
|
201
234
|
const permissionStore = useMemo(() => permissionRuleStore ?? {
|
|
202
235
|
load: () => loadTuiPermissionRules(runtimeCwd),
|
|
203
236
|
add: (input) => addTuiPermissionRule({ cwd: runtimeCwd, ...input }),
|
|
204
237
|
remove: removeTuiPermissionRule,
|
|
205
238
|
}, [permissionRuleStore, runtimeCwd]);
|
|
239
|
+
const recentDeniedStore = useMemo(() => suppliedRecentlyDeniedStore ?? createRecentlyDeniedStore(), [suppliedRecentlyDeniedStore]);
|
|
240
|
+
const presentationThemeStore = useMemo(() => themeStore ?? {
|
|
241
|
+
load: loadTuiThemeSettings,
|
|
242
|
+
save: saveTuiThemeSettings,
|
|
243
|
+
loadCustomThemes: () => loadTuiCustomThemes(),
|
|
244
|
+
createCustomTheme: (input) => createTuiCustomTheme(input),
|
|
245
|
+
updateCustomTheme: (theme, token, value) => updateTuiCustomTheme(theme, token, value),
|
|
246
|
+
deleteCustomTheme: (theme) => deleteTuiCustomTheme(theme),
|
|
247
|
+
}, [themeStore]);
|
|
248
|
+
const [customThemes, setCustomThemes] = useState([]);
|
|
249
|
+
const [themeSettings, setThemeSettings] = useState(initialThemeSettings ?? DEFAULT_TUI_THEME_SETTINGS);
|
|
250
|
+
const [runtimeSettings, setRuntimeSettings] = useState(suppliedRuntimeSettings ??
|
|
251
|
+
projectRuntimeSettings({ settings: {}, state: {} }));
|
|
252
|
+
const runtimeSettingsRef = useRef(runtimeSettings);
|
|
253
|
+
runtimeSettingsRef.current = runtimeSettings;
|
|
254
|
+
runtimeGitignoreRef.current = runtimeSettings.gitignore;
|
|
255
|
+
const [vimInsertMode, setVimInsertMode] = useState(true);
|
|
256
|
+
const activePalette = tuiPalette(TUI_THEMES.includes(themeSettings.theme)
|
|
257
|
+
? themeSettings.theme
|
|
258
|
+
: 'dark', themeSettings.syntaxHighlightingDisabled, process.env, themeSettings.customTheme);
|
|
206
259
|
const choices = useMemo(() => allowNewSession ? [null, ...initialSessions] : initialSessions, [allowNewSession, initialSessions]);
|
|
207
260
|
const [pickerIncludesNewSession, setPickerIncludesNewSession] = useState(allowNewSession);
|
|
208
261
|
const pickerIncludesNewSessionRef = useRef(allowNewSession);
|
|
@@ -216,6 +269,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
216
269
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
217
270
|
const selectedIndexRef = useRef(0);
|
|
218
271
|
const [sessionId, setSessionId] = useState(resume?.sessionId ?? null);
|
|
272
|
+
const sessionIdRef = useRef(resume?.sessionId ?? null);
|
|
273
|
+
sessionIdRef.current = sessionId;
|
|
274
|
+
const [activeSessionSummary, setActiveSessionSummary] = useState(() => resume?.sessionId
|
|
275
|
+
? initialSessions.find((session) => session.sessionId === resume.sessionId)
|
|
276
|
+
: undefined);
|
|
219
277
|
const [pendingFork, setPendingFork] = useState(resume?.forkSession === true);
|
|
220
278
|
const [input, setInput] = useState('');
|
|
221
279
|
const inputRef = useRef('');
|
|
@@ -250,6 +308,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
250
308
|
const [processSuspendRequested, setProcessSuspendRequested] = useState(false);
|
|
251
309
|
const processSuspendRequestedRef = useRef(false);
|
|
252
310
|
const [editorFooterMessage, setEditorFooterMessage] = useState();
|
|
311
|
+
const terminalSetupCommand = useMemo(() => terminalSetupTuiSlashCommand(process.env, process.platform), []);
|
|
253
312
|
const [availableSlashCommands, setAvailableSlashCommands] = useState(slashCommands);
|
|
254
313
|
const [availableAgents, setAvailableAgents] = useState(agents);
|
|
255
314
|
const [btwHistory, setBtwHistory] = useState([]);
|
|
@@ -261,6 +320,8 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
261
320
|
const [menu, setMenu] = useState(null);
|
|
262
321
|
const menuRef = useRef(null);
|
|
263
322
|
const [busy, setBusy] = useState(false);
|
|
323
|
+
const taskEntriesRef = useRef([]);
|
|
324
|
+
const mcpControllerRef = useRef(null);
|
|
264
325
|
const [compactProgress, setCompactProgress] = useState(0);
|
|
265
326
|
const initialPromptRef = useRef(initialPrompt?.trim() ?? '');
|
|
266
327
|
const [initialPromptPending, setInitialPromptPending] = useState(initialPromptRef.current.length > 0);
|
|
@@ -268,6 +329,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
268
329
|
const [activeText, setActiveText] = useState('');
|
|
269
330
|
const [activeThinking, setActiveThinking] = useState('');
|
|
270
331
|
const [thinkingExpanded, setThinkingExpanded] = useState(false);
|
|
332
|
+
const [turnDuration, setTurnDuration] = useState();
|
|
271
333
|
const [usage, setUsage] = useState();
|
|
272
334
|
const [costUsd, setCostUsd] = useState();
|
|
273
335
|
const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
|
|
@@ -277,7 +339,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
277
339
|
const turnNumberRef = useRef(0);
|
|
278
340
|
const turnMutatedFilesRef = useRef(false);
|
|
279
341
|
const permissionCallsRef = useRef(new Map());
|
|
342
|
+
const approvedSessionActionsRef = useRef(new Map());
|
|
280
343
|
const [recentDenied, setRecentDenied] = useState([]);
|
|
344
|
+
const [retryingDeniedId, setRetryingDeniedId] = useState(null);
|
|
345
|
+
const recentDeniedRef = useRef([]);
|
|
346
|
+
recentDeniedRef.current = recentDenied;
|
|
281
347
|
const [exitConfirmation, setExitConfirmation] = useState(false);
|
|
282
348
|
const exitConfirmationTimerRef = useRef(null);
|
|
283
349
|
const [runtimePreferences, setRuntimePreferences] = useState(() => ({
|
|
@@ -292,6 +358,8 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
292
358
|
const elicitationRef = useRef(null);
|
|
293
359
|
const [question, setQuestion] = useState(null);
|
|
294
360
|
const questionRef = useRef(null);
|
|
361
|
+
const questionTimeoutRef = useRef(null);
|
|
362
|
+
const recapShownRef = useRef(false);
|
|
295
363
|
const [planApproval, setPlanApproval] = useState(null);
|
|
296
364
|
const planApprovalRef = useRef(null);
|
|
297
365
|
const serviceRef = useRef(null);
|
|
@@ -303,7 +371,10 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
303
371
|
const serviceRetirementRef = useRef(null);
|
|
304
372
|
const serviceEpochRef = useRef(0);
|
|
305
373
|
const serviceCreationEpochRef = useRef(null);
|
|
306
|
-
const allSlashCommands = useMemo(() => mergeTuiSlashCommands(
|
|
374
|
+
const allSlashCommands = useMemo(() => mergeTuiSlashCommands([
|
|
375
|
+
...(terminalSetupCommand === null ? [] : [terminalSetupCommand]),
|
|
376
|
+
...availableSlashCommands,
|
|
377
|
+
]), [availableSlashCommands, terminalSetupCommand]);
|
|
307
378
|
const builtinSlashCommands = useMemo(() => allSlashCommands.filter((command) => command.source === 'builtin'), [allSlashCommands]);
|
|
308
379
|
const customSlashCommands = useMemo(() => allSlashCommands.filter((command) => command.source !== 'builtin'), [allSlashCommands]);
|
|
309
380
|
const commandQuery = commandPaletteOpen ? slashCommandQuery(input) : null;
|
|
@@ -451,6 +522,88 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
451
522
|
void closing.catch(() => undefined);
|
|
452
523
|
}, []);
|
|
453
524
|
const append = (line) => setHistory((current) => [...current, line]);
|
|
525
|
+
useEffect(() => {
|
|
526
|
+
if (initialThemeSettings !== undefined) {
|
|
527
|
+
if (initialThemeLoadError)
|
|
528
|
+
append({ kind: 'warning', text: initialThemeLoadError });
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
let cancelled = false;
|
|
532
|
+
void presentationThemeStore.load().then((loaded) => {
|
|
533
|
+
if (!cancelled)
|
|
534
|
+
setThemeSettings(themeSettingsWithCustomTheme(loaded, customThemes));
|
|
535
|
+
}, (error) => {
|
|
536
|
+
if (!cancelled)
|
|
537
|
+
append({
|
|
538
|
+
kind: 'warning',
|
|
539
|
+
text: `Unable to load theme settings: ${error instanceof Error ? error.message : String(error)}`,
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
return () => {
|
|
543
|
+
cancelled = true;
|
|
544
|
+
};
|
|
545
|
+
}, [
|
|
546
|
+
customThemes,
|
|
547
|
+
initialThemeLoadError,
|
|
548
|
+
initialThemeSettings,
|
|
549
|
+
presentationThemeStore,
|
|
550
|
+
]);
|
|
551
|
+
useEffect(() => {
|
|
552
|
+
let cancelled = false;
|
|
553
|
+
void Promise.resolve(presentationThemeStore.loadCustomThemes?.() ?? []).then((themes) => {
|
|
554
|
+
if (!cancelled && themes)
|
|
555
|
+
setCustomThemes(themes);
|
|
556
|
+
}, (error) => {
|
|
557
|
+
if (!cancelled)
|
|
558
|
+
append({
|
|
559
|
+
kind: 'warning',
|
|
560
|
+
text: `Unable to load custom themes: ${error instanceof Error ? error.message : String(error)}`,
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
return () => {
|
|
564
|
+
cancelled = true;
|
|
565
|
+
};
|
|
566
|
+
}, [presentationThemeStore]);
|
|
567
|
+
useEffect(() => {
|
|
568
|
+
if (suppliedRuntimeSettings !== undefined)
|
|
569
|
+
return;
|
|
570
|
+
let cancelled = false;
|
|
571
|
+
void loadConfigSettings(configTarget).then((snapshot) => {
|
|
572
|
+
if (!cancelled)
|
|
573
|
+
setRuntimeSettings(projectRuntimeSettings(snapshot));
|
|
574
|
+
}, (error) => {
|
|
575
|
+
if (!cancelled)
|
|
576
|
+
append({
|
|
577
|
+
kind: 'warning',
|
|
578
|
+
text: `Unable to load runtime settings: ${error instanceof Error ? error.message : String(error)}`,
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
return () => {
|
|
582
|
+
cancelled = true;
|
|
583
|
+
};
|
|
584
|
+
}, [configTarget, suppliedRuntimeSettings]);
|
|
585
|
+
useEffect(() => {
|
|
586
|
+
if (runtimeSettings.recap &&
|
|
587
|
+
resume?.sessionId &&
|
|
588
|
+
initialHistory.length > 0) {
|
|
589
|
+
const recap = sessionRecap(initialHistory);
|
|
590
|
+
if (recap && !recapShownRef.current) {
|
|
591
|
+
recapShownRef.current = true;
|
|
592
|
+
append({ kind: 'notice', text: recap });
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}, [initialHistory, resume?.sessionId, runtimeSettings.recap]);
|
|
596
|
+
useEffect(() => {
|
|
597
|
+
if (!runtimeSettings.defaultToAgentsView || availableAgents.length === 0)
|
|
598
|
+
return;
|
|
599
|
+
if (selectingSession || menuRef.current !== null)
|
|
600
|
+
return;
|
|
601
|
+
updateMenu({ kind: 'agents', agents: availableAgents, selectedIndex: 0 });
|
|
602
|
+
}, [
|
|
603
|
+
availableAgents.length,
|
|
604
|
+
runtimeSettings.defaultToAgentsView,
|
|
605
|
+
selectingSession,
|
|
606
|
+
]);
|
|
454
607
|
useEffect(() => {
|
|
455
608
|
let cancelled = false;
|
|
456
609
|
void keybindingsLoader(keybindingsRoot).then((loaded) => {
|
|
@@ -542,7 +695,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
542
695
|
await waitUntilRenderFlush();
|
|
543
696
|
let result;
|
|
544
697
|
await suspendTerminal(async () => {
|
|
545
|
-
result = await externalEditor(externalEditorRequest.prompt, {
|
|
698
|
+
result = await externalEditor(externalEditorInitialContent(externalEditorRequest.prompt, history, runtimeSettingsRef.current.externalEditorContext), {
|
|
546
699
|
cwd: runtimeCwdRef.current,
|
|
547
700
|
...(signal === undefined ? {} : { signal }),
|
|
548
701
|
});
|
|
@@ -567,7 +720,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
567
720
|
})();
|
|
568
721
|
onTurnChange?.(editing);
|
|
569
722
|
void editing.finally(() => onTurnChange?.(null));
|
|
570
|
-
}, [externalEditorRequest]);
|
|
723
|
+
}, [externalEditorRequest, history]);
|
|
571
724
|
useEffect(() => {
|
|
572
725
|
if (!keybindingsEditing)
|
|
573
726
|
return;
|
|
@@ -665,6 +818,27 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
665
818
|
menuRef.current = next;
|
|
666
819
|
setMenu(next);
|
|
667
820
|
};
|
|
821
|
+
const refreshTasks = async (existing) => {
|
|
822
|
+
const commands = await service();
|
|
823
|
+
if (!sessionId || !commands.taskSnapshots) {
|
|
824
|
+
const rows = workflowRows(commands.workflows?.() ?? []);
|
|
825
|
+
updateMenu({
|
|
826
|
+
kind: 'list',
|
|
827
|
+
title: 'Background',
|
|
828
|
+
rows,
|
|
829
|
+
emptyText: 'No tasks currently running',
|
|
830
|
+
selectedIndex: 0,
|
|
831
|
+
});
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const snapshot = await commands.taskSnapshots(sessionId);
|
|
835
|
+
const tasks = projectTuiTasks(snapshot);
|
|
836
|
+
const state = existing
|
|
837
|
+
? reconcileTuiTaskPanelState(existing.state, taskEntriesRef.current, tasks)
|
|
838
|
+
: initialTuiTaskPanelState(tasks);
|
|
839
|
+
taskEntriesRef.current = tasks;
|
|
840
|
+
updateMenu({ kind: 'tasks', tasks, state });
|
|
841
|
+
};
|
|
668
842
|
const updateBtwHistory = (updater) => {
|
|
669
843
|
const next = updater(btwHistoryRef.current);
|
|
670
844
|
btwHistoryRef.current = next;
|
|
@@ -782,7 +956,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
782
956
|
case 'tool-call':
|
|
783
957
|
if (['Edit', 'Write', 'NotebookEdit'].includes(event.call.name))
|
|
784
958
|
turnMutatedFilesRef.current = true;
|
|
785
|
-
permissionCallsRef.current.set(event.call.id,
|
|
959
|
+
permissionCallsRef.current.set(event.call.id, event.call);
|
|
786
960
|
append({
|
|
787
961
|
kind: 'tool',
|
|
788
962
|
call: redactToolCall(event.call, sensitiveValues),
|
|
@@ -790,9 +964,21 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
790
964
|
});
|
|
791
965
|
break;
|
|
792
966
|
case 'permission-decision':
|
|
793
|
-
if (event.behavior === 'deny'
|
|
794
|
-
|
|
795
|
-
|
|
967
|
+
if (event.behavior === 'deny' &&
|
|
968
|
+
event.source === 'auto-classifier' &&
|
|
969
|
+
event.autoModeOutcome === 'blocked') {
|
|
970
|
+
const denied = permissionCallsRef.current.get(event.callId);
|
|
971
|
+
const deniedSessionId = sessionIdRef.current;
|
|
972
|
+
if (denied && deniedSessionId) {
|
|
973
|
+
const action = {
|
|
974
|
+
id: `${event.callId}:${randomUUID()}`,
|
|
975
|
+
call: denied,
|
|
976
|
+
display: describeRecentlyDeniedAction(denied, sensitiveValues),
|
|
977
|
+
reason: event.reason ?? '',
|
|
978
|
+
sessionId: deniedSessionId,
|
|
979
|
+
};
|
|
980
|
+
void recentDeniedStore.record(action).then((entries) => setRecentDenied(entries), (error) => warn(error));
|
|
981
|
+
}
|
|
796
982
|
}
|
|
797
983
|
append({
|
|
798
984
|
kind: event.behavior === 'deny' ? 'warning' : 'notice',
|
|
@@ -939,9 +1125,13 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
939
1125
|
return;
|
|
940
1126
|
settled = true;
|
|
941
1127
|
signal?.removeEventListener('abort', abort);
|
|
942
|
-
if (
|
|
1128
|
+
if (questionTimeoutRef.current) {
|
|
1129
|
+
clearTimeout(questionTimeoutRef.current);
|
|
1130
|
+
questionTimeoutRef.current = null;
|
|
1131
|
+
}
|
|
1132
|
+
if (questionRef.current?.resolve === pending.resolve)
|
|
943
1133
|
questionRef.current = null;
|
|
944
|
-
setQuestion((current) =>
|
|
1134
|
+
setQuestion((current) => current?.resolve === pending.resolve ? null : current);
|
|
945
1135
|
resolveResult(result);
|
|
946
1136
|
},
|
|
947
1137
|
};
|
|
@@ -953,6 +1143,13 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
953
1143
|
clearComposerInput();
|
|
954
1144
|
questionRef.current = pending;
|
|
955
1145
|
setQuestion(pending);
|
|
1146
|
+
const timeout = questionTimeoutMilliseconds(runtimeSettingsRef.current.askUserQuestionTimeout);
|
|
1147
|
+
if (timeout !== undefined) {
|
|
1148
|
+
questionTimeoutRef.current = setTimeout(() => {
|
|
1149
|
+
questionTimeoutRef.current = null;
|
|
1150
|
+
questionRef.current?.resolve(null);
|
|
1151
|
+
}, timeout);
|
|
1152
|
+
}
|
|
956
1153
|
});
|
|
957
1154
|
const approvePlan = (request, signal) => new Promise((resolveApproval) => {
|
|
958
1155
|
let settled = false;
|
|
@@ -1005,6 +1202,14 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1005
1202
|
setRuntimePreferences(next);
|
|
1006
1203
|
return next;
|
|
1007
1204
|
};
|
|
1205
|
+
const isSessionActionApproved = (call) => {
|
|
1206
|
+
const activeSessionId = sessionIdRef.current;
|
|
1207
|
+
return activeSessionId
|
|
1208
|
+
? (approvedSessionActionsRef.current
|
|
1209
|
+
.get(activeSessionId)
|
|
1210
|
+
?.has(claudePermissionActionKey(call)) ?? false)
|
|
1211
|
+
: false;
|
|
1212
|
+
};
|
|
1008
1213
|
const service = async () => {
|
|
1009
1214
|
if (serviceRef.current)
|
|
1010
1215
|
return serviceRef.current;
|
|
@@ -1028,6 +1233,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1028
1233
|
: { model: preferences.model }),
|
|
1029
1234
|
effort: preferences.effort,
|
|
1030
1235
|
permissionMode: preferences.permissionMode,
|
|
1236
|
+
isSessionActionApproved,
|
|
1031
1237
|
additionalDirectories: preferences.additionalDirectories,
|
|
1032
1238
|
cwd: runtimeCwdRef.current,
|
|
1033
1239
|
...(signal ? { signal } : {}),
|
|
@@ -1066,6 +1272,13 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1066
1272
|
}
|
|
1067
1273
|
}
|
|
1068
1274
|
};
|
|
1275
|
+
const reloadRuntimeSettings = async (snapshot) => {
|
|
1276
|
+
const current = snapshot ?? (await loadConfigSettings(configTarget));
|
|
1277
|
+
const projected = projectRuntimeSettings(current);
|
|
1278
|
+
runtimeSettingsRef.current = projected;
|
|
1279
|
+
setRuntimeSettings(projected);
|
|
1280
|
+
return projected;
|
|
1281
|
+
};
|
|
1069
1282
|
const withLocalCommands = async (operation) => {
|
|
1070
1283
|
if (serviceRef.current)
|
|
1071
1284
|
return operation(serviceRef.current);
|
|
@@ -1076,6 +1289,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1076
1289
|
...(preferences.model === undefined ? {} : { model: preferences.model }),
|
|
1077
1290
|
effort: preferences.effort,
|
|
1078
1291
|
permissionMode: preferences.permissionMode,
|
|
1292
|
+
isSessionActionApproved,
|
|
1079
1293
|
additionalDirectories: preferences.additionalDirectories,
|
|
1080
1294
|
cwd: runtimeCwdRef.current,
|
|
1081
1295
|
...(signal ? { signal } : {}),
|
|
@@ -1088,7 +1302,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1088
1302
|
}
|
|
1089
1303
|
};
|
|
1090
1304
|
const openSession = (nextSessionId) => {
|
|
1305
|
+
sessionIdRef.current = nextSessionId;
|
|
1091
1306
|
setSessionId(nextSessionId);
|
|
1307
|
+
setActiveSessionSummary(nextSessionId === null
|
|
1308
|
+
? undefined
|
|
1309
|
+
: initialSessions.find((session) => session.sessionId === nextSessionId));
|
|
1092
1310
|
const loadId = sessionLoadRef.current + 1;
|
|
1093
1311
|
sessionLoadRef.current = loadId;
|
|
1094
1312
|
if (nextSessionId === null) {
|
|
@@ -1173,14 +1391,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1173
1391
|
setBusy(true);
|
|
1174
1392
|
setStatus('loading tasks');
|
|
1175
1393
|
try {
|
|
1176
|
-
|
|
1177
|
-
updateMenu({
|
|
1178
|
-
kind: 'list',
|
|
1179
|
-
title: 'Background',
|
|
1180
|
-
rows,
|
|
1181
|
-
emptyText: 'No tasks currently running',
|
|
1182
|
-
selectedIndex: 0,
|
|
1183
|
-
});
|
|
1394
|
+
await refreshTasks();
|
|
1184
1395
|
}
|
|
1185
1396
|
catch (error) {
|
|
1186
1397
|
warn(error);
|
|
@@ -1224,6 +1435,15 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1224
1435
|
});
|
|
1225
1436
|
return;
|
|
1226
1437
|
}
|
|
1438
|
+
if (!runtimeSettingsRef.current.copyFullResponse &&
|
|
1439
|
+
shouldShowCopyPicker(response.text)) {
|
|
1440
|
+
updateMenu({
|
|
1441
|
+
kind: 'copy',
|
|
1442
|
+
candidates: copyCandidates(response.text),
|
|
1443
|
+
selectedIndex: 0,
|
|
1444
|
+
});
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1227
1447
|
const copying = clipboardWriter(response.text).then(() => append({
|
|
1228
1448
|
kind: 'local-result',
|
|
1229
1449
|
text: `Copied ${position === 1 ? 'last' : `${ordinal(position)}-latest`} response to clipboard.`,
|
|
@@ -1297,16 +1517,43 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1297
1517
|
setBusy(true);
|
|
1298
1518
|
setStatus('loading MCP servers');
|
|
1299
1519
|
try {
|
|
1300
|
-
const
|
|
1520
|
+
const commands = await service();
|
|
1521
|
+
if (!commands.mcpInspect || !commands.mcpTools) {
|
|
1522
|
+
const servers = commands.runtimeInfo?.().mcpServers ?? [];
|
|
1523
|
+
updateMenu({
|
|
1524
|
+
kind: 'list',
|
|
1525
|
+
title: 'MCP servers',
|
|
1526
|
+
rows: servers.map((server) => ({
|
|
1527
|
+
label: server.name,
|
|
1528
|
+
description: server.status,
|
|
1529
|
+
})),
|
|
1530
|
+
emptyText: 'No MCP servers configured',
|
|
1531
|
+
selectedIndex: 0,
|
|
1532
|
+
});
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
const configRoot = process.env.CLAUDE_CONFIG_DIR;
|
|
1536
|
+
const management = new ClaudeMcpManagement({
|
|
1537
|
+
cwd: runtimeCwdRef.current,
|
|
1538
|
+
...(configRoot ? { configRoot } : {}),
|
|
1539
|
+
});
|
|
1540
|
+
const controller = new McpPanelController({
|
|
1541
|
+
cwd: runtimeCwdRef.current,
|
|
1542
|
+
management,
|
|
1543
|
+
runtime: mcpRuntimeFromSession({
|
|
1544
|
+
mcpInspect: commands.mcpInspect,
|
|
1545
|
+
mcpReconnect: commands.mcpReconnect ?? (async () => { }),
|
|
1546
|
+
mcpAuthenticate: commands.mcpAuthenticate ?? (async () => { }),
|
|
1547
|
+
mcpReload: commands.mcpReload ?? (async () => { }),
|
|
1548
|
+
mcpTools: commands.mcpTools,
|
|
1549
|
+
}),
|
|
1550
|
+
});
|
|
1551
|
+
mcpControllerRef.current = controller;
|
|
1552
|
+
const model = await controller.open();
|
|
1301
1553
|
updateMenu({
|
|
1302
|
-
kind: '
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
label: server.name,
|
|
1306
|
-
description: server.status,
|
|
1307
|
-
})),
|
|
1308
|
-
emptyText: 'No MCP servers configured',
|
|
1309
|
-
selectedIndex: 0,
|
|
1554
|
+
kind: 'mcp',
|
|
1555
|
+
model,
|
|
1556
|
+
state: initialTuiMcpPanelState(model),
|
|
1310
1557
|
});
|
|
1311
1558
|
}
|
|
1312
1559
|
catch (error) {
|
|
@@ -1814,6 +2061,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1814
2061
|
};
|
|
1815
2062
|
const submit = async (prompt, shellCommand, images = []) => {
|
|
1816
2063
|
const turnNumber = turnNumberRef.current + 1;
|
|
2064
|
+
const turnStartedAt = Date.now();
|
|
1817
2065
|
turnNumberRef.current = turnNumber;
|
|
1818
2066
|
turnMutatedFilesRef.current = false;
|
|
1819
2067
|
scheduledWaitRef.current?.abort();
|
|
@@ -1825,10 +2073,14 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1825
2073
|
? AbortSignal.any([signal, turnController.signal])
|
|
1826
2074
|
: turnController.signal;
|
|
1827
2075
|
setBusy(true);
|
|
2076
|
+
setTurnDuration(undefined);
|
|
1828
2077
|
setCommandPaletteOpen(false);
|
|
1829
2078
|
setStatus('assembling-context');
|
|
1830
2079
|
setActiveText('');
|
|
1831
2080
|
setActiveThinking('');
|
|
2081
|
+
if (runtimeSettingsRef.current.tips) {
|
|
2082
|
+
setStatus(spinnerTip(runtimeSettingsRef.current) ?? 'assembling-context');
|
|
2083
|
+
}
|
|
1832
2084
|
if (shellCommand === undefined)
|
|
1833
2085
|
append({ kind: 'user', text: prompt });
|
|
1834
2086
|
else
|
|
@@ -1838,6 +2090,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1838
2090
|
commands = await service();
|
|
1839
2091
|
let activeSessionId = sessionId;
|
|
1840
2092
|
const startedNewSession = activeSessionId === null;
|
|
2093
|
+
if (activeSessionId === null) {
|
|
2094
|
+
activeSessionId = randomUUID();
|
|
2095
|
+
sessionIdRef.current = activeSessionId;
|
|
2096
|
+
setSessionId(activeSessionId);
|
|
2097
|
+
}
|
|
1841
2098
|
if (activeSessionId && pendingFork) {
|
|
1842
2099
|
const fork = await commands.fork(activeSessionId, resume?.forkSessionId);
|
|
1843
2100
|
activeSessionId = fork.sessionId;
|
|
@@ -1845,20 +2102,21 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1845
2102
|
setPendingFork(false);
|
|
1846
2103
|
}
|
|
1847
2104
|
const result = shellCommand === undefined
|
|
1848
|
-
?
|
|
1849
|
-
? await commands.
|
|
1850
|
-
: await commands.
|
|
1851
|
-
:
|
|
1852
|
-
? commands.
|
|
1853
|
-
? await commands.
|
|
2105
|
+
? startedNewSession
|
|
2106
|
+
? await commands.run(prompt, turnSignal, activeSessionId, undefined, images)
|
|
2107
|
+
: await commands.resume(activeSessionId, prompt, turnSignal, undefined, images)
|
|
2108
|
+
: startedNewSession
|
|
2109
|
+
? commands.runShell
|
|
2110
|
+
? await commands.runShell(shellCommand, turnSignal, activeSessionId)
|
|
1854
2111
|
: (() => {
|
|
1855
2112
|
throw new Error('Interactive shell mode is unavailable');
|
|
1856
2113
|
})()
|
|
1857
|
-
: commands.
|
|
1858
|
-
? await commands.
|
|
2114
|
+
: commands.resumeShell
|
|
2115
|
+
? await commands.resumeShell(activeSessionId, shellCommand, turnSignal)
|
|
1859
2116
|
: (() => {
|
|
1860
2117
|
throw new Error('Interactive shell mode is unavailable');
|
|
1861
2118
|
})();
|
|
2119
|
+
sessionIdRef.current = result.sessionId;
|
|
1862
2120
|
setSessionId(result.sessionId);
|
|
1863
2121
|
if (startedNewSession &&
|
|
1864
2122
|
runtimePreferencesRef.current.permissionMode !== 'default') {
|
|
@@ -1887,6 +2145,17 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
1887
2145
|
setActiveText('');
|
|
1888
2146
|
setActiveThinking('');
|
|
1889
2147
|
setStatus('ready');
|
|
2148
|
+
setTurnDuration(Date.now() - turnStartedAt);
|
|
2149
|
+
if (runtimeSettingsRef.current.notifChannel !== 'notifications_disabled') {
|
|
2150
|
+
notifyTerminal({
|
|
2151
|
+
channel: runtimeSettingsRef.current.notifChannel,
|
|
2152
|
+
title: 'Praxis',
|
|
2153
|
+
message: 'Turn complete',
|
|
2154
|
+
...(notificationWriter === undefined
|
|
2155
|
+
? {}
|
|
2156
|
+
: { write: notificationWriter }),
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
1890
2159
|
}
|
|
1891
2160
|
catch (error) {
|
|
1892
2161
|
if (turnController.signal.aborted && !signal?.aborted) {
|
|
@@ -2059,9 +2328,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
2059
2328
|
? ['Help', 'Tabs']
|
|
2060
2329
|
: menuRef.current.kind === 'permission-dashboard'
|
|
2061
2330
|
? ['Settings', 'Tabs']
|
|
2062
|
-
: menuRef.current.kind === '
|
|
2063
|
-
? ['
|
|
2064
|
-
:
|
|
2331
|
+
: menuRef.current.kind === 'theme'
|
|
2332
|
+
? ['ThemePicker', 'Select']
|
|
2333
|
+
: menuRef.current.kind === 'model'
|
|
2334
|
+
? ['ModelPicker', 'Select']
|
|
2335
|
+
: ['Select']
|
|
2065
2336
|
: commandPaletteVisible || filePickerVisible
|
|
2066
2337
|
? ['Autocomplete', 'Chat']
|
|
2067
2338
|
: permission || planApproval
|
|
@@ -2091,6 +2362,24 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
2091
2362
|
return;
|
|
2092
2363
|
}
|
|
2093
2364
|
const isKeybinding = (action) => keybindingAction === action;
|
|
2365
|
+
if (runtimeSettingsRef.current.editor === 'vim' &&
|
|
2366
|
+
!permission &&
|
|
2367
|
+
!planApproval &&
|
|
2368
|
+
!question &&
|
|
2369
|
+
!elicitation &&
|
|
2370
|
+
menuRef.current === null) {
|
|
2371
|
+
if (!vimInsertMode) {
|
|
2372
|
+
if (value === 'i' || value === 'a')
|
|
2373
|
+
setVimInsertMode(true);
|
|
2374
|
+
else if (key.leftArrow || key.rightArrow)
|
|
2375
|
+
editComposer();
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
if (key.escape || value === '\u001B') {
|
|
2379
|
+
setVimInsertMode(false);
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2094
2383
|
if (permission) {
|
|
2095
2384
|
if (lower === 'y' || value === '1') {
|
|
2096
2385
|
permission.resolve(true);
|
|
@@ -2233,6 +2522,255 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
2233
2522
|
return;
|
|
2234
2523
|
}
|
|
2235
2524
|
const earlyMenu = menuRef.current;
|
|
2525
|
+
if (earlyMenu?.kind === 'copy') {
|
|
2526
|
+
if (key.escape || value === '\u001B') {
|
|
2527
|
+
updateMenu(null);
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
if (key.upArrow || key.downArrow) {
|
|
2531
|
+
updateMenu({
|
|
2532
|
+
...earlyMenu,
|
|
2533
|
+
selectedIndex: Math.max(0, Math.min(earlyMenu.candidates.length - 1, earlyMenu.selectedIndex + (key.upArrow ? -1 : 1))),
|
|
2534
|
+
});
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (/^[1-9]$/u.test(value)) {
|
|
2538
|
+
updateMenu({
|
|
2539
|
+
...earlyMenu,
|
|
2540
|
+
selectedIndex: Math.min(earlyMenu.candidates.length - 1, Number(value) - 1),
|
|
2541
|
+
});
|
|
2542
|
+
return;
|
|
2543
|
+
}
|
|
2544
|
+
if (key.return) {
|
|
2545
|
+
const candidate = earlyMenu.candidates[earlyMenu.selectedIndex];
|
|
2546
|
+
if (!candidate)
|
|
2547
|
+
return;
|
|
2548
|
+
const copying = clipboardWriter(candidate.text).then(() => {
|
|
2549
|
+
updateMenu(null);
|
|
2550
|
+
append({
|
|
2551
|
+
kind: 'local-result',
|
|
2552
|
+
text: `Copied ${candidate.label.toLowerCase()} to clipboard.`,
|
|
2553
|
+
});
|
|
2554
|
+
}, (error) => warn(error));
|
|
2555
|
+
onTurnChange?.(copying);
|
|
2556
|
+
void copying.finally(() => onTurnChange?.(null));
|
|
2557
|
+
}
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
if (earlyMenu?.kind === 'agents') {
|
|
2561
|
+
if (key.escape || value === '\u001B') {
|
|
2562
|
+
updateMenu(null);
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (key.upArrow || key.downArrow) {
|
|
2566
|
+
updateMenu({
|
|
2567
|
+
...earlyMenu,
|
|
2568
|
+
selectedIndex: Math.max(0, Math.min(earlyMenu.agents.length - 1, earlyMenu.selectedIndex + (key.upArrow ? -1 : 1))),
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
else if (key.return) {
|
|
2572
|
+
const agent = earlyMenu.agents[earlyMenu.selectedIndex];
|
|
2573
|
+
if (agent) {
|
|
2574
|
+
updateComposerInput(`@${agent.name} `);
|
|
2575
|
+
updateMenu(null);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
if (earlyMenu?.kind === 'tasks') {
|
|
2581
|
+
if (key.escape || value === '\u001B') {
|
|
2582
|
+
updateMenu(null);
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
const selected = earlyMenu.tasks[earlyMenu.state.selectedIndex];
|
|
2586
|
+
if ((value.toLowerCase() === 'x' || value === 'X') &&
|
|
2587
|
+
selected?.status === 'running') {
|
|
2588
|
+
const stopping = (async () => {
|
|
2589
|
+
setBusy(true);
|
|
2590
|
+
try {
|
|
2591
|
+
const commands = await service();
|
|
2592
|
+
if (!sessionId || !commands.stopTask)
|
|
2593
|
+
throw new Error('Task controls are unavailable.');
|
|
2594
|
+
await commands.stopTask(sessionId, selected.id);
|
|
2595
|
+
await refreshTasks(earlyMenu);
|
|
2596
|
+
}
|
|
2597
|
+
catch (error) {
|
|
2598
|
+
warn(error);
|
|
2599
|
+
}
|
|
2600
|
+
finally {
|
|
2601
|
+
setBusy(false);
|
|
2602
|
+
}
|
|
2603
|
+
})();
|
|
2604
|
+
onTurnChange?.(stopping);
|
|
2605
|
+
void stopping.finally(() => onTurnChange?.(null));
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
if (key.upArrow ||
|
|
2609
|
+
key.downArrow ||
|
|
2610
|
+
key.return ||
|
|
2611
|
+
key.leftArrow ||
|
|
2612
|
+
key.rightArrow) {
|
|
2613
|
+
const action = key.upArrow
|
|
2614
|
+
? { type: 'move', delta: -1 }
|
|
2615
|
+
: key.downArrow
|
|
2616
|
+
? { type: 'move', delta: 1 }
|
|
2617
|
+
: key.return
|
|
2618
|
+
? { type: 'open' }
|
|
2619
|
+
: { type: 'back' };
|
|
2620
|
+
const state = updateTuiTaskPanelState(earlyMenu.state, earlyMenu.tasks, action);
|
|
2621
|
+
updateMenu({ ...earlyMenu, state });
|
|
2622
|
+
}
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
if (earlyMenu?.kind === 'mcp') {
|
|
2626
|
+
if (key.escape || value === '\u001B') {
|
|
2627
|
+
const transition = mcpControllerRef.current
|
|
2628
|
+
? { state: earlyMenu.state, command: { type: 'close' } }
|
|
2629
|
+
: undefined;
|
|
2630
|
+
if (transition?.command?.type === 'close')
|
|
2631
|
+
updateMenu(null);
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
const input = key.upArrow
|
|
2635
|
+
? { type: 'up' }
|
|
2636
|
+
: key.downArrow
|
|
2637
|
+
? { type: 'down' }
|
|
2638
|
+
: key.return
|
|
2639
|
+
? { type: 'confirm' }
|
|
2640
|
+
: key.leftArrow
|
|
2641
|
+
? { type: 'back' }
|
|
2642
|
+
: null;
|
|
2643
|
+
if (input && mcpControllerRef.current) {
|
|
2644
|
+
const dispatch = mcpControllerRef.current.dispatch(earlyMenu.model, earlyMenu.state, input);
|
|
2645
|
+
const operation = (async () => {
|
|
2646
|
+
setBusy(true);
|
|
2647
|
+
try {
|
|
2648
|
+
const result = await dispatch;
|
|
2649
|
+
if (result.closed)
|
|
2650
|
+
updateMenu(null);
|
|
2651
|
+
else
|
|
2652
|
+
updateMenu({
|
|
2653
|
+
kind: 'mcp',
|
|
2654
|
+
model: result.model,
|
|
2655
|
+
state: result.state,
|
|
2656
|
+
});
|
|
2657
|
+
if (result.error)
|
|
2658
|
+
warn(result.error);
|
|
2659
|
+
}
|
|
2660
|
+
catch (error) {
|
|
2661
|
+
warn(error);
|
|
2662
|
+
}
|
|
2663
|
+
finally {
|
|
2664
|
+
setBusy(false);
|
|
2665
|
+
}
|
|
2666
|
+
})();
|
|
2667
|
+
onTurnChange?.(operation);
|
|
2668
|
+
void operation.finally(() => onTurnChange?.(null));
|
|
2669
|
+
}
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
if (earlyMenu?.kind === 'config') {
|
|
2673
|
+
if (key.escape || value === '\u001B') {
|
|
2674
|
+
if (earlyMenu.searchFocused && earlyMenu.query) {
|
|
2675
|
+
updateMenu({ ...earlyMenu, query: '' });
|
|
2676
|
+
}
|
|
2677
|
+
else {
|
|
2678
|
+
updateMenu(null);
|
|
2679
|
+
}
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
const rows = projectConfigRows(earlyMenu.snapshot, earlyMenu.query);
|
|
2683
|
+
if (key.leftArrow || key.rightArrow || key.tab) {
|
|
2684
|
+
const tabs = [
|
|
2685
|
+
'settings',
|
|
2686
|
+
'status',
|
|
2687
|
+
'config',
|
|
2688
|
+
'usage',
|
|
2689
|
+
'stats',
|
|
2690
|
+
];
|
|
2691
|
+
const index = tabs.indexOf(earlyMenu.tab);
|
|
2692
|
+
const direction = key.leftArrow || (key.tab && key.shift) ? -1 : 1;
|
|
2693
|
+
const tab = tabs[Math.max(0, Math.min(tabs.length - 1, index + direction))] ??
|
|
2694
|
+
'config';
|
|
2695
|
+
updateMenu({
|
|
2696
|
+
...earlyMenu,
|
|
2697
|
+
tab,
|
|
2698
|
+
selectedIndex: 0,
|
|
2699
|
+
searchFocused: tab === 'config',
|
|
2700
|
+
});
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
if (earlyMenu.tab !== 'config')
|
|
2704
|
+
return;
|
|
2705
|
+
if (key.upArrow || key.downArrow) {
|
|
2706
|
+
updateMenu({
|
|
2707
|
+
...earlyMenu,
|
|
2708
|
+
searchFocused: false,
|
|
2709
|
+
selectedIndex: Math.max(0, Math.min(Math.max(0, rows.length - 1), earlyMenu.selectedIndex + (key.upArrow ? -1 : 1))),
|
|
2710
|
+
});
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
if (value === '/' && !earlyMenu.searchFocused) {
|
|
2714
|
+
updateMenu({ ...earlyMenu, searchFocused: true });
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
if (earlyMenu.searchFocused && key.backspace) {
|
|
2718
|
+
updateMenu({ ...earlyMenu, query: earlyMenu.query.slice(0, -1) });
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
if (earlyMenu.searchFocused &&
|
|
2722
|
+
!key.ctrl &&
|
|
2723
|
+
!key.meta &&
|
|
2724
|
+
value &&
|
|
2725
|
+
printable) {
|
|
2726
|
+
updateMenu({
|
|
2727
|
+
...earlyMenu,
|
|
2728
|
+
query: earlyMenu.query + value,
|
|
2729
|
+
selectedIndex: 0,
|
|
2730
|
+
});
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
if (key.return || value === ' ') {
|
|
2734
|
+
const row = rows[earlyMenu.selectedIndex];
|
|
2735
|
+
if (!row)
|
|
2736
|
+
return;
|
|
2737
|
+
const definition = configSettingDefinition(row.definition.id);
|
|
2738
|
+
if (!definition || definition.values === 'language') {
|
|
2739
|
+
append({
|
|
2740
|
+
kind: 'warning',
|
|
2741
|
+
text: 'This setting requires a typed value and is not yet editable in the panel.',
|
|
2742
|
+
});
|
|
2743
|
+
return;
|
|
2744
|
+
}
|
|
2745
|
+
const values = definition.values;
|
|
2746
|
+
const currentIndex = values.indexOf(row.value);
|
|
2747
|
+
const value = values[(currentIndex + 1) % values.length];
|
|
2748
|
+
if (value === undefined)
|
|
2749
|
+
return;
|
|
2750
|
+
const saving = (async () => {
|
|
2751
|
+
setBusy(true);
|
|
2752
|
+
try {
|
|
2753
|
+
const snapshot = await saveConfigSetting(definition.id, value, configTarget);
|
|
2754
|
+
await reloadRuntimeSettings(snapshot);
|
|
2755
|
+
await retireService();
|
|
2756
|
+
updateMenu({ ...earlyMenu, snapshot });
|
|
2757
|
+
append({
|
|
2758
|
+
kind: 'local-result',
|
|
2759
|
+
text: `${definition.label} set to ${String(value)}`,
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
catch (error) {
|
|
2763
|
+
warn(error);
|
|
2764
|
+
}
|
|
2765
|
+
finally {
|
|
2766
|
+
setBusy(false);
|
|
2767
|
+
}
|
|
2768
|
+
})();
|
|
2769
|
+
onTurnChange?.(saving);
|
|
2770
|
+
void saving.finally(() => onTurnChange?.(null));
|
|
2771
|
+
}
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2236
2774
|
if (earlyMenu?.kind === 'memory') {
|
|
2237
2775
|
if (key.escape || value === '\u001B') {
|
|
2238
2776
|
updateMenu(null);
|
|
@@ -2792,6 +3330,68 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
2792
3330
|
selectedIndex: -1,
|
|
2793
3331
|
});
|
|
2794
3332
|
}
|
|
3333
|
+
else if (activeMenu.tabIndex === 0 &&
|
|
3334
|
+
activeMenu.selectedIndex >= 0 &&
|
|
3335
|
+
(key.return || value.toLowerCase() === 'r')) {
|
|
3336
|
+
const action = recentDenied[activeMenu.selectedIndex];
|
|
3337
|
+
if (!action)
|
|
3338
|
+
return;
|
|
3339
|
+
const operation = (async () => {
|
|
3340
|
+
setBusy(true);
|
|
3341
|
+
const approvals = approvedSessionActionsRef.current.get(action.sessionId) ??
|
|
3342
|
+
new Set();
|
|
3343
|
+
const actionKey = claudePermissionActionKey(action.call);
|
|
3344
|
+
const wasApproved = approvals.has(actionKey);
|
|
3345
|
+
approvals.add(actionKey);
|
|
3346
|
+
approvedSessionActionsRef.current.set(action.sessionId, approvals);
|
|
3347
|
+
try {
|
|
3348
|
+
const commands = await service();
|
|
3349
|
+
if (value.toLowerCase() === 'r') {
|
|
3350
|
+
if (!commands.retryRecentlyDenied) {
|
|
3351
|
+
throw new Error('Recently denied retry is unavailable.');
|
|
3352
|
+
}
|
|
3353
|
+
setRetryingDeniedId(action.id);
|
|
3354
|
+
const result = await commands.retryRecentlyDenied(action.sessionId, action.display, signal);
|
|
3355
|
+
sessionIdRef.current = result.sessionId;
|
|
3356
|
+
setSessionId(result.sessionId);
|
|
3357
|
+
setUsage(result.usage);
|
|
3358
|
+
setCostUsd(result.costUsd);
|
|
3359
|
+
append({ kind: 'assistant', text: result.text });
|
|
3360
|
+
updateMenu(null);
|
|
3361
|
+
}
|
|
3362
|
+
else {
|
|
3363
|
+
if (!commands.approveRecentlyDenied) {
|
|
3364
|
+
throw new Error('Recently denied approval is unavailable.');
|
|
3365
|
+
}
|
|
3366
|
+
await commands.approveRecentlyDenied(action.sessionId, action.display);
|
|
3367
|
+
const entries = await recentDeniedStore.remove(action.id);
|
|
3368
|
+
setRecentDenied(entries);
|
|
3369
|
+
append({
|
|
3370
|
+
kind: 'notice',
|
|
3371
|
+
text: `Approved ${action.display}`,
|
|
3372
|
+
});
|
|
3373
|
+
updateMenu({
|
|
3374
|
+
kind: 'permission-dashboard',
|
|
3375
|
+
tabIndex: entries.length > 0 ? 0 : 1,
|
|
3376
|
+
selectedIndex: entries.length > 0 ? 0 : -1,
|
|
3377
|
+
query: '',
|
|
3378
|
+
rules: activeMenu.rules,
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
catch (error) {
|
|
3383
|
+
if (!wasApproved)
|
|
3384
|
+
approvals.delete(actionKey);
|
|
3385
|
+
warn(error);
|
|
3386
|
+
}
|
|
3387
|
+
finally {
|
|
3388
|
+
setRetryingDeniedId(null);
|
|
3389
|
+
setBusy(false);
|
|
3390
|
+
}
|
|
3391
|
+
})();
|
|
3392
|
+
onTurnChange?.(operation);
|
|
3393
|
+
void operation.finally(() => onTurnChange?.(null));
|
|
3394
|
+
}
|
|
2795
3395
|
else if (key.return && behavior && activeMenu.selectedIndex === 0) {
|
|
2796
3396
|
clearComposerInput();
|
|
2797
3397
|
updateMenu({
|
|
@@ -3089,6 +3689,317 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3089
3689
|
}
|
|
3090
3690
|
return;
|
|
3091
3691
|
}
|
|
3692
|
+
if (activeMenu.kind === 'theme') {
|
|
3693
|
+
const themeOptionCount = TUI_THEMES.length + customThemes.length + 1;
|
|
3694
|
+
const lastThemeOptionIndex = themeOptionCount - 1;
|
|
3695
|
+
if (key.escape || value === '\u001B') {
|
|
3696
|
+
updateMenu(null);
|
|
3697
|
+
}
|
|
3698
|
+
else if (isKeybinding('theme:editCustom') || controlKey('e')) {
|
|
3699
|
+
const options = [
|
|
3700
|
+
...TUI_THEMES,
|
|
3701
|
+
...customThemes.map((theme) => `custom:${theme.slug}`),
|
|
3702
|
+
];
|
|
3703
|
+
const selected = options[activeMenu.selectedIndex];
|
|
3704
|
+
const theme = selected?.startsWith('custom:')
|
|
3705
|
+
? customThemes.find((entry) => `custom:${entry.slug}` === selected)
|
|
3706
|
+
: undefined;
|
|
3707
|
+
if (!theme) {
|
|
3708
|
+
append({
|
|
3709
|
+
kind: 'warning',
|
|
3710
|
+
text: 'Only custom themes can be edited.',
|
|
3711
|
+
});
|
|
3712
|
+
}
|
|
3713
|
+
else {
|
|
3714
|
+
updateMenu({
|
|
3715
|
+
kind: 'custom-theme-editor',
|
|
3716
|
+
theme,
|
|
3717
|
+
selectedIndex: 0,
|
|
3718
|
+
query: '',
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
else if (isKeybinding('theme:toggleSyntaxHighlighting')) {
|
|
3723
|
+
const syntaxHighlightingDisabled = !themeSettings.syntaxHighlightingDisabled;
|
|
3724
|
+
const saving = (async () => {
|
|
3725
|
+
setBusy(true);
|
|
3726
|
+
try {
|
|
3727
|
+
const committed = await presentationThemeStore.save({
|
|
3728
|
+
syntaxHighlightingDisabled,
|
|
3729
|
+
});
|
|
3730
|
+
setThemeSettings(committed);
|
|
3731
|
+
}
|
|
3732
|
+
catch (error) {
|
|
3733
|
+
warn(error);
|
|
3734
|
+
}
|
|
3735
|
+
finally {
|
|
3736
|
+
setBusy(false);
|
|
3737
|
+
}
|
|
3738
|
+
})();
|
|
3739
|
+
onTurnChange?.(saving);
|
|
3740
|
+
void saving.finally(() => onTurnChange?.(null));
|
|
3741
|
+
}
|
|
3742
|
+
else if (key.upArrow || key.downArrow) {
|
|
3743
|
+
updateMenu({
|
|
3744
|
+
...activeMenu,
|
|
3745
|
+
selectedIndex: Math.max(0, Math.min(lastThemeOptionIndex, activeMenu.selectedIndex + (key.upArrow ? -1 : 1))),
|
|
3746
|
+
});
|
|
3747
|
+
}
|
|
3748
|
+
else if (/^[1-9]$/u.test(value)) {
|
|
3749
|
+
updateMenu({
|
|
3750
|
+
...activeMenu,
|
|
3751
|
+
selectedIndex: Math.min(lastThemeOptionIndex, Number(value) - 1),
|
|
3752
|
+
});
|
|
3753
|
+
}
|
|
3754
|
+
else if (key.return) {
|
|
3755
|
+
const options = [
|
|
3756
|
+
...TUI_THEMES,
|
|
3757
|
+
...customThemes.map((theme) => `custom:${theme.slug}`),
|
|
3758
|
+
'__new__',
|
|
3759
|
+
];
|
|
3760
|
+
const selected = options[activeMenu.selectedIndex];
|
|
3761
|
+
if (!selected)
|
|
3762
|
+
return;
|
|
3763
|
+
if (selected === '__new__') {
|
|
3764
|
+
const base = themeSettings.theme.startsWith('custom:')
|
|
3765
|
+
? (themeSettings.customTheme?.base ?? 'dark')
|
|
3766
|
+
: themeSettings.theme === 'auto'
|
|
3767
|
+
? 'dark'
|
|
3768
|
+
: themeSettings.theme;
|
|
3769
|
+
updateMenu({ kind: 'custom-theme-create', base });
|
|
3770
|
+
return;
|
|
3771
|
+
}
|
|
3772
|
+
const saving = (async () => {
|
|
3773
|
+
setBusy(true);
|
|
3774
|
+
try {
|
|
3775
|
+
const customTheme = selected.startsWith('custom:')
|
|
3776
|
+
? customThemes.find((theme) => `custom:${theme.slug}` === selected)
|
|
3777
|
+
: undefined;
|
|
3778
|
+
const committed = await presentationThemeStore.save({
|
|
3779
|
+
theme: selected,
|
|
3780
|
+
...(customTheme === undefined ? {} : { customTheme }),
|
|
3781
|
+
});
|
|
3782
|
+
setThemeSettings(committed);
|
|
3783
|
+
updateMenu(null);
|
|
3784
|
+
append({
|
|
3785
|
+
kind: 'local-result',
|
|
3786
|
+
text: `Theme set to ${customTheme?.name ?? selected}`,
|
|
3787
|
+
});
|
|
3788
|
+
}
|
|
3789
|
+
catch (error) {
|
|
3790
|
+
warn(error);
|
|
3791
|
+
}
|
|
3792
|
+
finally {
|
|
3793
|
+
setBusy(false);
|
|
3794
|
+
}
|
|
3795
|
+
})();
|
|
3796
|
+
onTurnChange?.(saving);
|
|
3797
|
+
void saving.finally(() => onTurnChange?.(null));
|
|
3798
|
+
}
|
|
3799
|
+
return;
|
|
3800
|
+
}
|
|
3801
|
+
if (activeMenu.kind === 'custom-theme-create') {
|
|
3802
|
+
if (key.escape || value === '\u001B') {
|
|
3803
|
+
clearComposerInput();
|
|
3804
|
+
updateMenu(null);
|
|
3805
|
+
}
|
|
3806
|
+
else if (key.return) {
|
|
3807
|
+
const name = inputRef.current.trim() || 'my-theme';
|
|
3808
|
+
const creating = (async () => {
|
|
3809
|
+
setBusy(true);
|
|
3810
|
+
try {
|
|
3811
|
+
const create = presentationThemeStore.createCustomTheme;
|
|
3812
|
+
if (!create)
|
|
3813
|
+
throw new Error('Custom theme controls are unavailable.');
|
|
3814
|
+
const theme = await create({ name, base: activeMenu.base });
|
|
3815
|
+
setCustomThemes((current) => [...current, theme].sort((left, right) => left.name.localeCompare(right.name)));
|
|
3816
|
+
const committed = await presentationThemeStore.save({
|
|
3817
|
+
theme: `custom:${theme.slug}`,
|
|
3818
|
+
customTheme: theme,
|
|
3819
|
+
});
|
|
3820
|
+
setThemeSettings(committed);
|
|
3821
|
+
clearComposerInput();
|
|
3822
|
+
updateMenu(null);
|
|
3823
|
+
append({
|
|
3824
|
+
kind: 'local-result',
|
|
3825
|
+
text: `Using custom theme "${theme.name}"`,
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
catch (error) {
|
|
3829
|
+
warn(error);
|
|
3830
|
+
}
|
|
3831
|
+
finally {
|
|
3832
|
+
setBusy(false);
|
|
3833
|
+
}
|
|
3834
|
+
})();
|
|
3835
|
+
onTurnChange?.(creating);
|
|
3836
|
+
void creating.finally(() => onTurnChange?.(null));
|
|
3837
|
+
}
|
|
3838
|
+
else {
|
|
3839
|
+
editComposer();
|
|
3840
|
+
}
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
if (activeMenu.kind === 'custom-theme-editor') {
|
|
3844
|
+
const filteredTokens = CUSTOM_THEME_TOKENS.filter((token) => token.toLowerCase().includes(activeMenu.query.toLowerCase()));
|
|
3845
|
+
if (key.escape || value === '\u001B') {
|
|
3846
|
+
clearComposerInput();
|
|
3847
|
+
updateMenu(null);
|
|
3848
|
+
}
|
|
3849
|
+
else if (key.upArrow || key.downArrow) {
|
|
3850
|
+
updateMenu({
|
|
3851
|
+
...activeMenu,
|
|
3852
|
+
selectedIndex: Math.max(0, Math.min(Math.max(0, filteredTokens.length - 1), activeMenu.selectedIndex + (key.upArrow ? -1 : 1))),
|
|
3853
|
+
});
|
|
3854
|
+
}
|
|
3855
|
+
else if (key.return) {
|
|
3856
|
+
const token = filteredTokens[activeMenu.selectedIndex];
|
|
3857
|
+
if (token) {
|
|
3858
|
+
const current = activeMenu.theme.overrides[token] ?? '';
|
|
3859
|
+
clearComposerInput();
|
|
3860
|
+
updateMenu({
|
|
3861
|
+
kind: 'custom-theme-token',
|
|
3862
|
+
theme: activeMenu.theme,
|
|
3863
|
+
token,
|
|
3864
|
+
});
|
|
3865
|
+
updateComposerInput(current);
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
else if (key.tab) {
|
|
3869
|
+
const token = filteredTokens[activeMenu.selectedIndex];
|
|
3870
|
+
if (token && activeMenu.theme.overrides[token] !== undefined) {
|
|
3871
|
+
const resetting = (async () => {
|
|
3872
|
+
setBusy(true);
|
|
3873
|
+
try {
|
|
3874
|
+
const update = presentationThemeStore.updateCustomTheme;
|
|
3875
|
+
if (!update)
|
|
3876
|
+
throw new Error('Custom theme controls are unavailable.');
|
|
3877
|
+
const next = await update(activeMenu.theme, token, undefined);
|
|
3878
|
+
setCustomThemes((current) => current.map((entry) => entry.slug === next.slug ? next : entry));
|
|
3879
|
+
setThemeSettings((current) => ({
|
|
3880
|
+
...current,
|
|
3881
|
+
theme: `custom:${next.slug}`,
|
|
3882
|
+
customTheme: next,
|
|
3883
|
+
}));
|
|
3884
|
+
updateMenu({ ...activeMenu, theme: next });
|
|
3885
|
+
}
|
|
3886
|
+
catch (error) {
|
|
3887
|
+
warn(error);
|
|
3888
|
+
}
|
|
3889
|
+
finally {
|
|
3890
|
+
setBusy(false);
|
|
3891
|
+
}
|
|
3892
|
+
})();
|
|
3893
|
+
onTurnChange?.(resetting);
|
|
3894
|
+
void resetting.finally(() => onTurnChange?.(null));
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
else if (key.backspace || key.delete) {
|
|
3898
|
+
updateMenu({
|
|
3899
|
+
...activeMenu,
|
|
3900
|
+
query: activeMenu.query.slice(0, -1),
|
|
3901
|
+
selectedIndex: 0,
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3904
|
+
else if (!key.ctrl && !key.meta && value && printable) {
|
|
3905
|
+
updateMenu({
|
|
3906
|
+
...activeMenu,
|
|
3907
|
+
query: activeMenu.query + value,
|
|
3908
|
+
selectedIndex: 0,
|
|
3909
|
+
});
|
|
3910
|
+
}
|
|
3911
|
+
else if (controlKey('d')) {
|
|
3912
|
+
updateMenu({
|
|
3913
|
+
kind: 'custom-theme-delete',
|
|
3914
|
+
theme: activeMenu.theme,
|
|
3915
|
+
selectedIndex: 0,
|
|
3916
|
+
});
|
|
3917
|
+
}
|
|
3918
|
+
return;
|
|
3919
|
+
}
|
|
3920
|
+
if (activeMenu.kind === 'custom-theme-token') {
|
|
3921
|
+
if (key.escape || value === '\u001B') {
|
|
3922
|
+
clearComposerInput();
|
|
3923
|
+
updateMenu({
|
|
3924
|
+
kind: 'custom-theme-editor',
|
|
3925
|
+
theme: activeMenu.theme,
|
|
3926
|
+
selectedIndex: 0,
|
|
3927
|
+
query: '',
|
|
3928
|
+
});
|
|
3929
|
+
}
|
|
3930
|
+
else if (key.return) {
|
|
3931
|
+
const valueToSave = inputRef.current.trim();
|
|
3932
|
+
const saving = (async () => {
|
|
3933
|
+
setBusy(true);
|
|
3934
|
+
try {
|
|
3935
|
+
const update = presentationThemeStore.updateCustomTheme;
|
|
3936
|
+
if (!update)
|
|
3937
|
+
throw new Error('Custom theme controls are unavailable.');
|
|
3938
|
+
const next = await update(activeMenu.theme, activeMenu.token, valueToSave);
|
|
3939
|
+
setCustomThemes((current) => current.map((entry) => entry.slug === next.slug ? next : entry));
|
|
3940
|
+
setThemeSettings((current) => ({
|
|
3941
|
+
...current,
|
|
3942
|
+
theme: `custom:${next.slug}`,
|
|
3943
|
+
customTheme: next,
|
|
3944
|
+
}));
|
|
3945
|
+
clearComposerInput();
|
|
3946
|
+
updateMenu({
|
|
3947
|
+
kind: 'custom-theme-editor',
|
|
3948
|
+
theme: next,
|
|
3949
|
+
selectedIndex: 0,
|
|
3950
|
+
query: '',
|
|
3951
|
+
});
|
|
3952
|
+
}
|
|
3953
|
+
catch (error) {
|
|
3954
|
+
warn(error);
|
|
3955
|
+
}
|
|
3956
|
+
finally {
|
|
3957
|
+
setBusy(false);
|
|
3958
|
+
}
|
|
3959
|
+
})();
|
|
3960
|
+
onTurnChange?.(saving);
|
|
3961
|
+
void saving.finally(() => onTurnChange?.(null));
|
|
3962
|
+
}
|
|
3963
|
+
else {
|
|
3964
|
+
editComposer();
|
|
3965
|
+
}
|
|
3966
|
+
return;
|
|
3967
|
+
}
|
|
3968
|
+
if (activeMenu.kind === 'custom-theme-delete') {
|
|
3969
|
+
if (key.escape || value === '\u001B') {
|
|
3970
|
+
updateMenu(null);
|
|
3971
|
+
}
|
|
3972
|
+
else if (key.return || value.toLowerCase() === 'y') {
|
|
3973
|
+
const deleting = (async () => {
|
|
3974
|
+
setBusy(true);
|
|
3975
|
+
try {
|
|
3976
|
+
const remove = presentationThemeStore.deleteCustomTheme;
|
|
3977
|
+
if (!remove)
|
|
3978
|
+
throw new Error('Custom theme controls are unavailable.');
|
|
3979
|
+
await remove(activeMenu.theme);
|
|
3980
|
+
setCustomThemes((current) => current.filter((entry) => entry.slug !== activeMenu.theme.slug));
|
|
3981
|
+
const committed = await presentationThemeStore.save({
|
|
3982
|
+
theme: activeMenu.theme.base,
|
|
3983
|
+
});
|
|
3984
|
+
setThemeSettings(committed);
|
|
3985
|
+
updateMenu(null);
|
|
3986
|
+
append({
|
|
3987
|
+
kind: 'local-result',
|
|
3988
|
+
text: `Deleted custom theme "${activeMenu.theme.name}"`,
|
|
3989
|
+
});
|
|
3990
|
+
}
|
|
3991
|
+
catch (error) {
|
|
3992
|
+
warn(error);
|
|
3993
|
+
}
|
|
3994
|
+
finally {
|
|
3995
|
+
setBusy(false);
|
|
3996
|
+
}
|
|
3997
|
+
})();
|
|
3998
|
+
onTurnChange?.(deleting);
|
|
3999
|
+
void deleting.finally(() => onTurnChange?.(null));
|
|
4000
|
+
}
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
3092
4003
|
if (activeMenu.kind === 'compact-progress') {
|
|
3093
4004
|
if (key.escape || value === '\u001B' || isKeybinding('chat:cancel')) {
|
|
3094
4005
|
turnControllerRef.current?.abort();
|
|
@@ -3099,6 +4010,11 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3099
4010
|
updateMenu(null);
|
|
3100
4011
|
return;
|
|
3101
4012
|
}
|
|
4013
|
+
if (activeMenu.kind === 'mcp' ||
|
|
4014
|
+
activeMenu.kind === 'tasks' ||
|
|
4015
|
+
activeMenu.kind === 'config') {
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
3102
4018
|
const optionCount = activeMenu.kind === 'model'
|
|
3103
4019
|
? modelOptions.length
|
|
3104
4020
|
: EFFORT_OPTIONS.length;
|
|
@@ -3194,6 +4110,13 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3194
4110
|
if (key.tab)
|
|
3195
4111
|
return;
|
|
3196
4112
|
}
|
|
4113
|
+
if (runtimeSettingsRef.current.leftArrowOpensAgents &&
|
|
4114
|
+
key.leftArrow &&
|
|
4115
|
+
inputRef.current.length === 0 &&
|
|
4116
|
+
availableAgents.length > 0) {
|
|
4117
|
+
updateMenu({ kind: 'agents', agents: availableAgents, selectedIndex: 0 });
|
|
4118
|
+
return;
|
|
4119
|
+
}
|
|
3197
4120
|
if (isKeybinding('chat:cancel')) {
|
|
3198
4121
|
const now = Date.now();
|
|
3199
4122
|
if (now - lastEscapeAtRef.current <= 500)
|
|
@@ -3236,7 +4159,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3236
4159
|
return;
|
|
3237
4160
|
}
|
|
3238
4161
|
}
|
|
3239
|
-
const implicitShiftNewline = key.return && key.shift && keybindingAction === undefined;
|
|
4162
|
+
const implicitShiftNewline = key.return && (key.shift || key.meta) && keybindingAction === undefined;
|
|
3240
4163
|
if (isKeybinding('chat:submit') ||
|
|
3241
4164
|
isKeybinding('chat:newline') ||
|
|
3242
4165
|
implicitShiftNewline) {
|
|
@@ -3257,6 +4180,7 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3257
4180
|
if (!prompt || prompt === '!')
|
|
3258
4181
|
return;
|
|
3259
4182
|
const copyCommand = /^\/copy(?:\s+(\d+))?$/u.exec(prompt);
|
|
4183
|
+
const tuiCommand = /^\/tui(?:\s+(default|fullscreen))?$/u.exec(prompt);
|
|
3260
4184
|
const renameCommand = /^\/rename(?:\s+(.+))?$/u.exec(prompt);
|
|
3261
4185
|
const cdCommand = /^\/cd(?:\s+(.+))?$/u.exec(prompt);
|
|
3262
4186
|
const btwCommand = /^\/btw(?:\s+([\s\S]+))?$/u.exec(prompt);
|
|
@@ -3294,6 +4218,34 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3294
4218
|
selectedIndex: EFFORT_OPTIONS.indexOf(runtimePreferences.effort),
|
|
3295
4219
|
});
|
|
3296
4220
|
}
|
|
4221
|
+
else if (prompt === '/theme') {
|
|
4222
|
+
updateMenu({
|
|
4223
|
+
kind: 'theme',
|
|
4224
|
+
selectedIndex: Math.max(0, themeSettings.theme.startsWith('custom:')
|
|
4225
|
+
? TUI_THEMES.length +
|
|
4226
|
+
customThemes.findIndex((theme) => `custom:${theme.slug}` === themeSettings.theme)
|
|
4227
|
+
: TUI_THEMES.indexOf(themeSettings.theme)),
|
|
4228
|
+
});
|
|
4229
|
+
}
|
|
4230
|
+
else if (prompt === '/terminal-setup') {
|
|
4231
|
+
const setup = (async () => {
|
|
4232
|
+
setBusy(true);
|
|
4233
|
+
setStatus('configuring terminal');
|
|
4234
|
+
try {
|
|
4235
|
+
const result = await (terminalSetupOverride ?? setupTuiTerminal)();
|
|
4236
|
+
append({ kind: 'local-result', text: result });
|
|
4237
|
+
}
|
|
4238
|
+
catch (error) {
|
|
4239
|
+
warn(error);
|
|
4240
|
+
}
|
|
4241
|
+
finally {
|
|
4242
|
+
setBusy(false);
|
|
4243
|
+
setStatus('ready');
|
|
4244
|
+
}
|
|
4245
|
+
})();
|
|
4246
|
+
onTurnChange?.(setup);
|
|
4247
|
+
void setup.finally(() => onTurnChange?.(null));
|
|
4248
|
+
}
|
|
3297
4249
|
else if (prompt === '/keybindings') {
|
|
3298
4250
|
setKeybindingsEditing(true);
|
|
3299
4251
|
}
|
|
@@ -3382,10 +4334,12 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3382
4334
|
const loading = (async () => {
|
|
3383
4335
|
setBusy(true);
|
|
3384
4336
|
try {
|
|
4337
|
+
const entries = await recentDeniedStore.load();
|
|
4338
|
+
setRecentDenied(entries);
|
|
3385
4339
|
updateMenu({
|
|
3386
4340
|
kind: 'permission-dashboard',
|
|
3387
|
-
tabIndex: 1,
|
|
3388
|
-
selectedIndex: -1,
|
|
4341
|
+
tabIndex: entries.length > 0 ? 0 : 1,
|
|
4342
|
+
selectedIndex: entries.length > 0 ? 0 : -1,
|
|
3389
4343
|
query: '',
|
|
3390
4344
|
rules: await permissionStore.load(),
|
|
3391
4345
|
});
|
|
@@ -3511,11 +4465,69 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3511
4465
|
updateMenu({ kind: 'status', tabIndex: 1 });
|
|
3512
4466
|
}
|
|
3513
4467
|
else if (prompt === '/config') {
|
|
3514
|
-
|
|
4468
|
+
const initial = {
|
|
4469
|
+
kind: 'config',
|
|
4470
|
+
snapshot: { settings: {}, state: {} },
|
|
4471
|
+
tab: 'config',
|
|
4472
|
+
selectedIndex: 0,
|
|
4473
|
+
query: '',
|
|
4474
|
+
searchFocused: true,
|
|
4475
|
+
};
|
|
4476
|
+
updateMenu(initial);
|
|
4477
|
+
const loading = (async () => {
|
|
4478
|
+
setBusy(true);
|
|
4479
|
+
try {
|
|
4480
|
+
const snapshot = await loadConfigSettings(configTarget);
|
|
4481
|
+
if (menuRef.current?.kind === 'config')
|
|
4482
|
+
updateMenu({ ...menuRef.current, snapshot });
|
|
4483
|
+
}
|
|
4484
|
+
catch (error) {
|
|
4485
|
+
warn(error);
|
|
4486
|
+
}
|
|
4487
|
+
finally {
|
|
4488
|
+
setBusy(false);
|
|
4489
|
+
}
|
|
4490
|
+
})();
|
|
4491
|
+
onTurnChange?.(loading);
|
|
4492
|
+
void loading.finally(() => onTurnChange?.(null));
|
|
4493
|
+
}
|
|
4494
|
+
else if (tuiCommand) {
|
|
4495
|
+
const mode = tuiCommand[1];
|
|
4496
|
+
if (!mode) {
|
|
4497
|
+
append({
|
|
4498
|
+
kind: 'local-result',
|
|
4499
|
+
text: `TUI renderer: ${runtimeSettingsRef.current.tui}. Usage: /tui default|fullscreen`,
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
else {
|
|
4503
|
+
const saving = (async () => {
|
|
4504
|
+
setBusy(true);
|
|
4505
|
+
try {
|
|
4506
|
+
const snapshot = await saveConfigSetting('tui', mode, configTarget);
|
|
4507
|
+
await reloadRuntimeSettings(snapshot);
|
|
4508
|
+
onRendererChange?.(mode, sessionId);
|
|
4509
|
+
exit();
|
|
4510
|
+
}
|
|
4511
|
+
catch (error) {
|
|
4512
|
+
warn(error);
|
|
4513
|
+
}
|
|
4514
|
+
finally {
|
|
4515
|
+
setBusy(false);
|
|
4516
|
+
}
|
|
4517
|
+
})();
|
|
4518
|
+
onTurnChange?.(saving);
|
|
4519
|
+
void saving.finally(() => onTurnChange?.(null));
|
|
4520
|
+
}
|
|
3515
4521
|
}
|
|
3516
4522
|
else if (prompt === '/usage') {
|
|
3517
4523
|
updateMenu({ kind: 'status', tabIndex: 3 });
|
|
3518
4524
|
}
|
|
4525
|
+
else if (prompt === '/update') {
|
|
4526
|
+
append({
|
|
4527
|
+
kind: 'local-result',
|
|
4528
|
+
text: `Configured auto-update channel: ${autoUpdateTarget(runtimeSettingsRef.current.autoUpdatesChannel)}. Run praxis update to update now.`,
|
|
4529
|
+
});
|
|
4530
|
+
}
|
|
3519
4531
|
else if (prompt === '/skills' || prompt === '/skill') {
|
|
3520
4532
|
updateMenu({
|
|
3521
4533
|
kind: 'list',
|
|
@@ -3586,161 +4598,247 @@ export function InteractiveApp({ factory, initialSessions, initialPrompt, initia
|
|
|
3586
4598
|
}
|
|
3587
4599
|
editComposer();
|
|
3588
4600
|
});
|
|
3589
|
-
return (_jsx(Box, { flexDirection: "column", children: selectingSession ? (_jsx(SessionPicker, { sessions: filteredPickerChoices, selectedIndex: selectedIndex, screenReader: axScreenReader, query: sessionSearch })) : (_jsxs(_Fragment, { children: [!axScreenReader && history.length === 0 && !sessionId ? (_jsx(WelcomePanel, { display: runtimeDisplay, width: width })) : null, sessionId ? (_jsxs(Text, { dimColor: true, children: ["Session ", sessionId.slice(0, 8)] })) : null, _jsx(Transcript, { items: history, activeText: activeText, activeThinking: activeThinking, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded, screenReader: axScreenReader }), externalEditorRequest !== null ||
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
.
|
|
3604
|
-
?
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
:
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
:
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
4601
|
+
return (_jsx(TuiThemeProvider, { settings: themeSettings, children: _jsx(Box, { flexDirection: "column", children: selectingSession ? (_jsx(SessionPicker, { sessions: filteredPickerChoices, selectedIndex: selectedIndex, screenReader: axScreenReader, query: sessionSearch })) : (_jsxs(_Fragment, { children: [!axScreenReader && history.length === 0 && !sessionId ? (_jsx(WelcomePanel, { display: runtimeDisplay, width: width })) : null, sessionId ? (_jsxs(Text, { dimColor: true, children: ["Session ", sessionId.slice(0, 8)] })) : null, _jsx(Transcript, { items: history, activeText: activeText, activeThinking: activeThinking, thinkingExpanded: thinkingExpanded, detailedTranscript: thinkingExpanded || runtimeSettings.verbose, screenReader: axScreenReader }), externalEditorRequest !== null ||
|
|
4602
|
+
keybindingsEditing ||
|
|
4603
|
+
memoryEditorRequest !== null ? (_jsx(ExternalEditorWait, { screenReader: axScreenReader })) : permission ? (_jsxs(DialogFrame, { title: permission.kind === 'recovery'
|
|
4604
|
+
? `Retry interrupted ${permission.call.name}?`
|
|
4605
|
+
: `Allow ${permission.call.name}?`, screenReader: axScreenReader, children: [_jsx(Text, { bold: true, children: describeTool(permission.call, sensitiveValues) }), _jsxs(Text, { children: [selectionPrefix(true, axScreenReader), "1. Yes"] }), _jsx(Text, { children: " 2. No" }), _jsx(Text, { dimColor: true, children: "Enter/Esc declines \u00B7 y/n quick response" })] })) : planApproval ? (_jsxs(DialogFrame, { title: "Approve this plan and begin implementation?", screenReader: axScreenReader, children: [_jsx(Text, { dimColor: true, children: planApproval.request.planPath }), planApproval.request.plan ? (_jsx(Box, { marginY: 1, children: _jsx(Text, { children: planApproval.request.plan }) })) : null, _jsxs(Text, { children: [selectionPrefix(true, axScreenReader), "1. Yes, implement the plan"] }), _jsx(Text, { children: " 2. No, keep planning" })] })) : question ? (_jsxs(DialogFrame, { title: `${question.questions[question.index]?.header}: ${question.questions[question.index]?.question}`, screenReader: axScreenReader, children: [question.questions[question.index]?.options.map((option, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [index + 1, ". ", option.label, " \u2014 ", option.description] }), option.preview ? (_jsx(Text, { dimColor: true, children: option.preview })) : null] }, `${index}-${option.label}`))), _jsxs(Text, { children: [axScreenReader ? 'Current answer: ' : '› ', input || (axScreenReader ? '(empty)' : '')] }), _jsx(Text, { dimColor: true, children: question.questions[question.index]?.multiSelect
|
|
4606
|
+
? 'Enter comma-separated option numbers or custom text · Esc cancels'
|
|
4607
|
+
: 'Enter one option number or custom text · Esc cancels' })] })) : elicitation ? (_jsxs(DialogFrame, { title: `MCP elicitation (${elicitation.request.serverName})`, screenReader: axScreenReader, children: [_jsx(Text, { children: elicitation.request.message }), elicitation.request.url ? (_jsx(Text, { children: elicitation.request.url })) : null, elicitation.request.requestedSchema ? (_jsx(Text, { dimColor: true, children: JSON.stringify(elicitation.request.requestedSchema) })) : null, _jsxs(Text, { children: ["\u203A ", input] }), _jsx(Text, { dimColor: true, children: "Enter JSON object to accept \u00B7 Esc to cancel" })] })) : menu?.kind === 'model-input' ? (_jsxs(DialogFrame, { title: "Enter model ID", screenReader: axScreenReader, children: [_jsx(Text, { dimColor: true, children: "Enter a model ID supported by the configured provider." }), _jsxs(Text, { children: ["\u203A ", input] }), _jsx(Text, { dimColor: true, children: "Enter confirms \u00B7 Esc cancels" })] })) : menu?.kind === 'export-filename' ? (_jsxs(DialogFrame, { title: "Enter filename:", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["> ", input] }), _jsx(Text, { dimColor: true, children: "Enter to save \u00B7 Esc to go back" })] })) : menu?.kind === 'compact-progress' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "\u273B Compacting conversation\u2026" }), _jsxs(Text, { children: ['▰'.repeat(Math.floor(compactProgress / 2)), '▱'.repeat(50 - Math.floor(compactProgress / 2)), ' ', compactProgress, "%"] })] })) : menu?.kind === 'btw' ? (_jsx(BtwPanel, { entries: btwHistory, selectedIndex: menu.selectedIndex, scrollOffset: menu.scrollOffset, copied: btwCopied, width: width, screenReader: axScreenReader })) : menu?.kind === 'rewind' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: " Rewind" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', "Restore the code and/or conversation to the point before\u2026"] }), _jsx(Text, { children: " " }), (() => {
|
|
4608
|
+
const window = rewindPointWindow(menu.points, menu.selectedIndex);
|
|
4609
|
+
return (_jsxs(_Fragment, { children: [window.start > 0 ? (_jsxs(Text, { dimColor: true, children: [" \u2191 ", window.start, " more above"] })) : null, menu.points
|
|
4610
|
+
.slice(window.start, window.end)
|
|
4611
|
+
.map((point, offset) => {
|
|
4612
|
+
const index = window.start + offset;
|
|
4613
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { inverse: menu.selectedIndex === index, children: [menu.selectedIndex === index ? ' ❯ ' : ' ', point.prompt
|
|
4614
|
+
.replace(/\s+/gu, ' ')
|
|
4615
|
+
.slice(0, 72)] }), _jsxs(Text, { dimColor: true, children: [' ', point.fileChanges.length > 0
|
|
4616
|
+
? point.fileChanges
|
|
4617
|
+
.map((path) => path.startsWith(`${runtimeCwd}/`)
|
|
4618
|
+
? path.slice(runtimeCwd.length + 1)
|
|
4619
|
+
: path)
|
|
4620
|
+
.join(', ')
|
|
4621
|
+
: point.fileRestoreAvailable
|
|
4622
|
+
? 'No code changes'
|
|
4623
|
+
: '⚠ No code restore'] })] }, point.messageId));
|
|
4624
|
+
}), window.end < menu.points.length ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2193 ", menu.points.length - window.end, " more below"] })) : null] }));
|
|
4625
|
+
})(), _jsxs(Text, { inverse: menu.selectedIndex === menu.points.length, children: [menu.selectedIndex === menu.points.length ? ' ❯ ' : ' ', "(current)"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Enter to continue \u00B7 Esc to cancel" })] })) : menu?.kind === 'rewind-confirm' ? (_jsxs(DialogFrame, { title: "Confirm restore point", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["\u2502 ", menu.point.prompt] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "The conversation will be forked." }), _jsxs(Text, { children: ["The code will", ' ', menu.point.fileChanges.length > 0
|
|
4626
|
+
? `restore ${menu.point.fileChanges.join(', ')}.`
|
|
4627
|
+
: 'be unchanged.'] }), _jsx(Text, { children: " " }), rewindActions(menu.point).map((option, index) => (_jsxs(Text, { inverse: menu.selectedIndex === index, children: [menu.selectedIndex === index ? '❯ ' : ' ', index + 1, ". ", option.label] }, option.action))), _jsx(Text, { children: " " }), _jsx(Text, { color: activePalette.warning, children: "\u26A0 Rewinding does not affect files edited manually or via bash." })] })) : menu?.kind === 'rewind-context' ? (_jsxs(DialogFrame, { title: `Summarize ${menu.direction === 'from' ? 'from' : 'up to'} here`, screenReader: axScreenReader, children: [_jsx(Text, { children: menu.direction === 'from'
|
|
4628
|
+
? 'Messages after this point will be summarized.'
|
|
4629
|
+
: 'Messages up to this point will be summarized.' }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["add context (optional): ", input] }), _jsx(Text, { dimColor: true, children: "Enter to summarize \u00B7 Esc to go back" })] })) : menu?.kind === 'permission-rule-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: ["Add ", menu.behavior, " permission rule"] }), _jsx(Text, { children: "Permission rules are a tool name, optionally followed by a specifier in parentheses." }), _jsx(Text, { children: "e.g., WebFetch or Bash(ls *)" }), _jsx(Text, { children: " " }), _jsx(Box, { borderStyle: axScreenReader ? undefined : 'round', paddingX: axScreenReader ? 0 : 1, children: _jsx(Text, { ...(input ? {} : { dimColor: true }), children: input || 'Enter permission rule…' }) }), _jsx(Text, { dimColor: true, children: "Enter to submit \u00B7 Esc to cancel" })] })) : menu?.kind === 'workspace-directory-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Add directory to workspace" }), _jsx(Text, { children: "Praxis Code will be able to read files in this directory and make edits when auto-accept edits is on." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Enter the path to the directory:" }), _jsx(Box, { borderStyle: axScreenReader ? undefined : 'round', paddingX: axScreenReader ? 0 : 1, children: _jsx(Text, { ...(input ? {} : { dimColor: true }), children: input || 'Directory path…' }) }), _jsx(Text, { dimColor: true, children: "Tab to complete \u00B7 Enter to add \u00B7 Esc to cancel" })] })) : menu?.kind === 'permission-delete' ? (_jsxs(DialogFrame, { title: permissionDeleteTitle(menu.rule.behavior), screenReader: axScreenReader, children: [_jsx(Text, { children: menu.rule.rule }), permissionRuleDescription(menu.rule.rule) ? (_jsx(Text, { dimColor: true, children: permissionRuleDescription(menu.rule.rule) })) : null, _jsx(Text, { dimColor: true, children: permissionScopeLabel(menu.rule.scope) }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Are you sure you want to delete this permission rule?" }), _jsx(Text, { children: " " }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 0, children: [selectionPrefix(menu.selectedIndex === 0, axScreenReader), "1. Yes"] }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 1, children: [selectionPrefix(menu.selectedIndex === 1, axScreenReader), "2. No"] }), _jsx(Text, { dimColor: true, children: "Esc to cancel" })] })) : menu?.kind === 'workspace-directory-delete' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Remove directory from workspace?" }), _jsxs(Text, { children: [" ", menu.path] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Praxis Code will no longer have access to files in this directory." }), _jsx(Text, { children: " " }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 0, children: [selectionPrefix(menu.selectedIndex === 0, axScreenReader), "1. Yes"] }), _jsxs(Text, { inverse: !axScreenReader && menu.selectedIndex === 1, children: [selectionPrefix(menu.selectedIndex === 1, axScreenReader), "2. No"] }), _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Esc to cancel" })] })) : menu ? (menu.kind === 'help' ? (_jsx(HelpMenu, { tabIndex: menu.tabIndex, selectedIndex: menu.selectedIndex, builtinCommands: builtinSlashCommands, customCommands: customSlashCommands, width: width, screenReader: axScreenReader })) : menu.kind === 'diff' ? (_jsx(DiffDashboard, { snapshots: menu.snapshots, sourceIndex: menu.sourceIndex, selectedIndex: menu.selectedIndex, viewingFile: menu.viewingFile, scrollOffset: menu.scrollOffset, width: width, screenReader: axScreenReader })) : menu.kind === 'permission-dashboard' ? (_jsx(PermissionDashboard, { tabIndex: menu.tabIndex, selectedIndex: menu.selectedIndex, query: menu.query, rules: menu.rules, recentDenied: recentDenied, retryingDeniedId: retryingDeniedId, workspaceDirectories: workspaceDirectories, width: width, screenReader: axScreenReader })) : menu.kind === 'permission-scope' ? (_jsx(SelectionMenu, { title: "Where should this rule be saved?", description: `${menu.rule}${permissionRuleDescription(menu.rule)
|
|
4630
|
+
? ` · ${permissionRuleDescription(menu.rule)}`
|
|
4631
|
+
: ''}`, options: [
|
|
4632
|
+
{
|
|
4633
|
+
label: 'Project settings (local)',
|
|
4634
|
+
description: 'Saved in .claude/settings.local.json',
|
|
4635
|
+
},
|
|
4636
|
+
{
|
|
4637
|
+
label: 'Project settings',
|
|
4638
|
+
description: 'Checked in at .claude/settings.json',
|
|
4639
|
+
},
|
|
4640
|
+
{
|
|
4641
|
+
label: 'User settings',
|
|
4642
|
+
description: 'Saved in at ~/.claude/settings.json',
|
|
4643
|
+
},
|
|
4644
|
+
], selectedIndex: menu.selectedIndex, footer: "Enter to confirm \u00B7 Esc to cancel", width: width, screenReader: axScreenReader })) : menu.kind === 'status' ? (_jsx(StatusDashboard, { tabIndex: menu.tabIndex, version: runtimeDisplay.version, sessionId: sessionId, display: runtimeDisplay, ...(usage === undefined ? {} : { usage }), ...(costUsd === undefined ? {} : { costUsd }), turnCount: turnNumberRef.current, toolCount: history.filter((item) => item.kind === 'tool').length, commandCount: allSlashCommands.length, detailedTranscript: thinkingExpanded, width: width, screenReader: axScreenReader })) : menu.kind === 'copy' ? (_jsx(SelectionMenu, { title: "Copy response", description: "Choose the response content to copy.", options: menu.candidates.map((candidate) => ({
|
|
4645
|
+
label: candidate.label,
|
|
4646
|
+
description: candidate.description,
|
|
4647
|
+
})), selectedIndex: menu.selectedIndex, footer: "\u2191/\u2193 select \u00B7 Enter copies \u00B7 Esc cancels", width: width, screenReader: axScreenReader })) : menu.kind === 'agents' ? (_jsx(ListDashboard, { title: "Agents", rows: menu.agents.map((agent) => ({
|
|
4648
|
+
label: agent.name,
|
|
4649
|
+
description: agent.description,
|
|
4650
|
+
})), emptyText: "No agents configured", selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'config' ? (_jsx(ConfigDashboard, { tab: menu.tab, snapshot: menu.snapshot, query: menu.query, selectedIndex: menu.selectedIndex, searchFocused: menu.searchFocused, settings: [
|
|
4651
|
+
{
|
|
4652
|
+
label: 'Provider',
|
|
4653
|
+
value: runtimeDisplay.model ?? 'default',
|
|
4654
|
+
},
|
|
4655
|
+
{
|
|
4656
|
+
label: 'Permission mode',
|
|
4657
|
+
value: runtimeDisplay.permissionMode ?? 'default',
|
|
4658
|
+
},
|
|
4659
|
+
], status: {
|
|
4660
|
+
version: runtimeDisplay.version,
|
|
4661
|
+
sessionId: sessionId ?? 'new',
|
|
4662
|
+
cwd: runtimeCwd,
|
|
4663
|
+
model: runtimeDisplay.model ?? 'default',
|
|
4664
|
+
settingSources: ['user', 'project', 'local'],
|
|
4665
|
+
}, usage: {
|
|
4666
|
+
costUsd: costUsd ?? 0,
|
|
4667
|
+
apiDurationMs: 0,
|
|
4668
|
+
wallDurationMs: 0,
|
|
4669
|
+
linesAdded: 0,
|
|
4670
|
+
linesRemoved: 0,
|
|
4671
|
+
usage: usage ?? { inputTokens: 0, outputTokens: 0 },
|
|
4672
|
+
}, stats: [], width: width, screenReader: axScreenReader })) : menu.kind === 'mcp' ? (_jsx(McpPanel, { model: menu.model, state: menu.state, width: width, screenReader: axScreenReader })) : menu.kind === 'tasks' ? (_jsx(TaskPanel, { tasks: menu.tasks, state: menu.state, width: width, screenReader: axScreenReader })) : menu.kind === 'memory' ? (_jsx(MemoryDashboard, { autoMemoryEnabled: menu.autoMemoryEnabled, entries: menu.entries, selectedIndex: menu.selectedIndex, openedIndex: menu.openedIndex, loading: menu.loading, width: width, screenReader: axScreenReader })) : menu.kind === 'hooks' ? (_jsx(HookDashboard, { configuration: menu.configuration, depth: menu.depth, eventIndex: menu.eventIndex, matcherIndex: menu.matcherIndex, hookIndex: menu.hookIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'list' ? (_jsx(ListDashboard, { title: menu.title, rows: menu.rows, emptyText: menu.emptyText, selectedIndex: menu.selectedIndex, width: width, screenReader: axScreenReader })) : menu.kind === 'model' ? (_jsx(SelectionMenu, { title: "Select model", description: `Effort: ${runtimePreferences.effort}`, options: modelOptions, selectedIndex: menu.selectedIndex, footer: "\u2191/\u2193 select \u00B7 \u2190/\u2192 effort \u00B7 Enter applies to this session \u00B7 Esc cancels", width: width, screenReader: axScreenReader })) : menu.kind === 'effort' ? (_jsx(SelectionMenu, { title: "Select effort", description: "Controls how much reasoning effort the provider should use.", options: EFFORT_OPTIONS.map((option) => ({
|
|
4673
|
+
label: option,
|
|
4674
|
+
description: option === 'low'
|
|
4675
|
+
? 'Fastest and least deliberative.'
|
|
4676
|
+
: option === 'max'
|
|
4677
|
+
? 'Highest available reasoning effort.'
|
|
4678
|
+
: 'Use this effort for the next session turns.',
|
|
4679
|
+
selected: option === runtimePreferences.effort,
|
|
4680
|
+
})), selectedIndex: menu.selectedIndex, footer: "\u2191/\u2193 select \u00B7 Enter applies to this session \u00B7 Esc cancels", width: width, screenReader: axScreenReader })) : menu.kind === 'theme' ? (_jsx(ThemePicker, { currentTheme: themeSettings.theme, selectedIndex: menu.selectedIndex, customThemes: customThemes, syntaxHighlightingDisabled: themeSettings.syntaxHighlightingDisabled, width: width, screenReader: axScreenReader })) : menu.kind === 'custom-theme-create' ? (_jsxs(DialogFrame, { title: "New custom theme", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["Name: ", input || 'my-theme'] }), _jsxs(Text, { children: ["based on ", menu.base, " \u00B7 saved to ~/.claude/themes/theme.json"] }), _jsx(Text, { dimColor: true, children: "Enter to create \u00B7 Esc to cancel" })] })) : menu.kind === 'custom-theme-editor' ? (_jsx(CustomThemeEditor, { theme: menu.theme, width: width, screenReader: axScreenReader, value: menu.query, tokens: CUSTOM_THEME_TOKENS.filter((token) => token.toLowerCase().includes(menu.query.toLowerCase())), selectedIndex: menu.selectedIndex, query: menu.query })) : menu.kind === 'custom-theme-token' ? (_jsx(CustomThemeEditor, { theme: menu.theme, token: menu.token, width: width, screenReader: axScreenReader, value: input })) : menu.kind === 'custom-theme-delete' ? (_jsxs(DialogFrame, { title: "Delete custom theme", screenReader: axScreenReader, children: [_jsxs(Text, { children: ["Delete ", menu.theme.name, " permanently?"] }), _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Esc to cancel" })] })) : menu.kind === 'export' ? (_jsx(SelectionMenu, { title: "Export conversation", description: "Select export method", options: [
|
|
4681
|
+
{
|
|
4682
|
+
label: 'Copy to clipboard',
|
|
4683
|
+
description: 'Copy the conversation to your system clipboard',
|
|
4684
|
+
},
|
|
4685
|
+
{
|
|
4686
|
+
label: 'Save to file',
|
|
4687
|
+
description: 'Save the conversation to a file in the current directory',
|
|
4688
|
+
},
|
|
4689
|
+
], selectedIndex: menu.selectedIndex, footer: "Esc to cancel", width: width, screenReader: axScreenReader })) : null) : (_jsxs(_Fragment, { children: [commandPaletteVisible ? (_jsx(CommandPalette, { commands: matchingSlashCommands, selectedIndex: selectedSlashCommandIndex, width: width, screenReader: axScreenReader })) : null, filePickerVisible ? (_jsx(MentionPicker, { entries: matchingMentionEntries, selectedIndex: selectedFileIndex, width: width, screenReader: axScreenReader })) : null, exitConfirmation ? (_jsx(Text, { color: activePalette.warning, children: "Press Ctrl-C again to exit" })) : null, _jsx(Composer, { input: shellMode ? input.slice(1) : input, cursor: shellMode ? Math.max(0, inputCursor - 1) : inputCursor, shellMode: shellMode, busy: busy, clipboardBusy: clipboardBusy, status: status, display: runtimeDisplay, ...(usage === undefined ? {} : { usage }), ...(costUsd === undefined ? {} : { costUsd }), width: width, screenReader: axScreenReader, hasThinking: hasDetailedTranscript, thinkingExpanded: thinkingExpanded, reduceMotion: runtimeSettings.reduceMotion, progressBar: runtimeSettings.progressBar, ...(runtimeSettings.turnDuration
|
|
4690
|
+
? (() => {
|
|
4691
|
+
const duration = formatTurnDuration(turnDuration);
|
|
4692
|
+
return duration === undefined
|
|
4693
|
+
? {}
|
|
4694
|
+
: { turnDuration: duration };
|
|
4695
|
+
})()
|
|
4696
|
+
: {}), editorMode: runtimeSettings.editor, ...(runtimeSettings.prStatus &&
|
|
4697
|
+
activeSessionSummary?.prNumber
|
|
4698
|
+
? { prStatus: `PR #${activeSessionSummary.prNumber}` }
|
|
4699
|
+
: {}), shortcutsVisible: shortcutsVisible, ...(editorFooterMessage === undefined
|
|
4700
|
+
? {}
|
|
4701
|
+
: { footerMessage: editorFooterMessage }) })] }))] })) }) }));
|
|
3650
4702
|
}
|
|
3651
4703
|
export async function runInteractive(options) {
|
|
3652
4704
|
const controller = new AbortController();
|
|
3653
4705
|
const signal = options.signal
|
|
3654
4706
|
? AbortSignal.any([options.signal, controller.signal])
|
|
3655
4707
|
: controller.signal;
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
let
|
|
3662
|
-
|
|
3663
|
-
let
|
|
4708
|
+
let currentResume = options.resume;
|
|
4709
|
+
let currentInitialPrompt = options.initialPrompt;
|
|
4710
|
+
let currentRuntimeSettings = options.runtimeSettings ?? (await loadRuntimeSettings());
|
|
4711
|
+
let currentRenderer = currentRuntimeSettings.tui;
|
|
4712
|
+
let rendererChange = null;
|
|
4713
|
+
let rendererNotice;
|
|
4714
|
+
const readRendererChange = () => rendererChange;
|
|
4715
|
+
let initialThemeSettings = DEFAULT_TUI_THEME_SETTINGS;
|
|
4716
|
+
let initialThemeLoadError;
|
|
3664
4717
|
try {
|
|
3665
|
-
|
|
3666
|
-
initialSessions = options.sessionFilter
|
|
3667
|
-
? sessions.filter(options.sessionFilter)
|
|
3668
|
-
: sessions;
|
|
3669
|
-
initialSlashCommands = listing.slashCommands?.() ?? [];
|
|
3670
|
-
initialAgents = listing.agentDefinitions?.() ?? [];
|
|
3671
|
-
if (options.requireSession && initialSessions.length === 0) {
|
|
3672
|
-
throw new Error(options.missingSessionMessage ??
|
|
3673
|
-
'No conversation linked to a pull request in this project');
|
|
3674
|
-
}
|
|
4718
|
+
initialThemeSettings = await loadTuiThemeSettings();
|
|
3675
4719
|
}
|
|
3676
4720
|
catch (error) {
|
|
4721
|
+
initialThemeLoadError = `Unable to load theme settings: ${error instanceof Error ? error.message : String(error)}`;
|
|
4722
|
+
}
|
|
4723
|
+
while (true) {
|
|
4724
|
+
const listing = await options.factory.createService({
|
|
4725
|
+
eventSink: () => undefined,
|
|
4726
|
+
requireProvider: false,
|
|
4727
|
+
signal,
|
|
4728
|
+
});
|
|
4729
|
+
let initialSessions;
|
|
4730
|
+
let initialSlashCommands;
|
|
4731
|
+
let initialAgents;
|
|
3677
4732
|
try {
|
|
3678
|
-
await listing.
|
|
4733
|
+
const sessions = await listing.sessions();
|
|
4734
|
+
initialSessions = options.sessionFilter
|
|
4735
|
+
? sessions.filter(options.sessionFilter)
|
|
4736
|
+
: sessions;
|
|
4737
|
+
initialSlashCommands = listing.slashCommands?.() ?? [];
|
|
4738
|
+
initialAgents = listing.agentDefinitions?.() ?? [];
|
|
4739
|
+
if (options.requireSession && initialSessions.length === 0) {
|
|
4740
|
+
throw new Error(options.missingSessionMessage ??
|
|
4741
|
+
'No conversation linked to a pull request in this project');
|
|
4742
|
+
}
|
|
3679
4743
|
}
|
|
3680
|
-
catch {
|
|
3681
|
-
|
|
4744
|
+
catch (error) {
|
|
4745
|
+
try {
|
|
4746
|
+
await listing.close?.();
|
|
4747
|
+
}
|
|
4748
|
+
catch {
|
|
4749
|
+
// Preserve the session-listing failure as the primary error.
|
|
4750
|
+
}
|
|
4751
|
+
throw error;
|
|
3682
4752
|
}
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
: options.resume;
|
|
3692
|
-
let initialHistory = [];
|
|
3693
|
-
try {
|
|
3694
|
-
initialHistory =
|
|
3695
|
-
resume?.sessionId === undefined || listing.transcript === undefined
|
|
3696
|
-
? []
|
|
3697
|
-
: await listing.transcript(resume.sessionId);
|
|
3698
|
-
}
|
|
3699
|
-
catch (error) {
|
|
4753
|
+
const canonicalResumeSession = currentResume?.sessionId === undefined
|
|
4754
|
+
? undefined
|
|
4755
|
+
: initialSessions.find((session) => session.sessionId.toLowerCase() ===
|
|
4756
|
+
currentResume?.sessionId?.toLowerCase());
|
|
4757
|
+
const resume = canonicalResumeSession
|
|
4758
|
+
? { ...currentResume, sessionId: canonicalResumeSession.sessionId }
|
|
4759
|
+
: currentResume;
|
|
4760
|
+
let initialHistory = [];
|
|
3700
4761
|
try {
|
|
3701
|
-
|
|
4762
|
+
initialHistory =
|
|
4763
|
+
resume?.sessionId === undefined || listing.transcript === undefined
|
|
4764
|
+
? []
|
|
4765
|
+
: await listing.transcript(resume.sessionId);
|
|
3702
4766
|
}
|
|
3703
|
-
catch {
|
|
3704
|
-
|
|
4767
|
+
catch (error) {
|
|
4768
|
+
try {
|
|
4769
|
+
await listing.close?.();
|
|
4770
|
+
}
|
|
4771
|
+
catch {
|
|
4772
|
+
// Preserve the transcript-loading failure as the primary error.
|
|
4773
|
+
}
|
|
4774
|
+
throw error;
|
|
3705
4775
|
}
|
|
3706
|
-
|
|
4776
|
+
await listing.close?.();
|
|
4777
|
+
if (signal.aborted)
|
|
4778
|
+
break;
|
|
4779
|
+
const history = rendererNotice === undefined
|
|
4780
|
+
? initialHistory
|
|
4781
|
+
: [
|
|
4782
|
+
...initialHistory,
|
|
4783
|
+
{ kind: 'local-result', text: rendererNotice },
|
|
4784
|
+
];
|
|
4785
|
+
rendererNotice = undefined;
|
|
4786
|
+
let activeTurn = null;
|
|
4787
|
+
let cleanup = null;
|
|
4788
|
+
let backgrounded;
|
|
4789
|
+
rendererChange = null;
|
|
4790
|
+
const instance = render(_jsx(InteractiveApp, { factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: history, runtimeSettings: currentRuntimeSettings, initialThemeSettings: initialThemeSettings, ...(initialThemeLoadError === undefined
|
|
4791
|
+
? {}
|
|
4792
|
+
: { initialThemeLoadError }), ...(currentInitialPrompt === undefined
|
|
4793
|
+
? {}
|
|
4794
|
+
: { initialPrompt: currentInitialPrompt }), signal: signal, onCancel: () => controller.abort(), onTurnChange: (turn) => {
|
|
4795
|
+
activeTurn = turn;
|
|
4796
|
+
}, onCleanup: (closing) => {
|
|
4797
|
+
cleanup = closing;
|
|
4798
|
+
}, onRendererChange: (mode, sessionId) => {
|
|
4799
|
+
rendererChange = { mode, sessionId };
|
|
4800
|
+
currentRenderer = mode;
|
|
4801
|
+
currentRuntimeSettings = { ...currentRuntimeSettings, tui: mode };
|
|
4802
|
+
}, ...(options.onBackground === undefined
|
|
4803
|
+
? {}
|
|
4804
|
+
: {
|
|
4805
|
+
onBackground: async (request) => {
|
|
4806
|
+
const result = await options.onBackground?.(request);
|
|
4807
|
+
if (!result)
|
|
4808
|
+
throw new Error('Background launch returned no job');
|
|
4809
|
+
backgrounded = result;
|
|
4810
|
+
return result;
|
|
4811
|
+
},
|
|
4812
|
+
}), allowNewSession: !options.requireSession, ...(options.additionalDirectories === undefined
|
|
4813
|
+
? {}
|
|
4814
|
+
: { additionalDirectories: options.additionalDirectories }), ...(resume === undefined ? {} : { resume }), ...(options.display === undefined ? {} : { display: options.display }), ...(options.axScreenReader ? { axScreenReader: true } : {}), ...(options.allowDangerouslySkipPermissions
|
|
4815
|
+
? { allowDangerouslySkipPermissions: true }
|
|
4816
|
+
: {}) }), {
|
|
4817
|
+
exitOnCtrlC: false,
|
|
4818
|
+
incrementalRendering: !options.axScreenReader,
|
|
4819
|
+
interactive: true,
|
|
4820
|
+
alternateScreen: currentRenderer === 'fullscreen',
|
|
4821
|
+
});
|
|
4822
|
+
await instance.waitUntilExit();
|
|
4823
|
+
if (activeTurn)
|
|
4824
|
+
await activeTurn;
|
|
4825
|
+
if (cleanup)
|
|
4826
|
+
await cleanup;
|
|
4827
|
+
if (backgrounded)
|
|
4828
|
+
options.onBackgrounded?.(backgrounded);
|
|
4829
|
+
const change = readRendererChange();
|
|
4830
|
+
if (!change || signal.aborted)
|
|
4831
|
+
break;
|
|
4832
|
+
currentInitialPrompt = undefined;
|
|
4833
|
+
currentRenderer = change.mode;
|
|
4834
|
+
rendererNotice =
|
|
4835
|
+
change.mode === 'fullscreen'
|
|
4836
|
+
? 'Using flicker-free rendering'
|
|
4837
|
+
: 'Switched back to the classic renderer';
|
|
4838
|
+
currentResume = change.sessionId
|
|
4839
|
+
? { sessionId: change.sessionId }
|
|
4840
|
+
: undefined;
|
|
3707
4841
|
}
|
|
3708
|
-
await listing.close?.();
|
|
3709
|
-
let activeTurn = null;
|
|
3710
|
-
let cleanup = null;
|
|
3711
|
-
let backgrounded;
|
|
3712
|
-
const instance = render(_jsx(InteractiveApp, { factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: initialHistory, ...(options.initialPrompt === undefined
|
|
3713
|
-
? {}
|
|
3714
|
-
: { initialPrompt: options.initialPrompt }), signal: signal, onCancel: () => controller.abort(), onTurnChange: (turn) => {
|
|
3715
|
-
activeTurn = turn;
|
|
3716
|
-
}, onCleanup: (closing) => {
|
|
3717
|
-
cleanup = closing;
|
|
3718
|
-
}, ...(options.onBackground === undefined
|
|
3719
|
-
? {}
|
|
3720
|
-
: {
|
|
3721
|
-
onBackground: async (request) => {
|
|
3722
|
-
const result = await options.onBackground?.(request);
|
|
3723
|
-
if (!result)
|
|
3724
|
-
throw new Error('Background launch returned no job');
|
|
3725
|
-
backgrounded = result;
|
|
3726
|
-
return result;
|
|
3727
|
-
},
|
|
3728
|
-
}), allowNewSession: !options.requireSession, ...(options.additionalDirectories === undefined
|
|
3729
|
-
? {}
|
|
3730
|
-
: { additionalDirectories: options.additionalDirectories }), ...(resume === undefined ? {} : { resume }), ...(options.display === undefined ? {} : { display: options.display }), ...(options.axScreenReader ? { axScreenReader: true } : {}), ...(options.allowDangerouslySkipPermissions
|
|
3731
|
-
? { allowDangerouslySkipPermissions: true }
|
|
3732
|
-
: {}) }), {
|
|
3733
|
-
exitOnCtrlC: false,
|
|
3734
|
-
incrementalRendering: !options.axScreenReader,
|
|
3735
|
-
interactive: true,
|
|
3736
|
-
});
|
|
3737
|
-
await instance.waitUntilExit();
|
|
3738
|
-
if (activeTurn)
|
|
3739
|
-
await activeTurn;
|
|
3740
|
-
if (cleanup)
|
|
3741
|
-
await cleanup;
|
|
3742
|
-
if (backgrounded)
|
|
3743
|
-
options.onBackgrounded?.(backgrounded);
|
|
3744
4842
|
return signal.aborted ? 130 : 0;
|
|
3745
4843
|
}
|
|
3746
4844
|
//# sourceMappingURL=interactive.js.map
|