code-context-control 2.91.0__tar.gz → 2.92.1__tar.gz
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.
- {code_context_control-2.91.0/code_context_control.egg-info → code_context_control-2.92.1}/PKG-INFO +1 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/_hook_utils.py +38 -0
- code_context_control-2.92.1/cli/_shell_writes.py +120 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/c3.py +257 -49
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/oracle.html +2 -2
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_c3_signal.py +5 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_dispatch.py +4 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_edit_ledger.py +86 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_edit_unlock.py +8 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_pretool_enforce.py +217 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_server.py +43 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/topbar.js +4 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/mcp_server.py +9 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/compress.py +2 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1/code_context_control.egg-info}/PKG-INFO +1 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/code_context_control.egg-info/SOURCES.txt +7 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/oracle_server.py +15 -2
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/oracle_ui.html +31 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/local_session.py +72 -8
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/chat/send.js +6 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/core.js +39 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/header.js +13 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/pyproject.toml +1 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/claude_md.py +38 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/enforcement_policy.py +9 -3
- code_context_control-2.92.1/services/repo_shape.py +129 -0
- code_context_control-2.92.1/tests/test_failed_calls_do_not_unlock.py +124 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_codex_compat.py +2 -2
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_dispatch.py +4 -1
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_pretool_enforce.py +175 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_oracle_service_routes.py +56 -0
- code_context_control-2.92.1/tests/test_init_clear_preserves_user_content.py +236 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_session_bootstrap.py +69 -0
- code_context_control-2.92.1/tests/test_repo_shape.py +204 -0
- code_context_control-2.92.1/tests/test_shell_write_ledger.py +111 -0
- code_context_control-2.92.1/tests/test_shell_writes.py +84 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/LICENSE +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/README.md +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/commands/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/commands/common.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/commands/parser.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/docs.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/edits.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/access.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/credentials.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/index.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/masking.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/shared.css +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/tools.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/guide/workflow.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_artifact.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_c3read.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_filter.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_read.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/hub_ci.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/hub_ui.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/progress.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/server.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/_grants.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/agent.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/ci.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/delegate.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/edit.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/edit_verify.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/edits.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/federate.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/filter.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/impact.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/jira.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/locks.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/memory.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/override.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/project.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/read.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/search.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/session.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/shell.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/status.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/tasks.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/tools/validate.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/api.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/app.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/access.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/icons.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/shared.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui/theme.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui_legacy.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/cli/ui_nano.html +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/core/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/core/config.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/core/ide.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/core/mcp_toml.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/core/web_security.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/config.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/chat_poll.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/mobile_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/activity.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/agents.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/app.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/busy.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/insights.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/projects.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/settings.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/access_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/access_telemetry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/activity_log.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/agent_base.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/agent_locks.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/agents.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/artifact_defs.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/artifact_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/auto_memory.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/background_service.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/bench/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/bitbucket_client.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_act.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_cache.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_expr.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_failures.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_github.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_impact.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_intel.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_runner.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ci_workflow.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/circuit_breaker.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/compressor.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/context_snapshot.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/conversation_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/cred_telemetry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/credential_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/doc_index.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/e2e_tasks.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/edit_ledger.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/embedding_index.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/error_reporting.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/file_memory.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/git_context.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/hub_service.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/indexer.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/jira_client.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/jira_cloud.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/jira_credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/jira_data_center.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/jira_links.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/mask_activation.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/mask_mirror.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/mask_presets.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory_consolidator.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory_distiller.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory_graph.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory_grounder.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory_queue.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/memory_scorer.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/metrics.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/notifications.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ollama_bridge.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ollama_client.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/ollama_credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/oracle_service.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/output_filter.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/override_grants.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/override_policy.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/override_requests.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/override_wake.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/parser.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/project_manager.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/project_runtime.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/protocol.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/proxy_state.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/repo_map.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/retention.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/retrieval_broker.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/router.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/runtime.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/scanner.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/session_benchmark.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/session_manager.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/session_preloader.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/subprojects.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/task_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/telemetry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/text_index.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/time_tracker.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/tool_classifier.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/transcript_index.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/validation_cache.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/vector_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/watcher.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/services/win_subprocess.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/setup.cfg +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_access_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_background_service.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_chat_poll.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_act.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_cache.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_expr.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_impact.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_intel.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_runner.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ci_workflow.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_cred_telemetry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_credential_login_kind.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_credential_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_credential_ui_parity.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_edit_ledger_deadline.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_edit_verify.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_state.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_init_scan.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_client.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_links.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_memory_system.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mobile_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mobile_extras.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mobile_override_routes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_mobile_security_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_health_probe.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_output_filter.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_override_grants.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_override_grants_mcp.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_override_requests.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_override_wake.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_permissions.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_pm_api.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_project_manager.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_project_tool.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_related_facts.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_repo_map.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_retention.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_review_digest.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_service_durability.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_session_budget.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_subprojects.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_task_store.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_task_tool.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_validate.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_version_sync.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_web_security.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/backend.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/main.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/__init__.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/index_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/init_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/search_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/session_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/stats.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.91.0 → code_context_control-2.92.1}/tui/theme.tcss +0 -0
{code_context_control-2.91.0/code_context_control.egg-info → code_context_control-2.92.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.92.1
|
|
4
4
|
Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -7,6 +7,7 @@ All hook reads/writes of enforcement state MUST go through this module.
|
|
|
7
7
|
"""
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
+
import re
|
|
10
11
|
import sys
|
|
11
12
|
import traceback
|
|
12
13
|
from datetime import datetime, timezone
|
|
@@ -440,6 +441,43 @@ def get_tool_output(data: dict) -> tuple:
|
|
|
440
441
|
return resp if isinstance(resp, str) else "", False
|
|
441
442
|
|
|
442
443
|
|
|
444
|
+
# A c3 tool that failed did not read, search or edit anything, so its
|
|
445
|
+
# completion is not evidence that c3 was used (field report 2026-08-22,
|
|
446
|
+
# ISSUE-3: c3_compress on a missing path -> "Error: File not found" -> the
|
|
447
|
+
# sticky unlock and the signal both fired -> native Write allowed). Tools
|
|
448
|
+
# report failure as a leading "Error" / "[<tool>:error]" line, the masked-path
|
|
449
|
+
# refusal tag, or an MCP isError flag.
|
|
450
|
+
_FAILED_RESPONSE_RE = re.compile(
|
|
451
|
+
r"^\s*(?:Error\b|\[[a-z0-9_\-]+:error\]|\[c3-mask:unsupported\])", re.IGNORECASE)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def response_text_failed(text) -> bool:
|
|
455
|
+
"""True when a c3 tool's response text reads as a failure."""
|
|
456
|
+
return bool(_FAILED_RESPONSE_RE.match(str(text or "")))
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def tool_response_failed(data: dict) -> bool:
|
|
460
|
+
"""True when the PostToolUse payload carries a failed c3 tool response.
|
|
461
|
+
|
|
462
|
+
Handles Claude Code's plain string, an MCP-style dict with ``isError`` /
|
|
463
|
+
``content`` parts, a bare list of parts, and Gemini's ``llmContent``.
|
|
464
|
+
"""
|
|
465
|
+
resp = data.get("tool_response")
|
|
466
|
+
if isinstance(resp, dict):
|
|
467
|
+
if resp.get("isError") is True or resp.get("is_error") is True:
|
|
468
|
+
return True
|
|
469
|
+
content = resp.get("content")
|
|
470
|
+
if isinstance(content, list):
|
|
471
|
+
text = "\n".join((p.get("text") or "") for p in content if isinstance(p, dict))
|
|
472
|
+
return response_text_failed(text)
|
|
473
|
+
text, _ = get_tool_output(data)
|
|
474
|
+
return response_text_failed(text)
|
|
475
|
+
if isinstance(resp, list):
|
|
476
|
+
text = "\n".join((p.get("text") or "") if isinstance(p, dict) else str(p) for p in resp)
|
|
477
|
+
return response_text_failed(text)
|
|
478
|
+
return response_text_failed(resp if isinstance(resp, str) else "")
|
|
479
|
+
|
|
480
|
+
|
|
443
481
|
def get_tool_input_path(data: dict) -> str:
|
|
444
482
|
"""Extract file path from tool_input, handling Claude (file_path),
|
|
445
483
|
Gemini (path), and NotebookEdit (notebook_path)."""
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Best-effort extraction of the files a shell command writes.
|
|
2
|
+
|
|
3
|
+
Field report 2026-08-22, ISSUE-1's buried finding: ``Bash`` sits in the
|
|
4
|
+
PreToolUse matcher, but tool discipline never looked at what a shell
|
|
5
|
+
command does to files. An inline ``python -c`` write, ``cat > f <<EOF`` and
|
|
6
|
+
``sed -i`` were never nudged toward c3_edit and never reached the ledger,
|
|
7
|
+
so every agent that met a blocked ``Write`` simply went round it. This
|
|
8
|
+
module is the one place that answers "which files does this command
|
|
9
|
+
probably write?" for both the PreToolUse nudge (hook_pretool_enforce) and
|
|
10
|
+
the after-the-fact ledger entry (hook_edit_ledger).
|
|
11
|
+
|
|
12
|
+
Deliberately conservative, like the access guard's shell scan: a missed
|
|
13
|
+
target costs one missing advisory line and one missing ledger row; a false
|
|
14
|
+
target costs a misleading hint, so tokens that are shell syntax, streams
|
|
15
|
+
(``/dev/null``, ``&1``) or pip-style ``>=`` version specs are skipped.
|
|
16
|
+
Paths resolve against the directory the command runs in, following ``cd``.
|
|
17
|
+
Never raises.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
_SEGMENT_SPLIT = re.compile(r"&&|\|\||[;\n]")
|
|
26
|
+
_CD_RE = re.compile(r"""^\s*cd\s+(?:/d\s+)?(?P<dir>"[^"]+"|'[^']+'|\S+)\s*$""", re.IGNORECASE)
|
|
27
|
+
# `>` / `>>` / `>|` preceded by whitespace or line start (so `foo>=3.0` is not a redirect).
|
|
28
|
+
_REDIRECT_RE = re.compile(r"""(?:^|\s)\d?>{1,2}\|?\s*(?P<t>"[^"]+"|'[^']+'|\S+)""")
|
|
29
|
+
_TEE_RE = re.compile(r"""(?:^|\|)\s*tee\s+(?:-[a-zA-Z]+\s+)*(?P<t>"[^"]+"|'[^']+'|\S+)""")
|
|
30
|
+
_SED_INPLACE_RE = re.compile(r"""(?:^|\s)sed\s+(?P<rest>.*)$""")
|
|
31
|
+
_PY_OPEN_RE = re.compile(r"""open\(\s*(?P<q>['"])(?P<t>[^'"]+)(?P=q)\s*,\s*['"][wax]""")
|
|
32
|
+
_PY_WRITE_TEXT_RE = re.compile(r"""Path\(\s*(?P<q>['"])(?P<t>[^'"]+)(?P=q)\s*\)\s*\.write_(?:text|bytes)\(""")
|
|
33
|
+
_FILE_CMDS = {"cp", "mv", "touch", "rm", "install"}
|
|
34
|
+
_STREAMS = {"/dev/null", "/dev/stdout", "/dev/stderr", "nul", "&1", "&2", "-"}
|
|
35
|
+
_SYNTAX = set("$`(){}|<>*?")
|
|
36
|
+
MAX_TARGETS = 20
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _clean(tok: str) -> str:
|
|
40
|
+
return tok.strip().strip("\"'`;,")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _skip(tok: str) -> bool:
|
|
44
|
+
if not tok or tok.startswith("-") or tok.startswith("=") or tok in _STREAMS:
|
|
45
|
+
return True
|
|
46
|
+
if tok.lower() in _STREAMS or tok.startswith("&"):
|
|
47
|
+
return True
|
|
48
|
+
return bool(_SYNTAX & set(tok))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _segment_targets(segment: str) -> list[str]:
|
|
52
|
+
out: list[str] = []
|
|
53
|
+
for m in _REDIRECT_RE.finditer(segment):
|
|
54
|
+
out.append(_clean(m.group("t")))
|
|
55
|
+
for m in _TEE_RE.finditer(segment):
|
|
56
|
+
out.append(_clean(m.group("t")))
|
|
57
|
+
for m in _PY_OPEN_RE.finditer(segment):
|
|
58
|
+
out.append(m.group("t"))
|
|
59
|
+
for m in _PY_WRITE_TEXT_RE.finditer(segment):
|
|
60
|
+
out.append(m.group("t"))
|
|
61
|
+
words = [w for w in re.split(r"\s+", segment.strip()) if w]
|
|
62
|
+
if words:
|
|
63
|
+
head = words[0].rsplit("/", 1)[-1].lower()
|
|
64
|
+
args = [_clean(w) for w in words[1:] if not w.startswith("-")]
|
|
65
|
+
if head in ("cp", "mv", "install") and len(args) >= 2:
|
|
66
|
+
out.append(args[-1])
|
|
67
|
+
elif head in ("touch", "rm") and args:
|
|
68
|
+
out.extend(args)
|
|
69
|
+
elif head == "sed":
|
|
70
|
+
flags = [w for w in words[1:] if w.startswith("-")]
|
|
71
|
+
if any(f.startswith("-i") or f == "--in-place" for f in flags):
|
|
72
|
+
# last non-flag, non-script argument(s): the files
|
|
73
|
+
files = [a for a in args if not (a.startswith("s/") or a.startswith("s|") or "/" in a and a.count("/") >= 2 and a.startswith("s"))]
|
|
74
|
+
out.extend(files[-2:] if files else [])
|
|
75
|
+
return [t for t in out if not _skip(t)]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _cd_target(segment: str, cwd: str) -> str | None:
|
|
79
|
+
m = _CD_RE.match(segment)
|
|
80
|
+
if not m:
|
|
81
|
+
return None
|
|
82
|
+
target = m.group("dir").strip("\"'")
|
|
83
|
+
try:
|
|
84
|
+
return target if os.path.isabs(target) else os.path.normpath(os.path.join(cwd, target))
|
|
85
|
+
except (OSError, ValueError):
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def shell_write_targets(cmd: str, cwd: str) -> list[str]:
|
|
90
|
+
"""Absolute paths the command probably writes, in order, de-duplicated.
|
|
91
|
+
|
|
92
|
+
``cwd`` is the directory the command runs in (the project root for
|
|
93
|
+
Claude Code hooks). Relative targets resolve against it, and a ``cd``
|
|
94
|
+
segment moves it for the segments that follow.
|
|
95
|
+
"""
|
|
96
|
+
found: list[str] = []
|
|
97
|
+
seen: set[str] = set()
|
|
98
|
+
here = cwd
|
|
99
|
+
try:
|
|
100
|
+
for segment in _SEGMENT_SPLIT.split(cmd or ""):
|
|
101
|
+
for tok in _segment_targets(segment):
|
|
102
|
+
if re.match(r"^/[a-z]/", tok): # MSYS /c/foo -> C:/foo
|
|
103
|
+
tok = f"{tok[1]}:{tok[2:]}"
|
|
104
|
+
try:
|
|
105
|
+
p = Path(tok) if os.path.isabs(tok) else Path(here) / tok
|
|
106
|
+
key = os.path.normcase(os.path.normpath(str(p)))
|
|
107
|
+
except (OSError, ValueError):
|
|
108
|
+
continue
|
|
109
|
+
if key in seen:
|
|
110
|
+
continue
|
|
111
|
+
seen.add(key)
|
|
112
|
+
found.append(str(p))
|
|
113
|
+
if len(found) >= MAX_TARGETS:
|
|
114
|
+
return found
|
|
115
|
+
moved = _cd_target(segment, here)
|
|
116
|
+
if moved is not None:
|
|
117
|
+
here = moved
|
|
118
|
+
except Exception:
|
|
119
|
+
return found
|
|
120
|
+
return found
|
|
@@ -92,7 +92,7 @@ console = Console() if HAS_RICH else None
|
|
|
92
92
|
# Config
|
|
93
93
|
CONFIG_DIR = ".c3"
|
|
94
94
|
CONFIG_FILE = ".c3/config.json"
|
|
95
|
-
__version__ = "2.
|
|
95
|
+
__version__ = "2.92.1"
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def _compress_file_cli(compressor, path, mode="smart", **kw):
|
|
@@ -845,7 +845,8 @@ def _prompt_memory_llm(project_path: str) -> None:
|
|
|
845
845
|
+ ("cloud + local fallback." if section["cloud_enabled"] else "local only (private)."))
|
|
846
846
|
|
|
847
847
|
|
|
848
|
-
def _prompt_init_steps(project_path: str, ide_name: str, default_mode: str = "direct"
|
|
848
|
+
def _prompt_init_steps(project_path: str, ide_name: str, default_mode: str = "direct",
|
|
849
|
+
shape=None) -> tuple[str, bool]:
|
|
849
850
|
"""Run guided post-init setup steps for Git and MCP."""
|
|
850
851
|
chosen_ide = _select_init_ide(ide_name or "auto")
|
|
851
852
|
save_config({"ide": chosen_ide}, project_path)
|
|
@@ -916,7 +917,7 @@ def _prompt_init_steps(project_path: str, ide_name: str, default_mode: str = "di
|
|
|
916
917
|
)
|
|
917
918
|
include_wildcard = bool(wildcard_choice and wildcard_choice.startswith("Yes"))
|
|
918
919
|
|
|
919
|
-
chosen_enforcement = _prompt_enforcement(chosen_tier)
|
|
920
|
+
chosen_enforcement = _prompt_enforcement(chosen_tier, shape=shape)
|
|
920
921
|
|
|
921
922
|
_run_install_mcp(project_path, chosen_ide, mcp_mode=mcp_mode,
|
|
922
923
|
permissions=chosen_tier, include_mcp_wildcard=include_wildcard,
|
|
@@ -924,9 +925,70 @@ def _prompt_init_steps(project_path: str, ide_name: str, default_mode: str = "di
|
|
|
924
925
|
return chosen_ide, True
|
|
925
926
|
|
|
926
927
|
|
|
927
|
-
def
|
|
928
|
+
def _assess_repo_shape(project_path: str):
|
|
929
|
+
"""Measure the project (services.repo_shape). None when it cannot be measured."""
|
|
930
|
+
try:
|
|
931
|
+
from services import repo_shape
|
|
932
|
+
return repo_shape.assess(project_path)
|
|
933
|
+
except Exception:
|
|
934
|
+
_log.debug("repo shape assessment failed", exc_info=True)
|
|
935
|
+
return None
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
def _apply_repo_shape_default(project_path: str, explicit_enforcement: str | None = None,
|
|
939
|
+
shape=None) -> None:
|
|
940
|
+
"""Say what kind of project this is, and default a prose one to ``advisory``.
|
|
941
|
+
|
|
942
|
+
Field report 2026-08-22: a 636-file project, ~95% Markdown and .docx,
|
|
943
|
+
where every symbol-aware C3 tool had nothing to act on and strict
|
|
944
|
+
discipline was paid on every turn until C3 was uninstalled. The shape
|
|
945
|
+
is printed for every project; the default is written only for the prose
|
|
946
|
+
kind, only over a tier-derived or unset mode (never over ``c3 enforce``
|
|
947
|
+
or ``--enforcement``), with provenance ``set_by: repo-shape`` so
|
|
948
|
+
``c3 enforce`` shows where it came from, and with the way back printed.
|
|
949
|
+
"""
|
|
950
|
+
try:
|
|
951
|
+
from services import enforcement_policy as ep
|
|
952
|
+
from services import repo_shape
|
|
953
|
+
except Exception:
|
|
954
|
+
return
|
|
955
|
+
if shape is None:
|
|
956
|
+
shape = _assess_repo_shape(project_path)
|
|
957
|
+
if shape is None or shape.kind == repo_shape.KIND_EMPTY:
|
|
958
|
+
return
|
|
959
|
+
print(f"\n Repo shape: {shape.describe()}")
|
|
960
|
+
mode = repo_shape.recommended_mode(shape)
|
|
961
|
+
if not mode:
|
|
962
|
+
return
|
|
963
|
+
if explicit_enforcement:
|
|
964
|
+
print(f" Prose-heavy, but --enforcement {explicit_enforcement} was given — leaving it as set.")
|
|
965
|
+
return
|
|
966
|
+
current = ep.resolve(project_path)
|
|
967
|
+
if current.set_by == ep.SET_BY_USER:
|
|
968
|
+
print(f" Prose-heavy; tool discipline stays '{current.mode}' (your choice).")
|
|
969
|
+
return
|
|
970
|
+
if current.mode == mode:
|
|
971
|
+
print(f" Prose-heavy; tool discipline is already '{mode}'.")
|
|
972
|
+
return
|
|
973
|
+
try:
|
|
974
|
+
result = ep.set_mode(mode, project_path, set_by=ep.SET_BY_SHAPE, scope="project")
|
|
975
|
+
except ValueError:
|
|
976
|
+
return
|
|
977
|
+
if result.get("deferred"):
|
|
978
|
+
return
|
|
979
|
+
print(" [!] This is a documentation project: C3's symbol-aware tools (compress/read/impact/")
|
|
980
|
+
print(f" validate/CI) have little to act on here, so tool discipline is set to '{mode}' —")
|
|
981
|
+
print(" native Edit/Write are allowed with a nudge and the edit ledger still records them.")
|
|
982
|
+
print(" `c3 enforce strict` restores the hard block; `c3 enforce` shows the setting.")
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
def _prompt_enforcement(chosen_tier: str | None, shape=None) -> str | None:
|
|
928
986
|
"""Step 5/5 — tool discipline. Returns an explicit mode, or None to derive.
|
|
929
987
|
|
|
988
|
+
``shape`` (services.repo_shape.RepoShape) moves the suggested default to
|
|
989
|
+
``advisory`` for a documentation project and says why, so the user is
|
|
990
|
+
not asked to guess what C3's symbol tools will do with 600 Markdown files.
|
|
991
|
+
|
|
930
992
|
Asked separately from the permission tier because it answers a genuinely
|
|
931
993
|
different question: the tier controls what the IDE will let the agent call,
|
|
932
994
|
this controls how hard C3's own hook pushes it toward c3_* tools. Users hit
|
|
@@ -938,8 +1000,18 @@ def _prompt_enforcement(chosen_tier: str | None) -> str | None:
|
|
|
938
1000
|
return None
|
|
939
1001
|
|
|
940
1002
|
default_mode = ep.derive_from_tier(chosen_tier) if chosen_tier else ep.MODE_ADVISORY
|
|
1003
|
+
suggested_by = "Your permission tier suggests"
|
|
1004
|
+
try:
|
|
1005
|
+
from services import repo_shape
|
|
1006
|
+
shape_mode = repo_shape.recommended_mode(shape) if shape is not None else None
|
|
1007
|
+
except Exception:
|
|
1008
|
+
shape_mode = None
|
|
1009
|
+
if shape_mode:
|
|
1010
|
+
default_mode = shape_mode
|
|
1011
|
+
suggested_by = ("This is a documentation project — C3's symbol tools have "
|
|
1012
|
+
"little to act on here. Suggested")
|
|
941
1013
|
print()
|
|
942
|
-
print(f" (
|
|
1014
|
+
print(f" ({suggested_by}: {default_mode})")
|
|
943
1015
|
choice = _prompt_choice(
|
|
944
1016
|
"Step 5/5 — Tool discipline: how hard should C3 push toward c3_* tools?",
|
|
945
1017
|
[
|
|
@@ -1085,6 +1157,7 @@ def cmd_init(args):
|
|
|
1085
1157
|
if not c3_dir.exists() or not (c3_dir / "config.json").exists():
|
|
1086
1158
|
print_header(f"Initializing C3 for: {project_path}")
|
|
1087
1159
|
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
1160
|
+
shape = _assess_repo_shape(project_path)
|
|
1088
1161
|
try:
|
|
1089
1162
|
from services.project_manager import ProjectManager
|
|
1090
1163
|
ProjectManager().add_project(project_path)
|
|
@@ -1101,7 +1174,11 @@ def cmd_init(args):
|
|
|
1101
1174
|
enforcement=getattr(args, "enforcement", None),
|
|
1102
1175
|
)
|
|
1103
1176
|
else:
|
|
1104
|
-
_prompt_init_steps(project_path, requested_ide,
|
|
1177
|
+
_prompt_init_steps(project_path, requested_ide,
|
|
1178
|
+
default_mode=getattr(args, "mcp_mode", "direct"), shape=shape)
|
|
1179
|
+
_apply_repo_shape_default(project_path,
|
|
1180
|
+
explicit_enforcement=getattr(args, "enforcement", None),
|
|
1181
|
+
shape=shape)
|
|
1105
1182
|
# Detect Codex CLI availability
|
|
1106
1183
|
try:
|
|
1107
1184
|
from cli.tools.delegate import check_codex
|
|
@@ -1285,11 +1362,7 @@ def cmd_init(args):
|
|
|
1285
1362
|
if c3_dir.exists():
|
|
1286
1363
|
shutil.rmtree(c3_dir)
|
|
1287
1364
|
print(" Deleted .c3/")
|
|
1288
|
-
|
|
1289
|
-
p = Path(project_path) / filename
|
|
1290
|
-
if p.exists():
|
|
1291
|
-
p.unlink()
|
|
1292
|
-
print(f" Deleted {filename}")
|
|
1365
|
+
_remove_c3_instruction_docs(project_path)
|
|
1293
1366
|
print("\n[OK] C3 project files removed.")
|
|
1294
1367
|
return
|
|
1295
1368
|
|
|
@@ -1361,9 +1434,10 @@ def cmd_init(args):
|
|
|
1361
1434
|
|
|
1362
1435
|
elif selected.startswith("Wipe"):
|
|
1363
1436
|
confirm = input(
|
|
1364
|
-
"\n WARNING: This will permanently delete .c3
|
|
1365
|
-
"
|
|
1366
|
-
"
|
|
1437
|
+
"\n WARNING: This will permanently delete .c3/, remove C3's managed\n"
|
|
1438
|
+
" block from CLAUDE.md / AGENTS.md / GEMINI.md (a file is deleted only\n"
|
|
1439
|
+
" when nothing else is in it), and remove C3 MCP configurations from\n"
|
|
1440
|
+
" your IDE. Type 'yes' to confirm: "
|
|
1367
1441
|
).strip().lower()
|
|
1368
1442
|
if confirm != "yes":
|
|
1369
1443
|
print(" Wipe cancelled.")
|
|
@@ -1375,11 +1449,7 @@ def cmd_init(args):
|
|
|
1375
1449
|
shutil.rmtree(c3_dir)
|
|
1376
1450
|
print(" Deleted .c3/")
|
|
1377
1451
|
|
|
1378
|
-
|
|
1379
|
-
p = Path(project_path) / filename
|
|
1380
|
-
if p.exists():
|
|
1381
|
-
p.unlink()
|
|
1382
|
-
print(f" Deleted {filename}")
|
|
1452
|
+
_remove_c3_instruction_docs(project_path)
|
|
1383
1453
|
|
|
1384
1454
|
print("\n[OK] C3 project files removed.")
|
|
1385
1455
|
|
|
@@ -4555,6 +4625,115 @@ def _ensure_global_session_fallbacks(server_script: str, c3_mcp_exe: str | None
|
|
|
4555
4625
|
print(f"Warning: Could not update {antigravity_path} (global fallback skipped)")
|
|
4556
4626
|
|
|
4557
4627
|
|
|
4628
|
+
# Every hook script C3 has ever registered in a settings file — the v2.42+
|
|
4629
|
+
# dispatcher plus the pre-v2.42 per-hook scripts — so uninstall recognises an
|
|
4630
|
+
# entry written by any C3 version. A user hook is anything not naming one.
|
|
4631
|
+
_C3_HOOK_SCRIPTS = (
|
|
4632
|
+
"hook_dispatch.py",
|
|
4633
|
+
"hook_access_guard.py", "hook_artifact.py", "hook_auto_snapshot.py",
|
|
4634
|
+
"hook_c3_signal.py", "hook_c3read.py", "hook_edit_ledger.py",
|
|
4635
|
+
"hook_edit_unlock.py", "hook_filter.py", "hook_ghost_files.py",
|
|
4636
|
+
"hook_pretool_enforce.py", "hook_prompt_recall.py", "hook_read.py",
|
|
4637
|
+
"hook_session_stats.py", "hook_terse_advisor.py",
|
|
4638
|
+
)
|
|
4639
|
+
|
|
4640
|
+
|
|
4641
|
+
def _is_c3_hook_command(command) -> bool:
|
|
4642
|
+
return any(script in str(command or "") for script in _C3_HOOK_SCRIPTS)
|
|
4643
|
+
|
|
4644
|
+
|
|
4645
|
+
def _is_c3_permission(rule) -> bool:
|
|
4646
|
+
"""``mcp__c3__<tool>``, ``mcp__c3__*``, or the bare ``mcp__c3`` server grant."""
|
|
4647
|
+
r = str(rule or "").strip()
|
|
4648
|
+
return r == "mcp__c3" or r.startswith("mcp__c3__")
|
|
4649
|
+
|
|
4650
|
+
|
|
4651
|
+
def _c3_references_in_settings(settings: dict) -> list:
|
|
4652
|
+
"""Every C3-owned entry present in a settings dict: hook commands (named
|
|
4653
|
+
by event) and permission rules. Used to decide whether there is anything
|
|
4654
|
+
to remove and, after writing, to check that it was actually removed."""
|
|
4655
|
+
refs: list = []
|
|
4656
|
+
hooks = settings.get("hooks")
|
|
4657
|
+
if isinstance(hooks, dict):
|
|
4658
|
+
for event, groups in hooks.items():
|
|
4659
|
+
for group in groups or []:
|
|
4660
|
+
if not isinstance(group, dict):
|
|
4661
|
+
continue
|
|
4662
|
+
for hk in group.get("hooks") or []:
|
|
4663
|
+
if isinstance(hk, dict) and _is_c3_hook_command(hk.get("command")):
|
|
4664
|
+
refs.append(f"hooks.{event}")
|
|
4665
|
+
perms = settings.get("permissions")
|
|
4666
|
+
if isinstance(perms, dict):
|
|
4667
|
+
for key in ("allow", "deny", "ask"):
|
|
4668
|
+
for rule in perms.get(key) or []:
|
|
4669
|
+
if _is_c3_permission(rule):
|
|
4670
|
+
refs.append(f"permissions.{key}:{rule}")
|
|
4671
|
+
if "c3" in (settings.get("enabledMcpjsonServers") or []):
|
|
4672
|
+
refs.append("enabledMcpjsonServers:c3")
|
|
4673
|
+
return refs
|
|
4674
|
+
|
|
4675
|
+
|
|
4676
|
+
def _strip_c3_from_settings(settings: dict) -> dict:
|
|
4677
|
+
"""Remove every C3-owned entry from a Claude Code settings dict (in place,
|
|
4678
|
+
and returned). Touches ONLY what C3 wrote: hook entries whose command runs
|
|
4679
|
+
a C3 hook script — any event, any matcher; the previous cleanup looked at
|
|
4680
|
+
three PostToolUse matchers from the pre-v2.42 layout and nothing else, so
|
|
4681
|
+
PreToolUse/Stop/UserPromptSubmit and the whole ``permissions.allow`` list
|
|
4682
|
+
survived an "uninstall" — plus ``mcp__c3__*`` permission rules and the
|
|
4683
|
+
``c3`` MCP-server enablement. User hooks, user permission rules and every
|
|
4684
|
+
other key are left exactly as found; containers emptied by the removal
|
|
4685
|
+
are dropped so a file C3 created alone ends up empty (and the caller
|
|
4686
|
+
deletes it) rather than as ``{"hooks": {}}``.
|
|
4687
|
+
"""
|
|
4688
|
+
hooks = settings.get("hooks")
|
|
4689
|
+
if isinstance(hooks, dict):
|
|
4690
|
+
for event in list(hooks.keys()):
|
|
4691
|
+
kept_groups = []
|
|
4692
|
+
for group in hooks.get(event) or []:
|
|
4693
|
+
if not isinstance(group, dict):
|
|
4694
|
+
kept_groups.append(group)
|
|
4695
|
+
continue
|
|
4696
|
+
entries = group.get("hooks") or []
|
|
4697
|
+
if not any(isinstance(hk, dict) and _is_c3_hook_command(hk.get("command"))
|
|
4698
|
+
for hk in entries):
|
|
4699
|
+
kept_groups.append(group) # not ours — untouched
|
|
4700
|
+
continue
|
|
4701
|
+
kept = [hk for hk in entries
|
|
4702
|
+
if not (isinstance(hk, dict) and _is_c3_hook_command(hk.get("command")))]
|
|
4703
|
+
if kept:
|
|
4704
|
+
group = dict(group)
|
|
4705
|
+
group["hooks"] = kept
|
|
4706
|
+
kept_groups.append(group)
|
|
4707
|
+
if kept_groups:
|
|
4708
|
+
hooks[event] = kept_groups
|
|
4709
|
+
else:
|
|
4710
|
+
del hooks[event]
|
|
4711
|
+
if not hooks:
|
|
4712
|
+
del settings["hooks"]
|
|
4713
|
+
|
|
4714
|
+
perms = settings.get("permissions")
|
|
4715
|
+
if isinstance(perms, dict):
|
|
4716
|
+
for key in ("allow", "deny", "ask"):
|
|
4717
|
+
rules = perms.get(key)
|
|
4718
|
+
if isinstance(rules, list) and any(_is_c3_permission(r) for r in rules):
|
|
4719
|
+
kept_rules = [r for r in rules if not _is_c3_permission(r)]
|
|
4720
|
+
if kept_rules:
|
|
4721
|
+
perms[key] = kept_rules
|
|
4722
|
+
else:
|
|
4723
|
+
del perms[key]
|
|
4724
|
+
if not perms:
|
|
4725
|
+
del settings["permissions"]
|
|
4726
|
+
|
|
4727
|
+
enabled = settings.get("enabledMcpjsonServers")
|
|
4728
|
+
if isinstance(enabled, list) and "c3" in enabled:
|
|
4729
|
+
enabled = [s for s in enabled if s != "c3"]
|
|
4730
|
+
if enabled:
|
|
4731
|
+
settings["enabledMcpjsonServers"] = enabled
|
|
4732
|
+
else:
|
|
4733
|
+
del settings["enabledMcpjsonServers"]
|
|
4734
|
+
return settings
|
|
4735
|
+
|
|
4736
|
+
|
|
4558
4737
|
def _uninstall_mcp_all(project_path: str, include_global: bool = True):
|
|
4559
4738
|
"""Remove C3 MCP server configurations from all supported IDEs.
|
|
4560
4739
|
|
|
@@ -4658,37 +4837,28 @@ def _uninstall_mcp_all(project_path: str, include_global: bool = True):
|
|
|
4658
4837
|
with open(settings_path, 'r', encoding="utf-8") as f:
|
|
4659
4838
|
settings = json.load(f)
|
|
4660
4839
|
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
c3_hook_files = {"hook_filter.py", "hook_read.py", "hook_c3read.py", "hook_dispatch.py"}
|
|
4665
|
-
for h in hooks:
|
|
4666
|
-
if h.get("matcher") in ("Bash", "Read", "mcp__c3__c3_read"):
|
|
4667
|
-
h["hooks"] = [hook for hook in h.get("hooks", [])
|
|
4668
|
-
if not any(f in hook.get("command", "") for f in c3_hook_files)]
|
|
4669
|
-
if h["hooks"]:
|
|
4670
|
-
new_hooks.append(h)
|
|
4671
|
-
else:
|
|
4672
|
-
new_hooks.append(h)
|
|
4673
|
-
|
|
4674
|
-
if new_hooks:
|
|
4675
|
-
settings["hooks"]["PostToolUse"] = new_hooks
|
|
4676
|
-
elif "hooks" in settings and "PostToolUse" in settings["hooks"]:
|
|
4677
|
-
del settings["hooks"]["PostToolUse"]
|
|
4678
|
-
if not settings["hooks"]:
|
|
4679
|
-
del settings["hooks"]
|
|
4680
|
-
|
|
4681
|
-
# Remove enabled server
|
|
4682
|
-
if "enabledMcpjsonServers" in settings and "c3" in settings["enabledMcpjsonServers"]:
|
|
4683
|
-
settings["enabledMcpjsonServers"].remove("c3")
|
|
4684
|
-
|
|
4685
|
-
if not settings:
|
|
4686
|
-
settings_path.unlink()
|
|
4687
|
-
print(f" Deleted empty {settings_path}")
|
|
4840
|
+
before = _c3_references_in_settings(settings)
|
|
4841
|
+
if not before:
|
|
4842
|
+
print(f" No C3 entries in {settings_path}")
|
|
4688
4843
|
else:
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4844
|
+
settings = _strip_c3_from_settings(settings)
|
|
4845
|
+
if not settings:
|
|
4846
|
+
settings_path.unlink()
|
|
4847
|
+
print(f" Deleted empty {settings_path}")
|
|
4848
|
+
else:
|
|
4849
|
+
with open(settings_path, 'w', encoding="utf-8") as f:
|
|
4850
|
+
json.dump(settings, f, indent=2)
|
|
4851
|
+
# Postcondition: re-read what is on disk and report
|
|
4852
|
+
# what is true, not what was attempted.
|
|
4853
|
+
with open(settings_path, 'r', encoding="utf-8") as f:
|
|
4854
|
+
remaining = _c3_references_in_settings(json.load(f))
|
|
4855
|
+
if remaining:
|
|
4856
|
+
shown = ", ".join(remaining[:5]) + (" ..." if len(remaining) > 5 else "")
|
|
4857
|
+
print(f" [!] C3 entries remain in {settings_path} "
|
|
4858
|
+
f"({len(remaining)}): {shown}")
|
|
4859
|
+
else:
|
|
4860
|
+
print(f" Removed C3 hooks/settings from {settings_path} "
|
|
4861
|
+
f"({len(before)} entries)")
|
|
4692
4862
|
except Exception as e:
|
|
4693
4863
|
print(f" Warning: Could not update {settings_path}: {e}")
|
|
4694
4864
|
|
|
@@ -4872,6 +5042,44 @@ def _instruction_documents_for_project() -> list[tuple[str, str]]:
|
|
|
4872
5042
|
]
|
|
4873
5043
|
|
|
4874
5044
|
|
|
5045
|
+
def _remove_c3_instruction_docs(project_path: str) -> None:
|
|
5046
|
+
"""Take C3's managed block OUT of every instruction doc it ever wrote.
|
|
5047
|
+
|
|
5048
|
+
Uninstall counterpart of ``_sync_project_instruction_docs``. Before
|
|
5049
|
+
v2.92 this was a bare ``unlink()`` of CLAUDE.md / AGENTS.md / GEMINI.md —
|
|
5050
|
+
which, in a project whose CLAUDE.md also carried another tool's managed
|
|
5051
|
+
block (``<!-- YEP:BEGIN -->``) or the user's own notes, destroyed content
|
|
5052
|
+
C3 never owned, with no warning and no way back unless git had it.
|
|
5053
|
+
Same rule every regeneration path already follows: C3 owns its slice,
|
|
5054
|
+
everything outside the markers is preserved. A file is deleted only when
|
|
5055
|
+
the block was all there was.
|
|
5056
|
+
"""
|
|
5057
|
+
from services.claude_md import strip_c3_block
|
|
5058
|
+
|
|
5059
|
+
for filename, _ in _instruction_documents_for_project():
|
|
5060
|
+
p = Path(project_path) / filename
|
|
5061
|
+
if not p.exists():
|
|
5062
|
+
continue
|
|
5063
|
+
try:
|
|
5064
|
+
existing = p.read_text(encoding="utf-8", errors="replace")
|
|
5065
|
+
except OSError as exc:
|
|
5066
|
+
print(f" Warning: Could not read {filename}: {exc}")
|
|
5067
|
+
continue
|
|
5068
|
+
remainder = strip_c3_block(existing)
|
|
5069
|
+
if remainder is None:
|
|
5070
|
+
print(f" Kept {filename} (no C3 block in it — not ours to delete)")
|
|
5071
|
+
elif remainder.strip():
|
|
5072
|
+
try:
|
|
5073
|
+
p.write_text(remainder, encoding="utf-8")
|
|
5074
|
+
except OSError as exc:
|
|
5075
|
+
print(f" Warning: Could not update {filename}: {exc}")
|
|
5076
|
+
continue
|
|
5077
|
+
print(f" Removed C3 block from {filename} (kept the rest of the file)")
|
|
5078
|
+
else:
|
|
5079
|
+
p.unlink()
|
|
5080
|
+
print(f" Deleted {filename}")
|
|
5081
|
+
|
|
5082
|
+
|
|
4875
5083
|
_LEGACY_INSTRUCTION_DOCS = ("GEMINI.md",) # Gemini CLI profile removed in v2.52
|
|
4876
5084
|
|
|
4877
5085
|
|
|
@@ -330,10 +330,10 @@ curl -X POST \
|
|
|
330
330
|
<tr><td><code>c3 oracle serve --install</code></td> <td>Register the Oracle as a login/startup service — no terminal window — and start it now. Windows: Task Scheduler (Run-key fallback); macOS: launchd; Linux: systemd user unit</td></tr>
|
|
331
331
|
<tr><td><code>c3 oracle serve --uninstall</code></td> <td>Remove the startup registration (a running Oracle keeps running)</td></tr>
|
|
332
332
|
<tr><td><code>c3 oracle serve --status</code></td> <td>Installed? Running? Port, bind host, URL, and the launcher log path</td></tr>
|
|
333
|
-
<tr><td><code>c3 oracle open</code></td> <td>Sign in to a running dashboard via a single-use URL</td></tr>
|
|
333
|
+
<tr><td><code>c3 oracle open</code></td> <td>Sign in to a running dashboard via a single-use URL (the hub's <strong>Open Oracle</strong> button does the same thing)</td></tr>
|
|
334
334
|
</tbody>
|
|
335
335
|
</table>
|
|
336
|
-
<p style="font-size: 12px; color: var(--text-dim); margin: 8px 0 0;">The Hub exposes the same controls under <strong>Settings → Oracle service</strong> (Install / Uninstall / Start / Stop / Open). Starting the Oracle from there also fills in the Hub's <em>Oracle URL</em> when it is empty, so the Open Oracle button appears in the top bar
|
|
336
|
+
<p style="font-size: 12px; color: var(--text-dim); margin: 8px 0 0;">The Hub exposes the same controls under <strong>Settings → Oracle service</strong> (Install / Uninstall / Start / Stop / Open). Starting the Oracle from there also fills in the Hub's <em>Oracle URL</em> when it is empty, so the Open Oracle button appears in the top bar. That button opens a <strong>signed-in</strong> dashboard: it redeems the Oracle's owner-only bootstrap key for you. Reaching the dashboard by pasting its URL instead leaves it read-only — the page renders and the header looks healthy, but chat, Save and Test Ollama answer <code>401 unauthorized</code>.</p>
|
|
337
337
|
</section>
|
|
338
338
|
|
|
339
339
|
<hr class="divider">
|
|
@@ -26,7 +26,7 @@ from pathlib import Path
|
|
|
26
26
|
|
|
27
27
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
28
28
|
|
|
29
|
-
from cli._hook_utils import log_hook_error, record_c3_signal # noqa: E402
|
|
29
|
+
from cli._hook_utils import log_hook_error, record_c3_signal, tool_response_failed # noqa: E402
|
|
30
30
|
|
|
31
31
|
# Tools that unlock generic read operations (Grep/Glob without a file path)
|
|
32
32
|
_READ_UNLOCK_TOOLS = {"c3_search", "c3_compress", "c3_filter", "c3_read", "c3_impact", "c3_validate"}
|
|
@@ -45,6 +45,10 @@ def run(payload: dict, project_path: Path | None = None):
|
|
|
45
45
|
if not short_name.startswith("c3_"):
|
|
46
46
|
return None
|
|
47
47
|
|
|
48
|
+
# A failed call read nothing and edited nothing: no signal (ISSUE-3).
|
|
49
|
+
if tool_response_failed(payload):
|
|
50
|
+
return None
|
|
51
|
+
|
|
48
52
|
record_c3_signal(
|
|
49
53
|
short_name,
|
|
50
54
|
short_name in _READ_UNLOCK_TOOLS,
|
|
@@ -152,6 +152,10 @@ def _routes(event: str, raw_tool: str, norm_tool: str, host: str = HOST_CLAUDE):
|
|
|
152
152
|
yield "hook_artifact"
|
|
153
153
|
if raw_tool in _GHOST_TOOLS:
|
|
154
154
|
yield "hook_ghost_files"
|
|
155
|
+
# Shell file writes land in the ledger after the fact (after the
|
|
156
|
+
# ghost sweep, so a 0-byte redirect artifact is never logged).
|
|
157
|
+
if norm_tool == "Bash":
|
|
158
|
+
yield "hook_edit_ledger"
|
|
155
159
|
elif event == "stop":
|
|
156
160
|
yield "hook_session_stats"
|
|
157
161
|
yield "hook_auto_snapshot"
|