iCode-TUI 0.29.1__py3-none-any.whl
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.
- chrys/__init__.py +16 -0
- chrys/app/__init__.py +3 -0
- chrys/app/acp/__init__.py +3 -0
- chrys/app/acp/bridge.py +664 -0
- chrys/app/acp/content.py +132 -0
- chrys/app/acp/doc/frontend-api.md +425 -0
- chrys/app/acp/history.py +585 -0
- chrys/app/acp/server.py +1796 -0
- chrys/app/acp/session_manager.py +1815 -0
- chrys/app/acp/tool_status.py +45 -0
- chrys/app/cli/__init__.py +3 -0
- chrys/app/cli/acp.py +178 -0
- chrys/app/cli/app.py +165 -0
- chrys/app/cli/approvals.py +101 -0
- chrys/app/cli/headless.py +223 -0
- chrys/app/cli/launch_cwd.py +42 -0
- chrys/app/cli/profiles.py +298 -0
- chrys/app/cli/progress.py +857 -0
- chrys/app/cli/run.py +425 -0
- chrys/app/cli/serve.py +1593 -0
- chrys/app/cli/tool_summary.py +225 -0
- chrys/app/cli/trajectory.py +201 -0
- chrys/app/cli/workflow.py +601 -0
- chrys/app/features/__init__.py +3 -0
- chrys/app/features/buddy/__init__.py +3 -0
- chrys/app/features/buddy/actions.py +68 -0
- chrys/app/features/buddy/animation.py +76 -0
- chrys/app/features/buddy/commands.py +196 -0
- chrys/app/features/buddy/docs/design.md +187 -0
- chrys/app/features/buddy/hatchery.py +80 -0
- chrys/app/features/buddy/lifecycle.py +41 -0
- chrys/app/features/buddy/model.py +265 -0
- chrys/app/features/buddy/pixel_renderer.py +113 -0
- chrys/app/features/buddy/pixel_sprites.py +279 -0
- chrys/app/features/buddy/portrait.py +76 -0
- chrys/app/features/buddy/progression.py +58 -0
- chrys/app/features/buddy/replies.py +191 -0
- chrys/app/features/buddy/sprites/alien.toml +70 -0
- chrys/app/features/buddy/sprites/axolotl.toml +71 -0
- chrys/app/features/buddy/sprites/bat.toml +69 -0
- chrys/app/features/buddy/sprites/bee.toml +69 -0
- chrys/app/features/buddy/sprites/cactus.toml +73 -0
- chrys/app/features/buddy/sprites/capybara.toml +70 -0
- chrys/app/features/buddy/sprites/cat.toml +69 -0
- chrys/app/features/buddy/sprites/crab.toml +70 -0
- chrys/app/features/buddy/sprites/dragon.toml +71 -0
- chrys/app/features/buddy/sprites/duck.toml +69 -0
- chrys/app/features/buddy/sprites/fox.toml +69 -0
- chrys/app/features/buddy/sprites/frog.toml +70 -0
- chrys/app/features/buddy/sprites/ghost.toml +70 -0
- chrys/app/features/buddy/sprites/goose.toml +69 -0
- chrys/app/features/buddy/sprites/jelly.toml +70 -0
- chrys/app/features/buddy/sprites/llama.toml +70 -0
- chrys/app/features/buddy/sprites/mushroom.toml +69 -0
- chrys/app/features/buddy/sprites/octopus.toml +70 -0
- chrys/app/features/buddy/sprites/owl.toml +70 -0
- chrys/app/features/buddy/sprites/panda.toml +71 -0
- chrys/app/features/buddy/sprites/penguin.toml +69 -0
- chrys/app/features/buddy/sprites/rabbit.toml +70 -0
- chrys/app/features/buddy/sprites/robot.toml +72 -0
- chrys/app/features/buddy/sprites/shark.toml +70 -0
- chrys/app/features/buddy/sprites/snail.toml +69 -0
- chrys/app/features/buddy/sprites/snake.toml +69 -0
- chrys/app/features/buddy/sprites/tabby.toml +69 -0
- chrys/app/features/buddy/sprites/turtle.toml +69 -0
- chrys/app/features/buddy/store.py +143 -0
- chrys/app/features/session_title/__init__.py +7 -0
- chrys/app/features/session_title/updater.py +582 -0
- chrys/app/installer.py +651 -0
- chrys/app/parsing.py +17 -0
- chrys/app/tui/__init__.py +3 -0
- chrys/app/tui/app.py +1287 -0
- chrys/app/tui/behaviors/__init__.py +3 -0
- chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
- chrys/app/tui/behaviors/insert_clipboard.py +73 -0
- chrys/app/tui/behaviors/right_click_copy.py +146 -0
- chrys/app/tui/binding_display.py +139 -0
- chrys/app/tui/buddy_messages.py +17 -0
- chrys/app/tui/buddy_reply.py +86 -0
- chrys/app/tui/chrys.tcss +549 -0
- chrys/app/tui/clipboard.py +109 -0
- chrys/app/tui/copy_messages.py +10 -0
- chrys/app/tui/i18n.py +230 -0
- chrys/app/tui/language.py +58 -0
- chrys/app/tui/notifications/__init__.py +12 -0
- chrys/app/tui/notifications/drivers.py +180 -0
- chrys/app/tui/notifications/service.py +126 -0
- chrys/app/tui/notifications/settings.py +141 -0
- chrys/app/tui/screens/__init__.py +3 -0
- chrys/app/tui/screens/agents/__init__.py +8 -0
- chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
- chrys/app/tui/screens/agents/config.py +2142 -0
- chrys/app/tui/screens/agents/config.tcss +169 -0
- chrys/app/tui/screens/agents/panels/__init__.py +3 -0
- chrys/app/tui/screens/agents/panels/acp.py +1176 -0
- chrys/app/tui/screens/agents/panels/basic.py +497 -0
- chrys/app/tui/screens/agents/panels/compaction.py +231 -0
- chrys/app/tui/screens/agents/panels/config_card.py +218 -0
- chrys/app/tui/screens/agents/panels/instructions.py +91 -0
- chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
- chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
- chrys/app/tui/screens/agents/panels/memory.py +684 -0
- chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
- chrys/app/tui/screens/agents/panels/skills.py +806 -0
- chrys/app/tui/screens/agents/panels/subagents.py +652 -0
- chrys/app/tui/screens/agents/panels/tools.py +205 -0
- chrys/app/tui/screens/agents/picker.py +103 -0
- chrys/app/tui/screens/agents/skill_paths.py +38 -0
- chrys/app/tui/screens/agents/validation_messages.py +452 -0
- chrys/app/tui/screens/dialogs/__init__.py +35 -0
- chrys/app/tui/screens/dialogs/agent_load.py +834 -0
- chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
- chrys/app/tui/screens/dialogs/app_mode.py +55 -0
- chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
- chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
- chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
- chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
- chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
- chrys/app/tui/screens/dialogs/approval/body.py +110 -0
- chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
- chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
- chrys/app/tui/screens/dialogs/ask_user.py +148 -0
- chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
- chrys/app/tui/screens/dialogs/base.py +84 -0
- chrys/app/tui/screens/dialogs/confirm.py +184 -0
- chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
- chrys/app/tui/screens/dialogs/connection_test.py +183 -0
- chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
- chrys/app/tui/screens/dialogs/diagram.py +102 -0
- chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
- chrys/app/tui/screens/dialogs/editor.py +378 -0
- chrys/app/tui/screens/dialogs/editor.tcss +132 -0
- chrys/app/tui/screens/dialogs/file_picker.py +582 -0
- chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
- chrys/app/tui/screens/dialogs/fork_session.py +219 -0
- chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
- chrys/app/tui/screens/dialogs/image_compression.py +102 -0
- chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
- chrys/app/tui/screens/dialogs/man_page.py +172 -0
- chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
- chrys/app/tui/screens/dialogs/new_folder.py +277 -0
- chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
- chrys/app/tui/screens/dialogs/notice.tcss +14 -0
- chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
- chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
- chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
- chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
- chrys/app/tui/screens/dialogs/session_title.py +110 -0
- chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
- chrys/app/tui/screens/dialogs/tool_view.py +207 -0
- chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
- chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
- chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
- chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
- chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
- chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
- chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
- chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
- chrys/app/tui/screens/diff/__init__.py +22 -0
- chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
- chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
- chrys/app/tui/screens/diff/screen.py +1532 -0
- chrys/app/tui/screens/diff/screen.tcss +104 -0
- chrys/app/tui/screens/guides/__init__.py +7 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
- chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/index.py +215 -0
- chrys/app/tui/screens/guides/reader.py +173 -0
- chrys/app/tui/screens/guides/screen.py +442 -0
- chrys/app/tui/screens/guides/screen.tcss +52 -0
- chrys/app/tui/screens/logs/__init__.py +7 -0
- chrys/app/tui/screens/logs/screen.py +504 -0
- chrys/app/tui/screens/logs/screen.tcss +45 -0
- chrys/app/tui/screens/main/__init__.py +7 -0
- chrys/app/tui/screens/main/action_availability.py +30 -0
- chrys/app/tui/screens/main/buddy_command.py +143 -0
- chrys/app/tui/screens/main/commands.py +1073 -0
- chrys/app/tui/screens/main/config_actions.py +464 -0
- chrys/app/tui/screens/main/copy_actions.py +117 -0
- chrys/app/tui/screens/main/dialog_controllers.py +826 -0
- chrys/app/tui/screens/main/dialog_gateway.py +166 -0
- chrys/app/tui/screens/main/diff_controller.py +478 -0
- chrys/app/tui/screens/main/engine_read_model.py +184 -0
- chrys/app/tui/screens/main/event_handlers.py +2014 -0
- chrys/app/tui/screens/main/input_flow.py +241 -0
- chrys/app/tui/screens/main/live_diff.py +244 -0
- chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
- chrys/app/tui/screens/main/model_indicator.py +217 -0
- chrys/app/tui/screens/main/navigation.py +343 -0
- chrys/app/tui/screens/main/ports.py +571 -0
- chrys/app/tui/screens/main/recent_dirs.py +123 -0
- chrys/app/tui/screens/main/rollback_controller.py +586 -0
- chrys/app/tui/screens/main/runtime_info.py +71 -0
- chrys/app/tui/screens/main/screen.py +2265 -0
- chrys/app/tui/screens/main/screen.tcss +21 -0
- chrys/app/tui/screens/main/session_handlers.py +1127 -0
- chrys/app/tui/screens/main/session_title.py +239 -0
- chrys/app/tui/screens/main/settings_coordinator.py +365 -0
- chrys/app/tui/screens/main/settings_persistence.py +184 -0
- chrys/app/tui/screens/main/shell_mode.py +112 -0
- chrys/app/tui/screens/main/state.py +289 -0
- chrys/app/tui/screens/main/submit_state.py +11 -0
- chrys/app/tui/screens/main/subscriptions.py +177 -0
- chrys/app/tui/screens/main/suggestions.py +982 -0
- chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
- chrys/app/tui/screens/main/view_adapter.py +1567 -0
- chrys/app/tui/screens/main/workflow_browser.py +747 -0
- chrys/app/tui/screens/main/workflow_content.py +248 -0
- chrys/app/tui/screens/main/workflow_controller.py +240 -0
- chrys/app/tui/screens/main/workflow_feedback.py +137 -0
- chrys/app/tui/screens/main/workflow_flow.py +49 -0
- chrys/app/tui/screens/main/workflow_run_control.py +456 -0
- chrys/app/tui/screens/main/workflow_session_view.py +316 -0
- chrys/app/tui/screens/main/workspace_actions.py +272 -0
- chrys/app/tui/screens/main/workspace_branch.py +194 -0
- chrys/app/tui/screens/menus/__init__.py +8 -0
- chrys/app/tui/screens/menus/languages.py +114 -0
- chrys/app/tui/screens/models/__init__.py +7 -0
- chrys/app/tui/screens/models/picker.py +147 -0
- chrys/app/tui/screens/models/screen.py +2043 -0
- chrys/app/tui/screens/models/screen.tcss +255 -0
- chrys/app/tui/screens/sessions/__init__.py +8 -0
- chrys/app/tui/screens/sessions/presenter.py +586 -0
- chrys/app/tui/screens/sessions/screen.py +779 -0
- chrys/app/tui/screens/sessions/screen.tcss +127 -0
- chrys/app/tui/screens/settings/__init__.py +29 -0
- chrys/app/tui/screens/settings/danger.py +26 -0
- chrys/app/tui/screens/settings/dialog.py +281 -0
- chrys/app/tui/screens/settings/layout.py +519 -0
- chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
- chrys/app/tui/screens/settings/panes/__init__.py +3 -0
- chrys/app/tui/screens/settings/panes/notifications.py +248 -0
- chrys/app/tui/screens/settings/panes/sessions.py +148 -0
- chrys/app/tui/screens/settings/ports.py +55 -0
- chrys/app/tui/screens/settings/provenance.py +102 -0
- chrys/app/tui/screens/settings/rows.py +546 -0
- chrys/app/tui/screens/settings/settings.tcss +406 -0
- chrys/app/tui/screens/themes/__init__.py +8 -0
- chrys/app/tui/screens/themes/dialogs.py +330 -0
- chrys/app/tui/screens/themes/editor.py +632 -0
- chrys/app/tui/screens/themes/editor.tcss +268 -0
- chrys/app/tui/screens/themes/messages.py +95 -0
- chrys/app/tui/screens/themes/palette.py +694 -0
- chrys/app/tui/screens/themes/panel.py +113 -0
- chrys/app/tui/screens/themes/picker.py +203 -0
- chrys/app/tui/screens/themes/save.py +191 -0
- chrys/app/tui/support/__init__.py +3 -0
- chrys/app/tui/support/file_manager.py +64 -0
- chrys/app/tui/support/gc_freeze.py +1114 -0
- chrys/app/tui/support/path_shortcuts.py +50 -0
- chrys/app/tui/support/prompt_history.py +298 -0
- chrys/app/tui/support/workspace_mru.py +476 -0
- chrys/app/tui/terminal/__init__.py +6 -0
- chrys/app/tui/terminal/_pty_backend.py +114 -0
- chrys/app/tui/terminal/emulator/__init__.py +24 -0
- chrys/app/tui/terminal/emulator/buffer.py +456 -0
- chrys/app/tui/terminal/emulator/charsets.py +113 -0
- chrys/app/tui/terminal/emulator/core.py +963 -0
- chrys/app/tui/terminal/emulator/input.py +342 -0
- chrys/app/tui/terminal/emulator/parser.py +227 -0
- chrys/app/tui/terminal/emulator/pen.py +158 -0
- chrys/app/tui/terminal/launcher.py +455 -0
- chrys/app/tui/terminal/leaked_reports.py +44 -0
- chrys/app/tui/terminal/panel.py +323 -0
- chrys/app/tui/terminal/pty_process.py +405 -0
- chrys/app/tui/terminal/rendering.py +151 -0
- chrys/app/tui/terminal/shell.py +273 -0
- chrys/app/tui/terminal/shell_integration.py +199 -0
- chrys/app/tui/terminal/title.py +224 -0
- chrys/app/tui/terminal/widget.py +654 -0
- chrys/app/tui/theme.py +422 -0
- chrys/app/tui/theme_loader.py +201 -0
- chrys/app/tui/theme_stylesheet.py +49 -0
- chrys/app/tui/themes/__init__.py +3 -0
- chrys/app/tui/themes/document.py +120 -0
- chrys/app/tui/themes/preview.py +170 -0
- chrys/app/tui/themes/store.py +176 -0
- chrys/app/tui/toasts.py +69 -0
- chrys/app/tui/util/__init__.py +3 -0
- chrys/app/tui/util/context_pressure.py +146 -0
- chrys/app/tui/util/debug_log.py +292 -0
- chrys/app/tui/util/diff_entries.py +64 -0
- chrys/app/tui/util/formatting.py +53 -0
- chrys/app/tui/util/git_branch.py +326 -0
- chrys/app/tui/util/invocation_progress.py +57 -0
- chrys/app/tui/util/logo.py +32 -0
- chrys/app/tui/util/message_gate.py +35 -0
- chrys/app/tui/util/mutation_sources.py +14 -0
- chrys/app/tui/util/picker_text.py +27 -0
- chrys/app/tui/util/removal.py +47 -0
- chrys/app/tui/util/rich_style.py +36 -0
- chrys/app/tui/util/source_text.py +12 -0
- chrys/app/tui/util/static_update.py +88 -0
- chrys/app/tui/util/todo_format.py +51 -0
- chrys/app/tui/util/visibility.py +214 -0
- chrys/app/tui/widgets/__init__.py +60 -0
- chrys/app/tui/widgets/ask_user_controls.py +652 -0
- chrys/app/tui/widgets/ask_user_markdown.py +17 -0
- chrys/app/tui/widgets/ask_user_prompt.py +606 -0
- chrys/app/tui/widgets/buttons.py +52 -0
- chrys/app/tui/widgets/chat/__init__.py +3 -0
- chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
- chrys/app/tui/widgets/chat/chrome.py +296 -0
- chrys/app/tui/widgets/chat/compaction_card.py +353 -0
- chrys/app/tui/widgets/chat/context_fold.py +77 -0
- chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
- chrys/app/tui/widgets/chat/image_preview.py +214 -0
- chrys/app/tui/widgets/chat/live_renderer.py +383 -0
- chrys/app/tui/widgets/chat/messages.py +1039 -0
- chrys/app/tui/widgets/chat/panel.py +1769 -0
- chrys/app/tui/widgets/chat/ports.py +245 -0
- chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
- chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
- chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
- chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
- chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
- chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
- chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
- chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
- chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
- chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
- chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
- chrys/app/tui/widgets/chat/renderers/search.py +348 -0
- chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
- chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
- chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
- chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
- chrys/app/tui/widgets/chat/renderers/web.py +69 -0
- chrys/app/tui/widgets/chat/replay.py +1605 -0
- chrys/app/tui/widgets/chat/replay_mount.py +322 -0
- chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
- chrys/app/tui/widgets/chat/selection_controller.py +435 -0
- chrys/app/tui/widgets/chat/session_json.py +508 -0
- chrys/app/tui/widgets/chat/toc_model.py +156 -0
- chrys/app/tui/widgets/chat/tool_call.py +2478 -0
- chrys/app/tui/widgets/chat/tool_registry.py +753 -0
- chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
- chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
- chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
- chrys/app/tui/widgets/checkbox.py +24 -0
- chrys/app/tui/widgets/chrome/__init__.py +3 -0
- chrys/app/tui/widgets/chrome/app_header.py +283 -0
- chrys/app/tui/widgets/chrome/commands.py +95 -0
- chrys/app/tui/widgets/chrome/file_index.py +68 -0
- chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
- chrys/app/tui/widgets/chrome/footer.py +284 -0
- chrys/app/tui/widgets/chrome/image_paste.py +351 -0
- chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
- chrys/app/tui/widgets/chrome/status_bar.py +911 -0
- chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
- chrys/app/tui/widgets/click_affordance.py +24 -0
- chrys/app/tui/widgets/color_picker/__init__.py +254 -0
- chrys/app/tui/widgets/color_picker/controls.py +171 -0
- chrys/app/tui/widgets/color_picker/model.py +89 -0
- chrys/app/tui/widgets/dialog_buttons.py +58 -0
- chrys/app/tui/widgets/dialog_scroll.py +24 -0
- chrys/app/tui/widgets/diff_view/__init__.py +20 -0
- chrys/app/tui/widgets/diff_view/cells.py +59 -0
- chrys/app/tui/widgets/diff_view/code.py +280 -0
- chrys/app/tui/widgets/diff_view/compute.py +286 -0
- chrys/app/tui/widgets/diff_view/gutter.py +161 -0
- chrys/app/tui/widgets/diff_view/inline.py +77 -0
- chrys/app/tui/widgets/diff_view/palette.py +87 -0
- chrys/app/tui/widgets/diff_view/protocols.py +24 -0
- chrys/app/tui/widgets/diff_view/rows.py +138 -0
- chrys/app/tui/widgets/diff_view/unified.py +267 -0
- chrys/app/tui/widgets/diff_view/widget.py +209 -0
- chrys/app/tui/widgets/editor/__init__.py +79 -0
- chrys/app/tui/widgets/editor/coordinates.py +30 -0
- chrys/app/tui/widgets/editor/highlighter.py +139 -0
- chrys/app/tui/widgets/editor/keymaps.py +357 -0
- chrys/app/tui/widgets/editor/types.py +216 -0
- chrys/app/tui/widgets/editor/vim.py +1195 -0
- chrys/app/tui/widgets/editor/widget.py +401 -0
- chrys/app/tui/widgets/file_preview.py +58 -0
- chrys/app/tui/widgets/hatch.py +138 -0
- chrys/app/tui/widgets/input.py +66 -0
- chrys/app/tui/widgets/loading.py +125 -0
- chrys/app/tui/widgets/markdown/__init__.py +25 -0
- chrys/app/tui/widgets/markdown/_utils.py +21 -0
- chrys/app/tui/widgets/markdown/anchors.py +71 -0
- chrys/app/tui/widgets/markdown/blocks.py +115 -0
- chrys/app/tui/widgets/markdown/copyable.py +163 -0
- chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
- chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
- chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
- chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
- chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
- chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
- chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
- chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
- chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
- chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
- chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
- chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
- chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
- chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
- chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
- chrys/app/tui/widgets/markdown/links.py +47 -0
- chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
- chrys/app/tui/widgets/markdown/math/layout.py +739 -0
- chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
- chrys/app/tui/widgets/markdown/math/parser.py +325 -0
- chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
- chrys/app/tui/widgets/markdown/navigator.py +80 -0
- chrys/app/tui/widgets/markdown/parser.py +1057 -0
- chrys/app/tui/widgets/markdown/stream.py +73 -0
- chrys/app/tui/widgets/markdown/toc.py +98 -0
- chrys/app/tui/widgets/markdown/viewer.py +146 -0
- chrys/app/tui/widgets/markdown/widget.py +2046 -0
- chrys/app/tui/widgets/marquee.py +101 -0
- chrys/app/tui/widgets/option_menu.py +82 -0
- chrys/app/tui/widgets/page_navigator.py +118 -0
- chrys/app/tui/widgets/select.py +196 -0
- chrys/app/tui/widgets/selection.py +59 -0
- chrys/app/tui/widgets/sidebar/__init__.py +3 -0
- chrys/app/tui/widgets/sidebar/buddy.py +367 -0
- chrys/app/tui/widgets/sidebar/context.py +482 -0
- chrys/app/tui/widgets/sidebar/debug.py +195 -0
- chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
- chrys/app/tui/widgets/sidebar/panel.py +198 -0
- chrys/app/tui/widgets/sidebar/tasks.py +198 -0
- chrys/app/tui/widgets/sidebar/toc.py +166 -0
- chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
- chrys/app/tui/widgets/syntax_theme.py +85 -0
- chrys/app/tui/widgets/text_area.py +258 -0
- chrys/app/tui/widgets/trajectory/__init__.py +8 -0
- chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
- chrys/app/tui/widgets/trajectory/insights.py +1266 -0
- chrys/app/tui/widgets/trajectory/overview.py +1101 -0
- chrys/app/tui/widgets/trajectory/panel.py +983 -0
- chrys/app/tui/widgets/trajectory/presentation.py +311 -0
- chrys/app/tui/widgets/trajectory/session_info.py +124 -0
- chrys/app/tui/widgets/trajectory/text_view.py +124 -0
- chrys/app/tui/widgets/trajectory/timeline.py +380 -0
- chrys/app/tui/widgets/welcome.py +126 -0
- chrys/app/tui/widgets/workflow/__init__.py +5 -0
- chrys/app/tui/widgets/workflow/edges.py +89 -0
- chrys/app/tui/widgets/workflow/graph.py +869 -0
- chrys/app/tui/widgets/workflow/info.py +310 -0
- chrys/app/tui/widgets/workflow/node_view.py +27 -0
- chrys/app/tui/widgets/workflow/output.py +225 -0
- chrys/app/tui/widgets/workflow/panel.py +658 -0
- chrys/app/tui/widgets/workflow/projector.py +324 -0
- chrys/app/tui/widgets/workflow/records.py +69 -0
- chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
- chrys/app/tui/widgets/workflow/selection.py +151 -0
- chrys/app/tui/widgets/workflow/source.py +65 -0
- chrys/app/tui/widgets/workflow/text.py +257 -0
- chrys/app/tui/widgets/workflow/values.py +317 -0
- chrys/foundation/__init__.py +0 -0
- chrys/foundation/branding.py +15 -0
- chrys/foundation/config/__init__.py +7 -0
- chrys/foundation/config/coercion.py +374 -0
- chrys/foundation/config/context.py +34 -0
- chrys/foundation/config/env_file.py +141 -0
- chrys/foundation/config/env_layers.py +370 -0
- chrys/foundation/config/migrations.py +521 -0
- chrys/foundation/config/notification_events.py +22 -0
- chrys/foundation/config/process_settings.py +332 -0
- chrys/foundation/config/runtime_pointer.py +133 -0
- chrys/foundation/config/settings.py +1952 -0
- chrys/foundation/config/settings_store.py +996 -0
- chrys/foundation/config/spec.py +396 -0
- chrys/foundation/config/user_settings.py +140 -0
- chrys/foundation/config/warnings.py +262 -0
- chrys/foundation/config/web_values.py +135 -0
- chrys/foundation/config/yaml_store.py +487 -0
- chrys/foundation/errors/__init__.py +68 -0
- chrys/foundation/errors/_legacy.py +117 -0
- chrys/foundation/errors/_text.py +13 -0
- chrys/foundation/errors/_walk.py +85 -0
- chrys/foundation/errors/classify.py +278 -0
- chrys/foundation/errors/display.py +274 -0
- chrys/foundation/errors/formatting.py +283 -0
- chrys/foundation/errors/kinds.py +67 -0
- chrys/foundation/errors/network.py +497 -0
- chrys/foundation/errors/provider.py +445 -0
- chrys/foundation/errors/route.py +85 -0
- chrys/foundation/events/__init__.py +8 -0
- chrys/foundation/events/bus.py +143 -0
- chrys/foundation/events/types.py +1629 -0
- chrys/foundation/events/workflow.py +38 -0
- chrys/foundation/hosted_tools.py +142 -0
- chrys/foundation/i18n/__init__.py +32 -0
- chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
- chrys/foundation/i18n/catalogs.py +42 -0
- chrys/foundation/i18n/formatting.py +202 -0
- chrys/foundation/i18n/locale.py +92 -0
- chrys/foundation/i18n/localizer.py +187 -0
- chrys/foundation/i18n/messages.py +268 -0
- chrys/foundation/io/__init__.py +3 -0
- chrys/foundation/io/result_content.py +237 -0
- chrys/foundation/models/__init__.py +3 -0
- chrys/foundation/models/approval_reuse.py +23 -0
- chrys/foundation/models/ask_user.py +468 -0
- chrys/foundation/models/execution.py +18 -0
- chrys/foundation/models/history_markers.py +115 -0
- chrys/foundation/models/invocations.py +63 -0
- chrys/foundation/models/mutation_scope.py +28 -0
- chrys/foundation/models/session_env.py +87 -0
- chrys/foundation/models/session_surface.py +29 -0
- chrys/foundation/models/todos.py +74 -0
- chrys/foundation/models/turns.py +125 -0
- chrys/foundation/models/workflow_session.py +89 -0
- chrys/foundation/models/workspace.py +59 -0
- chrys/foundation/net/__init__.py +3 -0
- chrys/foundation/net/address_class.py +57 -0
- chrys/foundation/net/route_probe.py +94 -0
- chrys/foundation/net/url.py +107 -0
- chrys/foundation/observability/__init__.py +3 -0
- chrys/foundation/observability/exporters.py +174 -0
- chrys/foundation/observability/gate.py +31 -0
- chrys/foundation/observability/setup.py +497 -0
- chrys/foundation/observability/sink.py +228 -0
- chrys/foundation/patches/__init__.py +19 -0
- chrys/foundation/patches/mdurl_cache.py +59 -0
- chrys/foundation/patches/patcher.py +287 -0
- chrys/foundation/patches/staged_members.py +205 -0
- chrys/foundation/patches/textual_block_border.py +105 -0
- chrys/foundation/patches/textual_button_ansi.py +65 -0
- chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
- chrys/foundation/patches/textual_callback_cache.py +248 -0
- chrys/foundation/patches/textual_callback_dispatch.py +109 -0
- chrys/foundation/patches/textual_compositor_cjk.py +566 -0
- chrys/foundation/patches/textual_dispatch_cache.py +150 -0
- chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
- chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
- chrys/foundation/patches/textual_lru_acyclic.py +241 -0
- chrys/foundation/patches/textual_message_pump.py +136 -0
- chrys/foundation/patches/textual_node_diet.py +875 -0
- chrys/foundation/patches/textual_one_shot_timer.py +48 -0
- chrys/foundation/patches/textual_option_list.py +90 -0
- chrys/foundation/patches/textual_precompose.py +89 -0
- chrys/foundation/patches/textual_pruned_tabs.py +69 -0
- chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
- chrys/foundation/patches/textual_removed_node_caches.py +86 -0
- chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
- chrys/foundation/patches/textual_selection_extract.py +48 -0
- chrys/foundation/patches/textual_strip_cycles.py +119 -0
- chrys/foundation/patches/textual_tab_selection.py +1611 -0
- chrys/foundation/patches/textual_timer_skip.py +70 -0
- chrys/foundation/patches/textual_utf8_decoder.py +106 -0
- chrys/foundation/patches/textual_win_sleep.py +167 -0
- chrys/foundation/patches/watchdog_fsevents.py +70 -0
- chrys/foundation/patches/watchdog_windows.py +52 -0
- chrys/foundation/platform/__init__.py +556 -0
- chrys/foundation/platform/c_api.py +32 -0
- chrys/foundation/platform/child_reap.py +107 -0
- chrys/foundation/platform/command_line.py +95 -0
- chrys/foundation/platform/files.py +1470 -0
- chrys/foundation/platform/output_capture.py +113 -0
- chrys/foundation/platform/paths.py +193 -0
- chrys/foundation/platform/process.py +1468 -0
- chrys/foundation/platform/pty_output.py +19 -0
- chrys/foundation/platform/runtime_paths.py +281 -0
- chrys/foundation/platform/win_console.py +129 -0
- chrys/foundation/reasoning_origin.py +67 -0
- chrys/foundation/recovery.py +16 -0
- chrys/foundation/retry.py +278 -0
- chrys/foundation/text/__init__.py +3 -0
- chrys/foundation/text/encoding.py +603 -0
- chrys/foundation/text/images.py +304 -0
- chrys/foundation/text/lines.py +24 -0
- chrys/foundation/text/mentions.py +90 -0
- chrys/foundation/text/model_json.py +25 -0
- chrys/foundation/text/reading_order.py +20 -0
- chrys/foundation/text/tokenizer.py +25 -0
- chrys/foundation/text/tool_output.py +287 -0
- chrys/foundation/text/yaml_io.py +62 -0
- chrys/foundation/tool_call_context.py +202 -0
- chrys/foundation/tool_execution_stamp.py +184 -0
- chrys/foundation/tool_invocation_order.py +48 -0
- chrys/foundation/tool_kinds.py +102 -0
- chrys/foundation/tool_result_metadata.py +287 -0
- chrys/foundation/trajectory/__init__.py +10 -0
- chrys/foundation/trajectory/context.py +481 -0
- chrys/foundation/trajectory/envelope.py +571 -0
- chrys/foundation/trajectory/event_types.py +294 -0
- chrys/foundation/trajectory/finalizers.py +46 -0
- chrys/foundation/trajectory/fingerprint.py +62 -0
- chrys/foundation/trajectory/ids.py +155 -0
- chrys/foundation/trajectory/keys.py +99 -0
- chrys/foundation/trajectory/lease.py +173 -0
- chrys/foundation/trajectory/metadata.py +72 -0
- chrys/foundation/trajectory/reader.py +271 -0
- chrys/foundation/trajectory/recovery.py +225 -0
- chrys/foundation/trajectory/revisions.py +152 -0
- chrys/foundation/trajectory/segments.py +225 -0
- chrys/foundation/trajectory/tools.py +117 -0
- chrys/foundation/trajectory/usage.py +151 -0
- chrys/foundation/trajectory/writer.py +972 -0
- chrys/foundation/trajectory_timing.py +97 -0
- chrys/foundation/util/__init__.py +3 -0
- chrys/foundation/util/async_tasks.py +29 -0
- chrys/foundation/util/chrys_headers.py +23 -0
- chrys/foundation/util/env_templates.py +104 -0
- chrys/foundation/util/filenames.py +96 -0
- chrys/foundation/util/header_charset.py +138 -0
- chrys/foundation/util/httpx_helpers.py +12 -0
- chrys/foundation/util/lock.py +236 -0
- chrys/foundation/util/once_close.py +46 -0
- chrys/foundation/util/session_ids.py +29 -0
- chrys/foundation/util/shell_tokens.py +88 -0
- chrys/foundation/util/sub_agent_context.py +38 -0
- chrys/foundation/util/time.py +35 -0
- chrys/foundation/util/unicode_scalars.py +36 -0
- chrys/foundation/vendor/__init__.py +43 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
- chrys/kernel/__init__.py +256 -0
- chrys/kernel/_content.py +1866 -0
- chrys/kernel/_loop_recorder.py +631 -0
- chrys/kernel/_null_overlay.py +1026 -0
- chrys/kernel/_result_ceiling.py +216 -0
- chrys/kernel/_serialization.py +431 -0
- chrys/kernel/_tool_arg_errors.py +1629 -0
- chrys/kernel/_tool_execution.py +1172 -0
- chrys/kernel/_tool_expansion.py +26 -0
- chrys/kernel/_types.py +2598 -0
- chrys/kernel/agent.py +994 -0
- chrys/kernel/client.py +1084 -0
- chrys/kernel/compaction.py +1315 -0
- chrys/kernel/exceptions.py +211 -0
- chrys/kernel/exchanges.py +683 -0
- chrys/kernel/identity.py +95 -0
- chrys/kernel/images.py +36 -0
- chrys/kernel/instrumentation.py +1618 -0
- chrys/kernel/loop.py +2308 -0
- chrys/kernel/middleware.py +616 -0
- chrys/kernel/sessions.py +604 -0
- chrys/kernel/tools.py +1061 -0
- chrys/kernel/types.py +126 -0
- chrys/orchestration/__init__.py +3 -0
- chrys/orchestration/engine/__init__.py +3 -0
- chrys/orchestration/engine/assembly.py +236 -0
- chrys/orchestration/engine/build/__init__.py +3 -0
- chrys/orchestration/engine/build/builder.py +1061 -0
- chrys/orchestration/engine/build/construction.py +408 -0
- chrys/orchestration/engine/build/loaded.py +139 -0
- chrys/orchestration/engine/engine.py +789 -0
- chrys/orchestration/engine/execution.py +771 -0
- chrys/orchestration/engine/loader.py +865 -0
- chrys/orchestration/engine/rollback.py +918 -0
- chrys/orchestration/engine/run/__init__.py +3 -0
- chrys/orchestration/engine/run/active_injection.py +419 -0
- chrys/orchestration/engine/run/attachments.py +519 -0
- chrys/orchestration/engine/run/bindings.py +1095 -0
- chrys/orchestration/engine/run/coordinator.py +1062 -0
- chrys/orchestration/engine/run/finalizer.py +417 -0
- chrys/orchestration/engine/run/input_refs.py +60 -0
- chrys/orchestration/engine/run/prompt_content.py +457 -0
- chrys/orchestration/engine/run/resume.py +299 -0
- chrys/orchestration/engine/run/retry.py +948 -0
- chrys/orchestration/engine/run/runner.py +625 -0
- chrys/orchestration/engine/run/runtime_skills.py +146 -0
- chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
- chrys/orchestration/engine/run/turn_hooks.py +258 -0
- chrys/orchestration/engine/run/turn_state.py +149 -0
- chrys/orchestration/engine/run/working_dir.py +50 -0
- chrys/orchestration/engine/session_lifecycle.py +2013 -0
- chrys/orchestration/engine/state/__init__.py +3 -0
- chrys/orchestration/engine/state/active_session.py +183 -0
- chrys/orchestration/engine/state/controls.py +639 -0
- chrys/orchestration/engine/state/current_agent.py +26 -0
- chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
- chrys/orchestration/engine/state/machine.py +193 -0
- chrys/orchestration/engine/state/session_writer.py +434 -0
- chrys/orchestration/engine/trajectory.py +714 -0
- chrys/orchestration/engine/usage.py +118 -0
- chrys/orchestration/invoker/__init__.py +3 -0
- chrys/orchestration/invoker/acp.py +727 -0
- chrys/orchestration/invoker/acp_protocol.py +1115 -0
- chrys/orchestration/invoker/acp_spec.py +79 -0
- chrys/orchestration/invoker/attempts.py +994 -0
- chrys/orchestration/invoker/child_compaction.py +131 -0
- chrys/orchestration/invoker/child_history.py +99 -0
- chrys/orchestration/invoker/contracts.py +253 -0
- chrys/orchestration/invoker/evidence.py +94 -0
- chrys/orchestration/invoker/kernel.py +343 -0
- chrys/orchestration/invoker/origin.py +79 -0
- chrys/orchestration/invoker/resources.py +343 -0
- chrys/orchestration/invoker/runtime.py +299 -0
- chrys/orchestration/invoker/usage.py +80 -0
- chrys/orchestration/session_hooks.py +117 -0
- chrys/orchestration/session_host.py +1023 -0
- chrys/orchestration/session_resources.py +95 -0
- chrys/orchestration/session_usage.py +219 -0
- chrys/orchestration/startup.py +255 -0
- chrys/orchestration/sub_agents/__init__.py +3 -0
- chrys/orchestration/sub_agents/acp_policy.py +269 -0
- chrys/orchestration/sub_agents/kernel_policy.py +848 -0
- chrys/orchestration/sub_agents/registration.py +44 -0
- chrys/orchestration/sub_agents/shell.py +337 -0
- chrys/orchestration/sub_agents/tools.py +2228 -0
- chrys/orchestration/workflows/__init__.py +3 -0
- chrys/orchestration/workflows/agent_archive.py +156 -0
- chrys/orchestration/workflows/agent_node.py +894 -0
- chrys/orchestration/workflows/agent_node_build.py +687 -0
- chrys/orchestration/workflows/catalog.py +332 -0
- chrys/orchestration/workflows/coordinator.py +771 -0
- chrys/orchestration/workflows/hooks.py +176 -0
- chrys/orchestration/workflows/preview.py +322 -0
- chrys/orchestration/workflows/rollback.py +151 -0
- chrys/orchestration/workflows/runner.py +986 -0
- chrys/orchestration/workflows/session.py +390 -0
- chrys/orchestration/workflows/settings.py +97 -0
- chrys/orchestration/workflows/validation.py +674 -0
- chrys/orchestration/workflows/worker_client.py +1002 -0
- chrys/service/__init__.py +3 -0
- chrys/service/acp_client/__init__.py +65 -0
- chrys/service/acp_client/_transport.py +922 -0
- chrys/service/acp_client/client.py +1311 -0
- chrys/service/acp_client/errors.py +224 -0
- chrys/service/acp_client/protocol.py +220 -0
- chrys/service/acp_client/spawn.py +239 -0
- chrys/service/acp_client/spec.py +100 -0
- chrys/service/agent_middleware/__init__.py +60 -0
- chrys/service/agent_middleware/_metadata_keys.py +52 -0
- chrys/service/agent_middleware/control/__init__.py +3 -0
- chrys/service/agent_middleware/control/approval.py +769 -0
- chrys/service/agent_middleware/control/approval_hooks.py +62 -0
- chrys/service/agent_middleware/control/ask_user.py +207 -0
- chrys/service/agent_middleware/control/interrupt.py +50 -0
- chrys/service/agent_middleware/control/sleep.py +118 -0
- chrys/service/agent_middleware/control/todo.py +117 -0
- chrys/service/agent_middleware/events/__init__.py +3 -0
- chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
- chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
- chrys/service/agent_middleware/events/intermediate_text.py +128 -0
- chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
- chrys/service/agent_middleware/events/result_persistence.py +98 -0
- chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
- chrys/service/agent_middleware/events/tool_error_text.py +22 -0
- chrys/service/agent_middleware/events/tool_events.py +779 -0
- chrys/service/agent_middleware/injection.py +510 -0
- chrys/service/agent_middleware/reminders/__init__.py +91 -0
- chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
- chrys/service/agent_middleware/reminders/base.py +25 -0
- chrys/service/agent_middleware/reminders/context_usage.py +74 -0
- chrys/service/agent_middleware/reminders/file_change.py +63 -0
- chrys/service/agent_middleware/reminders/mcp.py +39 -0
- chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
- chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
- chrys/service/agent_middleware/reminders/skills.py +44 -0
- chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
- chrys/service/agent_middleware/reminders/todo.py +35 -0
- chrys/service/agent_middleware/reminders/turn_line.py +48 -0
- chrys/service/agent_middleware/response_validation.py +1380 -0
- chrys/service/agent_middleware/system_reminder.py +1107 -0
- chrys/service/agent_middleware/validators.py +396 -0
- chrys/service/analytics/__init__.py +124 -0
- chrys/service/analytics/_actions.py +896 -0
- chrys/service/analytics/_context_evidence.py +357 -0
- chrys/service/analytics/_critical_path.py +179 -0
- chrys/service/analytics/_facts.py +919 -0
- chrys/service/analytics/_insights.py +769 -0
- chrys/service/analytics/_metric_ops.py +83 -0
- chrys/service/analytics/_session_projection.py +460 -0
- chrys/service/analytics/_timeline.py +434 -0
- chrys/service/analytics/_turn_graph.py +1237 -0
- chrys/service/analytics/_turns.py +1377 -0
- chrys/service/analytics/_workflows.py +171 -0
- chrys/service/analytics/aggregation.py +1474 -0
- chrys/service/analytics/classification.py +128 -0
- chrys/service/analytics/export.py +803 -0
- chrys/service/analytics/findings.py +282 -0
- chrys/service/analytics/math.py +79 -0
- chrys/service/analytics/model.py +886 -0
- chrys/service/analytics/reader.py +321 -0
- chrys/service/approval/__init__.py +3 -0
- chrys/service/approval/arbitration.py +102 -0
- chrys/service/approval/command_identity.py +109 -0
- chrys/service/approval/correlation.py +57 -0
- chrys/service/approval/grant_store.py +108 -0
- chrys/service/approval/judge.py +589 -0
- chrys/service/approval/level.py +15 -0
- chrys/service/approval/policy.py +209 -0
- chrys/service/approval/reuse.py +223 -0
- chrys/service/approval/reuse_binding.py +133 -0
- chrys/service/approval/safety_classifier.py +536 -0
- chrys/service/approval/turn_context.py +49 -0
- chrys/service/context/__init__.py +3 -0
- chrys/service/context/compaction/__init__.py +38 -0
- chrys/service/context/compaction/breaker.py +156 -0
- chrys/service/context/compaction/budgets.py +93 -0
- chrys/service/context/compaction/calibration.py +77 -0
- chrys/service/context/compaction/compression.py +690 -0
- chrys/service/context/compaction/current_turn_drop.py +458 -0
- chrys/service/context/compaction/events.py +201 -0
- chrys/service/context/compaction/exclusions.py +261 -0
- chrys/service/context/compaction/groups.py +361 -0
- chrys/service/context/compaction/last_words.py +1996 -0
- chrys/service/context/compaction/last_words_state.py +752 -0
- chrys/service/context/compaction/scoped.py +472 -0
- chrys/service/context/compaction/spill.py +1947 -0
- chrys/service/context/compaction/strategy.py +1116 -0
- chrys/service/context/compaction/summaries.py +319 -0
- chrys/service/context/compaction/turns.py +375 -0
- chrys/service/context/manager.py +174 -0
- chrys/service/context/memory_loader.py +792 -0
- chrys/service/context/middleware/__init__.py +3 -0
- chrys/service/context/middleware/usage.py +332 -0
- chrys/service/context/providers/__init__.py +3 -0
- chrys/service/context/providers/context_management.py +432 -0
- chrys/service/context/providers/history.py +647 -0
- chrys/service/context/providers/memory.py +50 -0
- chrys/service/context/tool_content.py +22 -0
- chrys/service/hooks/__init__.py +37 -0
- chrys/service/hooks/detached_worker.py +118 -0
- chrys/service/hooks/docs/README.md +14 -0
- chrys/service/hooks/docs/authoring.md +684 -0
- chrys/service/hooks/docs/configuration.md +499 -0
- chrys/service/hooks/docs/design.md +359 -0
- chrys/service/hooks/docs/project-hooks.md +348 -0
- chrys/service/hooks/events.py +157 -0
- chrys/service/hooks/loader.py +665 -0
- chrys/service/hooks/manager.py +807 -0
- chrys/service/hooks/matcher.py +111 -0
- chrys/service/hooks/outbox.py +294 -0
- chrys/service/hooks/runner.py +572 -0
- chrys/service/hooks/schema.py +407 -0
- chrys/service/llm/__init__.py +7 -0
- chrys/service/llm/_structured_outputs.py +192 -0
- chrys/service/llm/anthropic_messages/__init__.py +9 -0
- chrys/service/llm/anthropic_messages/client.py +254 -0
- chrys/service/llm/anthropic_messages/decode.py +331 -0
- chrys/service/llm/anthropic_messages/history.py +279 -0
- chrys/service/llm/anthropic_messages/request.py +323 -0
- chrys/service/llm/anthropic_messages/server_tools.py +319 -0
- chrys/service/llm/anthropic_messages/stream.py +297 -0
- chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
- chrys/service/llm/chat_completions/__init__.py +9 -0
- chrys/service/llm/chat_completions/client.py +369 -0
- chrys/service/llm/chat_completions/decode.py +338 -0
- chrys/service/llm/chat_completions/history.py +657 -0
- chrys/service/llm/chat_completions/reasoning.py +244 -0
- chrys/service/llm/chat_completions/request.py +192 -0
- chrys/service/llm/chat_completions/stream.py +546 -0
- chrys/service/llm/chat_completions/validation.py +308 -0
- chrys/service/llm/clients.py +613 -0
- chrys/service/llm/images.py +65 -0
- chrys/service/llm/mock.py +413 -0
- chrys/service/llm/observer.py +446 -0
- chrys/service/llm/one_shot.py +106 -0
- chrys/service/llm/openai_exceptions.py +93 -0
- chrys/service/llm/openai_responses/__init__.py +10 -0
- chrys/service/llm/openai_responses/client.py +323 -0
- chrys/service/llm/openai_responses/decode.py +432 -0
- chrys/service/llm/openai_responses/history.py +519 -0
- chrys/service/llm/openai_responses/hosted.py +625 -0
- chrys/service/llm/openai_responses/replay.py +629 -0
- chrys/service/llm/openai_responses/request.py +342 -0
- chrys/service/llm/openai_responses/stream.py +847 -0
- chrys/service/llm/openai_timestamps.py +57 -0
- chrys/service/llm/providers.py +107 -0
- chrys/service/llm/proxy_route.py +54 -0
- chrys/service/llm/raw_http_log.py +309 -0
- chrys/service/llm/route_facts.py +63 -0
- chrys/service/llm/route_sessions.py +49 -0
- chrys/service/llm/sdk_anthropic.py +18 -0
- chrys/service/llm/sdk_headers.py +47 -0
- chrys/service/llm/sdk_openai.py +18 -0
- chrys/service/llm/sdk_retry.py +59 -0
- chrys/service/llm/wire_client.py +246 -0
- chrys/service/mcp/__init__.py +3 -0
- chrys/service/mcp/_connection.py +235 -0
- chrys/service/mcp/_http_transport.py +350 -0
- chrys/service/mcp/_progressive.py +459 -0
- chrys/service/mcp/_stdio_transport.py +614 -0
- chrys/service/mcp/_tool_mixins.py +96 -0
- chrys/service/mcp/adapter.py +1031 -0
- chrys/service/mcp/cache.py +476 -0
- chrys/service/mcp/content.py +32 -0
- chrys/service/mcp/errors.py +169 -0
- chrys/service/mcp/owned.py +1330 -0
- chrys/service/mcp/result_limits.py +168 -0
- chrys/service/mcp/thinking_warning.py +95 -0
- chrys/service/mcp/validation.py +74 -0
- chrys/service/mutations/__init__.py +84 -0
- chrys/service/mutations/coordination.py +894 -0
- chrys/service/mutations/detector.py +458 -0
- chrys/service/mutations/git_calibrator.py +397 -0
- chrys/service/mutations/git_state.py +647 -0
- chrys/service/mutations/pipeline.py +779 -0
- chrys/service/mutations/scanner.py +466 -0
- chrys/service/mutations/snapshot_files.py +233 -0
- chrys/service/mutations/store.py +625 -0
- chrys/service/mutations/tool_names.py +15 -0
- chrys/service/mutations/trace.py +246 -0
- chrys/service/mutations/tracker.py +1289 -0
- chrys/service/mutations/types.py +559 -0
- chrys/service/mutations/workspace_changes.py +1583 -0
- chrys/service/profiles/__init__.py +2 -0
- chrys/service/profiles/agents/__init__.py +3 -0
- chrys/service/profiles/agents/builtins/Code.yaml +199 -0
- chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
- chrys/service/profiles/agents/builtins/General.yaml +114 -0
- chrys/service/profiles/agents/builtins/QA.yaml +128 -0
- chrys/service/profiles/agents/loader.py +808 -0
- chrys/service/profiles/agents/registry.py +727 -0
- chrys/service/profiles/agents/schema.py +371 -0
- chrys/service/profiles/agents/serializer.py +196 -0
- chrys/service/profiles/coercion.py +22 -0
- chrys/service/profiles/models/__init__.py +3 -0
- chrys/service/profiles/models/env_bridge.py +157 -0
- chrys/service/profiles/models/loader.py +282 -0
- chrys/service/profiles/models/options.py +438 -0
- chrys/service/profiles/models/registry.py +104 -0
- chrys/service/profiles/models/resolver.py +286 -0
- chrys/service/profiles/models/schema.py +91 -0
- chrys/service/profiles/models/serializer.py +76 -0
- chrys/service/session/__init__.py +3 -0
- chrys/service/session/checkpoint.py +156 -0
- chrys/service/session/history.py +1616 -0
- chrys/service/session/message_metadata.py +151 -0
- chrys/service/session/persistence.py +435 -0
- chrys/service/session/runtime_metadata.py +210 -0
- chrys/service/session/sub_agent_logs.py +1069 -0
- chrys/service/session/sub_agent_transcript.py +654 -0
- chrys/service/skills/__init__.py +3 -0
- chrys/service/skills/adapter.py +408 -0
- chrys/service/skills/constants.py +8 -0
- chrys/service/skills/loader.py +575 -0
- chrys/service/skills/model.py +130 -0
- chrys/service/skills/provider.py +841 -0
- chrys/service/skills/runner.py +390 -0
- chrys/service/state/__init__.py +3 -0
- chrys/service/state/_fork.py +710 -0
- chrys/service/state/_session_files.py +175 -0
- chrys/service/state/_session_meta.py +552 -0
- chrys/service/state/locks.py +154 -0
- chrys/service/state/serializers.py +156 -0
- chrys/service/state/session_catalog.py +361 -0
- chrys/service/state/session_listing.py +84 -0
- chrys/service/state/session_migration.py +446 -0
- chrys/service/state/session_mru.py +315 -0
- chrys/service/state/store.py +2132 -0
- chrys/service/state/workflow.py +158 -0
- chrys/service/text_blocks.py +47 -0
- chrys/service/todos/__init__.py +3 -0
- chrys/service/todos/reminder.py +31 -0
- chrys/service/todos/tracker.py +58 -0
- chrys/service/tools/__init__.py +3 -0
- chrys/service/tools/approval_targets.py +92 -0
- chrys/service/tools/builtins/__init__.py +3 -0
- chrys/service/tools/builtins/ask_user.py +326 -0
- chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
- chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
- chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
- chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
- chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
- chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
- chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
- chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
- chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
- chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
- chrys/service/tools/builtins/doc_converter/registry.py +55 -0
- chrys/service/tools/builtins/filesystem.py +1071 -0
- chrys/service/tools/builtins/search.py +1265 -0
- chrys/service/tools/builtins/shell.py +967 -0
- chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
- chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
- chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
- chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
- chrys/service/tools/builtins/shell_filter.py +24 -0
- chrys/service/tools/builtins/sleep.py +91 -0
- chrys/service/tools/builtins/todo.py +64 -0
- chrys/service/tools/builtins/web/__init__.py +6 -0
- chrys/service/tools/builtins/web/build.py +268 -0
- chrys/service/tools/builtins/web/config.py +338 -0
- chrys/service/tools/builtins/web/credentials.py +37 -0
- chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
- chrys/service/tools/builtins/web/fetch/cache.py +68 -0
- chrys/service/tools/builtins/web/fetch/convert.py +56 -0
- chrys/service/tools/builtins/web/fetch/tool.py +223 -0
- chrys/service/tools/builtins/web/hosted.py +61 -0
- chrys/service/tools/builtins/web/http.py +330 -0
- chrys/service/tools/builtins/web/progress.py +18 -0
- chrys/service/tools/builtins/web/search/__init__.py +3 -0
- chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
- chrys/service/tools/builtins/web/search/html.py +152 -0
- chrys/service/tools/builtins/web/search/json_api.py +227 -0
- chrys/service/tools/builtins/web/search/providers.py +77 -0
- chrys/service/tools/builtins/web/search/tool.py +218 -0
- chrys/service/tools/builtins/web/search/types.py +28 -0
- chrys/service/tools/builtins/web/tls_identity.py +66 -0
- chrys/service/tools/kinds.py +115 -0
- chrys/service/tools/names.py +47 -0
- chrys/service/tools/registry.py +242 -0
- chrys/service/tools/result_metadata.py +103 -0
- chrys/service/tools/session_artifacts.py +196 -0
- chrys/service/tools/spill.py +218 -0
- chrys/service/tools/workspace_paths.py +81 -0
- chrys/service/trajectory/__init__.py +11 -0
- chrys/service/trajectory/approvals.py +201 -0
- chrys/service/trajectory/compaction.py +420 -0
- chrys/service/trajectory/fork.py +75 -0
- chrys/service/trajectory/hooks.py +217 -0
- chrys/service/trajectory/items.py +41 -0
- chrys/service/trajectory/preparation.py +374 -0
- chrys/service/trajectory/retries.py +95 -0
- chrys/service/trajectory/revisions.py +129 -0
- chrys/service/trajectory/segmented.py +163 -0
- chrys/service/trajectory/session.py +762 -0
- chrys/service/trajectory/state.py +83 -0
- chrys/service/trajectory/sub_agents.py +169 -0
- chrys/service/trajectory/tombstone.py +262 -0
- chrys/service/trajectory/tools.py +323 -0
- chrys/service/trajectory/validation.py +130 -0
- chrys/service/trajectory/waits.py +161 -0
- chrys/service/trajectory/workflow.py +148 -0
- chrys/service/vision.py +128 -0
- chrys/service/workflows/__init__.py +3 -0
- chrys/service/workflows/admission.py +151 -0
- chrys/service/workflows/artifacts.py +109 -0
- chrys/service/workflows/asks.py +130 -0
- chrys/service/workflows/builtins/__init__.py +12 -0
- chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
- chrys/service/workflows/builtins/demo-workflow.py +756 -0
- chrys/service/workflows/discovery.py +554 -0
- chrys/service/workflows/environment.py +284 -0
- chrys/service/workflows/graph.py +430 -0
- chrys/service/workflows/history.py +82 -0
- chrys/service/workflows/interpreter.py +107 -0
- chrys/service/workflows/journal.py +334 -0
- chrys/service/workflows/layout.py +56 -0
- chrys/service/workflows/ledger.py +149 -0
- chrys/service/workflows/model_selection.py +18 -0
- chrys/service/workflows/orphans.py +209 -0
- chrys/service/workflows/outcomes.py +46 -0
- chrys/service/workflows/protocol.py +210 -0
- chrys/service/workflows/records.py +142 -0
- chrys/service/workflows/scheduler.py +770 -0
- chrys/service/workflows/sdk/__init__.py +51 -0
- chrys/service/workflows/sdk/_ask.py +167 -0
- chrys/service/workflows/sdk/_builder.py +781 -0
- chrys/service/workflows/sdk/_values.py +92 -0
- chrys/service/workflows/sdk_artifact.py +74 -0
- chrys/service/workflows/store.py +494 -0
- chrys/service/workflows/transcript.py +102 -0
- chrys/service/workflows/values.py +73 -0
- chrys/service/workflows/worker_host.py +1820 -0
- chrys/workflows.py +38 -0
- icode_tui-0.29.1.dist-info/METADATA +104 -0
- icode_tui-0.29.1.dist-info/RECORD +1138 -0
- icode_tui-0.29.1.dist-info/WHEEL +4 -0
- icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
- icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
- icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
|
@@ -0,0 +1,982 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Suggestion system — commands, files, agent profiles, models, and prompt history."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from collections.abc import Callable, Sequence
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
from textual.content import Content
|
|
14
|
+
|
|
15
|
+
from chrys.app.tui.i18n import render_str
|
|
16
|
+
from chrys.app.tui.screens.main.buddy_command import BuddyCommandController
|
|
17
|
+
from chrys.app.tui.screens.main.commands import MainSlashCommandRegistry, SlashCommandActionPort
|
|
18
|
+
from chrys.app.tui.screens.main.model_indicator import is_model_selection_locked
|
|
19
|
+
from chrys.app.tui.screens.main.ports import BuddyCommandView, StartWorker, SuggestionPopupView
|
|
20
|
+
from chrys.app.tui.screens.main.state import MainScreenServices, MainScreenState
|
|
21
|
+
from chrys.app.tui.support.gc_freeze import GcFreezeBlockReason
|
|
22
|
+
from chrys.app.tui.widgets.chrome.commands import (
|
|
23
|
+
SlashCommandDef,
|
|
24
|
+
is_slash_command_candidate,
|
|
25
|
+
)
|
|
26
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionItem
|
|
27
|
+
from chrys.foundation.i18n import MessageRef, msg
|
|
28
|
+
from chrys.foundation.i18n.formatting import format_message
|
|
29
|
+
from chrys.foundation.platform import safe_getcwd
|
|
30
|
+
from chrys.service.profiles.models.schema import is_model_profile_selectable
|
|
31
|
+
|
|
32
|
+
_FILE_QUERY_DEBOUNCE_SECONDS = 0.04
|
|
33
|
+
_FILE_QUERY_LIMIT = 30
|
|
34
|
+
_PROMPT_HISTORY_LIMIT = 100
|
|
35
|
+
_PROMPT_HISTORY_LINE_BREAK = " ↵ "
|
|
36
|
+
|
|
37
|
+
_SUGGESTIONS_FILES_TITLE = msg("tui.suggestions.files_title", fallback="Files under {root}")
|
|
38
|
+
_SUGGESTIONS_COMMANDS_TITLE = msg("tui.suggestions.commands_title", fallback="Commands")
|
|
39
|
+
_SUGGESTIONS_AGENTS_TITLE = msg("tui.suggestions.agents_title", fallback="Agents")
|
|
40
|
+
_SUGGESTIONS_MODELS_TITLE = msg("tui.suggestions.models_title", fallback="Models")
|
|
41
|
+
_SUGGESTIONS_PROMPT_HISTORY_TITLE = msg("tui.suggestions.prompt_history_title", fallback="Prompt History")
|
|
42
|
+
_SUGGESTIONS_SYSTEM_COMMANDS = msg("tui.suggestions.system_commands", fallback="System Commands")
|
|
43
|
+
_SUGGESTIONS_LOADED_SKILLS = msg("tui.suggestions.loaded_skills", fallback="Loaded Skills")
|
|
44
|
+
_SUGGESTIONS_SHADOWED = msg("tui.suggestions.shadowed", fallback="shadowed by /{name}")
|
|
45
|
+
_SUGGESTIONS_BOUNDED_INDEX = msg(
|
|
46
|
+
"tui.suggestions.bounded_index",
|
|
47
|
+
fallback="More files exist outside this bounded index",
|
|
48
|
+
)
|
|
49
|
+
_SUGGESTIONS_INDEX_COUNTS = msg(
|
|
50
|
+
"tui.suggestions.index_counts",
|
|
51
|
+
fallback="{file_count} files / {suggestion_count} rows indexed",
|
|
52
|
+
)
|
|
53
|
+
_COMMAND_DISABLED_TITLE = msg("tui.commands.title.disabled", fallback="Command Disabled")
|
|
54
|
+
_COMMAND_UNAVAILABLE_RUNNING = msg(
|
|
55
|
+
"tui.commands.unavailable_while_running",
|
|
56
|
+
fallback="/{name} is not available while agent is running",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if TYPE_CHECKING:
|
|
60
|
+
from textual.worker import Worker
|
|
61
|
+
|
|
62
|
+
from chrys.app.tui.i18n import LocaleController
|
|
63
|
+
from chrys.app.tui.widgets.chrome.file_index import ProjectPathIndex
|
|
64
|
+
from chrys.app.tui.widgets.chrome.file_scanner import ProjectPathScanResult, ProjectPathSuggestion
|
|
65
|
+
from chrys.foundation.events.types import RuntimeSkillDetails
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class SuggestionCallbacks:
|
|
70
|
+
"""Screen-owned effects required by suggestions."""
|
|
71
|
+
|
|
72
|
+
notify_warning: Callable[[MessageRef | str, MessageRef | str, float | None], None]
|
|
73
|
+
start_worker: StartWorker
|
|
74
|
+
submit_user_text: Callable[[str], object]
|
|
75
|
+
start_agent_profile_switch: Callable[[str], object]
|
|
76
|
+
start_model_profile_switch: Callable[[str], object]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class SuggestionHandler:
|
|
80
|
+
"""Manages the suggestion list popup lifecycle for /, @, #, and $ triggers.
|
|
81
|
+
|
|
82
|
+
Owns suggestion-related state and the slash command definitions.
|
|
83
|
+
Constructed once by the main-screen owner.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
*,
|
|
89
|
+
state: MainScreenState,
|
|
90
|
+
services: MainScreenServices,
|
|
91
|
+
view: SuggestionPopupView,
|
|
92
|
+
command_actions: SlashCommandActionPort,
|
|
93
|
+
callbacks: SuggestionCallbacks,
|
|
94
|
+
buddy_view: BuddyCommandView,
|
|
95
|
+
locale_controller: LocaleController | None = None,
|
|
96
|
+
) -> None:
|
|
97
|
+
self._state = state
|
|
98
|
+
self._services = services
|
|
99
|
+
self._view = view
|
|
100
|
+
self._callbacks = callbacks
|
|
101
|
+
self._locale_controller = locale_controller
|
|
102
|
+
self._suggestion_mode: str | None = None
|
|
103
|
+
self._file_cache: list[ProjectPathSuggestion] | None = None
|
|
104
|
+
self._file_cache_root: str | None = None
|
|
105
|
+
self._file_index: ProjectPathIndex | None = None
|
|
106
|
+
self._file_index_root: str | None = None
|
|
107
|
+
self._file_cache_generation = 0
|
|
108
|
+
self._file_query_revision = 0
|
|
109
|
+
self._file_latest_query = ""
|
|
110
|
+
self._file_query_pending: tuple[str, int] | None = None
|
|
111
|
+
self._file_query_worker: Worker[None] | None = None
|
|
112
|
+
self._file_warmup_requested_root: str | None = None
|
|
113
|
+
self._file_warmup_worker: Worker[None] | None = None
|
|
114
|
+
self._prompt_history_revision = 0
|
|
115
|
+
self._prompt_history_draft = ""
|
|
116
|
+
self._slash_commands: list[SlashCommandDef] = []
|
|
117
|
+
self._active_command: SlashCommandDef | None = None
|
|
118
|
+
self._buddy_command = BuddyCommandController(
|
|
119
|
+
buddy_view,
|
|
120
|
+
render_message=self._render_message,
|
|
121
|
+
)
|
|
122
|
+
self._command_registry = MainSlashCommandRegistry(
|
|
123
|
+
actions=command_actions,
|
|
124
|
+
buddy=self._buddy_command,
|
|
125
|
+
render_message=self._render_message,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# -------------------------------------------------------------- #
|
|
129
|
+
# Slash command definitions
|
|
130
|
+
# -------------------------------------------------------------- #
|
|
131
|
+
|
|
132
|
+
def build_slash_commands(self) -> list[SlashCommandDef]:
|
|
133
|
+
"""Build slash command list with bound actions."""
|
|
134
|
+
self._slash_commands = self._command_registry.build()
|
|
135
|
+
return self._slash_commands
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def buddy_command(self) -> BuddyCommandController:
|
|
139
|
+
"""Return the stateful /buddy command controller."""
|
|
140
|
+
return self._buddy_command
|
|
141
|
+
|
|
142
|
+
def gc_freeze_block_reason(self) -> GcFreezeBlockReason | None:
|
|
143
|
+
"""Block while any suggestion popup is visible."""
|
|
144
|
+
return GcFreezeBlockReason.SUGGESTIONS_VISIBLE if self._view.suggestions_active else None
|
|
145
|
+
|
|
146
|
+
def prepare_for_gc_freeze(self) -> None:
|
|
147
|
+
"""Keep the acyclic warm file cache installed across freezes."""
|
|
148
|
+
|
|
149
|
+
def after_gc_freeze(self) -> None:
|
|
150
|
+
"""Suggestion state has no mutable cyclic container to renew."""
|
|
151
|
+
|
|
152
|
+
def abort_gc_freeze(self) -> None:
|
|
153
|
+
"""Suggestion state has no detached container to restore."""
|
|
154
|
+
|
|
155
|
+
def dispatch_slash_command(self, text: str) -> bool:
|
|
156
|
+
"""Try to match and execute a slash command. Returns True if handled."""
|
|
157
|
+
parts = text[1:].split(None, 1)
|
|
158
|
+
if not parts:
|
|
159
|
+
return False
|
|
160
|
+
name = parts[0]
|
|
161
|
+
arg = parts[1] if len(parts) > 1 else ""
|
|
162
|
+
for cmd in self._slash_commands:
|
|
163
|
+
if cmd.name == name or name in cmd.aliases:
|
|
164
|
+
if cmd.name in self._disabled_commands():
|
|
165
|
+
self._callbacks.notify_warning(
|
|
166
|
+
_COMMAND_UNAVAILABLE_RUNNING.bind(name=cmd.name),
|
|
167
|
+
_COMMAND_DISABLED_TITLE.bind(),
|
|
168
|
+
3,
|
|
169
|
+
)
|
|
170
|
+
return True
|
|
171
|
+
cmd.action(arg)
|
|
172
|
+
return True
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
# -------------------------------------------------------------- #
|
|
176
|
+
# Suggestion display and filtering
|
|
177
|
+
# -------------------------------------------------------------- #
|
|
178
|
+
|
|
179
|
+
def _disabled_commands(self) -> set[str]:
|
|
180
|
+
"""Return command names that should be disabled in the current state."""
|
|
181
|
+
if not self._state.run.agent_running:
|
|
182
|
+
return set()
|
|
183
|
+
return {cmd.name for cmd in self._slash_commands if not cmd.allow_while_running}
|
|
184
|
+
|
|
185
|
+
def _visible_slash_commands(self) -> list[SlashCommandDef]:
|
|
186
|
+
"""Return slash commands that should appear in the suggestion list."""
|
|
187
|
+
for cmd in self._slash_commands:
|
|
188
|
+
if cmd.name == "resume":
|
|
189
|
+
cmd.hidden = not self._services.state_store
|
|
190
|
+
return [cmd for cmd in self._slash_commands if not cmd.hidden]
|
|
191
|
+
|
|
192
|
+
def _show_suggestions(
|
|
193
|
+
self,
|
|
194
|
+
mode: str,
|
|
195
|
+
items: Sequence[SuggestionItem | tuple[str, str | Content]],
|
|
196
|
+
disabled: set[str] | None = None,
|
|
197
|
+
) -> None:
|
|
198
|
+
"""Show the suggestion list, optionally hiding the status bar."""
|
|
199
|
+
if mode != "files":
|
|
200
|
+
self._invalidate_file_query_results()
|
|
201
|
+
if mode != "history":
|
|
202
|
+
self._invalidate_prompt_history_results()
|
|
203
|
+
|
|
204
|
+
self._suggestion_mode = mode
|
|
205
|
+
self._view.show_suggestions(mode, list(items), disabled=disabled, title=self._suggestion_title(mode))
|
|
206
|
+
|
|
207
|
+
def _show_suggestions_loading(self, mode: str) -> None:
|
|
208
|
+
"""Open *mode* immediately with no selectable rows until results are ready."""
|
|
209
|
+
if mode != "files":
|
|
210
|
+
self._invalidate_file_query_results()
|
|
211
|
+
if mode != "history":
|
|
212
|
+
self._invalidate_prompt_history_results()
|
|
213
|
+
|
|
214
|
+
self._suggestion_mode = mode
|
|
215
|
+
self._view.show_suggestions_loading(mode, title=self._suggestion_title(mode))
|
|
216
|
+
|
|
217
|
+
def _suggestion_title(self, mode: str) -> str:
|
|
218
|
+
"""Border title for the suggestion popup; files mode names its scan root."""
|
|
219
|
+
if mode == "files":
|
|
220
|
+
return self._render_message(_SUGGESTIONS_FILES_TITLE.bind(root=self._current_file_root()))
|
|
221
|
+
definition = {
|
|
222
|
+
"commands": _SUGGESTIONS_COMMANDS_TITLE,
|
|
223
|
+
"agents": _SUGGESTIONS_AGENTS_TITLE,
|
|
224
|
+
"models": _SUGGESTIONS_MODELS_TITLE,
|
|
225
|
+
"history": _SUGGESTIONS_PROMPT_HISTORY_TITLE,
|
|
226
|
+
}.get(mode)
|
|
227
|
+
return "" if definition is None else self._render_message(definition.bind())
|
|
228
|
+
|
|
229
|
+
def _render_message(self, reference: MessageRef) -> str:
|
|
230
|
+
controller = self._locale_controller
|
|
231
|
+
if controller is None:
|
|
232
|
+
return format_message(reference)
|
|
233
|
+
return render_str(controller.localizer, reference)
|
|
234
|
+
|
|
235
|
+
def dismiss_suggestions(self) -> None:
|
|
236
|
+
"""Hide the suggestion list and restore status bar."""
|
|
237
|
+
self._invalidate_file_query_results()
|
|
238
|
+
self._invalidate_prompt_history_results()
|
|
239
|
+
self._view.hide_suggestions()
|
|
240
|
+
self._suggestion_mode = None
|
|
241
|
+
|
|
242
|
+
def _invalidate_prompt_history_results(self) -> None:
|
|
243
|
+
"""Invalidate an in-flight prompt-history load."""
|
|
244
|
+
self._prompt_history_revision += 1
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
def _prompt_history_label(text: str) -> str:
|
|
248
|
+
"""Render a multi-line prompt as one visible suggestion row."""
|
|
249
|
+
return _PROMPT_HISTORY_LINE_BREAK.join(text.splitlines())
|
|
250
|
+
|
|
251
|
+
def start_prompt_history(self) -> int:
|
|
252
|
+
"""Open prompt-history loading synchronously and return its request revision."""
|
|
253
|
+
self._invalidate_prompt_history_results()
|
|
254
|
+
revision = self._prompt_history_revision
|
|
255
|
+
self._prompt_history_draft = self._view.input_value()
|
|
256
|
+
self._show_suggestions_loading("history")
|
|
257
|
+
return revision
|
|
258
|
+
|
|
259
|
+
async def show_prompt_history_async(self, *, revision: int | None = None) -> None:
|
|
260
|
+
"""Load and show the newest global prompt-history entries first."""
|
|
261
|
+
if revision is None:
|
|
262
|
+
revision = self.start_prompt_history()
|
|
263
|
+
elif revision != self._prompt_history_revision or self._suggestion_mode != "history":
|
|
264
|
+
return
|
|
265
|
+
history = await self._view.load_prompt_history(max_entries=_PROMPT_HISTORY_LIMIT)
|
|
266
|
+
if (
|
|
267
|
+
revision != self._prompt_history_revision
|
|
268
|
+
or not self._view.is_attached
|
|
269
|
+
or self._suggestion_mode != "history"
|
|
270
|
+
):
|
|
271
|
+
return
|
|
272
|
+
items = [
|
|
273
|
+
SuggestionItem(
|
|
274
|
+
value=prompt,
|
|
275
|
+
label=self._prompt_history_label(prompt),
|
|
276
|
+
kind="history",
|
|
277
|
+
marquee_start=0,
|
|
278
|
+
)
|
|
279
|
+
for prompt in reversed(history)
|
|
280
|
+
]
|
|
281
|
+
self._view.update_suggestions(items, title=self._suggestion_title("history"))
|
|
282
|
+
|
|
283
|
+
# -------------------------------------------------------------- #
|
|
284
|
+
# Trigger handlers (called by thin @on forwarders in MainScreen)
|
|
285
|
+
# -------------------------------------------------------------- #
|
|
286
|
+
|
|
287
|
+
def on_slash_triggered(self) -> None:
|
|
288
|
+
"""Show slash command suggestions."""
|
|
289
|
+
disabled = self._disabled_commands()
|
|
290
|
+
items = self._command_suggestion_items(self._visible_slash_commands(), disabled)
|
|
291
|
+
items.extend(self._runtime_skill_suggestion_items())
|
|
292
|
+
self._show_suggestions("commands", items, disabled=disabled)
|
|
293
|
+
|
|
294
|
+
def _command_suggestion_items(
|
|
295
|
+
self,
|
|
296
|
+
commands: list[SlashCommandDef],
|
|
297
|
+
disabled: set[str],
|
|
298
|
+
) -> list[SuggestionItem]:
|
|
299
|
+
"""Build grouped slash-command suggestion items."""
|
|
300
|
+
items: list[SuggestionItem] = []
|
|
301
|
+
for cmd in commands:
|
|
302
|
+
description = self._render_message(cmd.description)
|
|
303
|
+
items.append(
|
|
304
|
+
SuggestionItem(
|
|
305
|
+
value=cmd.name,
|
|
306
|
+
# The second separator space carries the dim style: when the
|
|
307
|
+
# row is highlighted (reverse video), the bright-to-gray
|
|
308
|
+
# boundary then splits evenly between name and description.
|
|
309
|
+
label=Content.assemble(f"/{cmd.name} ", (" " + description, "dim")),
|
|
310
|
+
section=self._render_message(_SUGGESTIONS_SYSTEM_COMMANDS.bind()),
|
|
311
|
+
kind="command",
|
|
312
|
+
disabled=cmd.name in disabled,
|
|
313
|
+
marquee_start=len(f"/{cmd.name} ") if description else None,
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
return items
|
|
317
|
+
|
|
318
|
+
def _runtime_skill_suggestion_items(
|
|
319
|
+
self,
|
|
320
|
+
skills: list[RuntimeSkillDetails] | None = None,
|
|
321
|
+
) -> list[SuggestionItem]:
|
|
322
|
+
"""Build grouped runtime-skill suggestion items."""
|
|
323
|
+
items: list[SuggestionItem] = []
|
|
324
|
+
source_skills = skills if skills is not None else self._state.runtime.details.skill_details
|
|
325
|
+
for skill in source_skills:
|
|
326
|
+
shadowing_command = self._command_for_token(skill.name)
|
|
327
|
+
items.append(
|
|
328
|
+
SuggestionItem(
|
|
329
|
+
value=skill.name,
|
|
330
|
+
label=self._runtime_skill_label(skill.name, skill.description),
|
|
331
|
+
section=self._render_message(_SUGGESTIONS_LOADED_SKILLS.bind()),
|
|
332
|
+
kind="skill",
|
|
333
|
+
disabled=shadowing_command is not None,
|
|
334
|
+
disabled_reason=(
|
|
335
|
+
self._render_message(_SUGGESTIONS_SHADOWED.bind(name=shadowing_command.name))
|
|
336
|
+
if shadowing_command is not None
|
|
337
|
+
else ""
|
|
338
|
+
),
|
|
339
|
+
marquee_start=len(f"/{skill.name} ") if skill.description.strip() else None,
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
return items
|
|
343
|
+
|
|
344
|
+
@staticmethod
|
|
345
|
+
def _runtime_skill_label(name: str, description: str) -> Content:
|
|
346
|
+
"""Build a literal-text label from user-authored skill metadata."""
|
|
347
|
+
description = description.strip()
|
|
348
|
+
label = Text.assemble(f"/{name}")
|
|
349
|
+
if description:
|
|
350
|
+
# Second separator space styled dim — see _command_suggestion_items.
|
|
351
|
+
label.append(" ")
|
|
352
|
+
label.append(Text(" " + description, style="dim"))
|
|
353
|
+
return Content.from_rich_text(label)
|
|
354
|
+
|
|
355
|
+
def _command_for_token(self, token: str) -> SlashCommandDef | None:
|
|
356
|
+
"""Return the slash command that would handle *token*, including aliases."""
|
|
357
|
+
for cmd in self._slash_commands:
|
|
358
|
+
if cmd.name == token or token in cmd.aliases:
|
|
359
|
+
return cmd
|
|
360
|
+
return None
|
|
361
|
+
|
|
362
|
+
def on_file_triggered(self) -> None:
|
|
363
|
+
"""Show file search suggestions."""
|
|
364
|
+
self._file_latest_query = ""
|
|
365
|
+
self._invalidate_file_query_results()
|
|
366
|
+
self._show_suggestions_loading("files")
|
|
367
|
+
self._callbacks.start_worker(self.show_file_suggestions_async)
|
|
368
|
+
|
|
369
|
+
def on_agent_triggered(self) -> None:
|
|
370
|
+
"""Show agent profile suggestions."""
|
|
371
|
+
if self._state.run.agent_running or self._state.run.agent_loading or self._services.execution_busy():
|
|
372
|
+
return
|
|
373
|
+
items, disabled = self._get_agent_items()
|
|
374
|
+
self._show_suggestions("agents", items, disabled=disabled)
|
|
375
|
+
|
|
376
|
+
def _get_agent_items(self) -> tuple[list[SuggestionItem], set[str]]:
|
|
377
|
+
"""Build suggestion items and disabled set from available agent profiles."""
|
|
378
|
+
registry = self._services.agent_registry
|
|
379
|
+
if registry is None:
|
|
380
|
+
return [], set()
|
|
381
|
+
profiles = registry.list_profiles()
|
|
382
|
+
items: list[SuggestionItem] = []
|
|
383
|
+
disabled: set[str] = set()
|
|
384
|
+
for p in profiles:
|
|
385
|
+
label = p.display_name or p.name
|
|
386
|
+
is_current = label == self._state.runtime.profile
|
|
387
|
+
prefix = "◦ " if is_current else " "
|
|
388
|
+
items.append(
|
|
389
|
+
SuggestionItem(
|
|
390
|
+
value=p.name,
|
|
391
|
+
label=Content.assemble(f"{prefix}{label} ", (" " + p.description, "dim")),
|
|
392
|
+
marquee_start=len(f"{prefix}{label} ") if p.description else None,
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
if is_current:
|
|
396
|
+
disabled.add(p.name)
|
|
397
|
+
return items, disabled
|
|
398
|
+
|
|
399
|
+
def _agent_query_matches(self, text: str) -> tuple[list[str], dict[str, SuggestionItem], set[str]]:
|
|
400
|
+
"""Return agent names matching the current # query plus labels and disabled names."""
|
|
401
|
+
query = text.lstrip("#\uff03")
|
|
402
|
+
all_items, disabled = self._get_agent_items()
|
|
403
|
+
item_by_name = {item.value: item for item in all_items}
|
|
404
|
+
if not query:
|
|
405
|
+
return [item.value for item in all_items], item_by_name, disabled
|
|
406
|
+
|
|
407
|
+
from chrys.app.tui.widgets.chrome.file_scanner import fuzzy_filter
|
|
408
|
+
|
|
409
|
+
all_names = [item.value for item in all_items]
|
|
410
|
+
matches = [value for value in fuzzy_filter(query, all_names) if value in item_by_name]
|
|
411
|
+
return matches, item_by_name, disabled
|
|
412
|
+
|
|
413
|
+
def _agent_fallback_should_submit(self, text: str) -> bool:
|
|
414
|
+
"""Return whether an unselected # entry should fall through to chat submit."""
|
|
415
|
+
matches, _label_by_name, disabled = self._agent_query_matches(text)
|
|
416
|
+
if not matches:
|
|
417
|
+
return True
|
|
418
|
+
return any(match not in disabled for match in matches)
|
|
419
|
+
|
|
420
|
+
def on_model_triggered(self) -> None:
|
|
421
|
+
"""Show selectable model-profile suggestions when model selection is unlocked."""
|
|
422
|
+
if (
|
|
423
|
+
self._state.run.agent_running
|
|
424
|
+
or self._state.run.agent_loading
|
|
425
|
+
or self._services.execution_busy()
|
|
426
|
+
or self._model_selection_is_locked()
|
|
427
|
+
):
|
|
428
|
+
return
|
|
429
|
+
items, disabled = self._get_model_items()
|
|
430
|
+
self._show_suggestions("models", items, disabled=disabled)
|
|
431
|
+
|
|
432
|
+
def _model_selection_is_locked(self) -> bool:
|
|
433
|
+
"""Return whether runtime policy prevents changing the active model."""
|
|
434
|
+
return is_model_selection_locked(
|
|
435
|
+
self._state.runtime.details.model,
|
|
436
|
+
runtime_confirmed=self._state.runtime.details_confirmed,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
def _current_model_profile_id(self) -> str:
|
|
440
|
+
"""Return the best-known current model-profile id for suggestion styling."""
|
|
441
|
+
if self._state.runtime.details_confirmed:
|
|
442
|
+
return self._state.runtime.details.model.profile_id
|
|
443
|
+
return self._services.active_model_profile_id
|
|
444
|
+
|
|
445
|
+
def _get_model_items(self) -> tuple[list[SuggestionItem], set[str]]:
|
|
446
|
+
"""Build suggestion items and disabled set from selectable model profiles."""
|
|
447
|
+
registry = self._services.model_registry
|
|
448
|
+
if registry is None:
|
|
449
|
+
return [], set()
|
|
450
|
+
current_profile_id = self._current_model_profile_id()
|
|
451
|
+
items: list[SuggestionItem] = []
|
|
452
|
+
disabled: set[str] = set()
|
|
453
|
+
for profile in registry.list_profiles():
|
|
454
|
+
if not is_model_profile_selectable(profile):
|
|
455
|
+
continue
|
|
456
|
+
is_current = profile.id == current_profile_id
|
|
457
|
+
prefix = "◦ " if is_current else " "
|
|
458
|
+
items.append(
|
|
459
|
+
SuggestionItem(
|
|
460
|
+
value=profile.id,
|
|
461
|
+
label=Content.assemble(f"{prefix}{profile.name} ", (" " + profile.model_id, "dim")),
|
|
462
|
+
kind="model",
|
|
463
|
+
marquee_start=len(f"{prefix}{profile.name} ") if profile.model_id else None,
|
|
464
|
+
)
|
|
465
|
+
)
|
|
466
|
+
if is_current:
|
|
467
|
+
disabled.add(profile.id)
|
|
468
|
+
return items, disabled
|
|
469
|
+
|
|
470
|
+
def _model_query_matches(self, text: str) -> tuple[list[str], dict[str, SuggestionItem], set[str]]:
|
|
471
|
+
"""Return model profile ids matching the current $ query."""
|
|
472
|
+
query = text.lstrip("$\uff04")
|
|
473
|
+
all_items, disabled = self._get_model_items()
|
|
474
|
+
item_by_id = {item.value: item for item in all_items}
|
|
475
|
+
if not query:
|
|
476
|
+
return [item.value for item in all_items], item_by_id, disabled
|
|
477
|
+
|
|
478
|
+
registry = self._services.model_registry
|
|
479
|
+
if registry is None:
|
|
480
|
+
return [], item_by_id, disabled
|
|
481
|
+
selectable_profiles = [profile for profile in registry.list_profiles() if profile.id in item_by_id]
|
|
482
|
+
|
|
483
|
+
from chrys.app.tui.widgets.chrome.file_scanner import fuzzy_filter
|
|
484
|
+
|
|
485
|
+
names = [profile.name for profile in selectable_profiles]
|
|
486
|
+
matching_names = fuzzy_filter(query, names)
|
|
487
|
+
ids_by_name: dict[str, list[str]] = {}
|
|
488
|
+
for profile in selectable_profiles:
|
|
489
|
+
ids_by_name.setdefault(profile.name, []).append(profile.id)
|
|
490
|
+
matches = [ids_by_name[name].pop(0) for name in matching_names]
|
|
491
|
+
return matches, item_by_id, disabled
|
|
492
|
+
|
|
493
|
+
def _model_fallback_should_submit(self, text: str) -> bool:
|
|
494
|
+
"""Return whether an unselected $ entry should fall through to chat submit."""
|
|
495
|
+
matches, _item_by_id, disabled = self._model_query_matches(text)
|
|
496
|
+
if not matches:
|
|
497
|
+
return True
|
|
498
|
+
return any(match not in disabled for match in matches)
|
|
499
|
+
|
|
500
|
+
async def show_file_suggestions_async(self) -> None:
|
|
501
|
+
"""Show file suggestions; ``on_file_triggered`` runs this in a screen worker."""
|
|
502
|
+
if self._suggestion_mode != "files" or not self._view.is_attached:
|
|
503
|
+
return
|
|
504
|
+
root = self._current_file_root()
|
|
505
|
+
if self._has_current_file_index(root):
|
|
506
|
+
self._show_latest_file_query_or_head()
|
|
507
|
+
return
|
|
508
|
+
|
|
509
|
+
self._file_warmup_requested_root = root
|
|
510
|
+
worker = self._file_warmup_worker
|
|
511
|
+
if worker is None or worker.is_finished:
|
|
512
|
+
worker = self._view.run_file_suggestion_worker(
|
|
513
|
+
self._run_file_warmup_loop(),
|
|
514
|
+
name="file-suggestion-warmup",
|
|
515
|
+
group="file-suggestions",
|
|
516
|
+
)
|
|
517
|
+
self._file_warmup_worker = worker
|
|
518
|
+
await worker.wait()
|
|
519
|
+
|
|
520
|
+
def _current_file_root(self) -> str:
|
|
521
|
+
"""Return the cwd that file suggestions should be scoped to."""
|
|
522
|
+
return self._state.workspace_marker.current_cwd or self._state.workspace.current_cwd or safe_getcwd()
|
|
523
|
+
|
|
524
|
+
def _has_current_file_index(self, root: str) -> bool:
|
|
525
|
+
"""Return whether the current cache/index pair is ready for *root*."""
|
|
526
|
+
return (
|
|
527
|
+
self._file_cache is not None
|
|
528
|
+
and self._file_cache_root == root
|
|
529
|
+
and self._file_index is not None
|
|
530
|
+
and self._file_index_root == root
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
async def _run_file_warmup_loop(self) -> None:
|
|
534
|
+
"""Coalesced scan/index warmup for file suggestions."""
|
|
535
|
+
from chrys.app.tui.widgets.chrome.file_scanner import scan_project_paths
|
|
536
|
+
|
|
537
|
+
while self._file_warmup_requested_root is not None:
|
|
538
|
+
root = self._file_warmup_requested_root
|
|
539
|
+
self._file_warmup_requested_root = None
|
|
540
|
+
if self._has_current_file_index(root):
|
|
541
|
+
self._show_latest_file_query_or_head()
|
|
542
|
+
continue
|
|
543
|
+
|
|
544
|
+
cache_generation = self._file_cache_generation
|
|
545
|
+
if not self._file_warmup_context_current(root, cache_generation):
|
|
546
|
+
self._request_latest_file_warmup_if_active()
|
|
547
|
+
continue
|
|
548
|
+
|
|
549
|
+
scan = await scan_project_paths(root)
|
|
550
|
+
if not self._file_warmup_context_current(root, cache_generation):
|
|
551
|
+
self._request_latest_file_warmup_if_active()
|
|
552
|
+
continue
|
|
553
|
+
|
|
554
|
+
index = await asyncio.to_thread(self._build_file_index, scan)
|
|
555
|
+
if not self._file_warmup_context_current(root, cache_generation):
|
|
556
|
+
self._request_latest_file_warmup_if_active()
|
|
557
|
+
continue
|
|
558
|
+
|
|
559
|
+
self._install_file_cache(root, scan, index)
|
|
560
|
+
self._show_latest_file_query_or_head()
|
|
561
|
+
|
|
562
|
+
def _request_latest_file_warmup_if_active(self) -> None:
|
|
563
|
+
"""Request one follow-up warmup for the current root if file suggestions are still active."""
|
|
564
|
+
if self._suggestion_mode == "files" and self._view.is_attached:
|
|
565
|
+
self._file_warmup_requested_root = self._current_file_root()
|
|
566
|
+
|
|
567
|
+
def _file_warmup_context_current(self, root: str, cache_generation: int) -> bool:
|
|
568
|
+
"""Return whether a warmed scan/index is still allowed to install."""
|
|
569
|
+
return (
|
|
570
|
+
self._view.is_attached
|
|
571
|
+
and self._suggestion_mode == "files"
|
|
572
|
+
and self._current_file_root() == root
|
|
573
|
+
and cache_generation == self._file_cache_generation
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
def _install_file_cache(
|
|
577
|
+
self,
|
|
578
|
+
root: str,
|
|
579
|
+
scan: ProjectPathScanResult,
|
|
580
|
+
index: ProjectPathIndex,
|
|
581
|
+
) -> None:
|
|
582
|
+
"""Install a scanned file cache and already-built query index for *root*."""
|
|
583
|
+
self._file_cache = scan.paths
|
|
584
|
+
self._file_cache_root = root
|
|
585
|
+
self._file_index = index
|
|
586
|
+
self._file_index_root = root
|
|
587
|
+
|
|
588
|
+
def _current_file_index(self) -> ProjectPathIndex | None:
|
|
589
|
+
"""Return the current file index without building one synchronously."""
|
|
590
|
+
if self._file_cache is None:
|
|
591
|
+
return None
|
|
592
|
+
root = self._file_cache_root or self._current_file_root()
|
|
593
|
+
if self._file_index is None or self._file_index_root != root:
|
|
594
|
+
return None
|
|
595
|
+
return self._file_index
|
|
596
|
+
|
|
597
|
+
@staticmethod
|
|
598
|
+
def _build_file_index(scan: ProjectPathScanResult) -> ProjectPathIndex:
|
|
599
|
+
"""Build a project path index from a scanner result."""
|
|
600
|
+
from chrys.app.tui.widgets.chrome.file_index import ProjectPathIndex
|
|
601
|
+
|
|
602
|
+
return ProjectPathIndex.build(scan)
|
|
603
|
+
|
|
604
|
+
def _file_suggestion_items(
|
|
605
|
+
self,
|
|
606
|
+
paths: list[ProjectPathSuggestion],
|
|
607
|
+
*,
|
|
608
|
+
index: ProjectPathIndex | None = None,
|
|
609
|
+
) -> list[SuggestionItem]:
|
|
610
|
+
"""Build suggestion rows for project file and directory paths."""
|
|
611
|
+
items = [SuggestionItem(value=path.path, label=path.path, kind=path.kind) for path in paths]
|
|
612
|
+
if index is not None and index.truncated:
|
|
613
|
+
items.append(
|
|
614
|
+
SuggestionItem(
|
|
615
|
+
value="__chrys_file_results_truncated__",
|
|
616
|
+
label=self._render_message(_SUGGESTIONS_BOUNDED_INDEX.bind()),
|
|
617
|
+
kind="status",
|
|
618
|
+
disabled=True,
|
|
619
|
+
disabled_reason=self._render_message(
|
|
620
|
+
_SUGGESTIONS_INDEX_COUNTS.bind(
|
|
621
|
+
file_count=f"{index.file_count:,}",
|
|
622
|
+
suggestion_count=f"{index.suggestion_count:,}",
|
|
623
|
+
)
|
|
624
|
+
),
|
|
625
|
+
)
|
|
626
|
+
)
|
|
627
|
+
return items
|
|
628
|
+
|
|
629
|
+
def _show_latest_file_query_or_head(self) -> None:
|
|
630
|
+
"""Display the current file query, preserving immediate empty-query head behavior."""
|
|
631
|
+
if self._suggestion_mode != "files" or not self._view.is_attached:
|
|
632
|
+
return
|
|
633
|
+
index = self._current_file_index()
|
|
634
|
+
if index is None:
|
|
635
|
+
return
|
|
636
|
+
query = self._file_latest_query
|
|
637
|
+
if query:
|
|
638
|
+
self._enqueue_file_query(query)
|
|
639
|
+
else:
|
|
640
|
+
self._invalidate_file_query_results()
|
|
641
|
+
self._show_or_update_file_suggestions(index.head(limit=_FILE_QUERY_LIMIT))
|
|
642
|
+
|
|
643
|
+
def _show_or_update_file_suggestions(self, paths: list[ProjectPathSuggestion]) -> None:
|
|
644
|
+
"""Show or update the file suggestion list with *paths*."""
|
|
645
|
+
if self._suggestion_mode != "files" or not self._view.is_attached:
|
|
646
|
+
return
|
|
647
|
+
|
|
648
|
+
items = self._file_suggestion_items(paths, index=self._current_file_index())
|
|
649
|
+
if self._view.suggestions_active:
|
|
650
|
+
self._view.update_suggestions(items, title=self._suggestion_title("files"))
|
|
651
|
+
else:
|
|
652
|
+
self._show_suggestions("files", items)
|
|
653
|
+
|
|
654
|
+
def _invalidate_file_query_results(self) -> None:
|
|
655
|
+
"""Invalidate pending/returned file query results."""
|
|
656
|
+
self._file_query_revision += 1
|
|
657
|
+
self._file_query_pending = None
|
|
658
|
+
|
|
659
|
+
def _enqueue_file_query(self, query: str) -> None:
|
|
660
|
+
"""Record and schedule the latest file query."""
|
|
661
|
+
self._file_latest_query = query
|
|
662
|
+
self._file_query_revision += 1
|
|
663
|
+
revision = self._file_query_revision
|
|
664
|
+
if self._file_cache is None and self._file_index is None:
|
|
665
|
+
return
|
|
666
|
+
self._file_query_pending = (query, revision)
|
|
667
|
+
if self._file_query_worker is None or self._file_query_worker.is_finished:
|
|
668
|
+
self._file_query_worker = self._view.run_file_suggestion_worker(
|
|
669
|
+
self._run_file_query_loop(),
|
|
670
|
+
name="file-suggestion-query",
|
|
671
|
+
group="file-suggestions",
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
async def _run_file_query_loop(self) -> None:
|
|
675
|
+
"""Debounced, coalesced file-query worker loop."""
|
|
676
|
+
while self._file_query_pending is not None:
|
|
677
|
+
query, revision = self._file_query_pending
|
|
678
|
+
self._file_query_pending = None
|
|
679
|
+
await asyncio.sleep(_FILE_QUERY_DEBOUNCE_SECONDS)
|
|
680
|
+
if self._file_query_pending is not None:
|
|
681
|
+
continue
|
|
682
|
+
|
|
683
|
+
index = await self._ensure_file_index_for_query(revision)
|
|
684
|
+
root = self._file_index_root
|
|
685
|
+
if index is None or root is None or not self._file_query_context_current(revision, root, index):
|
|
686
|
+
continue
|
|
687
|
+
|
|
688
|
+
matches = await asyncio.to_thread(index.query, query, limit=_FILE_QUERY_LIMIT)
|
|
689
|
+
if self._file_query_context_current(revision, root, index):
|
|
690
|
+
self._apply_file_query_result(matches)
|
|
691
|
+
|
|
692
|
+
async def _ensure_file_index_for_query(self, revision: int) -> ProjectPathIndex | None:
|
|
693
|
+
"""Build the current cache index off-loop when a test-injected cache needs one."""
|
|
694
|
+
index = self._current_file_index()
|
|
695
|
+
if index is not None:
|
|
696
|
+
return index
|
|
697
|
+
if self._file_cache is None:
|
|
698
|
+
return None
|
|
699
|
+
|
|
700
|
+
root = self._file_cache_root or self._current_file_root()
|
|
701
|
+
paths = self._file_cache
|
|
702
|
+
cache_generation = self._file_cache_generation
|
|
703
|
+
from chrys.app.tui.widgets.chrome.file_scanner import ProjectPathScanResult
|
|
704
|
+
|
|
705
|
+
scan = ProjectPathScanResult.from_suggestions(root=root, paths=paths)
|
|
706
|
+
index = await asyncio.to_thread(self._build_file_index, scan)
|
|
707
|
+
if (
|
|
708
|
+
revision != self._file_query_revision
|
|
709
|
+
or cache_generation != self._file_cache_generation
|
|
710
|
+
or paths is not self._file_cache
|
|
711
|
+
or self._current_file_root() != root
|
|
712
|
+
):
|
|
713
|
+
return None
|
|
714
|
+
self._file_index = index
|
|
715
|
+
self._file_index_root = root
|
|
716
|
+
return index
|
|
717
|
+
|
|
718
|
+
def _file_query_context_current(self, revision: int, root: str, index: ProjectPathIndex) -> bool:
|
|
719
|
+
"""Return whether a query result is still allowed to update the UI."""
|
|
720
|
+
return (
|
|
721
|
+
revision == self._file_query_revision
|
|
722
|
+
and self._view.is_attached
|
|
723
|
+
and self._suggestion_mode == "files"
|
|
724
|
+
and self._file_index is index
|
|
725
|
+
and self._file_index_root == root
|
|
726
|
+
and self._current_file_root() == root
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
def _apply_file_query_result(self, matches: list[ProjectPathSuggestion]) -> None:
|
|
730
|
+
"""Apply current file-query results to the suggestion list."""
|
|
731
|
+
if not self._view.is_attached:
|
|
732
|
+
return
|
|
733
|
+
self._show_or_update_file_suggestions(matches)
|
|
734
|
+
|
|
735
|
+
# -------------------------------------------------------------- #
|
|
736
|
+
# Filtering as user types
|
|
737
|
+
# -------------------------------------------------------------- #
|
|
738
|
+
|
|
739
|
+
def on_text_changed(self, text: str) -> None:
|
|
740
|
+
"""Re-filter suggestion list as user types."""
|
|
741
|
+
if self._suggestion_mode is None:
|
|
742
|
+
return
|
|
743
|
+
|
|
744
|
+
if self._suggestion_mode == "commands":
|
|
745
|
+
if text not in ("/", "\uff0f") and not is_slash_command_candidate(text):
|
|
746
|
+
self.dismiss_suggestions()
|
|
747
|
+
return
|
|
748
|
+
query = text.lstrip("/\uff0f")
|
|
749
|
+
if " " in query:
|
|
750
|
+
# Support second-level suggestions when user types "/cmd ..." directly.
|
|
751
|
+
parts = query.split(None, 1)
|
|
752
|
+
cmd_name = parts[0] if parts else ""
|
|
753
|
+
sub_query = parts[1] if len(parts) > 1 else ""
|
|
754
|
+
cmd = next(
|
|
755
|
+
(c for c in self._slash_commands if c.name == cmd_name or cmd_name in c.aliases),
|
|
756
|
+
None,
|
|
757
|
+
)
|
|
758
|
+
if not cmd or not cmd.subcommands:
|
|
759
|
+
self.dismiss_suggestions()
|
|
760
|
+
return
|
|
761
|
+
self._suggestion_mode = "subcommands"
|
|
762
|
+
self._active_command = cmd
|
|
763
|
+
from chrys.app.tui.widgets.chrome.file_scanner import fuzzy_filter
|
|
764
|
+
|
|
765
|
+
all_subs = cmd.subcommands()
|
|
766
|
+
if sub_query:
|
|
767
|
+
matches = fuzzy_filter(sub_query, [v for v, _ in all_subs])
|
|
768
|
+
sub_by_value = dict(all_subs)
|
|
769
|
+
items = [(m, sub_by_value[m]) for m in matches if m in sub_by_value]
|
|
770
|
+
self._view.show_subcommand_suggestions(items)
|
|
771
|
+
else:
|
|
772
|
+
initial = cmd.initial() if cmd.initial else None
|
|
773
|
+
self._view.show_subcommand_suggestions(all_subs, initial=initial)
|
|
774
|
+
return
|
|
775
|
+
from chrys.app.tui.widgets.chrome.file_scanner import fuzzy_filter
|
|
776
|
+
|
|
777
|
+
visible = self._visible_slash_commands()
|
|
778
|
+
all_names: list[str] = []
|
|
779
|
+
name_to_cmd: dict[str, SlashCommandDef] = {}
|
|
780
|
+
for cmd in visible:
|
|
781
|
+
for n in [cmd.name, *cmd.aliases]:
|
|
782
|
+
all_names.append(n)
|
|
783
|
+
name_to_cmd[n] = cmd
|
|
784
|
+
matches = fuzzy_filter(query, all_names, max_results=20)
|
|
785
|
+
# Preserve fuzzy_filter order (prefix matches before contains matches)
|
|
786
|
+
seen: set[str] = set()
|
|
787
|
+
ordered_cmds: list[SlashCommandDef] = []
|
|
788
|
+
for m in matches:
|
|
789
|
+
cmd = name_to_cmd.get(m)
|
|
790
|
+
if cmd and cmd.name not in seen:
|
|
791
|
+
seen.add(cmd.name)
|
|
792
|
+
ordered_cmds.append(cmd)
|
|
793
|
+
disabled = self._disabled_commands()
|
|
794
|
+
items = self._command_suggestion_items(ordered_cmds, disabled)
|
|
795
|
+
skills = self._state.runtime.details.skill_details
|
|
796
|
+
if query:
|
|
797
|
+
skill_names = [skill.name for skill in skills]
|
|
798
|
+
skill_matches = fuzzy_filter(query, skill_names, max_results=20)
|
|
799
|
+
skill_by_name = {skill.name: skill for skill in skills}
|
|
800
|
+
ordered_skills = [skill_by_name[name] for name in skill_matches if name in skill_by_name]
|
|
801
|
+
else:
|
|
802
|
+
ordered_skills = skills
|
|
803
|
+
items.extend(self._runtime_skill_suggestion_items(list(ordered_skills)))
|
|
804
|
+
self._view.update_suggestions(
|
|
805
|
+
items,
|
|
806
|
+
disabled=self._disabled_commands(),
|
|
807
|
+
title=self._suggestion_title("commands"),
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
elif self._suggestion_mode == "subcommands":
|
|
811
|
+
cmd = self._active_command
|
|
812
|
+
if not cmd or not cmd.subcommands or not is_slash_command_candidate(text):
|
|
813
|
+
self.dismiss_suggestions()
|
|
814
|
+
return
|
|
815
|
+
parts = text.split(None, 1)
|
|
816
|
+
query = parts[1] if len(parts) > 1 else ""
|
|
817
|
+
from chrys.app.tui.widgets.chrome.file_scanner import fuzzy_filter
|
|
818
|
+
|
|
819
|
+
all_subs = cmd.subcommands()
|
|
820
|
+
if query:
|
|
821
|
+
matches = fuzzy_filter(query, [v for v, _ in all_subs])
|
|
822
|
+
sub_by_value = dict(all_subs)
|
|
823
|
+
items = [(m, sub_by_value[m]) for m in matches if m in sub_by_value]
|
|
824
|
+
self._view.update_suggestions(items, title=self._suggestion_title("subcommands"))
|
|
825
|
+
else:
|
|
826
|
+
initial = cmd.initial() if cmd.initial else None
|
|
827
|
+
self._view.show_subcommand_suggestions(all_subs, initial=initial)
|
|
828
|
+
|
|
829
|
+
elif self._suggestion_mode == "files":
|
|
830
|
+
idx = max(text.rfind("@"), text.rfind("\uff20"))
|
|
831
|
+
if idx < 0:
|
|
832
|
+
self.dismiss_suggestions()
|
|
833
|
+
return
|
|
834
|
+
query = text[idx + 1 :]
|
|
835
|
+
if query:
|
|
836
|
+
self._enqueue_file_query(query)
|
|
837
|
+
else:
|
|
838
|
+
self._file_latest_query = ""
|
|
839
|
+
self._invalidate_file_query_results()
|
|
840
|
+
index = self._current_file_index()
|
|
841
|
+
if index is not None:
|
|
842
|
+
self._view.update_suggestions(
|
|
843
|
+
self._file_suggestion_items(index.head(limit=_FILE_QUERY_LIMIT), index=index),
|
|
844
|
+
title=self._suggestion_title("files"),
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
elif self._suggestion_mode == "agents":
|
|
848
|
+
if "#" not in text and "\uff03" not in text:
|
|
849
|
+
self.dismiss_suggestions()
|
|
850
|
+
return
|
|
851
|
+
matches, item_by_name, disabled = self._agent_query_matches(text)
|
|
852
|
+
items = [item_by_name[name] for name in matches]
|
|
853
|
+
self._view.update_suggestions(items, disabled=disabled, title=self._suggestion_title("agents"))
|
|
854
|
+
|
|
855
|
+
elif self._suggestion_mode == "models":
|
|
856
|
+
if "$" not in text and "\uff04" not in text:
|
|
857
|
+
self.dismiss_suggestions()
|
|
858
|
+
return
|
|
859
|
+
matches, item_by_id, disabled = self._model_query_matches(text)
|
|
860
|
+
items = [item_by_id[profile_id] for profile_id in matches]
|
|
861
|
+
self._view.update_suggestions(items, disabled=disabled, title=self._suggestion_title("models"))
|
|
862
|
+
|
|
863
|
+
elif self._suggestion_mode == "history" and text != self._prompt_history_draft:
|
|
864
|
+
self.dismiss_suggestions()
|
|
865
|
+
|
|
866
|
+
# -------------------------------------------------------------- #
|
|
867
|
+
# Navigation and selection
|
|
868
|
+
# -------------------------------------------------------------- #
|
|
869
|
+
|
|
870
|
+
def on_suggestion_navigate(self, direction: str) -> None:
|
|
871
|
+
self._view.move_suggestion_cursor(direction)
|
|
872
|
+
|
|
873
|
+
def on_suggestion_select(self, execute: bool) -> bool:
|
|
874
|
+
"""Handle Tab/Enter on suggestion list. Returns True if handled."""
|
|
875
|
+
selected = self._view.select_highlighted_suggestion(execute=execute)
|
|
876
|
+
if not selected and execute:
|
|
877
|
+
mode = self._suggestion_mode
|
|
878
|
+
if mode not in {"commands", "subcommands", "agents", "models"}:
|
|
879
|
+
return True
|
|
880
|
+
|
|
881
|
+
text = self._view.input_value()
|
|
882
|
+
if mode == "agents" and not self._agent_fallback_should_submit(text):
|
|
883
|
+
return True
|
|
884
|
+
if mode == "models" and not self._model_fallback_should_submit(text):
|
|
885
|
+
return True
|
|
886
|
+
self.dismiss_suggestions()
|
|
887
|
+
self._view.set_input_value("")
|
|
888
|
+
if text and (mode in {"agents", "models"} or not self.dispatch_slash_command(text)):
|
|
889
|
+
self._callbacks.submit_user_text(text)
|
|
890
|
+
return True
|
|
891
|
+
return bool(selected)
|
|
892
|
+
|
|
893
|
+
def on_suggestion_selected(self, mode: str, text: str, execute: bool, kind: str = "") -> None:
|
|
894
|
+
"""Handle a confirmed suggestion selection."""
|
|
895
|
+
if mode == "commands":
|
|
896
|
+
if kind == "skill":
|
|
897
|
+
self.dismiss_suggestions()
|
|
898
|
+
if execute:
|
|
899
|
+
self._view.set_input_value("")
|
|
900
|
+
self._callbacks.submit_user_text(f"/{text}")
|
|
901
|
+
else:
|
|
902
|
+
self._view.replace_input_trigger_text("/", f"/{text} ")
|
|
903
|
+
return
|
|
904
|
+
cmd = next((c for c in self._slash_commands if c.name == text), None)
|
|
905
|
+
if cmd is None:
|
|
906
|
+
self.dismiss_suggestions()
|
|
907
|
+
return
|
|
908
|
+
if cmd.name in self._disabled_commands():
|
|
909
|
+
return
|
|
910
|
+
if cmd.subcommands:
|
|
911
|
+
self._view.replace_input_trigger_text("/", f"/{text} ")
|
|
912
|
+
self._suggestion_mode = "subcommands"
|
|
913
|
+
self._active_command = cmd
|
|
914
|
+
sub_items = cmd.subcommands()
|
|
915
|
+
initial = cmd.initial() if cmd.initial else None
|
|
916
|
+
self._view.show_subcommand_suggestions(sub_items, initial=initial)
|
|
917
|
+
return
|
|
918
|
+
if execute:
|
|
919
|
+
self.dismiss_suggestions()
|
|
920
|
+
self._view.set_input_value("")
|
|
921
|
+
cmd.action("")
|
|
922
|
+
else:
|
|
923
|
+
self._view.replace_input_trigger_text("/", f"/{text} ")
|
|
924
|
+
self.dismiss_suggestions()
|
|
925
|
+
|
|
926
|
+
elif mode == "subcommands":
|
|
927
|
+
cmd = self._active_command
|
|
928
|
+
if not cmd or cmd.name in self._disabled_commands():
|
|
929
|
+
self.dismiss_suggestions()
|
|
930
|
+
return
|
|
931
|
+
# For subcommands that require parameters (e.g., "name"), keep input open
|
|
932
|
+
if text == "name":
|
|
933
|
+
self._view.replace_input_trigger_text(f"/{cmd.name} ", f"/{cmd.name} name ")
|
|
934
|
+
self._suggestion_mode = None
|
|
935
|
+
self._active_command = None
|
|
936
|
+
self.dismiss_suggestions()
|
|
937
|
+
else:
|
|
938
|
+
# Execute immediately for parameterless subcommands
|
|
939
|
+
self.dismiss_suggestions()
|
|
940
|
+
self._view.set_input_value("")
|
|
941
|
+
cmd.action(text)
|
|
942
|
+
|
|
943
|
+
elif mode == "files":
|
|
944
|
+
if kind == "status":
|
|
945
|
+
return
|
|
946
|
+
selected_path = text
|
|
947
|
+
if kind == "directory" and not selected_path.endswith(("/", "\\")):
|
|
948
|
+
selected_path = f"{selected_path}/"
|
|
949
|
+
path = f'"{selected_path}"' if " " in selected_path else selected_path
|
|
950
|
+
self._view.replace_input_trigger_text("@", f"@{path} ")
|
|
951
|
+
self.dismiss_suggestions()
|
|
952
|
+
|
|
953
|
+
elif mode == "agents":
|
|
954
|
+
self.dismiss_suggestions()
|
|
955
|
+
self._view.set_input_value("")
|
|
956
|
+
self._callbacks.start_agent_profile_switch(text)
|
|
957
|
+
|
|
958
|
+
elif mode == "models":
|
|
959
|
+
self.dismiss_suggestions()
|
|
960
|
+
self._view.set_input_value("")
|
|
961
|
+
self._callbacks.start_model_profile_switch(text)
|
|
962
|
+
|
|
963
|
+
elif mode == "history":
|
|
964
|
+
self.dismiss_suggestions()
|
|
965
|
+
self._view.set_input_value(text)
|
|
966
|
+
|
|
967
|
+
@property
|
|
968
|
+
def suggestion_mode(self) -> str | None:
|
|
969
|
+
return self._suggestion_mode
|
|
970
|
+
|
|
971
|
+
@property
|
|
972
|
+
def file_cache(self) -> list[ProjectPathSuggestion] | None:
|
|
973
|
+
return self._file_cache
|
|
974
|
+
|
|
975
|
+
@file_cache.setter
|
|
976
|
+
def file_cache(self, value: list[ProjectPathSuggestion] | None) -> None:
|
|
977
|
+
self._file_cache = value
|
|
978
|
+
self._file_cache_root = None
|
|
979
|
+
self._file_index = None
|
|
980
|
+
self._file_index_root = None
|
|
981
|
+
self._file_cache_generation += 1
|
|
982
|
+
self._invalidate_file_query_results()
|