ocaya 2.14.0__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.
- .kiro/specs/ocaya/.config.kiro +1 -0
- .kiro/specs/ocaya/design.md +464 -0
- .kiro/specs/ocaya/requirements.md +191 -0
- .kiro/specs/ocaya/tasks.md +221 -0
- ocaya/__init__.py +13 -0
- ocaya/acp/__init__.py +0 -0
- ocaya/acp/acp_agent_loop.py +1948 -0
- ocaya/acp/acp_logger.py +97 -0
- ocaya/acp/commands/__init__.py +5 -0
- ocaya/acp/commands/registry.py +89 -0
- ocaya/acp/entrypoint.py +112 -0
- ocaya/acp/exceptions.py +140 -0
- ocaya/acp/session.py +75 -0
- ocaya/acp/title.py +69 -0
- ocaya/acp/tools/__init__.py +0 -0
- ocaya/acp/tools/base.py +53 -0
- ocaya/acp/tools/builtins/bash.py +168 -0
- ocaya/acp/tools/builtins/edit.py +114 -0
- ocaya/acp/tools/builtins/grep.py +79 -0
- ocaya/acp/tools/builtins/read.py +132 -0
- ocaya/acp/tools/builtins/skill.py +77 -0
- ocaya/acp/tools/builtins/task.py +73 -0
- ocaya/acp/tools/builtins/todo.py +68 -0
- ocaya/acp/tools/builtins/web_fetch.py +83 -0
- ocaya/acp/tools/builtins/web_search.py +80 -0
- ocaya/acp/tools/builtins/write_file.py +104 -0
- ocaya/acp/tools/events.py +8 -0
- ocaya/acp/tools/session_update.py +196 -0
- ocaya/acp/utils.py +338 -0
- ocaya/cli/__init__.py +0 -0
- ocaya/cli/autocompletion/__init__.py +0 -0
- ocaya/cli/autocompletion/base.py +22 -0
- ocaya/cli/autocompletion/path_completion.py +177 -0
- ocaya/cli/autocompletion/slash_command.py +95 -0
- ocaya/cli/cache.py +32 -0
- ocaya/cli/cli.py +279 -0
- ocaya/cli/clipboard.py +190 -0
- ocaya/cli/commands.py +261 -0
- ocaya/cli/entrypoint.py +251 -0
- ocaya/cli/history_manager.py +105 -0
- ocaya/cli/narrator_manager/__init__.py +15 -0
- ocaya/cli/narrator_manager/narrator_manager.py +269 -0
- ocaya/cli/narrator_manager/narrator_manager_port.py +45 -0
- ocaya/cli/narrator_manager/telemetry.py +30 -0
- ocaya/cli/plan_offer/adapters/http_whoami_gateway.py +45 -0
- ocaya/cli/plan_offer/decide_plan_offer.py +127 -0
- ocaya/cli/plan_offer/ports/whoami_gateway.py +67 -0
- ocaya/cli/profiler.py +89 -0
- ocaya/cli/stderr_guard.py +101 -0
- ocaya/cli/terminal_detect.py +83 -0
- ocaya/cli/textual_ui/__init__.py +0 -0
- ocaya/cli/textual_ui/app.py +3395 -0
- ocaya/cli/textual_ui/app.tcss +1606 -0
- ocaya/cli/textual_ui/constants.py +11 -0
- ocaya/cli/textual_ui/external_editor.py +38 -0
- ocaya/cli/textual_ui/handlers/__init__.py +5 -0
- ocaya/cli/textual_ui/handlers/event_handler.py +269 -0
- ocaya/cli/textual_ui/message_queue.py +85 -0
- ocaya/cli/textual_ui/notifications/__init__.py +11 -0
- ocaya/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- ocaya/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +60 -0
- ocaya/cli/textual_ui/notifications/ports/__init__.py +0 -0
- ocaya/cli/textual_ui/notifications/ports/notification_port.py +16 -0
- ocaya/cli/textual_ui/quit_manager.py +64 -0
- ocaya/cli/textual_ui/recording/__init__.py +5 -0
- ocaya/cli/textual_ui/recording/recording_indicator.py +79 -0
- ocaya/cli/textual_ui/remote/__init__.py +8 -0
- ocaya/cli/textual_ui/remote/remote_session_manager.py +211 -0
- ocaya/cli/textual_ui/scheduled_loop_runner.py +99 -0
- ocaya/cli/textual_ui/session_exit.py +31 -0
- ocaya/cli/textual_ui/widgets/__init__.py +0 -0
- ocaya/cli/textual_ui/widgets/approval_app.py +270 -0
- ocaya/cli/textual_ui/widgets/banner/banner.py +158 -0
- ocaya/cli/textual_ui/widgets/banner/petit_chat.py +197 -0
- ocaya/cli/textual_ui/widgets/braille_renderer.py +58 -0
- ocaya/cli/textual_ui/widgets/chat_input/__init__.py +7 -0
- ocaya/cli/textual_ui/widgets/chat_input/body.py +308 -0
- ocaya/cli/textual_ui/widgets/chat_input/completion_manager.py +62 -0
- ocaya/cli/textual_ui/widgets/chat_input/completion_popup.py +76 -0
- ocaya/cli/textual_ui/widgets/chat_input/container.py +283 -0
- ocaya/cli/textual_ui/widgets/chat_input/paste_path.py +117 -0
- ocaya/cli/textual_ui/widgets/chat_input/text_area.py +445 -0
- ocaya/cli/textual_ui/widgets/compact.py +43 -0
- ocaya/cli/textual_ui/widgets/config_app.py +184 -0
- ocaya/cli/textual_ui/widgets/connector_auth_app.py +227 -0
- ocaya/cli/textual_ui/widgets/context_progress.py +30 -0
- ocaya/cli/textual_ui/widgets/debug_console.py +244 -0
- ocaya/cli/textual_ui/widgets/feedback_bar.py +64 -0
- ocaya/cli/textual_ui/widgets/feedback_bar_manager.py +23 -0
- ocaya/cli/textual_ui/widgets/load_more.py +43 -0
- ocaya/cli/textual_ui/widgets/loading.py +231 -0
- ocaya/cli/textual_ui/widgets/mcp_app.py +646 -0
- ocaya/cli/textual_ui/widgets/messages.py +590 -0
- ocaya/cli/textual_ui/widgets/model_picker.py +75 -0
- ocaya/cli/textual_ui/widgets/narrator_status.py +65 -0
- ocaya/cli/textual_ui/widgets/no_markup_static.py +11 -0
- ocaya/cli/textual_ui/widgets/path_display.py +31 -0
- ocaya/cli/textual_ui/widgets/proxy_setup_app.py +122 -0
- ocaya/cli/textual_ui/widgets/question_app.py +562 -0
- ocaya/cli/textual_ui/widgets/queued_messages.py +97 -0
- ocaya/cli/textual_ui/widgets/rewind_app.py +147 -0
- ocaya/cli/textual_ui/widgets/session_picker.py +133 -0
- ocaya/cli/textual_ui/widgets/spinner.py +194 -0
- ocaya/cli/textual_ui/widgets/status_message.py +76 -0
- ocaya/cli/textual_ui/widgets/teleport_message.py +31 -0
- ocaya/cli/textual_ui/widgets/theme_picker.py +114 -0
- ocaya/cli/textual_ui/widgets/thinking_picker.py +79 -0
- ocaya/cli/textual_ui/widgets/tool_widgets.py +368 -0
- ocaya/cli/textual_ui/widgets/tools.py +216 -0
- ocaya/cli/textual_ui/widgets/voice_app.py +173 -0
- ocaya/cli/textual_ui/widgets/vscode_compat.py +26 -0
- ocaya/cli/textual_ui/windowing/__init__.py +29 -0
- ocaya/cli/textual_ui/windowing/history.py +115 -0
- ocaya/cli/textual_ui/windowing/history_windowing.py +67 -0
- ocaya/cli/textual_ui/windowing/state.py +119 -0
- ocaya/cli/turn_summary/__init__.py +20 -0
- ocaya/cli/turn_summary/noop.py +38 -0
- ocaya/cli/turn_summary/port.py +52 -0
- ocaya/cli/turn_summary/tracker.py +147 -0
- ocaya/cli/turn_summary/utils.py +30 -0
- ocaya/cli/update_notifier/__init__.py +47 -0
- ocaya/cli/update_notifier/adapters/filesystem_update_cache_repository.py +77 -0
- ocaya/cli/update_notifier/adapters/github_update_gateway.py +104 -0
- ocaya/cli/update_notifier/adapters/pypi_update_gateway.py +110 -0
- ocaya/cli/update_notifier/ports/update_cache_repository.py +16 -0
- ocaya/cli/update_notifier/ports/update_gateway.py +53 -0
- ocaya/cli/update_notifier/update.py +139 -0
- ocaya/cli/update_notifier/whats_new.py +50 -0
- ocaya/cli/voice_manager/__init__.py +15 -0
- ocaya/cli/voice_manager/telemetry.py +30 -0
- ocaya/cli/voice_manager/voice_manager.py +269 -0
- ocaya/cli/voice_manager/voice_manager_port.py +58 -0
- ocaya/cli/vscode_extension_promo/__init__.py +43 -0
- ocaya/cli/vscode_extension_promo/_port.py +14 -0
- ocaya/cli/vscode_extension_promo/adapters/__init__.py +0 -0
- ocaya/cli/vscode_extension_promo/adapters/filesystem_repository.py +43 -0
- ocaya/core/__init__.py +15 -0
- ocaya/core/agent_loop.py +1862 -0
- ocaya/core/agents/__init__.py +29 -0
- ocaya/core/agents/manager.py +185 -0
- ocaya/core/agents/models.py +208 -0
- ocaya/core/audio_player/__init__.py +21 -0
- ocaya/core/audio_player/audio_player.py +130 -0
- ocaya/core/audio_player/audio_player_port.py +40 -0
- ocaya/core/audio_player/utils.py +12 -0
- ocaya/core/audio_recorder/__init__.py +23 -0
- ocaya/core/audio_recorder/audio_recorder.py +287 -0
- ocaya/core/audio_recorder/audio_recorder_port.py +64 -0
- ocaya/core/auth/__init__.py +6 -0
- ocaya/core/auth/crypto.py +137 -0
- ocaya/core/auth/github.py +184 -0
- ocaya/core/autocompletion/__init__.py +0 -0
- ocaya/core/autocompletion/completers.py +381 -0
- ocaya/core/autocompletion/file_indexer/__init__.py +10 -0
- ocaya/core/autocompletion/file_indexer/ignore_rules.py +170 -0
- ocaya/core/autocompletion/file_indexer/indexer.py +187 -0
- ocaya/core/autocompletion/file_indexer/store.py +188 -0
- ocaya/core/autocompletion/file_indexer/watcher.py +75 -0
- ocaya/core/autocompletion/fuzzy.py +189 -0
- ocaya/core/autocompletion/path_prompt.py +153 -0
- ocaya/core/autocompletion/path_prompt_adapter.py +181 -0
- ocaya/core/compaction.py +47 -0
- ocaya/core/config/__init__.py +131 -0
- ocaya/core/config/_settings.py +1176 -0
- ocaya/core/config/builder.py +122 -0
- ocaya/core/config/harness_files/__init__.py +17 -0
- ocaya/core/config/harness_files/_harness_manager.py +266 -0
- ocaya/core/config/harness_files/_paths.py +9 -0
- ocaya/core/config/layer.py +307 -0
- ocaya/core/config/layers/__init__.py +13 -0
- ocaya/core/config/layers/environment.py +44 -0
- ocaya/core/config/layers/overrides.py +34 -0
- ocaya/core/config/layers/project.py +91 -0
- ocaya/core/config/layers/user.py +39 -0
- ocaya/core/config/orchestrator.py +53 -0
- ocaya/core/config/patch.py +101 -0
- ocaya/core/config/schema.py +157 -0
- ocaya/core/config/types.py +25 -0
- ocaya/core/config/vibe_schema.py +242 -0
- ocaya/core/data_retention.py +7 -0
- ocaya/core/experiments/__init__.py +58 -0
- ocaya/core/experiments/_constants.py +15 -0
- ocaya/core/experiments/active.py +17 -0
- ocaya/core/experiments/client.py +81 -0
- ocaya/core/experiments/manager.py +110 -0
- ocaya/core/experiments/models.py +79 -0
- ocaya/core/experiments/session.py +81 -0
- ocaya/core/feedback.py +39 -0
- ocaya/core/hooks/__init__.py +0 -0
- ocaya/core/hooks/config.py +100 -0
- ocaya/core/hooks/executor.py +55 -0
- ocaya/core/hooks/manager.py +140 -0
- ocaya/core/hooks/models.py +98 -0
- ocaya/core/llm/__init__.py +0 -0
- ocaya/core/llm/backend/_image.py +38 -0
- ocaya/core/llm/backend/anthropic.py +619 -0
- ocaya/core/llm/backend/base.py +39 -0
- ocaya/core/llm/backend/factory.py +7 -0
- ocaya/core/llm/backend/generic.py +443 -0
- ocaya/core/llm/backend/mistral.py +442 -0
- ocaya/core/llm/backend/openai_responses.py +653 -0
- ocaya/core/llm/backend/reasoning_adapter.py +236 -0
- ocaya/core/llm/backend/vertex.py +130 -0
- ocaya/core/llm/exceptions.py +225 -0
- ocaya/core/llm/format.py +185 -0
- ocaya/core/llm/types.py +94 -0
- ocaya/core/log_reader.py +224 -0
- ocaya/core/logger.py +71 -0
- ocaya/core/loop.py +250 -0
- ocaya/core/middleware.py +266 -0
- ocaya/core/nuage/__init__.py +0 -0
- ocaya/core/nuage/agent_models.py +26 -0
- ocaya/core/nuage/client.py +205 -0
- ocaya/core/nuage/events.py +227 -0
- ocaya/core/nuage/exceptions.py +46 -0
- ocaya/core/nuage/remote_events_source.py +207 -0
- ocaya/core/nuage/remote_workflow_event_models.py +137 -0
- ocaya/core/nuage/remote_workflow_event_translator.py +1313 -0
- ocaya/core/nuage/streaming.py +32 -0
- ocaya/core/nuage/workflow.py +44 -0
- ocaya/core/output_formatters.py +106 -0
- ocaya/core/paths/__init__.py +41 -0
- ocaya/core/paths/_agents_home.py +14 -0
- ocaya/core/paths/_local_config_files.py +80 -0
- ocaya/core/paths/_ocaya_home.py +38 -0
- ocaya/core/paths/conventions.py +3 -0
- ocaya/core/plan_session.py +42 -0
- ocaya/core/programmatic.py +101 -0
- ocaya/core/prompts/__init__.py +113 -0
- ocaya/core/prompts/agents_doc.md +5 -0
- ocaya/core/prompts/cli.md +111 -0
- ocaya/core/prompts/cli_2026-05.md +135 -0
- ocaya/core/prompts/compact.md +12 -0
- ocaya/core/prompts/compact_summary_prefix.md +1 -0
- ocaya/core/prompts/dangerous_directory.md +5 -0
- ocaya/core/prompts/explore.md +50 -0
- ocaya/core/prompts/jarvis.md +64 -0
- ocaya/core/prompts/lean.md +159 -0
- ocaya/core/prompts/minimal.md +12 -0
- ocaya/core/prompts/ocaya.md +63 -0
- ocaya/core/prompts/project_context.md +4 -0
- ocaya/core/prompts/tests.md +1 -0
- ocaya/core/prompts/turn_summary.md +10 -0
- ocaya/core/proxy_setup.py +65 -0
- ocaya/core/rewind/__init__.py +10 -0
- ocaya/core/rewind/manager.py +192 -0
- ocaya/core/scratchpad.py +61 -0
- ocaya/core/session/image_snapshot.py +56 -0
- ocaya/core/session/last_session_pointer.py +132 -0
- ocaya/core/session/resume_sessions.py +95 -0
- ocaya/core/session/saved_sessions.py +87 -0
- ocaya/core/session/session_id.py +34 -0
- ocaya/core/session/session_loader.py +292 -0
- ocaya/core/session/session_logger.py +456 -0
- ocaya/core/session/session_migration.py +41 -0
- ocaya/core/session/title_format.py +51 -0
- ocaya/core/skills/__init__.py +13 -0
- ocaya/core/skills/builtins/__init__.py +6 -0
- ocaya/core/skills/builtins/ocaya.py +584 -0
- ocaya/core/skills/manager.py +193 -0
- ocaya/core/skills/models.py +109 -0
- ocaya/core/skills/parser.py +39 -0
- ocaya/core/system_prompt.py +394 -0
- ocaya/core/telemetry/__init__.py +0 -0
- ocaya/core/telemetry/build_metadata.py +67 -0
- ocaya/core/telemetry/send.py +368 -0
- ocaya/core/telemetry/types.py +54 -0
- ocaya/core/teleport/errors.py +9 -0
- ocaya/core/teleport/git.py +222 -0
- ocaya/core/teleport/nuage.py +137 -0
- ocaya/core/teleport/telemetry.py +80 -0
- ocaya/core/teleport/teleport.py +196 -0
- ocaya/core/teleport/types.py +39 -0
- ocaya/core/tools/arity.py +158 -0
- ocaya/core/tools/base.py +400 -0
- ocaya/core/tools/builtins/ask_user_question.py +132 -0
- ocaya/core/tools/builtins/bash.py +538 -0
- ocaya/core/tools/builtins/clipboard.py +169 -0
- ocaya/core/tools/builtins/edit.py +205 -0
- ocaya/core/tools/builtins/exit_plan_mode.py +137 -0
- ocaya/core/tools/builtins/grep.py +364 -0
- ocaya/core/tools/builtins/open_app.py +153 -0
- ocaya/core/tools/builtins/prompts/__init__.py +0 -0
- ocaya/core/tools/builtins/prompts/ask_user_question.md +84 -0
- ocaya/core/tools/builtins/prompts/bash.md +73 -0
- ocaya/core/tools/builtins/prompts/edit.md +19 -0
- ocaya/core/tools/builtins/prompts/grep.md +4 -0
- ocaya/core/tools/builtins/prompts/read.md +19 -0
- ocaya/core/tools/builtins/prompts/skill.md +19 -0
- ocaya/core/tools/builtins/prompts/task.md +24 -0
- ocaya/core/tools/builtins/prompts/todo.md +199 -0
- ocaya/core/tools/builtins/prompts/webfetch.md +7 -0
- ocaya/core/tools/builtins/prompts/websearch.md +25 -0
- ocaya/core/tools/builtins/prompts/write_file.md +27 -0
- ocaya/core/tools/builtins/read.py +235 -0
- ocaya/core/tools/builtins/skill.py +126 -0
- ocaya/core/tools/builtins/task.py +190 -0
- ocaya/core/tools/builtins/todo.py +129 -0
- ocaya/core/tools/builtins/webfetch.py +257 -0
- ocaya/core/tools/builtins/websearch.py +178 -0
- ocaya/core/tools/builtins/write_file.py +139 -0
- ocaya/core/tools/connectors/__init__.py +9 -0
- ocaya/core/tools/connectors/connector_registry.py +508 -0
- ocaya/core/tools/connectors/counts.py +26 -0
- ocaya/core/tools/manager.py +455 -0
- ocaya/core/tools/mcp/__init__.py +33 -0
- ocaya/core/tools/mcp/registry.py +181 -0
- ocaya/core/tools/mcp/tools.py +468 -0
- ocaya/core/tools/mcp_sampling.py +126 -0
- ocaya/core/tools/mcp_settings.py +75 -0
- ocaya/core/tools/permissions.py +68 -0
- ocaya/core/tools/ui.py +121 -0
- ocaya/core/tools/utils.py +125 -0
- ocaya/core/tracing.py +137 -0
- ocaya/core/transcribe/__init__.py +23 -0
- ocaya/core/transcribe/factory.py +19 -0
- ocaya/core/transcribe/mistral_transcribe_client.py +85 -0
- ocaya/core/transcribe/transcribe_client_port.py +44 -0
- ocaya/core/trusted_folders.py +174 -0
- ocaya/core/tts/__init__.py +7 -0
- ocaya/core/tts/factory.py +15 -0
- ocaya/core/tts/mistral_tts_client.py +58 -0
- ocaya/core/tts/tts_client_port.py +19 -0
- ocaya/core/types.py +589 -0
- ocaya/core/utils/__init__.py +73 -0
- ocaya/core/utils/async_subprocess.py +65 -0
- ocaya/core/utils/concurrency.py +59 -0
- ocaya/core/utils/display.py +19 -0
- ocaya/core/utils/http.py +59 -0
- ocaya/core/utils/io.py +216 -0
- ocaya/core/utils/matching.py +35 -0
- ocaya/core/utils/merge.py +115 -0
- ocaya/core/utils/paths.py +42 -0
- ocaya/core/utils/platform.py +45 -0
- ocaya/core/utils/retry.py +138 -0
- ocaya/core/utils/slug.py +113 -0
- ocaya/core/utils/tags.py +80 -0
- ocaya/core/utils/time.py +7 -0
- ocaya/core/utils/tokens.py +29 -0
- ocaya/setup/auth/__init__.py +39 -0
- ocaya/setup/auth/api_key_persistence.py +78 -0
- ocaya/setup/auth/auth_state.py +151 -0
- ocaya/setup/auth/browser_sign_in.py +212 -0
- ocaya/setup/auth/browser_sign_in_gateway.py +55 -0
- ocaya/setup/auth/http_browser_sign_in_gateway.py +355 -0
- ocaya/setup/onboarding/__init__.py +152 -0
- ocaya/setup/onboarding/base.py +14 -0
- ocaya/setup/onboarding/context.py +219 -0
- ocaya/setup/onboarding/gradient_text.py +30 -0
- ocaya/setup/onboarding/onboarding.tcss +453 -0
- ocaya/setup/onboarding/screens/__init__.py +15 -0
- ocaya/setup/onboarding/screens/api_key.py +135 -0
- ocaya/setup/onboarding/screens/auth_method.py +123 -0
- ocaya/setup/onboarding/screens/browser_sign_in.py +534 -0
- ocaya/setup/onboarding/screens/theme_selection.py +141 -0
- ocaya/setup/onboarding/screens/welcome.py +117 -0
- ocaya/setup/trusted_folders/trust_folder_dialog.py +306 -0
- ocaya/setup/trusted_folders/trust_folder_dialog.tcss +170 -0
- ocaya/whats_new.md +4 -0
- ocaya-2.14.0.dist-info/METADATA +573 -0
- ocaya-2.14.0.dist-info/RECORD +364 -0
- ocaya-2.14.0.dist-info/WHEEL +4 -0
- ocaya-2.14.0.dist-info/entry_points.txt +3 -0
- ocaya-2.14.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"specId": "c355ffb6-2295-4000-b342-f7d0e63324c7", "workflowType": "requirements-first", "specType": "feature"}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
# Design Document: OCAYA
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
OCAYA is a rebrand and repurpose of the open-source `mistral-vibe` Python CLI. This is not a new architecture — it is a targeted transformation across three concerns:
|
|
6
|
+
|
|
7
|
+
1. **Identity rename** — every package name, class name, path, environment variable, log filename, and TUI string that says "vibe" or "Mistral Vibe" becomes "ocaya" or "OCAYA".
|
|
8
|
+
2. **Persona swap** — the default system prompt changes from a coding-focused instruction document (`cli.md`) to a web-first general-purpose instruction document (`ocaya.md`).
|
|
9
|
+
3. **Tool enablement** — two already-drafted tools (`clipboard.py`, `open_app.py`) are confirmed as active built-ins and any rough edges in their implementation are cleaned up.
|
|
10
|
+
|
|
11
|
+
Mistral AI remains the sole LLM backend. Browser automation (Playwright, browser-use, Selenium) is explicitly out of scope.
|
|
12
|
+
|
|
13
|
+
### Key Design Constraints
|
|
14
|
+
|
|
15
|
+
- No new architectural layers are introduced.
|
|
16
|
+
- The agent loop, session mechanics, MCP/ACP bridge, voice pipeline, skills system, and all existing tool logic are preserved verbatim.
|
|
17
|
+
- The `env_prefix="VIBE_"` in `OcayaConfig` is **preserved** — the requirement only mandates renaming `VIBE_HOME` (which is resolved outside of the pydantic-settings env prefix, in the paths module). Changing `env_prefix` would silently break all existing user `VIBE_*` config env vars; this change is intentionally deferred.
|
|
18
|
+
- The `clipboard.py` and `open_app.py` files already exist in the builtins directory and are auto-discovered by `ToolManager` — no explicit registration code is required beyond verifying the files are valid and their import paths are updated to `ocaya.*`.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
The transformation is a layer-by-layer rename following dependency order. No new components are added; existing components are updated in place.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
┌─────────────────────────────────────────────────────────┐
|
|
28
|
+
│ pyproject.toml (package name, scripts, tool config) │
|
|
29
|
+
├─────────────────────────────────────────────────────────┤
|
|
30
|
+
│ ocaya/ (was: vibe/) │
|
|
31
|
+
│ ├── __init__.py OCAYA_ROOT, __version__ │
|
|
32
|
+
│ ├── acp/ ACP server (unchanged logic) │
|
|
33
|
+
│ ├── cli/ Textual TUI ← branding here │
|
|
34
|
+
│ └── core/ │
|
|
35
|
+
│ ├── agents/models.py VibeConfig ref → OcayaConfig│
|
|
36
|
+
│ ├── config/_settings.py VibeConfig → OcayaConfig │
|
|
37
|
+
│ ├── paths/_ocaya_home.py was: _vibe_home.py │
|
|
38
|
+
│ ├── prompts/ │
|
|
39
|
+
│ │ ├── __init__.py SystemPrompt.OCAYA added │
|
|
40
|
+
│ │ └── ocaya.md new default system prompt │
|
|
41
|
+
│ ├── tools/builtins/ │
|
|
42
|
+
│ │ ├── clipboard.py import path updated │
|
|
43
|
+
│ │ └── open_app.py import path updated │
|
|
44
|
+
│ └── skills/builtins/ocaya.py was: vibe.py │
|
|
45
|
+
├─────────────────────────────────────────────────────────┤
|
|
46
|
+
│ tests/ (import paths updated: vibe. → ocaya.) │
|
|
47
|
+
└─────────────────────────────────────────────────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Migration Order
|
|
51
|
+
|
|
52
|
+
Changes must be applied in this order to avoid import errors at any intermediate state:
|
|
53
|
+
|
|
54
|
+
1. Rename `vibe/` → `ocaya/` (the physical directory rename)
|
|
55
|
+
2. Update `ocaya/__init__.py` — rename `VIBE_ROOT` → `OCAYA_ROOT`
|
|
56
|
+
3. Update `ocaya/core/paths/_vibe_home.py` → `_ocaya_home.py`
|
|
57
|
+
4. Update `ocaya/core/prompts/__init__.py` — add `SystemPrompt.OCAYA`, update `PROMPTS_DIR`
|
|
58
|
+
5. Create `ocaya/core/prompts/ocaya.md`
|
|
59
|
+
6. Update `ocaya/core/config/_settings.py` — rename `VibeConfig` → `OcayaConfig`, update `system_prompt_id` default
|
|
60
|
+
7. Update `ocaya/core/agents/models.py` — update all `VibeConfig` references
|
|
61
|
+
8. Update all other `ocaya/**/*.py` files — fix import paths and string constants
|
|
62
|
+
9. Update `ocaya/core/tools/builtins/clipboard.py` and `open_app.py` — fix import paths
|
|
63
|
+
10. Update `ocaya/core/skills/builtins/vibe.py` → `ocaya.py` — content update
|
|
64
|
+
11. Update TUI branding strings in `ocaya/cli/`
|
|
65
|
+
12. Update `pyproject.toml`
|
|
66
|
+
13. Update all `tests/**/*.py` files — fix import paths
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Components and Interfaces
|
|
71
|
+
|
|
72
|
+
### pyproject.toml
|
|
73
|
+
|
|
74
|
+
| Field | Before | After |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `[project] name` | `"mistral-vibe"` | `"ocaya"` |
|
|
77
|
+
| `[project] description` | `"Minimal CLI coding agent by Mistral"` | `"OCAYA — web-first general-purpose AI assistant"` |
|
|
78
|
+
| `[project] keywords` | includes `"coding-assistant"`, `"mistral-vibe"` | replace with `"ocaya"`, `"general-assistant"` |
|
|
79
|
+
| `[project.scripts] vibe` | `"vibe.cli.entrypoint:main"` | removed |
|
|
80
|
+
| `[project.scripts] ocaya` | (absent) | `"ocaya.cli.entrypoint:main"` |
|
|
81
|
+
| `[project.scripts] vibe-acp` | `"vibe.acp.entrypoint:main"` | removed |
|
|
82
|
+
| `[project.scripts] ocaya-acp` | (absent) | `"ocaya.acp.entrypoint:main"` |
|
|
83
|
+
| `[tool.hatch.build.targets.wheel] include` | `["vibe/"]` | `["ocaya/"]` |
|
|
84
|
+
| `[tool.pyright] include` | `["vibe/**/*.py", ...]` | `["ocaya/**/*.py", ...]` |
|
|
85
|
+
| `[tool.ruff] include` | `["vibe/**/*.py", ...]` | `["ocaya/**/*.py", ...]` |
|
|
86
|
+
| `[tool.ruff.lint.isort] known-first-party` | `["vibe"]` | `["ocaya"]` |
|
|
87
|
+
|
|
88
|
+
Project URLs pointing to GitHub repository path segments (e.g. `mistralai/mistral-vibe`) are left unchanged as they are not human-readable branding fields.
|
|
89
|
+
|
|
90
|
+
### `ocaya/__init__.py`
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
OCAYA_ROOT = Path(__file__).parent
|
|
94
|
+
__version__ = "2.14.0"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`VIBE_ROOT` is removed. All internal references to `VIBE_ROOT` become `OCAYA_ROOT`.
|
|
98
|
+
|
|
99
|
+
### `ocaya/core/paths/_ocaya_home.py` (was `_vibe_home.py`)
|
|
100
|
+
|
|
101
|
+
Full replacement:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from __future__ import annotations
|
|
105
|
+
|
|
106
|
+
from collections.abc import Callable
|
|
107
|
+
import os
|
|
108
|
+
from pathlib import Path
|
|
109
|
+
|
|
110
|
+
from ocaya import OCAYA_ROOT
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class GlobalPath:
|
|
114
|
+
def __init__(self, resolver: Callable[[], Path]) -> None:
|
|
115
|
+
self._resolver = resolver
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def path(self) -> Path:
|
|
119
|
+
return self._resolver()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
_DEFAULT_OCAYA_HOME = Path.home() / ".ocaya"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _get_ocaya_home() -> Path:
|
|
126
|
+
if ocaya_home := os.getenv("OCAYA_HOME"):
|
|
127
|
+
return Path(ocaya_home).expanduser().resolve()
|
|
128
|
+
return _DEFAULT_OCAYA_HOME
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
OCAYA_HOME = GlobalPath(_get_ocaya_home)
|
|
132
|
+
GLOBAL_ENV_FILE = GlobalPath(lambda: OCAYA_HOME.path / ".env")
|
|
133
|
+
SESSION_LOG_DIR = GlobalPath(lambda: OCAYA_HOME.path / "logs" / "session")
|
|
134
|
+
TRUSTED_FOLDERS_FILE = GlobalPath(lambda: OCAYA_HOME.path / "trusted_folders.toml")
|
|
135
|
+
LOG_DIR = GlobalPath(lambda: OCAYA_HOME.path / "logs")
|
|
136
|
+
LOG_FILE = GlobalPath(lambda: OCAYA_HOME.path / "logs" / "ocaya.log")
|
|
137
|
+
CACHE_FILE = GlobalPath(lambda: OCAYA_HOME.path / "cache.toml")
|
|
138
|
+
HISTORY_FILE = GlobalPath(lambda: OCAYA_HOME.path / "ocayahistory")
|
|
139
|
+
PLANS_DIR = GlobalPath(lambda: OCAYA_HOME.path / "plans")
|
|
140
|
+
|
|
141
|
+
DEFAULT_TOOL_DIR = GlobalPath(lambda: OCAYA_ROOT / "core" / "tools" / "builtins")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The `paths/__init__.py` re-export must be updated: all exported names (`VIBE_HOME`, `LOG_FILE`, `HISTORY_FILE`, etc.) are renamed to match.
|
|
145
|
+
|
|
146
|
+
### `ocaya/core/config/_settings.py`
|
|
147
|
+
|
|
148
|
+
Two targeted changes:
|
|
149
|
+
|
|
150
|
+
**1. Class rename:**
|
|
151
|
+
```python
|
|
152
|
+
# Before
|
|
153
|
+
class VibeConfig(BaseSettings):
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
# After
|
|
157
|
+
class OcayaConfig(BaseSettings):
|
|
158
|
+
...
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**2. `system_prompt_id` default:**
|
|
162
|
+
```python
|
|
163
|
+
# Before
|
|
164
|
+
system_prompt_id: str = SystemPrompt.CLI
|
|
165
|
+
|
|
166
|
+
# After
|
|
167
|
+
system_prompt_id: str = SystemPrompt.OCAYA
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**3. `env_prefix` stays as `"VIBE_"`** — preserves all existing user `VIBE_*` environment variable overrides. The requirement for renaming `VIBE_HOME` is handled entirely in the paths module (direct `os.getenv("OCAYA_HOME")` call), which is architecturally separate from pydantic-settings' env prefix mechanism.
|
|
171
|
+
|
|
172
|
+
**4. Internal model validators** referencing `VibeConfig` by name in type annotations (`-> VibeConfig`) are updated to `-> OcayaConfig`.
|
|
173
|
+
|
|
174
|
+
**5. `DEFAULT_VIBE_BASE_URL` constant:** The variable name `DEFAULT_VIBE_BASE_URL` and `vibe_base_url` field name are internal Python identifiers that map to an external Mistral service URL (`chat.mistral.ai`). Renaming these would require either a field alias or a user-visible config key change. Since the backing URL is a Mistral-owned endpoint and changing the config key would break existing `config.toml` files, these internal identifiers are renamed to `DEFAULT_OCAYA_BASE_URL` / `ocaya_base_url` with a `validation_alias` to accept the legacy `vibe_base_url` key from existing config files.
|
|
175
|
+
|
|
176
|
+
### `ocaya/core/prompts/__init__.py`
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
class SystemPrompt(Prompt):
|
|
180
|
+
OCAYA = auto() # new — points to ocaya.md
|
|
181
|
+
CLI = auto() # kept for backward compatibility (custom system_prompt_id="cli")
|
|
182
|
+
EXPLORE = auto()
|
|
183
|
+
TESTS = auto()
|
|
184
|
+
LEAN = auto()
|
|
185
|
+
MINIMAL = auto()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`OCAYA` is added as the first member so it occupies the `"ocaya"` value. `CLI` is kept so that any user who has `system_prompt_id = "cli"` in their `config.toml` continues to work.
|
|
189
|
+
|
|
190
|
+
`PROMPTS_DIR` changes from `VIBE_ROOT / "core" / "prompts"` to `OCAYA_ROOT / "core" / "prompts"`.
|
|
191
|
+
|
|
192
|
+
### `ocaya/core/agents/models.py`
|
|
193
|
+
|
|
194
|
+
- All `TYPE_CHECKING` imports of `VibeConfig` → `OcayaConfig`.
|
|
195
|
+
- The `apply_to_config` method signature: `base: OcayaConfig` and local import `from ocaya.core.config import OcayaConfig as OC`.
|
|
196
|
+
- The `EXPLORE` agent's `overrides` already uses `"system_prompt_id": "explore"` — no change needed there.
|
|
197
|
+
- All `from vibe.*` imports → `from ocaya.*`.
|
|
198
|
+
|
|
199
|
+
### `ocaya/core/tools/builtins/clipboard.py` and `open_app.py`
|
|
200
|
+
|
|
201
|
+
Both files exist and are functionally complete. The only required change is updating all `from vibe.core.*` imports to `from ocaya.core.*`. No logic changes.
|
|
202
|
+
|
|
203
|
+
The `ToolManager` auto-discovers all non-underscore-prefixed `.py` files under `DEFAULT_TOOL_DIR` (which now resolves to `ocaya/core/tools/builtins/`). Both files are already there — no registration code change is needed.
|
|
204
|
+
|
|
205
|
+
The `_compute_module_name` function in `manager.py` uses `parts.index("vibe")` to detect canonical module names. After renaming, this must be updated to detect `"ocaya"` instead.
|
|
206
|
+
|
|
207
|
+
### `ocaya/core/skills/builtins/ocaya.py` (was `vibe.py`)
|
|
208
|
+
|
|
209
|
+
The file is renamed and the `SkillInfo` content is updated:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
SKILL = SkillInfo(
|
|
213
|
+
name="ocaya",
|
|
214
|
+
description="Understand the OCAYA application internals: configuration, OCAYA_HOME structure, ...",
|
|
215
|
+
user_invocable=False,
|
|
216
|
+
prompt="""# OCAYA Self-Awareness
|
|
217
|
+
...
|
|
218
|
+
""",
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
All occurrences of `~/.vibe`, `VIBE_HOME`, `vibe.log`, `vibehistory`, `Mistral Vibe`, and `vibe` (as product name) in the skill prompt are updated to their OCAYA equivalents. The `.vibe/` project-local config directory references become `.ocaya/`.
|
|
223
|
+
|
|
224
|
+
### TUI Branding (`ocaya/cli/`)
|
|
225
|
+
|
|
226
|
+
The Textual TUI uses string constants scattered across several widgets. The following surfaces must be audited and updated:
|
|
227
|
+
|
|
228
|
+
| Surface | Location | Change |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| App title | `App` CSS / `TITLE` class var | `"Mistral Vibe"` → `"OCAYA"` |
|
|
231
|
+
| Startup splash/banner | `WelcomeWidget` or equivalent | Remove ASCII art; replace with `"OCAYA"` heading |
|
|
232
|
+
| Status bar agent name | `StatusBar` widget | Label text → `"OCAYA"` |
|
|
233
|
+
| Help panel heading | `HelpPanel` widget | `"Mistral Vibe Help"` → `"OCAYA Help"` |
|
|
234
|
+
| Error dialogs | Any `ModalScreen` / `Dialog` with branding | Replace legacy branding with `"OCAYA"` |
|
|
235
|
+
| Version string | `--version` flag handler, startup banner | Format: `f"OCAYA {__version__}"` |
|
|
236
|
+
|
|
237
|
+
No CSS color variables, layout, or animation logic is changed.
|
|
238
|
+
|
|
239
|
+
### `tests/` directory
|
|
240
|
+
|
|
241
|
+
Every test file that imports from `vibe.*` is updated to import from `ocaya.*`. The `tests/conftest.py` autouse fixtures (`config_dir`, `tmp_working_directory`) that reference `VIBE_HOME` / `~/.vibe` are updated to reference `OCAYA_HOME` / `~/.ocaya`.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Data Models
|
|
246
|
+
|
|
247
|
+
### `OcayaConfig` (was `VibeConfig`)
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
class OcayaConfig(BaseSettings):
|
|
251
|
+
system_prompt_id: str = SystemPrompt.OCAYA # was: SystemPrompt.CLI = "cli"
|
|
252
|
+
# All other fields: unchanged
|
|
253
|
+
model_config = SettingsConfigDict(
|
|
254
|
+
env_prefix="VIBE_", # preserved — see rationale above
|
|
255
|
+
case_sensitive=False,
|
|
256
|
+
extra="ignore",
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
No fields are added or removed. The only data model change is the `system_prompt_id` default.
|
|
261
|
+
|
|
262
|
+
### `SystemPrompt` enum
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
class SystemPrompt(Prompt):
|
|
266
|
+
OCAYA = auto() # value: "ocaya" — new default
|
|
267
|
+
CLI = auto() # value: "cli" — preserved for compatibility
|
|
268
|
+
EXPLORE = auto()
|
|
269
|
+
TESTS = auto()
|
|
270
|
+
LEAN = auto()
|
|
271
|
+
MINIMAL = auto()
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The `OCAYA` member must be ordered first so its `auto()` value is `"ocaya"`. Because `StrEnum` with `auto()` derives the string value from the member name lowercased, member order relative to each other does not matter for value correctness — what matters is the name.
|
|
275
|
+
|
|
276
|
+
### Path constants
|
|
277
|
+
|
|
278
|
+
| Old constant | New constant | Old value | New value |
|
|
279
|
+
|---|---|---|---|
|
|
280
|
+
| `VIBE_HOME` | `OCAYA_HOME` | `~/.vibe` | `~/.ocaya` |
|
|
281
|
+
| `LOG_FILE` | `LOG_FILE` | `~/.vibe/logs/vibe.log` | `~/.ocaya/logs/ocaya.log` |
|
|
282
|
+
| `HISTORY_FILE` | `HISTORY_FILE` | `~/.vibe/vibehistory` | `~/.ocaya/ocayahistory` |
|
|
283
|
+
| `VIBE_ROOT` | `OCAYA_ROOT` | `Path(__file__).parent` | `Path(__file__).parent` |
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## System Prompt Design (`ocaya/core/prompts/ocaya.md`)
|
|
288
|
+
|
|
289
|
+
The `ocaya.md` file replaces `cli.md` as the default system prompt. It must cover all seven required areas. The structure below is the authoritative outline:
|
|
290
|
+
|
|
291
|
+
### Required Sections
|
|
292
|
+
|
|
293
|
+
**1. Identity**
|
|
294
|
+
```
|
|
295
|
+
You are OCAYA, a capable general-purpose AI assistant. Today's date is $current_date.
|
|
296
|
+
```
|
|
297
|
+
Must not contain "Mistral Vibe", "Jarvis", or "Hermes". Must contain "OCAYA".
|
|
298
|
+
|
|
299
|
+
**2. Capabilities**
|
|
300
|
+
Explicit list (per Requirements 5.5, 9.4):
|
|
301
|
+
- Web research and summarisation
|
|
302
|
+
- Document writing and editing
|
|
303
|
+
- File and folder management
|
|
304
|
+
- System automation and scripting
|
|
305
|
+
- Opening applications and URLs
|
|
306
|
+
- Clipboard operations
|
|
307
|
+
- General knowledge Q&A
|
|
308
|
+
|
|
309
|
+
This section must NOT restrict the agent to coding tasks (per Requirement 9.3).
|
|
310
|
+
|
|
311
|
+
**3. Web-First Instructions**
|
|
312
|
+
Explicit instruction (per Requirements 5.6, 6.1, 6.3):
|
|
313
|
+
- Before answering any query involving current events, live prices, external documentation, news, or user-provided URLs, invoke `websearch` or `webfetch` first — never rely on training-data memory alone for these categories.
|
|
314
|
+
- When a user message contains a URL, invoke `webfetch` with that URL before responding.
|
|
315
|
+
- Include at least one source citation (title + URL) whenever information was obtained via `websearch`.
|
|
316
|
+
|
|
317
|
+
**4. Action-First Principle**
|
|
318
|
+
Instruction (per Requirement 5.4):
|
|
319
|
+
- Take action first, explain after. Do not ask clarifying questions when the task is clear enough to attempt.
|
|
320
|
+
- For file operations, scripting, or software installation, use the `bash` tool to perform the operation rather than only describing it.
|
|
321
|
+
|
|
322
|
+
**5. Hard Limits**
|
|
323
|
+
Explicit prohibitions (per Requirement 5.7):
|
|
324
|
+
- Do not run `git commit`, `git push`, or any remote-push variant without explicit user instruction.
|
|
325
|
+
- Do not delete files without asking for confirmation.
|
|
326
|
+
- Do not send emails, post to social media, or initiate financial transactions without explicit user instruction.
|
|
327
|
+
- Before executing any shell command with blast radius outside the current machine, state the risk in one sentence and wait for confirmation.
|
|
328
|
+
|
|
329
|
+
**6. Tone**
|
|
330
|
+
Instruction (per Requirement 5.4):
|
|
331
|
+
- Be direct and warm. Skip filler phrases: no "Certainly", "Of course", "Happy to", "Great question".
|
|
332
|
+
- Keep responses focused. Code and structured output before prose.
|
|
333
|
+
|
|
334
|
+
**7. Source Citation**
|
|
335
|
+
Instruction (per Requirement 6.3):
|
|
336
|
+
- Whenever information was retrieved via `websearch`, include at least one inline citation with the source title and URL.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Correctness Properties
|
|
341
|
+
|
|
342
|
+
*A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
|
343
|
+
|
|
344
|
+
Most of this feature's changes are identity/rename operations verified by static assertions or example-based tests. Three areas yield genuine universal properties suitable for property-based testing.
|
|
345
|
+
|
|
346
|
+
### Property 1: OCAYA_HOME Path Resolution
|
|
347
|
+
|
|
348
|
+
*For any* non-empty string `p`, when the `OCAYA_HOME` environment variable is set to `p`, calling `_get_ocaya_home()` should return `Path(p).expanduser().resolve()` — that is, the path is always fully normalized regardless of the form of the input string (relative, absolute, `~`-prefixed, with redundant separators, etc.).
|
|
349
|
+
**Validates: Requirements 2.2**
|
|
350
|
+
|
|
351
|
+
### Property 2: Version String Prefix
|
|
352
|
+
|
|
353
|
+
*For any* valid version string `v` (e.g. `"2.14.0"`, `"3.0.0-alpha"`, `"1.0"`), the formatted version display string should start with `"OCAYA "` followed by `v`, regardless of the version value.
|
|
354
|
+
**Validates: Requirements 4.3**
|
|
355
|
+
|
|
356
|
+
### Property 3: OpenApp Rejects All Blank Targets
|
|
357
|
+
|
|
358
|
+
*For any* string composed entirely of whitespace characters (including the empty string, strings of spaces, tabs, newlines, or any combination), invoking `OpenApp.run` with that string as the `target` argument should raise a `ToolError` with the message `"target cannot be empty"`.
|
|
359
|
+
**Validates: Requirements 8.6**
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Error Handling
|
|
364
|
+
|
|
365
|
+
### Home Directory Resolution
|
|
366
|
+
|
|
367
|
+
- If `OCAYA_HOME` is set to an empty string: `os.getenv("OCAYA_HOME")` returns `""` which is falsy; the walrus operator branch is not taken and the default `~/.ocaya` is used. No warning is required for the empty-string case because the env var being unset is semantically equivalent.
|
|
368
|
+
- If `OCAYA_HOME` points to a non-existent but resolvable path: `Path(...).expanduser().resolve()` succeeds; the directory is created on first access by the setup layer.
|
|
369
|
+
- If `OCAYA_HOME` is set to an unresolvable path (e.g. contains null bytes): `Path()` construction raises `ValueError`; this propagates as a startup error with a message printed to stderr.
|
|
370
|
+
|
|
371
|
+
### Prompt Loading Fallback
|
|
372
|
+
|
|
373
|
+
`load_system_prompt` already implements a fallback chain: custom project/user prompt dirs → built-in enum prompt → fallback `.md` file in `PROMPTS_DIR`. If a user has a custom `system_prompt_id` that resolves to a missing file, `MissingPromptFileError` is raised. Requirement 5.8 specifies a softer fallback (fall back to `ocaya.md`, log warning, continue). This requires wrapping the `load_system_prompt` call in the config property:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
@property
|
|
377
|
+
def system_prompt(self) -> str:
|
|
378
|
+
try:
|
|
379
|
+
return load_system_prompt(self.system_prompt_id)
|
|
380
|
+
except MissingPromptFileError:
|
|
381
|
+
logger.warning(
|
|
382
|
+
"system_prompt_id=%s not found; falling back to ocaya",
|
|
383
|
+
self.system_prompt_id,
|
|
384
|
+
)
|
|
385
|
+
return load_system_prompt(SystemPrompt.OCAYA)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Tool Discovery After Rename
|
|
389
|
+
|
|
390
|
+
`ToolManager._compute_module_name` uses `parts.index("vibe")` to detect canonical module names for deduplication. After the package rename this must become `parts.index("ocaya")`. If it is left as `"vibe"`, every builtin tool gets a hash-based synthetic module name instead of its canonical `ocaya.core.tools.builtins.*` name, causing Pydantic class identity mismatches (two copies of `ClipboardArgs` etc.). This is a subtle correctness issue that must be fixed as part of the rename.
|
|
391
|
+
|
|
392
|
+
### Clipboard and OpenApp on Linux Without Desktop Utilities
|
|
393
|
+
|
|
394
|
+
Both tools have explicit `ToolError` paths for missing `xclip`/`xsel` (clipboard) and `xdg-open`/`gio` (open_app). These are already implemented in the drafted files — no additional error handling is needed. The error messages already name the missing utility and provide the `apt install` instruction as required by Requirements 7.4 and 8.7.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Testing Strategy
|
|
399
|
+
|
|
400
|
+
### Unit Tests (example-based)
|
|
401
|
+
|
|
402
|
+
Focus on concrete behavioral assertions:
|
|
403
|
+
|
|
404
|
+
- `test_ocaya_home_default`: assert `_get_ocaya_home()` with no env var returns `Path.home() / ".ocaya"`.
|
|
405
|
+
- `test_ocaya_config_defaults`: assert `OcayaConfig()` has `system_prompt_id == "ocaya"`.
|
|
406
|
+
- `test_system_prompt_loads_ocaya_md`: assert `OcayaConfig().system_prompt` contains `"OCAYA"` and does not contain `"Mistral Vibe"`.
|
|
407
|
+
- `test_system_prompt_fallback`: assert that a missing custom `system_prompt_id` falls back to `ocaya.md` and logs a warning.
|
|
408
|
+
- `test_clipboard_config_permission`: assert `ClipboardConfig().permission == ToolPermission.ASK`.
|
|
409
|
+
- `test_open_app_config_permission`: assert `OpenAppConfig().permission == ToolPermission.ASK`.
|
|
410
|
+
- `test_clipboard_read_empty`: mock `_read_clipboard` to return `""`; assert result is `ClipboardResult(action="read", text="")`.
|
|
411
|
+
- `test_open_app_linux_no_opener`: mock platform as `"linux"`, mock all subprocess calls to raise `FileNotFoundError`; assert `ToolError` with message naming `xdg-open`.
|
|
412
|
+
- `test_ocaya_md_contains_required_sections`: read `ocaya.md`; assert presence of keywords for each of the 7 required sections.
|
|
413
|
+
- `test_pyproject_no_legacy_branding`: parse `pyproject.toml`; assert name, description, keywords, scripts contain no `"mistral-vibe"`, `"Mistral Vibe"`, or `"vibe"` (as a standalone word in user-facing fields).
|
|
414
|
+
- `test_no_browser_automation_deps`: parse `pyproject.toml` dependencies; assert none match `playwright|browser-use|selenium|puppeteer|pyppeteer`.
|
|
415
|
+
|
|
416
|
+
### Property-Based Tests
|
|
417
|
+
|
|
418
|
+
Use `hypothesis` (already a transitive dev dependency via `pytest`). Each test runs minimum 100 examples.
|
|
419
|
+
|
|
420
|
+
**Property 1: OCAYA_HOME path resolution**
|
|
421
|
+
```python
|
|
422
|
+
# Feature: ocaya, Property 1: OCAYA_HOME path resolution
|
|
423
|
+
@given(st.text(min_size=1).filter(lambda s: s.strip()))
|
|
424
|
+
@settings(max_examples=100)
|
|
425
|
+
def test_ocaya_home_resolution(tmp_path, path_str):
|
|
426
|
+
resolved = Path(path_str).expanduser().resolve()
|
|
427
|
+
with mock.patch.dict(os.environ, {"OCAYA_HOME": path_str}):
|
|
428
|
+
assert _get_ocaya_home() == resolved
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Property 2: Version string prefix**
|
|
432
|
+
```python
|
|
433
|
+
# Feature: ocaya, Property 2: Version string prefix
|
|
434
|
+
@given(st.from_regex(r"[0-9]+\.[0-9]+\.[0-9]+(\.[a-z0-9]+)?", fullmatch=True))
|
|
435
|
+
@settings(max_examples=100)
|
|
436
|
+
def test_version_string_prefix(version_str):
|
|
437
|
+
result = format_version_string(version_str)
|
|
438
|
+
assert result.startswith("OCAYA ")
|
|
439
|
+
assert version_str in result
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Property 3: OpenApp rejects all blank targets**
|
|
443
|
+
```python
|
|
444
|
+
# Feature: ocaya, Property 3: OpenApp rejects all blank targets
|
|
445
|
+
@given(st.text(alphabet=st.characters(whitelist_categories=("Zs", "Cc"))))
|
|
446
|
+
@settings(max_examples=100)
|
|
447
|
+
async def test_open_app_blank_target_raises(whitespace_str):
|
|
448
|
+
tool = OpenApp.from_config(lambda: OpenAppConfig())
|
|
449
|
+
args = OpenAppArgs(target=whitespace_str)
|
|
450
|
+
with pytest.raises(ToolError, match="target cannot be empty"):
|
|
451
|
+
async for _ in tool.run(args):
|
|
452
|
+
pass
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Integration Tests
|
|
456
|
+
|
|
457
|
+
- Verify `ToolManager` discovers `clipboard` and `open_app` in `available_tools` after rename (real filesystem, no mocks).
|
|
458
|
+
- Verify the ACP server starts and returns a valid `initialized` response via `ocaya-acp`.
|
|
459
|
+
- Verify all built-in agent profiles load without error from `OcayaConfig` and produce correct tool permission sets.
|
|
460
|
+
- Verify session resume (`--resume`) works with sessions stored under `~/.ocaya`.
|
|
461
|
+
|
|
462
|
+
### Snapshot Tests
|
|
463
|
+
|
|
464
|
+
Existing `pytest-textual-snapshot` tests for TUI components are updated: expected snapshots are regenerated after branding changes to capture the new "OCAYA" labels.
|