code-context-control 2.79.0__tar.gz → 2.80.0__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.79.0/code_context_control.egg-info → code_context_control-2.80.0}/PKG-INFO +1 -1
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/c3.py +6 -4
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/commands/parser.py +3 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/mcp_server.py +7 -3
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/ci.py +4 -3
- {code_context_control-2.79.0 → code_context_control-2.80.0/code_context_control.egg-info}/PKG-INFO +1 -1
- {code_context_control-2.79.0 → code_context_control-2.80.0}/code_context_control.egg-info/SOURCES.txt +2 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/pyproject.toml +1 -1
- code_context_control-2.80.0/services/ci_expr.py +435 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/ci_runner.py +139 -10
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/ci_workflow.py +72 -17
- code_context_control-2.80.0/tests/test_ci_expr.py +340 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/LICENSE +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/README.md +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/commands/common.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/docs.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/edits.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/access.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/credentials.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/index.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/masking.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_read.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_server.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/hub_ci.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/progress.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/server.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/_grants.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/delegate.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/edit_verify.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/jira.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/locks.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/override.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/project.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/read.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/search.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/session.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/status.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/tasks.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/api.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/app.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/access.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/core/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/core/config.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/core/ide.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/core/web_security.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/config.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/oracle_server.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/chat_poll.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/mobile_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/app.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/settings.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/access_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/access_telemetry.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/activity_log.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/agent_base.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/agent_locks.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/agents.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/artifact_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/auto_memory.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/ci_failures.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/claude_md.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/compressor.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/conversation_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/credential_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/doc_index.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/embedding_index.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/enforcement_policy.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/error_reporting.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/file_memory.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/git_context.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/hub_service.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/indexer.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/jira_client.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/jira_cloud.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/jira_credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/jira_data_center.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/jira_links.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/mask_activation.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/mask_mirror.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/mask_presets.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory_graph.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory_queue.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/metrics.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/notifications.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/ollama_client.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/output_filter.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/override_grants.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/override_policy.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/override_requests.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/override_wake.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/parser.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/project_manager.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/project_runtime.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/protocol.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/proxy_state.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/repo_map.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/retention.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/router.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/runtime.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/scanner.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/session_manager.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/session_preloader.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/subprojects.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/task_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/telemetry.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/text_index.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/time_tracker.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/transcript_index.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/validation_cache.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/vector_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/watcher.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/setup.cfg +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_access_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_chat_poll.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_ci_runner.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_ci_workflow.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_credential_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_edit_ledger_deadline.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_edit_verify.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_codex_compat.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_init_scan.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_client.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_links.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mobile_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mobile_extras.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mobile_override_routes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_mobile_security_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_override_grants.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_override_grants_mcp.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_override_requests.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_override_wake.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_pm_api.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_repo_map.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_retention.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_task_store.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_task_tool.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_validate.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_version_sync.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/backend.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/main.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.79.0 → code_context_control-2.80.0}/tui/theme.tcss +0 -0
{code_context_control-2.79.0/code_context_control.egg-info → code_context_control-2.80.0}/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.80.0
|
|
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
|
|
@@ -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.80.0"
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def _compress_file_cli(compressor, path, mode="smart", **kw):
|
|
@@ -6065,7 +6065,7 @@ def _ci_main(args) -> int:
|
|
|
6065
6065
|
from services import ci_runner as cr
|
|
6066
6066
|
from services.ci_workflow import inspect_project
|
|
6067
6067
|
if sub == "inspect":
|
|
6068
|
-
payload = inspect_project(project_path)
|
|
6068
|
+
payload = inspect_project(project_path, event=getattr(args, "event", "") or "")
|
|
6069
6069
|
elif sub in ("run", "rerun"):
|
|
6070
6070
|
only = None
|
|
6071
6071
|
if sub == "rerun":
|
|
@@ -6078,7 +6078,8 @@ def _ci_main(args) -> int:
|
|
|
6078
6078
|
project_path, selector=getattr(args, "job", ""),
|
|
6079
6079
|
allow_foreign=bool(getattr(args, "allow_foreign", False)),
|
|
6080
6080
|
only=only, timeout=getattr(args, "timeout", 0) or cr.DEFAULT_STEP_TIMEOUT,
|
|
6081
|
-
workflow=getattr(args, "workflow", "")
|
|
6081
|
+
workflow=getattr(args, "workflow", ""),
|
|
6082
|
+
event=getattr(args, "event", "") or "").to_dict()
|
|
6082
6083
|
elif sub == "runs":
|
|
6083
6084
|
payload = {"runs": cr.list_runs(project_path)}
|
|
6084
6085
|
else:
|
|
@@ -6096,7 +6097,8 @@ def _ci_main(args) -> int:
|
|
|
6096
6097
|
sub, getattr(args, "job", "") or "", getattr(args, "run_id", "") or "",
|
|
6097
6098
|
bool(getattr(args, "allow_foreign", False)),
|
|
6098
6099
|
getattr(args, "workflow", "") or "", getattr(args, "tail", 200) or 200,
|
|
6099
|
-
getattr(args, "timeout", 0) or 0, svc, finalize
|
|
6100
|
+
getattr(args, "timeout", 0) or 0, svc, finalize,
|
|
6101
|
+
getattr(args, "event", "") or "")
|
|
6100
6102
|
print(out)
|
|
6101
6103
|
# Exit code is the finding: a non-full verdict must not read as success to
|
|
6102
6104
|
# a shell script or a pre-push hook.
|
|
@@ -483,6 +483,9 @@ def build_parser(version: str, parse_cli_ide_arg):
|
|
|
483
483
|
sub.add_argument("--path", dest="project_path", default=".",
|
|
484
484
|
help="Project directory (default: current)")
|
|
485
485
|
sub.add_argument("--json", action="store_true", help="Machine-readable output")
|
|
486
|
+
sub.add_argument("--event", default="",
|
|
487
|
+
help="GitHub event to simulate (push, pull_request) — "
|
|
488
|
+
"only needed when an `if:` reads github.event_name")
|
|
486
489
|
return sub
|
|
487
490
|
|
|
488
491
|
_ci_common(ci_subs.add_parser(
|
|
@@ -769,7 +769,8 @@ async def c3_locks(action: str = "list", paths: str = "", intent: str = "",
|
|
|
769
769
|
@mcp.tool()
|
|
770
770
|
async def c3_ci(action: str = "inspect", job: str = "", run_id: str = "",
|
|
771
771
|
allow_foreign: bool = False, workflow: str = "",
|
|
772
|
-
tail: int = 200, timeout: int = 0,
|
|
772
|
+
tail: int = 200, timeout: int = 0, event: str = "",
|
|
773
|
+
ctx: Context = None) -> str:
|
|
773
774
|
"""RUN THIS REPO'S REAL CI LOCALLY — before pushing, not after.
|
|
774
775
|
Reads .github/workflows/*.yml as the source of truth (no second CI config).
|
|
775
776
|
actions: inspect (default) | run | rerun | status | failures | logs | runs.
|
|
@@ -783,7 +784,10 @@ async def c3_ci(action: str = "inspect", job: str = "", run_id: str = "",
|
|
|
783
784
|
that means "safe to push". PARTIAL_PASS means something did not run
|
|
784
785
|
(different OS, unsupported action, or you selected a subset); it is NOT a
|
|
785
786
|
green light. Jobs targeting another OS are refused unless allow_foreign=true,
|
|
786
|
-
which runs them and labels the result cross-OS.
|
|
787
|
+
which runs them and labels the result cross-OS.
|
|
788
|
+
event: declare which GitHub event you are simulating ('push',
|
|
789
|
+
'pull_request'). Needed only when an `if:` reads github.event_name —
|
|
790
|
+
there is no event locally, so C3 refuses to guess one."""
|
|
787
791
|
svc = _svc(ctx)
|
|
788
792
|
|
|
789
793
|
def finalize(name, args, resp, summ, **kw):
|
|
@@ -792,7 +796,7 @@ async def c3_ci(action: str = "inspect", job: str = "", run_id: str = "",
|
|
|
792
796
|
from cli.tools.ci import handle_ci
|
|
793
797
|
return await asyncio.to_thread(handle_ci, action, job, run_id,
|
|
794
798
|
allow_foreign, workflow, tail, timeout,
|
|
795
|
-
svc, finalize)
|
|
799
|
+
svc, finalize, event)
|
|
796
800
|
|
|
797
801
|
|
|
798
802
|
@mcp.tool()
|
|
@@ -74,7 +74,8 @@ def _failure_rows(jobs: list, limit: int = 25) -> list:
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def handle_ci(action: str, job: str, run_id: str, allow_foreign: bool,
|
|
77
|
-
workflow: str, tail: int, timeout: int, svc, finalize
|
|
77
|
+
workflow: str, tail: int, timeout: int, svc, finalize,
|
|
78
|
+
event: str = "") -> str:
|
|
78
79
|
"""Route c3_ci actions."""
|
|
79
80
|
project = str(svc.project_path)
|
|
80
81
|
action = (action or "inspect").strip().lower()
|
|
@@ -84,7 +85,7 @@ def handle_ci(action: str, job: str, run_id: str, allow_foreign: bool,
|
|
|
84
85
|
|
|
85
86
|
# ── inspect ──────────────────────────────────────────────────────────
|
|
86
87
|
if action in ("", "inspect", "jobs"):
|
|
87
|
-
data = inspect_project(project)
|
|
88
|
+
data = inspect_project(project, event=event)
|
|
88
89
|
if not data["workflows"]:
|
|
89
90
|
return finalize(
|
|
90
91
|
"c3_ci", args,
|
|
@@ -150,7 +151,7 @@ def handle_ci(action: str, job: str, run_id: str, allow_foreign: bool,
|
|
|
150
151
|
|
|
151
152
|
result = cr.run_ci(project, selector=job, allow_foreign=allow_foreign,
|
|
152
153
|
only=only, timeout=timeout or cr.DEFAULT_STEP_TIMEOUT,
|
|
153
|
-
workflow=workflow)
|
|
154
|
+
workflow=workflow, event=event)
|
|
154
155
|
run = result.to_dict()
|
|
155
156
|
lines = [_verdict_line(run), f"run {run['run_id']} host={run['host_os']}"]
|
|
156
157
|
fp = run.get("fingerprint") or {}
|
{code_context_control-2.79.0 → code_context_control-2.80.0/code_context_control.egg-info}/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.80.0
|
|
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
|
|
@@ -185,6 +185,7 @@ services/auto_memory.py
|
|
|
185
185
|
services/benchmark_dashboard.py
|
|
186
186
|
services/bitbucket_client.py
|
|
187
187
|
services/bitbucket_credentials.py
|
|
188
|
+
services/ci_expr.py
|
|
188
189
|
services/ci_failures.py
|
|
189
190
|
services/ci_runner.py
|
|
190
191
|
services/ci_workflow.py
|
|
@@ -280,6 +281,7 @@ tests/test_bitbucket_tool.py
|
|
|
280
281
|
tests/test_c3_bridge_project_artifacts.py
|
|
281
282
|
tests/test_c3_shell.py
|
|
282
283
|
tests/test_chat_poll.py
|
|
284
|
+
tests/test_ci_expr.py
|
|
283
285
|
tests/test_ci_runner.py
|
|
284
286
|
tests/test_ci_workflow.py
|
|
285
287
|
tests/test_circuit_breaker.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code-context-control"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.80.0"
|
|
8
8
|
description = "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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""AgentCI — a GitHub Actions expression evaluator, for `if:` conditions.
|
|
2
|
+
|
|
3
|
+
Before this existed, `if:` was parsed and ignored, so every guarded step ran
|
|
4
|
+
locally whether or not CI would have run it. That over-runs rather than
|
|
5
|
+
under-runs — it cannot manufacture a false green — but it wastes time and
|
|
6
|
+
reports failures for steps that were never going to execute.
|
|
7
|
+
|
|
8
|
+
Three rules keep the evaluator honest, and they are the whole design:
|
|
9
|
+
|
|
10
|
+
1. **An expression we cannot parse blocks the job.** Not "assume true" (which
|
|
11
|
+
silently runs steps CI skips) and not "assume false" (which silently skips
|
|
12
|
+
steps CI runs). Both are guesses; a blocker is a fact.
|
|
13
|
+
|
|
14
|
+
2. **A reference we cannot resolve blocks the job.** `github.event_name` has no
|
|
15
|
+
honest local value — there is no event. Rather than inventing `"push"` and
|
|
16
|
+
evaluating a condition against fiction, an unknown reference raises, and the
|
|
17
|
+
caller can pass `--event pull_request` to state what it is simulating.
|
|
18
|
+
|
|
19
|
+
3. **Status functions are runtime, not parse-time.** `success()` depends on
|
|
20
|
+
what already ran, so validation happens when the DAG is built and evaluation
|
|
21
|
+
happens in the runner, with a context that knows results.
|
|
22
|
+
|
|
23
|
+
Grammar implemented (the subset that appears in real workflows):
|
|
24
|
+
|
|
25
|
+
expr := or
|
|
26
|
+
or := and ( '||' and )*
|
|
27
|
+
and := cmp ( '&&' cmp )*
|
|
28
|
+
cmp := unary ( ('=='|'!='|'<'|'<='|'>'|'>=') unary )?
|
|
29
|
+
unary := '!' unary | primary
|
|
30
|
+
primary := literal | path | call | '(' expr ')'
|
|
31
|
+
|
|
32
|
+
Functions: success, failure, always, cancelled, contains, startsWith,
|
|
33
|
+
endsWith, format, join, fromJSON, toJSON.
|
|
34
|
+
"""
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import json
|
|
38
|
+
import re
|
|
39
|
+
from dataclasses import dataclass, field
|
|
40
|
+
|
|
41
|
+
# Context roots we can populate. A reference to anything else — `secrets`,
|
|
42
|
+
# `vars`, `inputs` — is unresolvable locally and must block rather than
|
|
43
|
+
# evaluate to empty, which is how a job passes here and fails in CI.
|
|
44
|
+
KNOWN_ROOTS = {"github", "env", "matrix", "runner", "job", "needs", "steps",
|
|
45
|
+
"strategy"}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ExprError(ValueError):
|
|
49
|
+
"""The expression could not be parsed."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class UnknownRef(ValueError):
|
|
53
|
+
"""The expression referenced something we cannot honestly resolve."""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ── Tokenizer ───────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
_TOKEN_RE = re.compile(r"""
|
|
59
|
+
(?P<ws>\s+)
|
|
60
|
+
| (?P<str>'(?:[^']|'')*')
|
|
61
|
+
| (?P<num>-?\d+(?:\.\d+)?)
|
|
62
|
+
| (?P<op>==|!=|<=|>=|&&|\|\||[<>!(),.\[\]])
|
|
63
|
+
| (?P<ident>[A-Za-z_][A-Za-z0-9_-]*)
|
|
64
|
+
""", re.VERBOSE)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class Token:
|
|
69
|
+
kind: str
|
|
70
|
+
value: str
|
|
71
|
+
pos: int
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def tokenize(src: str) -> list:
|
|
75
|
+
tokens: list = []
|
|
76
|
+
i = 0
|
|
77
|
+
while i < len(src):
|
|
78
|
+
m = _TOKEN_RE.match(src, i)
|
|
79
|
+
if not m:
|
|
80
|
+
raise ExprError(f"unexpected character {src[i]!r} at {i}")
|
|
81
|
+
i = m.end()
|
|
82
|
+
kind = m.lastgroup
|
|
83
|
+
if kind == "ws":
|
|
84
|
+
continue
|
|
85
|
+
tokens.append(Token(kind, m.group(), m.start()))
|
|
86
|
+
return tokens
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ── Parser → a tiny AST of tuples ───────────────────────────────────────────
|
|
90
|
+
# ("lit", value) ("path", [parts]) ("call", name, [args])
|
|
91
|
+
# ("not", node) ("bin", op, left, right)
|
|
92
|
+
|
|
93
|
+
class _Parser:
|
|
94
|
+
def __init__(self, tokens: list):
|
|
95
|
+
self.tokens = tokens
|
|
96
|
+
self.i = 0
|
|
97
|
+
|
|
98
|
+
def peek(self):
|
|
99
|
+
return self.tokens[self.i] if self.i < len(self.tokens) else None
|
|
100
|
+
|
|
101
|
+
def take(self, value: str = ""):
|
|
102
|
+
tok = self.peek()
|
|
103
|
+
if tok is None:
|
|
104
|
+
raise ExprError("unexpected end of expression")
|
|
105
|
+
if value and tok.value != value:
|
|
106
|
+
raise ExprError(f"expected {value!r} but found {tok.value!r}")
|
|
107
|
+
self.i += 1
|
|
108
|
+
return tok
|
|
109
|
+
|
|
110
|
+
def parse(self):
|
|
111
|
+
node = self.parse_or()
|
|
112
|
+
if self.peek() is not None:
|
|
113
|
+
raise ExprError(f"trailing input at {self.peek().value!r}")
|
|
114
|
+
return node
|
|
115
|
+
|
|
116
|
+
def parse_or(self):
|
|
117
|
+
node = self.parse_and()
|
|
118
|
+
while self.peek() and self.peek().value == "||":
|
|
119
|
+
self.take()
|
|
120
|
+
node = ("bin", "||", node, self.parse_and())
|
|
121
|
+
return node
|
|
122
|
+
|
|
123
|
+
def parse_and(self):
|
|
124
|
+
node = self.parse_cmp()
|
|
125
|
+
while self.peek() and self.peek().value == "&&":
|
|
126
|
+
self.take()
|
|
127
|
+
node = ("bin", "&&", node, self.parse_cmp())
|
|
128
|
+
return node
|
|
129
|
+
|
|
130
|
+
def parse_cmp(self):
|
|
131
|
+
node = self.parse_unary()
|
|
132
|
+
tok = self.peek()
|
|
133
|
+
if tok and tok.value in ("==", "!=", "<", "<=", ">", ">="):
|
|
134
|
+
self.take()
|
|
135
|
+
node = ("bin", tok.value, node, self.parse_unary())
|
|
136
|
+
return node
|
|
137
|
+
|
|
138
|
+
def parse_unary(self):
|
|
139
|
+
tok = self.peek()
|
|
140
|
+
if tok and tok.value == "!":
|
|
141
|
+
self.take()
|
|
142
|
+
return ("not", self.parse_unary())
|
|
143
|
+
return self.parse_primary()
|
|
144
|
+
|
|
145
|
+
def parse_primary(self):
|
|
146
|
+
tok = self.peek()
|
|
147
|
+
if tok is None:
|
|
148
|
+
raise ExprError("unexpected end of expression")
|
|
149
|
+
|
|
150
|
+
if tok.value == "(":
|
|
151
|
+
self.take("(")
|
|
152
|
+
node = self.parse_or()
|
|
153
|
+
self.take(")")
|
|
154
|
+
return node
|
|
155
|
+
|
|
156
|
+
if tok.kind == "str":
|
|
157
|
+
self.take()
|
|
158
|
+
# GitHub escapes a single quote by doubling it.
|
|
159
|
+
return ("lit", tok.value[1:-1].replace("''", "'"))
|
|
160
|
+
|
|
161
|
+
if tok.kind == "num":
|
|
162
|
+
self.take()
|
|
163
|
+
text = tok.value
|
|
164
|
+
return ("lit", float(text) if "." in text else int(text))
|
|
165
|
+
|
|
166
|
+
if tok.kind == "ident":
|
|
167
|
+
self.take()
|
|
168
|
+
low = tok.value.lower()
|
|
169
|
+
if low in ("true", "false"):
|
|
170
|
+
return ("lit", low == "true")
|
|
171
|
+
if low == "null":
|
|
172
|
+
return ("lit", None)
|
|
173
|
+
nxt = self.peek()
|
|
174
|
+
if nxt and nxt.value == "(":
|
|
175
|
+
return self.parse_call(tok.value)
|
|
176
|
+
return self.parse_path(tok.value)
|
|
177
|
+
|
|
178
|
+
raise ExprError(f"unexpected token {tok.value!r}")
|
|
179
|
+
|
|
180
|
+
def parse_call(self, name: str):
|
|
181
|
+
self.take("(")
|
|
182
|
+
args: list = []
|
|
183
|
+
if self.peek() and self.peek().value != ")":
|
|
184
|
+
args.append(self.parse_or())
|
|
185
|
+
while self.peek() and self.peek().value == ",":
|
|
186
|
+
self.take(",")
|
|
187
|
+
args.append(self.parse_or())
|
|
188
|
+
self.take(")")
|
|
189
|
+
return ("call", name.lower(), args)
|
|
190
|
+
|
|
191
|
+
def parse_path(self, root: str):
|
|
192
|
+
parts = [root]
|
|
193
|
+
while self.peek() and self.peek().value in (".", "["):
|
|
194
|
+
tok = self.take()
|
|
195
|
+
if tok.value == ".":
|
|
196
|
+
nxt = self.take()
|
|
197
|
+
if nxt.kind not in ("ident", "num"):
|
|
198
|
+
raise ExprError(f"bad property name {nxt.value!r}")
|
|
199
|
+
parts.append(nxt.value)
|
|
200
|
+
else:
|
|
201
|
+
inner = self.parse_or()
|
|
202
|
+
self.take("]")
|
|
203
|
+
if inner[0] != "lit":
|
|
204
|
+
raise ExprError("only literal index access is supported")
|
|
205
|
+
parts.append(str(inner[1]))
|
|
206
|
+
return ("path", parts)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def parse(src: str):
|
|
210
|
+
return _Parser(tokenize(src)).parse()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ── Evaluation ──────────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
@dataclass
|
|
216
|
+
class EvalContext:
|
|
217
|
+
"""Everything an `if:` may legitimately read, locally.
|
|
218
|
+
|
|
219
|
+
`statuses` carries the runtime facts that make status functions meaningful:
|
|
220
|
+
whether anything has failed so far in the relevant scope.
|
|
221
|
+
"""
|
|
222
|
+
values: dict = field(default_factory=dict) # github/env/matrix/... roots
|
|
223
|
+
failed: bool = False # something upstream failed
|
|
224
|
+
cancelled: bool = False # never true locally
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _truthy(value) -> bool:
|
|
228
|
+
if isinstance(value, bool):
|
|
229
|
+
return value
|
|
230
|
+
if value is None:
|
|
231
|
+
return False
|
|
232
|
+
if isinstance(value, (int, float)):
|
|
233
|
+
return value != 0
|
|
234
|
+
if isinstance(value, str):
|
|
235
|
+
return value != ""
|
|
236
|
+
if isinstance(value, (list, dict)):
|
|
237
|
+
return True
|
|
238
|
+
return bool(value)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _coerce_pair(left, right) -> tuple:
|
|
242
|
+
"""GitHub compares loosely. Numbers stay numbers; otherwise compare as
|
|
243
|
+
lower-cased strings, which is what `== 'Push'` vs `'push'` relies on."""
|
|
244
|
+
if isinstance(left, bool) or isinstance(right, bool):
|
|
245
|
+
return _truthy(left), _truthy(right)
|
|
246
|
+
if isinstance(left, (int, float)) and isinstance(right, (int, float)):
|
|
247
|
+
return left, right
|
|
248
|
+
return (str(left).lower() if left is not None else "",
|
|
249
|
+
str(right).lower() if right is not None else "")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _resolve(parts: list, ctx: EvalContext):
|
|
253
|
+
root = parts[0]
|
|
254
|
+
if root not in KNOWN_ROOTS:
|
|
255
|
+
raise UnknownRef(
|
|
256
|
+
f"`{'.'.join(parts)}` — the `{root}` context is not available "
|
|
257
|
+
"locally, so this condition cannot be evaluated honestly")
|
|
258
|
+
value = ctx.values.get(root)
|
|
259
|
+
if value is None:
|
|
260
|
+
raise UnknownRef(f"`{root}` context is not populated for this run")
|
|
261
|
+
for part in parts[1:]:
|
|
262
|
+
if isinstance(value, dict):
|
|
263
|
+
if part not in value:
|
|
264
|
+
raise UnknownRef(
|
|
265
|
+
f"`{'.'.join(parts)}` — `{part}` is unknown locally. "
|
|
266
|
+
"If this is the event, pass the event you are simulating.")
|
|
267
|
+
value = value[part]
|
|
268
|
+
else:
|
|
269
|
+
raise UnknownRef(f"`{'.'.join(parts)}` — `{part}` is not a mapping")
|
|
270
|
+
return value
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _call(name: str, args: list, ctx: EvalContext):
|
|
274
|
+
if name == "success":
|
|
275
|
+
return not ctx.failed and not ctx.cancelled
|
|
276
|
+
if name == "failure":
|
|
277
|
+
return ctx.failed and not ctx.cancelled
|
|
278
|
+
if name == "always":
|
|
279
|
+
return True
|
|
280
|
+
if name == "cancelled":
|
|
281
|
+
return ctx.cancelled
|
|
282
|
+
|
|
283
|
+
if name == "contains":
|
|
284
|
+
haystack, needle = args[0], args[1]
|
|
285
|
+
if isinstance(haystack, list):
|
|
286
|
+
return any(_coerce_pair(item, needle)[0] == _coerce_pair(item, needle)[1]
|
|
287
|
+
for item in haystack)
|
|
288
|
+
return str(needle).lower() in str(haystack).lower()
|
|
289
|
+
if name == "startswith":
|
|
290
|
+
return str(args[0]).lower().startswith(str(args[1]).lower())
|
|
291
|
+
if name == "endswith":
|
|
292
|
+
return str(args[0]).lower().endswith(str(args[1]).lower())
|
|
293
|
+
if name == "format":
|
|
294
|
+
out = str(args[0])
|
|
295
|
+
for idx, val in enumerate(args[1:]):
|
|
296
|
+
out = out.replace("{" + str(idx) + "}", str(val))
|
|
297
|
+
return out
|
|
298
|
+
if name == "join":
|
|
299
|
+
items = args[0] if isinstance(args[0], list) else [args[0]]
|
|
300
|
+
sep = str(args[1]) if len(args) > 1 else ","
|
|
301
|
+
return sep.join(str(i) for i in items)
|
|
302
|
+
if name == "tojson":
|
|
303
|
+
return json.dumps(args[0])
|
|
304
|
+
if name == "fromjson":
|
|
305
|
+
try:
|
|
306
|
+
return json.loads(str(args[0]))
|
|
307
|
+
except json.JSONDecodeError as exc:
|
|
308
|
+
raise ExprError(f"fromJSON: {exc}") from exc
|
|
309
|
+
|
|
310
|
+
raise ExprError(f"unsupported function `{name}()`")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _eval(node, ctx: EvalContext):
|
|
314
|
+
kind = node[0]
|
|
315
|
+
if kind == "lit":
|
|
316
|
+
return node[1]
|
|
317
|
+
if kind == "path":
|
|
318
|
+
return _resolve(node[1], ctx)
|
|
319
|
+
if kind == "call":
|
|
320
|
+
return _call(node[1], [_eval(a, ctx) for a in node[2]], ctx)
|
|
321
|
+
if kind == "not":
|
|
322
|
+
return not _truthy(_eval(node[1], ctx))
|
|
323
|
+
if kind == "bin":
|
|
324
|
+
op = node[1]
|
|
325
|
+
# Short-circuit, so `x && y.z` does not blow up on an unknown y.z when
|
|
326
|
+
# x is already false — same as GitHub, and it keeps honest conditions
|
|
327
|
+
# evaluable even when a branch references something we lack.
|
|
328
|
+
if op == "&&":
|
|
329
|
+
left = _eval(node[2], ctx)
|
|
330
|
+
return _eval(node[3], ctx) if _truthy(left) else left
|
|
331
|
+
if op == "||":
|
|
332
|
+
left = _eval(node[2], ctx)
|
|
333
|
+
return left if _truthy(left) else _eval(node[3], ctx)
|
|
334
|
+
left, right = _eval(node[2], ctx), _eval(node[3], ctx)
|
|
335
|
+
a, b = _coerce_pair(left, right)
|
|
336
|
+
if op == "==":
|
|
337
|
+
return a == b
|
|
338
|
+
if op == "!=":
|
|
339
|
+
return a != b
|
|
340
|
+
try:
|
|
341
|
+
if op == "<":
|
|
342
|
+
return a < b
|
|
343
|
+
if op == "<=":
|
|
344
|
+
return a <= b
|
|
345
|
+
if op == ">":
|
|
346
|
+
return a > b
|
|
347
|
+
if op == ">=":
|
|
348
|
+
return a >= b
|
|
349
|
+
except TypeError as exc:
|
|
350
|
+
raise ExprError(f"cannot compare {left!r} and {right!r}") from exc
|
|
351
|
+
raise ExprError(f"cannot evaluate node {node!r}")
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# ── Public surface ──────────────────────────────────────────────────────────
|
|
355
|
+
|
|
356
|
+
_STATUS_FNS = ("success(", "failure(", "always(", "cancelled(")
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def normalize(src: str) -> str:
|
|
360
|
+
"""An `if:` value is already an expression; `${{ }}` around it is optional.
|
|
361
|
+
|
|
362
|
+
GitHub also implies `success() &&` unless the condition names a status
|
|
363
|
+
function itself, which is why `if: always()` is the documented way to run a
|
|
364
|
+
step after a failure.
|
|
365
|
+
"""
|
|
366
|
+
text = str(src or "").strip()
|
|
367
|
+
if text.startswith("${{") and text.endswith("}}"):
|
|
368
|
+
text = text[3:-2].strip()
|
|
369
|
+
if not text:
|
|
370
|
+
return ""
|
|
371
|
+
if not any(fn in text.replace(" ", "").lower() for fn in _STATUS_FNS):
|
|
372
|
+
text = f"success() && ({text})"
|
|
373
|
+
return text
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def validate(src: str, github_fields: set = None) -> str:
|
|
377
|
+
"""Parse-check an `if:` at DAG-build time. Returns '' or a blocker reason.
|
|
378
|
+
|
|
379
|
+
Checks syntax, reference roots, and — because it is knowable now — which
|
|
380
|
+
`github.*` fields we can actually supply. Values that legitimately vary at
|
|
381
|
+
runtime (`needs.*.result`, `steps.*`) are accepted here and resolved later.
|
|
382
|
+
|
|
383
|
+
`github.event_name` is the field this exists for: there is no event
|
|
384
|
+
locally, so a condition on it is unjudgeable until the caller declares
|
|
385
|
+
which event they are simulating.
|
|
386
|
+
"""
|
|
387
|
+
text = normalize(src)
|
|
388
|
+
if not text:
|
|
389
|
+
return ""
|
|
390
|
+
try:
|
|
391
|
+
node = parse(text)
|
|
392
|
+
except ExprError as exc:
|
|
393
|
+
return f"cannot parse `if:` condition ({exc})"
|
|
394
|
+
|
|
395
|
+
bad_roots: list = []
|
|
396
|
+
bad_fields: list = []
|
|
397
|
+
_collect_refs(node, bad_roots, bad_fields, github_fields or set())
|
|
398
|
+
if bad_roots:
|
|
399
|
+
return (f"`if:` condition reads the `{sorted(set(bad_roots))[0]}` "
|
|
400
|
+
"context, which is not available locally")
|
|
401
|
+
if bad_fields:
|
|
402
|
+
field = sorted(set(bad_fields))[0]
|
|
403
|
+
hint = (" — pass the event you are simulating (e.g. --event push)"
|
|
404
|
+
if field == "event_name" else "")
|
|
405
|
+
return (f"`if:` condition reads `github.{field}`, which has no honest "
|
|
406
|
+
f"local value{hint}")
|
|
407
|
+
return ""
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _collect_refs(node, bad_roots: list, bad_fields: list,
|
|
411
|
+
github_fields: set) -> None:
|
|
412
|
+
kind = node[0]
|
|
413
|
+
if kind == "path":
|
|
414
|
+
parts = node[1]
|
|
415
|
+
if parts[0] not in KNOWN_ROOTS:
|
|
416
|
+
bad_roots.append(parts[0])
|
|
417
|
+
elif parts[0] == "github" and len(parts) > 1:
|
|
418
|
+
if parts[1] not in github_fields:
|
|
419
|
+
bad_fields.append(parts[1])
|
|
420
|
+
elif kind == "call":
|
|
421
|
+
for arg in node[2]:
|
|
422
|
+
_collect_refs(arg, bad_roots, bad_fields, github_fields)
|
|
423
|
+
elif kind == "not":
|
|
424
|
+
_collect_refs(node[1], bad_roots, bad_fields, github_fields)
|
|
425
|
+
elif kind == "bin":
|
|
426
|
+
_collect_refs(node[2], bad_roots, bad_fields, github_fields)
|
|
427
|
+
_collect_refs(node[3], bad_roots, bad_fields, github_fields)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def evaluate(src: str, ctx: EvalContext) -> bool:
|
|
431
|
+
"""True when the guarded job/step should run. Raises on an honest unknown."""
|
|
432
|
+
text = normalize(src)
|
|
433
|
+
if not text:
|
|
434
|
+
return True
|
|
435
|
+
return _truthy(_eval(parse(text), ctx))
|