code-context-control 2.80.0__tar.gz → 2.81.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.
Files changed (429) hide show
  1. {code_context_control-2.80.0/code_context_control.egg-info → code_context_control-2.81.0}/PKG-INFO +2 -2
  2. {code_context_control-2.80.0 → code_context_control-2.81.0}/README.md +1 -1
  3. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/c3.py +9 -3
  4. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/commands/parser.py +12 -0
  5. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_server.py +11 -4
  6. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/hub_ci.js +23 -7
  7. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/mcp_server.py +5 -3
  8. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/ci.py +40 -4
  9. {code_context_control-2.80.0 → code_context_control-2.81.0/code_context_control.egg-info}/PKG-INFO +2 -2
  10. {code_context_control-2.80.0 → code_context_control-2.81.0}/code_context_control.egg-info/SOURCES.txt +2 -0
  11. {code_context_control-2.80.0 → code_context_control-2.81.0}/pyproject.toml +1 -1
  12. code_context_control-2.81.0/services/ci_act.py +274 -0
  13. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ci_runner.py +160 -13
  14. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ci_workflow.py +58 -17
  15. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/claude_md.py +1 -1
  16. code_context_control-2.81.0/tests/test_ci_act.py +418 -0
  17. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ci_runner.py +10 -3
  18. {code_context_control-2.80.0 → code_context_control-2.81.0}/LICENSE +0 -0
  19. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/__init__.py +0 -0
  20. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/_hook_utils.py +0 -0
  21. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/commands/__init__.py +0 -0
  22. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/commands/common.py +0 -0
  23. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/docs.html +0 -0
  24. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/edits.html +0 -0
  25. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/access.html +0 -0
  26. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/bitbucket.html +0 -0
  27. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/credentials.html +0 -0
  28. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/getting-started.html +0 -0
  29. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/index.html +0 -0
  30. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/masking.html +0 -0
  31. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/oracle.html +0 -0
  32. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/shared.css +0 -0
  33. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/tools.html +0 -0
  34. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/guide/workflow.html +0 -0
  35. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_access_guard.py +0 -0
  36. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_artifact.py +0 -0
  37. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_auto_snapshot.py +0 -0
  38. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_c3_signal.py +0 -0
  39. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_c3read.py +0 -0
  40. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_dispatch.py +0 -0
  41. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_edit_ledger.py +0 -0
  42. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_edit_unlock.py +0 -0
  43. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_filter.py +0 -0
  44. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_ghost_files.py +0 -0
  45. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_pretool_enforce.py +0 -0
  46. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_prompt_recall.py +0 -0
  47. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_read.py +0 -0
  48. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_session_stats.py +0 -0
  49. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hook_terse_advisor.py +0 -0
  50. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/app.js +0 -0
  51. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/add_project.js +0 -0
  52. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/config_editor.js +0 -0
  53. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  54. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/drill_health.js +0 -0
  55. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/drill_panel.js +0 -0
  56. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  57. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  58. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/drill_views.js +0 -0
  59. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/global_search.js +0 -0
  60. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  61. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  62. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/hub_locks.js +0 -0
  63. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  64. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/modals.js +0 -0
  65. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/project_card.js +0 -0
  66. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/project_tree.js +0 -0
  67. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/session_drawer.js +0 -0
  68. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/settings_modal.js +0 -0
  69. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/sidebar.js +0 -0
  70. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/summary_bar.js +0 -0
  71. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/task_board.js +0 -0
  72. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/toasts.js +0 -0
  73. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/components/topbar.js +0 -0
  74. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui/state.js +0 -0
  75. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/hub_ui.html +0 -0
  76. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/mcp_proxy.py +0 -0
  77. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/progress.py +0 -0
  78. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/server.py +0 -0
  79. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/__init__.py +0 -0
  80. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/_grants.py +0 -0
  81. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/_helpers.py +0 -0
  82. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/agent.py +0 -0
  83. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/artifacts.py +0 -0
  84. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/bitbucket.py +0 -0
  85. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/compress.py +0 -0
  86. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/credentials.py +0 -0
  87. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/delegate.py +0 -0
  88. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/edit.py +0 -0
  89. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/edit_verify.py +0 -0
  90. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/edits.py +0 -0
  91. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/federate.py +0 -0
  92. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/filter.py +0 -0
  93. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/impact.py +0 -0
  94. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/jira.py +0 -0
  95. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/locks.py +0 -0
  96. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/memory.py +0 -0
  97. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/override.py +0 -0
  98. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/project.py +0 -0
  99. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/read.py +0 -0
  100. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/search.py +0 -0
  101. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/session.py +0 -0
  102. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/shell.py +0 -0
  103. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/status.py +0 -0
  104. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/tasks.py +0 -0
  105. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/tools/validate.py +0 -0
  106. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/api.js +0 -0
  107. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/app.js +0 -0
  108. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/access.js +0 -0
  109. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/bitbucket.js +0 -0
  110. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/chat.js +0 -0
  111. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/credentials.js +0 -0
  112. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/dashboard.js +0 -0
  113. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/edits.js +0 -0
  114. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/enforcement.js +0 -0
  115. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/instructions.js +0 -0
  116. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/jira.js +0 -0
  117. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/memory.js +0 -0
  118. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/sessions.js +0 -0
  119. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/settings.js +0 -0
  120. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/sidebar.js +0 -0
  121. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/components/tasks.js +0 -0
  122. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/icons.js +0 -0
  123. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/pm_shared.js +0 -0
  124. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/shared.js +0 -0
  125. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui/theme.js +0 -0
  126. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui.html +0 -0
  127. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui_legacy.html +0 -0
  128. {code_context_control-2.80.0 → code_context_control-2.81.0}/cli/ui_nano.html +0 -0
  129. {code_context_control-2.80.0 → code_context_control-2.81.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  130. {code_context_control-2.80.0 → code_context_control-2.81.0}/code_context_control.egg-info/entry_points.txt +0 -0
  131. {code_context_control-2.80.0 → code_context_control-2.81.0}/code_context_control.egg-info/requires.txt +0 -0
  132. {code_context_control-2.80.0 → code_context_control-2.81.0}/code_context_control.egg-info/top_level.txt +0 -0
  133. {code_context_control-2.80.0 → code_context_control-2.81.0}/core/__init__.py +0 -0
  134. {code_context_control-2.80.0 → code_context_control-2.81.0}/core/config.py +0 -0
  135. {code_context_control-2.80.0 → code_context_control-2.81.0}/core/ide.py +0 -0
  136. {code_context_control-2.80.0 → code_context_control-2.81.0}/core/mcp_toml.py +0 -0
  137. {code_context_control-2.80.0 → code_context_control-2.81.0}/core/web_security.py +0 -0
  138. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/__init__.py +0 -0
  139. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/config.py +0 -0
  140. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/mcp_oracle.py +0 -0
  141. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/oracle_server.py +0 -0
  142. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/oracle_ui.html +0 -0
  143. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/__init__.py +0 -0
  144. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/activity_reporter.py +0 -0
  145. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/api_auth.py +0 -0
  146. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/c3_bridge.py +0 -0
  147. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/chat_engine.py +0 -0
  148. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/chat_poll.py +0 -0
  149. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/chat_store.py +0 -0
  150. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/cross_memory.py +0 -0
  151. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/discovery_audit.py +0 -0
  152. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/federated_graph.py +0 -0
  153. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/health_checker.py +0 -0
  154. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/insight_engine.py +0 -0
  155. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/local_session.py +0 -0
  156. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/memory_reader.py +0 -0
  157. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/memory_writer.py +0 -0
  158. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/mobile_api.py +0 -0
  159. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/project_scanner.py +0 -0
  160. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/review_agent.py +0 -0
  161. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/tool_executor.py +0 -0
  162. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/services/tool_registry.py +0 -0
  163. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/activity.js +0 -0
  164. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/agents.js +0 -0
  165. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/app.js +0 -0
  166. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/busy.js +0 -0
  167. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/chat/conversations.js +0 -0
  168. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/chat/input.js +0 -0
  169. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/chat/markdown.js +0 -0
  170. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/chat/send.js +0 -0
  171. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/chat/stream_renderer.js +0 -0
  172. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/chat/toolbar.js +0 -0
  173. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/core.js +0 -0
  174. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/crossgraph.js +0 -0
  175. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/header.js +0 -0
  176. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/insights.js +0 -0
  177. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/projects.js +0 -0
  178. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/qrcode.js +0 -0
  179. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/settings.js +0 -0
  180. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/suggestions.js +0 -0
  181. {code_context_control-2.80.0 → code_context_control-2.81.0}/oracle/ui/theme_tabs.js +0 -0
  182. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/__init__.py +0 -0
  183. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/access_guard.py +0 -0
  184. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/access_telemetry.py +0 -0
  185. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/activity_log.py +0 -0
  186. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/agent_base.py +0 -0
  187. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/agent_locks.py +0 -0
  188. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/agents.py +0 -0
  189. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/artifact_defs.py +0 -0
  190. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/artifact_store.py +0 -0
  191. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/auto_memory.py +0 -0
  192. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/bench/__init__.py +0 -0
  193. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/bench/external/__init__.py +0 -0
  194. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/bench/external/aider_polyglot.py +0 -0
  195. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/bench/external/swe_bench.py +0 -0
  196. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/benchmark_dashboard.py +0 -0
  197. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/bitbucket_client.py +0 -0
  198. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/bitbucket_credentials.py +0 -0
  199. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ci_expr.py +0 -0
  200. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ci_failures.py +0 -0
  201. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/circuit_breaker.py +0 -0
  202. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/compressor.py +0 -0
  203. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/context_snapshot.py +0 -0
  204. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/conversation_store.py +0 -0
  205. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/credential_store.py +0 -0
  206. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/doc_index.py +0 -0
  207. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/e2e_benchmark.py +0 -0
  208. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/e2e_evaluator.py +0 -0
  209. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/e2e_tasks.py +0 -0
  210. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/edit_ledger.py +0 -0
  211. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/embedding_index.py +0 -0
  212. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/enforcement_policy.py +0 -0
  213. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/error_reporting.py +0 -0
  214. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/file_memory.py +0 -0
  215. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/git_context.py +0 -0
  216. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/hub_service.py +0 -0
  217. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/indexer.py +0 -0
  218. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/jira_client.py +0 -0
  219. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/jira_cloud.py +0 -0
  220. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/jira_credentials.py +0 -0
  221. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/jira_data_center.py +0 -0
  222. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/jira_links.py +0 -0
  223. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/mask_activation.py +0 -0
  224. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/mask_mirror.py +0 -0
  225. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/mask_presets.py +0 -0
  226. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory.py +0 -0
  227. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory_consolidator.py +0 -0
  228. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory_distiller.py +0 -0
  229. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory_graph.py +0 -0
  230. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory_grounder.py +0 -0
  231. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory_queue.py +0 -0
  232. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/memory_scorer.py +0 -0
  233. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/metrics.py +0 -0
  234. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/notifications.py +0 -0
  235. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ollama_bridge.py +0 -0
  236. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ollama_client.py +0 -0
  237. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/ollama_credentials.py +0 -0
  238. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/output_filter.py +0 -0
  239. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/override_grants.py +0 -0
  240. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/override_policy.py +0 -0
  241. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/override_requests.py +0 -0
  242. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/override_wake.py +0 -0
  243. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/parser.py +0 -0
  244. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/project_manager.py +0 -0
  245. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/project_runtime.py +0 -0
  246. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/protocol.py +0 -0
  247. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/proxy_state.py +0 -0
  248. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/repo_map.py +0 -0
  249. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/retention.py +0 -0
  250. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/retrieval_broker.py +0 -0
  251. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/router.py +0 -0
  252. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/runtime.py +0 -0
  253. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/scanner.py +0 -0
  254. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/session_benchmark.py +0 -0
  255. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/session_manager.py +0 -0
  256. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/session_preloader.py +0 -0
  257. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/subprojects.py +0 -0
  258. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/task_store.py +0 -0
  259. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/telemetry.py +0 -0
  260. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/text_index.py +0 -0
  261. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/time_tracker.py +0 -0
  262. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/tool_classifier.py +0 -0
  263. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/transcript_index.py +0 -0
  264. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/validation_cache.py +0 -0
  265. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/vector_store.py +0 -0
  266. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/watcher.py +0 -0
  267. {code_context_control-2.80.0 → code_context_control-2.81.0}/services/win_subprocess.py +0 -0
  268. {code_context_control-2.80.0 → code_context_control-2.81.0}/setup.cfg +0 -0
  269. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_access_builtin_optout.py +0 -0
  270. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_access_guard.py +0 -0
  271. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_access_guard_meta.py +0 -0
  272. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_access_guard_shell_project.py +0 -0
  273. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_access_guard_surfaces.py +0 -0
  274. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_access_guard_wiring.py +0 -0
  275. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_activity_reporter.py +0 -0
  276. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_agent_locks.py +0 -0
  277. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_aider_polyglot.py +0 -0
  278. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_antigravity_transition.py +0 -0
  279. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_artifact_store.py +0 -0
  280. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_artifact_tool.py +0 -0
  281. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  282. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_bitbucket_client.py +0 -0
  283. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_bitbucket_config_fallback.py +0 -0
  284. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_bitbucket_credentials.py +0 -0
  285. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_bitbucket_tool.py +0 -0
  286. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  287. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_c3_shell.py +0 -0
  288. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_chat_poll.py +0 -0
  289. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ci_expr.py +0 -0
  290. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ci_workflow.py +0 -0
  291. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_circuit_breaker.py +0 -0
  292. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_claude_md_merge.py +0 -0
  293. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_cli_smoke.py +0 -0
  294. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_compressor_large_file.py +0 -0
  295. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_credential_store.py +0 -0
  296. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_credentials_routes.py +0 -0
  297. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_credentials_tool.py +0 -0
  298. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_creds_cli_smoke.py +0 -0
  299. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_delegate_cascade.py +0 -0
  300. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_delegate_version_probe.py +0 -0
  301. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_e2e_benchmark.py +0 -0
  302. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_edit_ledger_deadline.py +0 -0
  303. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_edit_ledger_hook.py +0 -0
  304. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_edit_locking.py +0 -0
  305. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_edit_normalization.py +0 -0
  306. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_edit_verify.py +0 -0
  307. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_embedding_index_deadlock.py +0 -0
  308. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_enforcement_flip.py +0 -0
  309. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_enforcement_policy.py +0 -0
  310. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_enforcement_routes.py +0 -0
  311. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_federated_graph.py +0 -0
  312. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_filter_backoff.py +0 -0
  313. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ghost_files.py +0 -0
  314. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ghost_generation.py +0 -0
  315. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_git_branch_awareness.py +0 -0
  316. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_access_guard.py +0 -0
  317. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_codex_compat.py +0 -0
  318. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_dispatch.py +0 -0
  319. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_pretool_enforce.py +0 -0
  320. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_prompt_recall.py +0 -0
  321. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_smoke.py +0 -0
  322. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_state.py +0 -0
  323. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hook_utf8_stdio.py +0 -0
  324. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_credentials_route.py +0 -0
  325. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_credentials_routes_write.py +0 -0
  326. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_enforcement_routes.py +0 -0
  327. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_inspect_api.py +0 -0
  328. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_locks_routes.py +0 -0
  329. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_server_smoke.py +0 -0
  330. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_hub_subproject_links.py +0 -0
  331. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ide_workflow_adaptation.py +0 -0
  332. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_init_scan.py +0 -0
  333. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_install_mcp_entrypoint.py +0 -0
  334. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_cli_smoke.py +0 -0
  335. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_client.py +0 -0
  336. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_config_fallback.py +0 -0
  337. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_credentials.py +0 -0
  338. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_links.py +0 -0
  339. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_routes.py +0 -0
  340. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_jira_tool.py +0 -0
  341. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_lazy_store_init.py +0 -0
  342. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mask_guard.py +0 -0
  343. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mask_routes.py +0 -0
  344. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mask_surfaces.py +0 -0
  345. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mcp_host_guard.py +0 -0
  346. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mcp_memory_timeout.py +0 -0
  347. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mcp_server_smoke.py +0 -0
  348. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mcp_toml.py +0 -0
  349. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_memory_distiller.py +0 -0
  350. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_memory_graph_api.py +0 -0
  351. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_memory_queue.py +0 -0
  352. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_memory_system.py +0 -0
  353. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mobile_api.py +0 -0
  354. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mobile_extras.py +0 -0
  355. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mobile_override_routes.py +0 -0
  356. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_mobile_security_api.py +0 -0
  357. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_no_stale_model_pins.py +0 -0
  358. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_notification_dedup.py +0 -0
  359. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_notification_discipline.py +0 -0
  360. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_ollama_credentials.py +0 -0
  361. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_api_auth.py +0 -0
  362. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_apikey_api.py +0 -0
  363. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_c3_bridge.py +0 -0
  364. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_chat_engine.py +0 -0
  365. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_chat_store.py +0 -0
  366. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_discovery_api.py +0 -0
  367. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_discovery_audit.py +0 -0
  368. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_local_auth.py +0 -0
  369. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_ollama_bridge.py +0 -0
  370. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_scanner_cache.py +0 -0
  371. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_security_fixes.py +0 -0
  372. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_session_bootstrap.py +0 -0
  373. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_subproject_awareness.py +0 -0
  374. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_oracle_ui_bundle.py +0 -0
  375. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_output_filter.py +0 -0
  376. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_override_grants.py +0 -0
  377. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_override_grants_mcp.py +0 -0
  378. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_override_requests.py +0 -0
  379. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_override_wake.py +0 -0
  380. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_permissions.py +0 -0
  381. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_pm_api.py +0 -0
  382. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_project_manager.py +0 -0
  383. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_project_manager_merge.py +0 -0
  384. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_project_tool.py +0 -0
  385. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_read_coercion.py +0 -0
  386. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_read_edit_parity.py +0 -0
  387. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_related_facts.py +0 -0
  388. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_repo_map.py +0 -0
  389. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_response_boilerplate.py +0 -0
  390. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_retention.py +0 -0
  391. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_review_digest.py +0 -0
  392. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_service_durability.py +0 -0
  393. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_session_benchmark.py +0 -0
  394. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_session_budget.py +0 -0
  395. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_shell_creds.py +0 -0
  396. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_shell_robustness.py +0 -0
  397. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_subproject_exclusion.py +0 -0
  398. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_subproject_federation.py +0 -0
  399. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_subprojects.py +0 -0
  400. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_swe_bench.py +0 -0
  401. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_task_store.py +0 -0
  402. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_task_tool.py +0 -0
  403. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_time_tracker.py +0 -0
  404. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_token_telemetry.py +0 -0
  405. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_tool_registry.py +0 -0
  406. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_upgrade_and_version.py +0 -0
  407. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_validate.py +0 -0
  408. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_vault_write_guard.py +0 -0
  409. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_version_sync.py +0 -0
  410. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_web_security.py +0 -0
  411. {code_context_control-2.80.0 → code_context_control-2.81.0}/tests/test_windows_reliability.py +0 -0
  412. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/__init__.py +0 -0
  413. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/backend.py +0 -0
  414. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/main.py +0 -0
  415. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/__init__.py +0 -0
  416. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/benchmark_view.py +0 -0
  417. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/claudemd_view.py +0 -0
  418. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/compress_view.py +0 -0
  419. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/index_view.py +0 -0
  420. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/init_view.py +0 -0
  421. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/mcp_view.py +0 -0
  422. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/optimize_view.py +0 -0
  423. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/pipe_view.py +0 -0
  424. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/projects_view.py +0 -0
  425. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/search_view.py +0 -0
  426. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/session_view.py +0 -0
  427. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/stats.py +0 -0
  428. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/screens/ui_view.py +0 -0
  429. {code_context_control-2.80.0 → code_context_control-2.81.0}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.80.0
3
+ Version: 2.81.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
@@ -202,7 +202,7 @@ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
202
202
  | `c3_session` | Snapshot, restore, log decisions, compact history |
203
203
  | `c3_impact` | Blast-radius analysis before editing shared symbols |
204
204
  | `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
205
- | `c3_ci` | Run this repo's real `.github/workflows` locally, with structured failures, before pushing (v2.79.0) |
205
+ | `c3_ci` | Run this repo's real `.github/workflows` locally — in containers via `act` — with structured failures, before pushing (v2.79.0) |
206
206
  | `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
207
207
  | `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
208
208
  | `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter, `verify` (did a failed `c3_edit` still land?) |
@@ -139,7 +139,7 @@ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
139
139
  | `c3_session` | Snapshot, restore, log decisions, compact history |
140
140
  | `c3_impact` | Blast-radius analysis before editing shared symbols |
141
141
  | `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
142
- | `c3_ci` | Run this repo's real `.github/workflows` locally, with structured failures, before pushing (v2.79.0) |
142
+ | `c3_ci` | Run this repo's real `.github/workflows` locally — in containers via `act` — with structured failures, before pushing (v2.79.0) |
143
143
  | `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
144
144
  | `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
145
145
  | `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter, `verify` (did a failed `c3_edit` still land?) |
@@ -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.80.0"
95
+ __version__ = "2.81.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -6079,7 +6079,10 @@ def _ci_main(args) -> int:
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
6081
  workflow=getattr(args, "workflow", ""),
6082
- event=getattr(args, "event", "") or "").to_dict()
6082
+ event=getattr(args, "event", "") or "",
6083
+ engine=getattr(args, "engine", "auto") or "auto",
6084
+ allow_side_effects=bool(getattr(args, "allow_side_effects", False)),
6085
+ network=getattr(args, "network", "") or "").to_dict()
6083
6086
  elif sub == "runs":
6084
6087
  payload = {"runs": cr.list_runs(project_path)}
6085
6088
  else:
@@ -6098,7 +6101,10 @@ def _ci_main(args) -> int:
6098
6101
  bool(getattr(args, "allow_foreign", False)),
6099
6102
  getattr(args, "workflow", "") or "", getattr(args, "tail", 200) or 200,
6100
6103
  getattr(args, "timeout", 0) or 0, svc, finalize,
6101
- getattr(args, "event", "") or "")
6104
+ getattr(args, "event", "") or "",
6105
+ getattr(args, "engine", "auto") or "auto",
6106
+ bool(getattr(args, "allow_side_effects", False)),
6107
+ getattr(args, "network", "") or "")
6102
6108
  print(out)
6103
6109
  # Exit code is the finding: a non-full verdict must not read as success to
6104
6110
  # a shell script or a pre-push hook.
@@ -500,6 +500,16 @@ def build_parser(version: str, parse_cli_ide_arg):
500
500
  "can never yield FULL_CI_PASS)")
501
501
  ci_run.add_argument("--timeout", type=int, default=0,
502
502
  help="Per-step timeout in seconds (default 900)")
503
+ ci_run.add_argument("--engine", choices=["auto", "native", "act"],
504
+ default="auto",
505
+ help="auto (default) uses act for Linux jobs when act + "
506
+ "Docker are present, native otherwise")
507
+ ci_run.add_argument("--allow-side-effects", action="store_true",
508
+ help="Let the act engine run jobs that look like they "
509
+ "publish or deploy. Refused by default.")
510
+ ci_run.add_argument("--network", default="",
511
+ help="Container network for the act engine (e.g. `none` "
512
+ "to cut egress). Default: act's own.")
503
513
 
504
514
  ci_rerun = _ci_common(ci_subs.add_parser(
505
515
  "rerun", help="Re-run only the jobs that failed in the last run"))
@@ -520,6 +530,8 @@ def build_parser(version: str, parse_cli_ide_arg):
520
530
  ci_logs.add_argument("--tail", type=int, default=200)
521
531
 
522
532
  _ci_common(ci_subs.add_parser("runs", help="Recent local CI runs"))
533
+ _ci_common(ci_subs.add_parser(
534
+ "doctor", help="Which execution engines are available on this machine"))
523
535
 
524
536
  # ── Override Requests (v2.69.0, docs/override-requests.md) ──────────
525
537
  # Human-only approval surface. A grant is single-use, session-bound,
@@ -1293,11 +1293,12 @@ _ci_runs: dict = {} # project path -> in-flight state
1293
1293
  _ci_lock = threading.Lock()
1294
1294
 
1295
1295
 
1296
- def _ci_worker(project: str, job: str, allow_foreign: bool, workflow: str) -> None:
1296
+ def _ci_worker(project: str, job: str, allow_foreign: bool, workflow: str,
1297
+ engine: str = "auto") -> None:
1297
1298
  from services import ci_runner as cr
1298
1299
  try:
1299
1300
  result = cr.run_ci(project, selector=job, allow_foreign=allow_foreign,
1300
- workflow=workflow)
1301
+ workflow=workflow, engine=engine)
1301
1302
  payload = {"running": False, "done": True, "error": None,
1302
1303
  "run_id": result.run_id, "verdict": result.verdict,
1303
1304
  "note": result.note}
@@ -1320,7 +1321,12 @@ def api_ci_inspect():
1320
1321
  except ValueError as e:
1321
1322
  return jsonify({"error": str(e)}), 404
1322
1323
  try:
1323
- return jsonify(inspect_project(resolved))
1324
+ from services import ci_act
1325
+ data = inspect_project(resolved)
1326
+ # The UI has to be able to say WHY a job cannot run here, and
1327
+ # "install act" is a different answer from "macOS is impossible".
1328
+ data["engines"] = ci_act.availability()
1329
+ return jsonify(data)
1324
1330
  except Exception as e:
1325
1331
  return jsonify({"error": f"{type(e).__name__}: {e}"}), 500
1326
1332
 
@@ -1391,7 +1397,8 @@ def api_ci_run_start():
1391
1397
  threading.Thread(
1392
1398
  target=_ci_worker,
1393
1399
  args=(resolved, str(data.get("job") or ""),
1394
- bool(data.get("allow_foreign")), str(data.get("workflow") or "")),
1400
+ bool(data.get("allow_foreign")), str(data.get("workflow") or ""),
1401
+ str(data.get("engine") or "auto")),
1395
1402
  daemon=True,
1396
1403
  ).start()
1397
1404
  return jsonify({"started": True})
@@ -142,6 +142,7 @@ function HubCI({ projects, onOpenDrill }) {
142
142
  };
143
143
 
144
144
  const jobs = (inspect && inspect.jobs) || [];
145
+ const engines = inspect && inspect.engines;
145
146
  const runJobs = ((run && run.jobs) || []).filter(j => j.status !== 'deselected');
146
147
 
147
148
  return (
@@ -172,8 +173,16 @@ function HubCI({ projects, onOpenDrill }) {
172
173
  border: `1px solid ${T.border}`, borderRadius: 6, padding: '8px 10px', lineHeight: 1.5,
173
174
  }}>
174
175
  Runs the repository's real <span className="mono">.github/workflows</span> here —
175
- C3 does not define a second CI config. Jobs targeting another OS are refused
176
- unless you opt in, and a cross-OS result is indicative, never equivalent.
176
+ C3 does not define a second CI config.{' '}
177
+ {engines && engines.ok ? (
178
+ <span>Linux jobs run in a container via <span className="mono">act</span>{' '}
179
+ ({engines.act_version}, docker {engines.docker_version}), real actions included.</span>
180
+ ) : (
181
+ <span style={{ color: T.warn }}>
182
+ Container engine unavailable — {(engines && engines.reason) || 'checking…'}
183
+ </span>
184
+ )}{' '}
185
+ macOS jobs can never run locally: there are no macOS containers.
177
186
  </div>
178
187
 
179
188
  {err && (
@@ -236,8 +245,12 @@ function HubCI({ projects, onOpenDrill }) {
236
245
  <span className="mono" style={{
237
246
  color: T.text, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
238
247
  }}>{j.key}</span>
239
- {j.cross_os && j.status === 'passed' && (
240
- <span className="mono" title={`Ran on this host, but targets ${j.runs_on}.`}
248
+ {j.status === 'passed' && j.fidelity === 'container' && (
249
+ <span className="mono" title={`Ran in a container of ${j.runs_on} via act — real actions included.`}
250
+ style={{ fontSize: 10, color: T.accent }}>container</span>
251
+ )}
252
+ {j.status === 'passed' && j.fidelity === 'cross-os' && (
253
+ <span className="mono" title={`Ran on this host, but targets ${j.runs_on}. Indicative, not equivalent.`}
241
254
  style={{ fontSize: 10, color: T.blue }}>cross-OS</span>
242
255
  )}
243
256
  <div style={{ flex: 1 }} />
@@ -277,9 +290,12 @@ function HubCI({ projects, onOpenDrill }) {
277
290
  borderTop: `1px solid ${T.border}`, fontSize: 11,
278
291
  }}>
279
292
  <CiStatusPill
280
- status={!j.supported ? 'unsupported' : (j.foreign_runner ? 'foreign' : 'passed')}
281
- title={!j.supported ? (j.blockers || []).join('; ')
282
- : (j.foreign_runner ? `targets ${j.runs_on}` : 'runnable on this host')} />
293
+ status={(!j.supported && !j.act_could_run) ? 'unsupported'
294
+ : ((j.foreign_runner && !(j.act_could_run && engines && engines.ok))
295
+ ? 'foreign' : 'passed')}
296
+ title={(!j.supported && !j.act_could_run) ? (j.blockers || []).join('; ')
297
+ : (j.act_could_run && !j.supported ? 'the act engine can run this'
298
+ : (j.foreign_runner ? `targets ${j.runs_on}` : 'runnable on this host'))} />
283
299
  <span className="mono" style={{ color: T.textMuted }}>{j.key}</span>
284
300
  {!!(j.needs || []).length && (
285
301
  <span className="mono" style={{ fontSize: 10, color: T.textDim }}>
@@ -770,10 +770,11 @@ async def c3_locks(action: str = "list", paths: str = "", intent: str = "",
770
770
  async def c3_ci(action: str = "inspect", job: str = "", run_id: str = "",
771
771
  allow_foreign: bool = False, workflow: str = "",
772
772
  tail: int = 200, timeout: int = 0, event: str = "",
773
- ctx: Context = None) -> str:
773
+ engine: str = "auto", allow_side_effects: bool = False,
774
+ network: str = "", ctx: Context = None) -> str:
774
775
  """RUN THIS REPO'S REAL CI LOCALLY — before pushing, not after.
775
776
  Reads .github/workflows/*.yml as the source of truth (no second CI config).
776
- actions: inspect (default) | run | rerun | status | failures | logs | runs.
777
+ actions: inspect | run | rerun | status | failures | logs | runs | doctor.
777
778
  inspect: workflows, the job DAG, and which jobs are runnable on THIS host.
778
779
  run: execute in dependency order; job='lint' or 'test (ubuntu-latest, 3.12)'
779
780
  selects one (bare job name = all its matrix cells). A job whose dependency
@@ -796,7 +797,8 @@ async def c3_ci(action: str = "inspect", job: str = "", run_id: str = "",
796
797
  from cli.tools.ci import handle_ci
797
798
  return await asyncio.to_thread(handle_ci, action, job, run_id,
798
799
  allow_foreign, workflow, tail, timeout,
799
- svc, finalize, event)
800
+ svc, finalize, event, engine,
801
+ allow_side_effects, network)
800
802
 
801
803
 
802
804
  @mcp.tool()
@@ -39,7 +39,12 @@ def _job_rows(jobs: list, include_deselected: bool = False) -> list:
39
39
  if status == cr.DESELECTED and not include_deselected:
40
40
  continue
41
41
  mark = _MARK.get(status, status.upper())
42
- cross = " [cross-OS]" if job.get("cross_os") and status == cr.PASSED else ""
42
+ fid = job.get("fidelity", "")
43
+ cross = ""
44
+ if status == cr.PASSED and fid == cr.FIDELITY_CONTAINER:
45
+ cross = " [container]"
46
+ elif status == cr.PASSED and fid == cr.FIDELITY_CROSS_OS:
47
+ cross = " [cross-OS]"
43
48
  ms = job.get("duration_ms") or 0
44
49
  timing = f" {ms / 1000:.1f}s" if ms else ""
45
50
  line = f" {mark:<9} {job.get('key', '?')}{cross}{timing}"
@@ -75,7 +80,8 @@ def _failure_rows(jobs: list, limit: int = 25) -> list:
75
80
 
76
81
  def handle_ci(action: str, job: str, run_id: str, allow_foreign: bool,
77
82
  workflow: str, tail: int, timeout: int, svc, finalize,
78
- event: str = "") -> str:
83
+ event: str = "", engine: str = "auto",
84
+ allow_side_effects: bool = False, network: str = "") -> str:
79
85
  """Route c3_ci actions."""
80
86
  project = str(svc.project_path)
81
87
  action = (action or "inspect").strip().lower()
@@ -151,7 +157,9 @@ def handle_ci(action: str, job: str, run_id: str, allow_foreign: bool,
151
157
 
152
158
  result = cr.run_ci(project, selector=job, allow_foreign=allow_foreign,
153
159
  only=only, timeout=timeout or cr.DEFAULT_STEP_TIMEOUT,
154
- workflow=workflow, event=event)
160
+ workflow=workflow, event=event, engine=engine,
161
+ allow_side_effects=allow_side_effects,
162
+ network=network)
155
163
  run = result.to_dict()
156
164
  lines = [_verdict_line(run), f"run {run['run_id']} host={run['host_os']}"]
157
165
  fp = run.get("fingerprint") or {}
@@ -224,6 +232,34 @@ def handle_ci(action: str, job: str, run_id: str, allow_foreign: bool,
224
232
  f"{row.get('started_at', '')[:19]} {tally}")
225
233
  return finalize("c3_ci", args, "\n".join(lines), f"{len(rows)} runs")
226
234
 
235
+ # ── doctor ───────────────────────────────────────────────────────────
236
+ if action == "doctor":
237
+ from services import ci_act
238
+ state = ci_act.availability()
239
+ lines = [
240
+ "Local CI execution engines:",
241
+ "",
242
+ f" native available — runs jobs whose runs-on matches "
243
+ f"{cr.host_os()}",
244
+ ]
245
+ if state["ok"]:
246
+ lines += [
247
+ f" act available — {state['act_version']}, "
248
+ f"docker {state['docker_version']}",
249
+ f" runs Linux jobs in a container "
250
+ f"({ci_act.DEFAULT_IMAGE}), real `uses:` actions included",
251
+ ]
252
+ else:
253
+ lines += [" act UNAVAILABLE", f" {state['reason']}"]
254
+ lines += [
255
+ "",
256
+ "macOS jobs cannot run locally on any engine — there are no macOS "
257
+ "containers, so a matrix containing them can never reach "
258
+ "FULL_CI_PASS here.",
259
+ ]
260
+ return finalize("c3_ci", args, "\n".join(lines),
261
+ "act ready" if state["ok"] else "act unavailable")
262
+
227
263
  return finalize("c3_ci", args,
228
264
  f"Unknown action '{action}'. Use: inspect, run, rerun, "
229
- "status, failures, logs, runs.", "unknown action")
265
+ "status, failures, logs, runs, doctor.", "unknown action")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.80.0
3
+ Version: 2.81.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
@@ -202,7 +202,7 @@ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
202
202
  | `c3_session` | Snapshot, restore, log decisions, compact history |
203
203
  | `c3_impact` | Blast-radius analysis before editing shared symbols |
204
204
  | `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
205
- | `c3_ci` | Run this repo's real `.github/workflows` locally, with structured failures, before pushing (v2.79.0) |
205
+ | `c3_ci` | Run this repo's real `.github/workflows` locally — in containers via `act` — with structured failures, before pushing (v2.79.0) |
206
206
  | `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
207
207
  | `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
208
208
  | `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter, `verify` (did a failed `c3_edit` still land?) |
@@ -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_act.py
188
189
  services/ci_expr.py
189
190
  services/ci_failures.py
190
191
  services/ci_runner.py
@@ -281,6 +282,7 @@ tests/test_bitbucket_tool.py
281
282
  tests/test_c3_bridge_project_artifacts.py
282
283
  tests/test_c3_shell.py
283
284
  tests/test_chat_poll.py
285
+ tests/test_ci_act.py
284
286
  tests/test_ci_expr.py
285
287
  tests/test_ci_runner.py
286
288
  tests/test_ci_workflow.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-context-control"
7
- version = "2.80.0"
7
+ version = "2.81.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,274 @@
1
+ """AgentCI — the `act` execution engine (Linux jobs in real containers).
2
+
3
+ The native engine runs a job's shell steps on this machine. That is faithful
4
+ when `runs-on` matches the host and an approximation otherwise, and it cannot
5
+ run `uses:` actions at all. This engine hands a Linux job to `nektos/act`,
6
+ which runs it in a container using GitHub's own workflow semantics — real
7
+ actions included.
8
+
9
+ What that buys, measured on this repository from Windows: 3 of 15 jobs
10
+ runnable natively, 11 with containers. The remaining four are macOS cells, and
11
+ no amount of Docker fixes those — **there are no macOS containers**, so a full
12
+ local pass on a matrix containing them stays out of reach. Saying so is part of
13
+ the job.
14
+
15
+ Three things learned by probing rather than by reading, all load-bearing:
16
+
17
+ 1. **`--bind` is required on Windows.** act's default copies the workspace into
18
+ the container; against a Windows host path that silently produced an empty
19
+ directory and every step failed on missing files. `--bind` mounts the real
20
+ working tree and works even on a mapped drive whose path contains spaces and
21
+ parentheses. The trade-off is that the container writes into the real tree —
22
+ the same thing the native engine already does, so behaviour is consistent.
23
+
24
+ 2. **`-P <label>=<image>` must always be passed.** Without it act prompts
25
+ interactively on first use to choose an image size, which would hang any
26
+ automated run.
27
+
28
+ 3. **`-W <file>` is mandatory when two workflows share a job name.** act says so
29
+ itself on this repo, where `CI` and `Release` both define `build` — the same
30
+ collision the DAG scopes around.
31
+
32
+ Safety: this engine never passes real secrets or tokens. act reads `.secrets`
33
+ and `.env` from the repository by default, so both are explicitly pointed at
34
+ empty files. A publish step therefore executes and fails at authentication
35
+ instead of publishing, which is the intended outcome for a local run.
36
+ """
37
+ from __future__ import annotations
38
+
39
+ import os
40
+ import re
41
+ import shutil
42
+ import subprocess
43
+ import tempfile
44
+ import time
45
+ from pathlib import Path
46
+
47
+ # act's community runner images. These mirror a good deal of the GitHub
48
+ # runner's preinstalled toolchain; a bare `ubuntu:24.04` does not, and a job
49
+ # that assumes a preinstalled tool then fails locally while passing in CI. A
50
+ # false red is the safe direction, but it is still noise, so default to the
51
+ # image built for the purpose.
52
+ DEFAULT_IMAGE = "catthehacker/ubuntu:act-latest"
53
+ RUNNER_IMAGES = {
54
+ "ubuntu-latest": DEFAULT_IMAGE,
55
+ "ubuntu-24.04": "catthehacker/ubuntu:act-24.04",
56
+ "ubuntu-22.04": "catthehacker/ubuntu:act-22.04",
57
+ "ubuntu-20.04": "catthehacker/ubuntu:act-20.04",
58
+ }
59
+
60
+ # Pulling a runner image is ~1 GB on first use, so the act engine gets its own,
61
+ # larger default ceiling than a native shell step.
62
+ DEFAULT_ACT_TIMEOUT = 3600
63
+
64
+
65
+ def image_for(runs_on: str) -> str:
66
+ label = str(runs_on or "").strip().lower()
67
+ return RUNNER_IMAGES.get(label, DEFAULT_IMAGE)
68
+
69
+
70
+ def find_act() -> str:
71
+ """Path to the act binary. C3_ACT_PATH wins, then PATH."""
72
+ override = (os.environ.get("C3_ACT_PATH") or "").strip()
73
+ if override and Path(override).is_file():
74
+ return override
75
+ return shutil.which("act") or ""
76
+
77
+
78
+ def _probe(cmd: list, timeout: int = 30) -> tuple:
79
+ try:
80
+ out = subprocess.run(cmd, capture_output=True, text=True,
81
+ timeout=timeout, stdin=subprocess.DEVNULL,
82
+ **({"creationflags": subprocess.CREATE_NO_WINDOW}
83
+ if os.name == "nt" else {}))
84
+ return out.returncode, (out.stdout or "") + (out.stderr or "")
85
+ except Exception as exc:
86
+ return 127, f"{type(exc).__name__}: {exc}"
87
+
88
+
89
+ def availability() -> dict:
90
+ """Can this engine run at all? Reports WHY not, never just False.
91
+
92
+ Both halves are checked because they fail independently and a user with
93
+ act installed but Docker stopped deserves to be told which one to fix.
94
+ """
95
+ act = find_act()
96
+ info: dict = {"ok": False, "act": act, "act_version": "",
97
+ "docker": False, "docker_version": "", "reason": ""}
98
+ if not act:
99
+ info["reason"] = ("`act` is not installed. Install it with "
100
+ "`winget install nektos.act` (Windows), "
101
+ "`brew install act` (macOS), or see "
102
+ "https://nektosact.com — then re-run.")
103
+ return info
104
+ rc, out = _probe([act, "--version"])
105
+ if rc != 0:
106
+ info["reason"] = f"`act --version` failed: {out.strip()[:200]}"
107
+ return info
108
+ info["act_version"] = out.strip().splitlines()[0] if out.strip() else ""
109
+
110
+ rc, out = _probe(["docker", "version", "--format", "{{.Server.Version}}"])
111
+ if rc != 0:
112
+ info["reason"] = ("Docker is not reachable — the act engine needs a "
113
+ "running daemon. Start Docker Desktop (or dockerd) "
114
+ f"and retry. Probe said: {out.strip()[:160]}")
115
+ return info
116
+ info["docker"] = True
117
+ info["docker_version"] = out.strip().splitlines()[-1] if out.strip() else ""
118
+ info["ok"] = True
119
+ return info
120
+
121
+
122
+ # ── Side-effect gate ────────────────────────────────────────────────────────
123
+ # With a real action runner, jobs that were previously unrunnable become
124
+ # runnable — including ones whose entire purpose is to publish. Capability no
125
+ # longer protects anybody, so policy has to.
126
+ PUBLISHING_ACTIONS = (
127
+ "pypa/gh-action-pypi-publish",
128
+ "softprops/action-gh-release",
129
+ "ncipollo/release-action",
130
+ "actions/create-release",
131
+ "docker/build-push-action",
132
+ "peaceiris/actions-gh-pages",
133
+ "actions/deploy-pages",
134
+ "js-devtools/npm-publish",
135
+ "gradle/gradle-publish",
136
+ "cycjimmy/semantic-release-action",
137
+ )
138
+ _PUBLISH_CMD = re.compile(
139
+ r"\b(twine\s+upload|npm\s+publish|cargo\s+publish|gh\s+release\s+create"
140
+ r"|docker\s+push|poetry\s+publish|aws\s+s3\s+(cp|sync)|kubectl\s+apply"
141
+ r"|terraform\s+apply|gcloud\s+.*deploy)\b", re.IGNORECASE)
142
+
143
+
144
+ def side_effects(inst) -> list:
145
+ """Reasons this job looks like it publishes or deploys something."""
146
+ found: list = []
147
+ for step in getattr(inst, "steps", []):
148
+ base = str(getattr(step, "uses", "") or "").split("@", 1)[0].lower()
149
+ if base in PUBLISHING_ACTIONS:
150
+ found.append(f"step {step.index} uses `{step.uses}`")
151
+ match = _PUBLISH_CMD.search(str(getattr(step, "run", "") or ""))
152
+ if match:
153
+ found.append(f"step {step.index} runs `{match.group(0)}`")
154
+ return found
155
+
156
+
157
+ # ── Command construction ────────────────────────────────────────────────────
158
+
159
+ def build_command(inst, project, act_path: str, event: str = "",
160
+ image: str = "", network: str = "",
161
+ secret_file: str = "", env_file: str = "",
162
+ artifact_dir: str = "", pull: bool = True) -> list:
163
+ """The exact argv handed to act. Pure, so it is unit-testable."""
164
+ cmd: list = [act_path]
165
+ if event:
166
+ cmd.append(event)
167
+
168
+ workflow_path = getattr(inst, "workflow_path", "") or ""
169
+ if workflow_path:
170
+ try:
171
+ rel = Path(workflow_path).resolve().relative_to(Path(project).resolve())
172
+ cmd += ["-W", str(rel).replace("\\", "/")]
173
+ except ValueError:
174
+ cmd += ["-W", str(workflow_path)]
175
+
176
+ cmd += ["-j", inst.job_id]
177
+
178
+ for key, value in sorted((inst.matrix or {}).items()):
179
+ cmd += ["--matrix", f"{key}:{value}"]
180
+
181
+ cmd += ["-P", f"{inst.runs_on}={image or image_for(inst.runs_on)}"]
182
+
183
+ # See the module docstring: without --bind the workspace arrives empty on
184
+ # Windows and every step fails on missing files.
185
+ cmd.append("--bind")
186
+ cmd.append(f"--pull={'true' if pull else 'false'}")
187
+
188
+ if network:
189
+ cmd += ["--network", network]
190
+ # Never let act source the repository's real .secrets / .env.
191
+ cmd += ["--secret-file", secret_file or os.devnull]
192
+ cmd += ["--env-file", env_file or os.devnull]
193
+ if artifact_dir:
194
+ cmd += ["--artifact-server-path", artifact_dir]
195
+ return cmd
196
+
197
+
198
+ # act prefixes every line it emits with `[Workflow/job]`, and marks actual
199
+ # program output with a `|` gutter:
200
+ #
201
+ # [CI/lint] ⭐ Run Main ruff check . <- act's own narration
202
+ # [CI/lint] | app/x.py:3:1: F401 ... <- what the command printed
203
+ #
204
+ # Handing the raw log to the failure parsers captures the prefix as part of the
205
+ # filename — `file=[CI/lint] ⭐ Run Main echo "app/x.py` — which makes the
206
+ # structured failure useless. Parse the gutter lines only.
207
+ _ACT_OUTPUT_LINE = re.compile(r"^\[[^\]]*\]\s{0,3}\|\s?(.*)$")
208
+
209
+
210
+ def program_output(text: str) -> str:
211
+ """Just what the commands printed, with act's prefixes removed.
212
+
213
+ Falls back to the raw text when nothing matches, so an act failure that
214
+ never reached a command (a bad image, a daemon error) still reaches the
215
+ parsers and the reader rather than becoming an empty log.
216
+ """
217
+ lines = [m.group(1) for m in
218
+ (_ACT_OUTPUT_LINE.match(ln) for ln in (text or "").splitlines())
219
+ if m]
220
+ return "\n".join(lines) if lines else (text or "")
221
+
222
+
223
+ def run_job(inst, project, timeout: int = DEFAULT_ACT_TIMEOUT,
224
+ event: str = "", network: str = "", artifact_dir: str = "",
225
+ act_path: str = "") -> dict:
226
+ """Run one job through act. Returns {exit_code, output, timed_out, command}."""
227
+ act_path = act_path or find_act()
228
+ if not act_path:
229
+ return {"exit_code": 127, "output": "act is not installed",
230
+ "timed_out": False, "command": ""}
231
+
232
+ tmpdir = tempfile.mkdtemp(prefix="c3act-")
233
+ empty = Path(tmpdir) / "empty"
234
+ empty.write_text("", encoding="utf-8")
235
+
236
+ cmd = build_command(inst, project, act_path, event=event, network=network,
237
+ secret_file=str(empty), env_file=str(empty),
238
+ artifact_dir=artifact_dir)
239
+
240
+ from cli.tools.shell import _kill_tree, _popen_kwargs
241
+
242
+ start = time.time()
243
+ timed_out = False
244
+ try:
245
+ proc = subprocess.Popen(
246
+ cmd, cwd=str(project), stdout=subprocess.PIPE,
247
+ stderr=subprocess.STDOUT, text=True, encoding="utf-8",
248
+ errors="replace", **_popen_kwargs(),
249
+ )
250
+ except (OSError, ValueError) as exc:
251
+ shutil.rmtree(tmpdir, ignore_errors=True)
252
+ return {"exit_code": 126, "output": f"{type(exc).__name__}: {exc}",
253
+ "timed_out": False, "command": " ".join(cmd)}
254
+
255
+ try:
256
+ output, _ = proc.communicate(timeout=timeout)
257
+ except subprocess.TimeoutExpired:
258
+ _kill_tree(proc)
259
+ try:
260
+ output, _ = proc.communicate(timeout=5)
261
+ except Exception:
262
+ output = ""
263
+ timed_out = True
264
+ output = (output or "") + (
265
+ f"\n[c3:ci] act exceeded {timeout}s and its container tree was killed\n")
266
+
267
+ shutil.rmtree(tmpdir, ignore_errors=True)
268
+ return {
269
+ "exit_code": -1 if timed_out else (proc.returncode or 0),
270
+ "output": output or "",
271
+ "timed_out": timed_out,
272
+ "duration_ms": round((time.time() - start) * 1000),
273
+ "command": " ".join(cmd),
274
+ }