code-context-control 2.66.0__tar.gz → 2.70.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 (407) hide show
  1. {code_context_control-2.66.0/code_context_control.egg-info → code_context_control-2.70.0}/PKG-INFO +55 -3
  2. {code_context_control-2.66.0 → code_context_control-2.70.0}/README.md +54 -2
  3. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/c3.py +296 -1
  4. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/commands/parser.py +102 -0
  5. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_access_guard.py +20 -0
  6. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_pretool_enforce.py +22 -0
  7. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_server.py +178 -55
  8. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_panel.js +2 -0
  9. code_context_control-2.70.0/cli/hub_ui/components/hub_enforcement.js +1162 -0
  10. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/mcp_server.py +24 -0
  11. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/server.py +18 -0
  12. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/delegate.py +78 -35
  13. code_context_control-2.70.0/cli/tools/override.py +173 -0
  14. {code_context_control-2.66.0 → code_context_control-2.70.0/code_context_control.egg-info}/PKG-INFO +55 -3
  15. {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/SOURCES.txt +12 -0
  16. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/config.py +21 -0
  17. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/oracle_server.py +11 -0
  18. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/oracle_ui.html +19 -0
  19. code_context_control-2.70.0/oracle/services/mobile_api.py +1618 -0
  20. code_context_control-2.70.0/oracle/ui/qrcode.js +2297 -0
  21. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/settings.js +43 -0
  22. {code_context_control-2.66.0 → code_context_control-2.70.0}/pyproject.toml +1 -1
  23. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/access_guard.py +32 -1
  24. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/access_telemetry.py +48 -0
  25. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/credential_store.py +37 -0
  26. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/embedding_index.py +79 -10
  27. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/enforcement_policy.py +140 -30
  28. code_context_control-2.70.0/services/override_grants.py +511 -0
  29. code_context_control-2.70.0/services/override_policy.py +391 -0
  30. code_context_control-2.70.0/services/override_requests.py +379 -0
  31. code_context_control-2.70.0/tests/test_delegate_version_probe.py +247 -0
  32. code_context_control-2.70.0/tests/test_embedding_index_deadlock.py +321 -0
  33. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_enforcement_policy.py +245 -4
  34. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_enforcement_routes.py +29 -0
  35. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_enforcement_routes.py +213 -0
  36. code_context_control-2.70.0/tests/test_mobile_api.py +370 -0
  37. code_context_control-2.70.0/tests/test_mobile_security_api.py +719 -0
  38. code_context_control-2.70.0/tests/test_override_grants.py +442 -0
  39. code_context_control-2.70.0/tests/test_override_requests.py +329 -0
  40. code_context_control-2.66.0/cli/hub_ui/components/hub_enforcement.js +0 -342
  41. {code_context_control-2.66.0 → code_context_control-2.70.0}/LICENSE +0 -0
  42. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/__init__.py +0 -0
  43. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/_hook_utils.py +0 -0
  44. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/commands/__init__.py +0 -0
  45. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/commands/common.py +0 -0
  46. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/docs.html +0 -0
  47. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/edits.html +0 -0
  48. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/access.html +0 -0
  49. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/bitbucket.html +0 -0
  50. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/credentials.html +0 -0
  51. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/getting-started.html +0 -0
  52. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/index.html +0 -0
  53. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/masking.html +0 -0
  54. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/oracle.html +0 -0
  55. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/shared.css +0 -0
  56. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/tools.html +0 -0
  57. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/guide/workflow.html +0 -0
  58. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_artifact.py +0 -0
  59. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_auto_snapshot.py +0 -0
  60. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_c3_signal.py +0 -0
  61. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_c3read.py +0 -0
  62. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_dispatch.py +0 -0
  63. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_edit_ledger.py +0 -0
  64. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_edit_unlock.py +0 -0
  65. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_filter.py +0 -0
  66. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_ghost_files.py +0 -0
  67. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_prompt_recall.py +0 -0
  68. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_read.py +0 -0
  69. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_session_stats.py +0 -0
  70. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hook_terse_advisor.py +0 -0
  71. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/app.js +0 -0
  72. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/add_project.js +0 -0
  73. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/config_editor.js +0 -0
  74. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  75. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_health.js +0 -0
  76. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  77. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  78. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/drill_views.js +0 -0
  79. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/global_search.js +0 -0
  80. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  81. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/hub_locks.js +0 -0
  82. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  83. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/modals.js +0 -0
  84. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/project_card.js +0 -0
  85. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/project_tree.js +0 -0
  86. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/session_drawer.js +0 -0
  87. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/settings_modal.js +0 -0
  88. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/sidebar.js +0 -0
  89. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/summary_bar.js +0 -0
  90. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/task_board.js +0 -0
  91. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/toasts.js +0 -0
  92. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/components/topbar.js +0 -0
  93. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui/state.js +0 -0
  94. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/hub_ui.html +0 -0
  95. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/mcp_proxy.py +0 -0
  96. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/progress.py +0 -0
  97. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/__init__.py +0 -0
  98. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/_helpers.py +0 -0
  99. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/agent.py +0 -0
  100. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/artifacts.py +0 -0
  101. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/bitbucket.py +0 -0
  102. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/compress.py +0 -0
  103. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/credentials.py +0 -0
  104. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/edit.py +0 -0
  105. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/edits.py +0 -0
  106. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/federate.py +0 -0
  107. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/filter.py +0 -0
  108. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/impact.py +0 -0
  109. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/jira.py +0 -0
  110. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/locks.py +0 -0
  111. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/memory.py +0 -0
  112. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/project.py +0 -0
  113. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/read.py +0 -0
  114. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/search.py +0 -0
  115. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/session.py +0 -0
  116. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/shell.py +0 -0
  117. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/status.py +0 -0
  118. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/tasks.py +0 -0
  119. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/tools/validate.py +0 -0
  120. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/api.js +0 -0
  121. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/app.js +0 -0
  122. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/access.js +0 -0
  123. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/bitbucket.js +0 -0
  124. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/chat.js +0 -0
  125. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/credentials.js +0 -0
  126. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/dashboard.js +0 -0
  127. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/edits.js +0 -0
  128. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/enforcement.js +0 -0
  129. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/instructions.js +0 -0
  130. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/jira.js +0 -0
  131. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/memory.js +0 -0
  132. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/sessions.js +0 -0
  133. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/settings.js +0 -0
  134. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/sidebar.js +0 -0
  135. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/components/tasks.js +0 -0
  136. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/icons.js +0 -0
  137. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/pm_shared.js +0 -0
  138. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/shared.js +0 -0
  139. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui/theme.js +0 -0
  140. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui.html +0 -0
  141. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui_legacy.html +0 -0
  142. {code_context_control-2.66.0 → code_context_control-2.70.0}/cli/ui_nano.html +0 -0
  143. {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  144. {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/entry_points.txt +0 -0
  145. {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/requires.txt +0 -0
  146. {code_context_control-2.66.0 → code_context_control-2.70.0}/code_context_control.egg-info/top_level.txt +0 -0
  147. {code_context_control-2.66.0 → code_context_control-2.70.0}/core/__init__.py +0 -0
  148. {code_context_control-2.66.0 → code_context_control-2.70.0}/core/config.py +0 -0
  149. {code_context_control-2.66.0 → code_context_control-2.70.0}/core/ide.py +0 -0
  150. {code_context_control-2.66.0 → code_context_control-2.70.0}/core/mcp_toml.py +0 -0
  151. {code_context_control-2.66.0 → code_context_control-2.70.0}/core/web_security.py +0 -0
  152. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/__init__.py +0 -0
  153. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/mcp_oracle.py +0 -0
  154. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/__init__.py +0 -0
  155. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/activity_reporter.py +0 -0
  156. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/api_auth.py +0 -0
  157. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/c3_bridge.py +0 -0
  158. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/chat_engine.py +0 -0
  159. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/chat_store.py +0 -0
  160. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/cross_memory.py +0 -0
  161. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/discovery_audit.py +0 -0
  162. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/federated_graph.py +0 -0
  163. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/health_checker.py +0 -0
  164. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/insight_engine.py +0 -0
  165. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/local_session.py +0 -0
  166. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/memory_reader.py +0 -0
  167. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/memory_writer.py +0 -0
  168. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/project_scanner.py +0 -0
  169. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/review_agent.py +0 -0
  170. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/tool_executor.py +0 -0
  171. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/services/tool_registry.py +0 -0
  172. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/activity.js +0 -0
  173. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/agents.js +0 -0
  174. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/app.js +0 -0
  175. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/busy.js +0 -0
  176. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/conversations.js +0 -0
  177. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/input.js +0 -0
  178. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/markdown.js +0 -0
  179. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/send.js +0 -0
  180. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/stream_renderer.js +0 -0
  181. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/chat/toolbar.js +0 -0
  182. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/core.js +0 -0
  183. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/crossgraph.js +0 -0
  184. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/header.js +0 -0
  185. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/insights.js +0 -0
  186. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/projects.js +0 -0
  187. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/suggestions.js +0 -0
  188. {code_context_control-2.66.0 → code_context_control-2.70.0}/oracle/ui/theme_tabs.js +0 -0
  189. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/__init__.py +0 -0
  190. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/activity_log.py +0 -0
  191. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/agent_base.py +0 -0
  192. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/agent_locks.py +0 -0
  193. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/agents.py +0 -0
  194. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/artifact_defs.py +0 -0
  195. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/artifact_store.py +0 -0
  196. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/auto_memory.py +0 -0
  197. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/__init__.py +0 -0
  198. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/external/__init__.py +0 -0
  199. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/external/aider_polyglot.py +0 -0
  200. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bench/external/swe_bench.py +0 -0
  201. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/benchmark_dashboard.py +0 -0
  202. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bitbucket_client.py +0 -0
  203. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/bitbucket_credentials.py +0 -0
  204. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/circuit_breaker.py +0 -0
  205. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/claude_md.py +0 -0
  206. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/compressor.py +0 -0
  207. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/context_snapshot.py +0 -0
  208. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/conversation_store.py +0 -0
  209. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/doc_index.py +0 -0
  210. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/e2e_benchmark.py +0 -0
  211. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/e2e_evaluator.py +0 -0
  212. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/e2e_tasks.py +0 -0
  213. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/edit_ledger.py +0 -0
  214. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/error_reporting.py +0 -0
  215. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/file_memory.py +0 -0
  216. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/git_context.py +0 -0
  217. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/hub_service.py +0 -0
  218. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/indexer.py +0 -0
  219. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_client.py +0 -0
  220. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_cloud.py +0 -0
  221. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_credentials.py +0 -0
  222. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_data_center.py +0 -0
  223. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/jira_links.py +0 -0
  224. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/mask_activation.py +0 -0
  225. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/mask_mirror.py +0 -0
  226. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/mask_presets.py +0 -0
  227. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory.py +0 -0
  228. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_consolidator.py +0 -0
  229. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_distiller.py +0 -0
  230. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_graph.py +0 -0
  231. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_grounder.py +0 -0
  232. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_queue.py +0 -0
  233. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/memory_scorer.py +0 -0
  234. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/metrics.py +0 -0
  235. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/notifications.py +0 -0
  236. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/ollama_bridge.py +0 -0
  237. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/ollama_client.py +0 -0
  238. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/ollama_credentials.py +0 -0
  239. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/output_filter.py +0 -0
  240. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/parser.py +0 -0
  241. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/project_manager.py +0 -0
  242. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/project_runtime.py +0 -0
  243. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/protocol.py +0 -0
  244. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/proxy_state.py +0 -0
  245. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/repo_map.py +0 -0
  246. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/retention.py +0 -0
  247. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/retrieval_broker.py +0 -0
  248. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/router.py +0 -0
  249. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/runtime.py +0 -0
  250. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/scanner.py +0 -0
  251. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/session_benchmark.py +0 -0
  252. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/session_manager.py +0 -0
  253. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/session_preloader.py +0 -0
  254. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/subprojects.py +0 -0
  255. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/task_store.py +0 -0
  256. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/telemetry.py +0 -0
  257. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/text_index.py +0 -0
  258. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/time_tracker.py +0 -0
  259. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/tool_classifier.py +0 -0
  260. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/transcript_index.py +0 -0
  261. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/validation_cache.py +0 -0
  262. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/vector_store.py +0 -0
  263. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/watcher.py +0 -0
  264. {code_context_control-2.66.0 → code_context_control-2.70.0}/services/win_subprocess.py +0 -0
  265. {code_context_control-2.66.0 → code_context_control-2.70.0}/setup.cfg +0 -0
  266. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_builtin_optout.py +0 -0
  267. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard.py +0 -0
  268. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_meta.py +0 -0
  269. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_shell_project.py +0 -0
  270. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_surfaces.py +0 -0
  271. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_access_guard_wiring.py +0 -0
  272. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_activity_reporter.py +0 -0
  273. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_agent_locks.py +0 -0
  274. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_aider_polyglot.py +0 -0
  275. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_antigravity_transition.py +0 -0
  276. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_artifact_store.py +0 -0
  277. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_artifact_tool.py +0 -0
  278. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  279. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_client.py +0 -0
  280. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_config_fallback.py +0 -0
  281. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_credentials.py +0 -0
  282. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_bitbucket_tool.py +0 -0
  283. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  284. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_c3_shell.py +0 -0
  285. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_circuit_breaker.py +0 -0
  286. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_claude_md_merge.py +0 -0
  287. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_cli_smoke.py +0 -0
  288. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_compressor_large_file.py +0 -0
  289. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_credential_store.py +0 -0
  290. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_credentials_routes.py +0 -0
  291. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_credentials_tool.py +0 -0
  292. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_creds_cli_smoke.py +0 -0
  293. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_delegate_cascade.py +0 -0
  294. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_e2e_benchmark.py +0 -0
  295. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_edit_ledger_hook.py +0 -0
  296. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_edit_locking.py +0 -0
  297. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_edit_normalization.py +0 -0
  298. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_enforcement_flip.py +0 -0
  299. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_federated_graph.py +0 -0
  300. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_filter_backoff.py +0 -0
  301. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ghost_files.py +0 -0
  302. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ghost_generation.py +0 -0
  303. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_git_branch_awareness.py +0 -0
  304. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_access_guard.py +0 -0
  305. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_dispatch.py +0 -0
  306. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_pretool_enforce.py +0 -0
  307. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_prompt_recall.py +0 -0
  308. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_smoke.py +0 -0
  309. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_state.py +0 -0
  310. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hook_utf8_stdio.py +0 -0
  311. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_credentials_route.py +0 -0
  312. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_credentials_routes_write.py +0 -0
  313. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_inspect_api.py +0 -0
  314. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_locks_routes.py +0 -0
  315. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_server_smoke.py +0 -0
  316. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_hub_subproject_links.py +0 -0
  317. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ide_workflow_adaptation.py +0 -0
  318. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_init_scan.py +0 -0
  319. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_install_mcp_entrypoint.py +0 -0
  320. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_cli_smoke.py +0 -0
  321. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_client.py +0 -0
  322. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_config_fallback.py +0 -0
  323. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_credentials.py +0 -0
  324. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_links.py +0 -0
  325. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_routes.py +0 -0
  326. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_jira_tool.py +0 -0
  327. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_lazy_store_init.py +0 -0
  328. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mask_guard.py +0 -0
  329. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mask_routes.py +0 -0
  330. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mask_surfaces.py +0 -0
  331. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_host_guard.py +0 -0
  332. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_memory_timeout.py +0 -0
  333. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_server_smoke.py +0 -0
  334. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_mcp_toml.py +0 -0
  335. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_distiller.py +0 -0
  336. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_graph_api.py +0 -0
  337. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_queue.py +0 -0
  338. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_memory_system.py +0 -0
  339. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_no_stale_model_pins.py +0 -0
  340. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_notification_dedup.py +0 -0
  341. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_notification_discipline.py +0 -0
  342. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_ollama_credentials.py +0 -0
  343. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_api_auth.py +0 -0
  344. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_apikey_api.py +0 -0
  345. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_c3_bridge.py +0 -0
  346. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_chat_engine.py +0 -0
  347. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_chat_store.py +0 -0
  348. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_discovery_api.py +0 -0
  349. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_discovery_audit.py +0 -0
  350. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_local_auth.py +0 -0
  351. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_ollama_bridge.py +0 -0
  352. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_scanner_cache.py +0 -0
  353. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_security_fixes.py +0 -0
  354. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_session_bootstrap.py +0 -0
  355. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_subproject_awareness.py +0 -0
  356. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_oracle_ui_bundle.py +0 -0
  357. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_output_filter.py +0 -0
  358. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_permissions.py +0 -0
  359. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_pm_api.py +0 -0
  360. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_project_manager.py +0 -0
  361. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_project_manager_merge.py +0 -0
  362. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_project_tool.py +0 -0
  363. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_read_coercion.py +0 -0
  364. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_read_edit_parity.py +0 -0
  365. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_related_facts.py +0 -0
  366. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_repo_map.py +0 -0
  367. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_response_boilerplate.py +0 -0
  368. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_retention.py +0 -0
  369. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_review_digest.py +0 -0
  370. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_service_durability.py +0 -0
  371. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_session_benchmark.py +0 -0
  372. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_session_budget.py +0 -0
  373. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_shell_creds.py +0 -0
  374. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_shell_robustness.py +0 -0
  375. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_subproject_exclusion.py +0 -0
  376. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_subproject_federation.py +0 -0
  377. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_subprojects.py +0 -0
  378. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_swe_bench.py +0 -0
  379. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_task_store.py +0 -0
  380. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_task_tool.py +0 -0
  381. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_time_tracker.py +0 -0
  382. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_token_telemetry.py +0 -0
  383. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_tool_registry.py +0 -0
  384. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_upgrade_and_version.py +0 -0
  385. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_validate.py +0 -0
  386. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_vault_write_guard.py +0 -0
  387. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_version_sync.py +0 -0
  388. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_web_security.py +0 -0
  389. {code_context_control-2.66.0 → code_context_control-2.70.0}/tests/test_windows_reliability.py +0 -0
  390. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/__init__.py +0 -0
  391. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/backend.py +0 -0
  392. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/main.py +0 -0
  393. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/__init__.py +0 -0
  394. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/benchmark_view.py +0 -0
  395. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/claudemd_view.py +0 -0
  396. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/compress_view.py +0 -0
  397. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/index_view.py +0 -0
  398. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/init_view.py +0 -0
  399. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/mcp_view.py +0 -0
  400. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/optimize_view.py +0 -0
  401. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/pipe_view.py +0 -0
  402. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/projects_view.py +0 -0
  403. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/search_view.py +0 -0
  404. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/session_view.py +0 -0
  405. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/stats.py +0 -0
  406. {code_context_control-2.66.0 → code_context_control-2.70.0}/tui/screens/ui_view.py +0 -0
  407. {code_context_control-2.66.0 → code_context_control-2.70.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.66.0
3
+ Version: 2.70.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
@@ -186,7 +186,7 @@ Twelve tabs. The dashboard above shows token savings, indexed files, the live se
186
186
 
187
187
  ## The MCP tool suite
188
188
 
189
- C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
189
+ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
190
190
 
191
191
  | Tool | What it does |
192
192
  |---|---|
@@ -201,6 +201,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
201
201
  | `c3_memory` | Fact store with categories, recall, graph queries, `index`→`fetch` two-step |
202
202
  | `c3_session` | Snapshot, restore, log decisions, compact history |
203
203
  | `c3_impact` | Blast-radius analysis before editing shared symbols |
204
+ | `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
204
205
  | `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
205
206
  | `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
206
207
  | `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter |
@@ -211,7 +212,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
211
212
  | `c3_jira` | Jira Cloud + Data Center — JQL, issues, transitions (v2.56.0) |
212
213
  | `c3_project` | Cross-project discovery & operations; guarded writes (v2.31.0) |
213
214
 
214
- Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task`.
215
+ Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task` / `c3_locks`.
215
216
 
216
217
  On Windows `c3_shell` uses Git Bash when available. Git Bash bundles no `jq`; use `python -m json.tool` for portable JSON formatting.
217
218
 
@@ -242,12 +243,61 @@ c3 access mask activate # purge pre-mask artifacts, buil
242
243
  - **Masked means read-only, always.** Cropped rows have no inverse, so an edit in transformed coordinates would corrupt the one file you protected.
243
244
  - **Four deterministic presets, no LLM in the read path:** `redact_secrets`, `redact_columns` (salted one-way pseudonyms — joins survive, no reverse dictionary), `sample_rows`, `signatures_only`.
244
245
  - **Rule changes are human-only** (UI tab or CLI) and ledger-logged. Agents have no mutation surface.
246
+ - **Built-ins can be switched off when you need to** (v2.65.0). `**/.env*`, `**/.c3/**`, `**/.claude/settings*.json` and `**/.git/**` yield to `c3 access builtin disable <glob>`, which makes you retype the glob first. It takes **two keys** — a config entry *and* a keyring attestation — so an agent that manages to write `config.json` still cannot grant itself write access to your settings. The credential vault stays absolute.
245
247
  - **Honest coverage.** This guards *cooperative* agents against mistakes and prompt injection. It is not a sandbox — a raw shell outside C3's tools still sees the real bytes.
246
248
 
247
249
  → [Access Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/access-guard.md) · [Mask Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/mask-guard.md)
248
250
 
249
251
  ---
250
252
 
253
+ ## Agent Locks — several agents, one repo (v2.65.0)
254
+
255
+ Guards answer *may the agent touch this?* Locks answer *is someone already touching it?*
256
+
257
+ Two agents editing one file used to be silent data loss. `c3_edit` serialized
258
+ same-file edits with an in-process lock, but every session runs its own `c3-mcp`
259
+ server — so two sessions could interleave read → replace → write and lose an edit
260
+ with no error on either side. Create mode was worse: both agents "succeeded" and
261
+ one file won.
262
+
263
+ Now there are two mechanisms, because they solve different problems:
264
+
265
+ - **Torn writes** — a cross-process file lock, held across create, single-edit and
266
+ batch alike. No daemon, no configuration, nothing to switch on.
267
+ - **Overlapping work** — a *lease*. `c3_edit` takes one on the file it edits,
268
+ carrying the intent from your edit summary. A second agent gets told who holds
269
+ it and why:
270
+
271
+ ```text
272
+ [c3-lock:held] services/router.py is held by claude-code:a3f19c2b.
273
+ intent: "refactor retry backoff"
274
+ lease expires in 6m12s.
275
+ ```
276
+
277
+ ```bash
278
+ c3 locks list # who holds what, across the project
279
+ c3 locks force-release services/router.py # break a stuck lease (human-only, audited)
280
+ ```
281
+
282
+ - **TTL is the real release mechanism.** Agents forget to release, so nothing
283
+ assumes they will — a crashed agent can never wedge a repo.
284
+ - **Acquisition is all-or-nothing** over a sorted path list, so two agents grabbing
285
+ the same pair in opposite order cannot deadlock.
286
+ - **`force-release` bumps a fencing counter**, so a holder that comes back is stale
287
+ by construction rather than by hope. It lives in the CLI and the Hub, never in
288
+ the agent-facing tool.
289
+ - **The Hub's Locks tab badges a project it cannot read as `UNREADABLE`**, not as
290
+ zero leases — "all clear" is a different claim from "we don't know".
291
+ - **Honest coverage, again.** Leases gate C3's own tool surfaces. A raw `c3_shell`
292
+ redirect, a non-Claude agent, or a human in an editor is *not* covered, and the
293
+ UI says so on screen.
294
+
295
+ Set `locks.enabled: false` in `.c3/config.json` to opt out.
296
+
297
+ → [Agent Locks](https://github.com/drknowhow/code-context-control/blob/main/docs/agent-locks.md)
298
+
299
+ ---
300
+
251
301
  ## Integrations
252
302
 
253
303
  | | What you get | Guide |
@@ -319,6 +369,8 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
319
369
  ## Security & privacy
320
370
 
321
371
  - **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
372
+ - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
373
+ - **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
322
374
  - **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
323
375
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
324
376
  - **API keys** for third-party providers are read from the environment and never persisted by C3.
@@ -123,7 +123,7 @@ Twelve tabs. The dashboard above shows token savings, indexed files, the live se
123
123
 
124
124
  ## The MCP tool suite
125
125
 
126
- C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
126
+ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
127
127
 
128
128
  | Tool | What it does |
129
129
  |---|---|
@@ -138,6 +138,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
138
138
  | `c3_memory` | Fact store with categories, recall, graph queries, `index`→`fetch` two-step |
139
139
  | `c3_session` | Snapshot, restore, log decisions, compact history |
140
140
  | `c3_impact` | Blast-radius analysis before editing shared symbols |
141
+ | `c3_locks` | Agent leases — who is working on which file, so two agents don't collide (v2.65.0) |
141
142
  | `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini |
142
143
  | `c3_agent` | Workflows: `review_changes`, `investigate`, `preflight`, `prepare_context`, `validate_compress` |
143
144
  | `c3_edits` | Edit-ledger queries, version diffs, restore points, per-branch filter |
@@ -148,7 +149,7 @@ C3 exposes **20 tools** as a native MCP server. Your IDE calls them directly:
148
149
  | `c3_jira` | Jira Cloud + Data Center — JQL, issues, transitions (v2.56.0) |
149
150
  | `c3_project` | Cross-project discovery & operations; guarded writes (v2.31.0) |
150
151
 
151
- Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task`.
152
+ Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task` / `c3_locks`.
152
153
 
153
154
  On Windows `c3_shell` uses Git Bash when available. Git Bash bundles no `jq`; use `python -m json.tool` for portable JSON formatting.
154
155
 
@@ -179,12 +180,61 @@ c3 access mask activate # purge pre-mask artifacts, buil
179
180
  - **Masked means read-only, always.** Cropped rows have no inverse, so an edit in transformed coordinates would corrupt the one file you protected.
180
181
  - **Four deterministic presets, no LLM in the read path:** `redact_secrets`, `redact_columns` (salted one-way pseudonyms — joins survive, no reverse dictionary), `sample_rows`, `signatures_only`.
181
182
  - **Rule changes are human-only** (UI tab or CLI) and ledger-logged. Agents have no mutation surface.
183
+ - **Built-ins can be switched off when you need to** (v2.65.0). `**/.env*`, `**/.c3/**`, `**/.claude/settings*.json` and `**/.git/**` yield to `c3 access builtin disable <glob>`, which makes you retype the glob first. It takes **two keys** — a config entry *and* a keyring attestation — so an agent that manages to write `config.json` still cannot grant itself write access to your settings. The credential vault stays absolute.
182
184
  - **Honest coverage.** This guards *cooperative* agents against mistakes and prompt injection. It is not a sandbox — a raw shell outside C3's tools still sees the real bytes.
183
185
 
184
186
  → [Access Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/access-guard.md) · [Mask Guard](https://github.com/drknowhow/code-context-control/blob/main/docs/mask-guard.md)
185
187
 
186
188
  ---
187
189
 
190
+ ## Agent Locks — several agents, one repo (v2.65.0)
191
+
192
+ Guards answer *may the agent touch this?* Locks answer *is someone already touching it?*
193
+
194
+ Two agents editing one file used to be silent data loss. `c3_edit` serialized
195
+ same-file edits with an in-process lock, but every session runs its own `c3-mcp`
196
+ server — so two sessions could interleave read → replace → write and lose an edit
197
+ with no error on either side. Create mode was worse: both agents "succeeded" and
198
+ one file won.
199
+
200
+ Now there are two mechanisms, because they solve different problems:
201
+
202
+ - **Torn writes** — a cross-process file lock, held across create, single-edit and
203
+ batch alike. No daemon, no configuration, nothing to switch on.
204
+ - **Overlapping work** — a *lease*. `c3_edit` takes one on the file it edits,
205
+ carrying the intent from your edit summary. A second agent gets told who holds
206
+ it and why:
207
+
208
+ ```text
209
+ [c3-lock:held] services/router.py is held by claude-code:a3f19c2b.
210
+ intent: "refactor retry backoff"
211
+ lease expires in 6m12s.
212
+ ```
213
+
214
+ ```bash
215
+ c3 locks list # who holds what, across the project
216
+ c3 locks force-release services/router.py # break a stuck lease (human-only, audited)
217
+ ```
218
+
219
+ - **TTL is the real release mechanism.** Agents forget to release, so nothing
220
+ assumes they will — a crashed agent can never wedge a repo.
221
+ - **Acquisition is all-or-nothing** over a sorted path list, so two agents grabbing
222
+ the same pair in opposite order cannot deadlock.
223
+ - **`force-release` bumps a fencing counter**, so a holder that comes back is stale
224
+ by construction rather than by hope. It lives in the CLI and the Hub, never in
225
+ the agent-facing tool.
226
+ - **The Hub's Locks tab badges a project it cannot read as `UNREADABLE`**, not as
227
+ zero leases — "all clear" is a different claim from "we don't know".
228
+ - **Honest coverage, again.** Leases gate C3's own tool surfaces. A raw `c3_shell`
229
+ redirect, a non-Claude agent, or a human in an editor is *not* covered, and the
230
+ UI says so on screen.
231
+
232
+ Set `locks.enabled: false` in `.c3/config.json` to opt out.
233
+
234
+ → [Agent Locks](https://github.com/drknowhow/code-context-control/blob/main/docs/agent-locks.md)
235
+
236
+ ---
237
+
188
238
  ## Integrations
189
239
 
190
240
  | | What you get | Guide |
@@ -256,6 +306,8 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
256
306
  ## Security & privacy
257
307
 
258
308
  - **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
309
+ - **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires the **Discovery Bearer token on every method, GETs included**, validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (QR code carrying URL + token). Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
310
+ - **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
259
311
  - **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
260
312
  - **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
261
313
  - **API keys** for third-party providers are read from the environment and never persisted by C3.
@@ -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.66.0"
95
+ __version__ = "2.70.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -261,6 +261,7 @@ _C3_MCP_ALLOW = [
261
261
  "mcp__c3__c3_bitbucket",
262
262
  "mcp__c3__c3_jira", "mcp__c3__c3_credentials",
263
263
  "mcp__c3__c3_project", "mcp__c3__c3_task", "mcp__c3__c3_artifacts",
264
+ "mcp__c3__c3_override",
264
265
  ]
265
266
 
266
267
  # Obsolete MCP tool names from earlier C3 versions. `c3 permissions clean`
@@ -6209,6 +6210,299 @@ def _enforce_audit(result: dict, project_path: str) -> None:
6209
6210
  pass
6210
6211
 
6211
6212
 
6213
+ _OVERRIDE_DEFAULT_TOOL = {"read": "Read", "write": "Edit"}
6214
+
6215
+
6216
+ def cmd_override(args):
6217
+ """`c3 override` — Override Requests: allow one blocked call, once.
6218
+
6219
+ The human half of docs/override-requests.md. Every verb here mints or
6220
+ inspects a *grant*; none of them edits policy. That separation is the
6221
+ entire point of the feature: today the only way to unblock an agent is to
6222
+ weaken a rule permanently, and a guard that spends its life in `advisory`
6223
+ is not a guard.
6224
+
6225
+ No agent surface reaches this command, and no approval here can touch the
6226
+ 'never' rows of spec §2 — the credential vault, Tier-0 absolute denies, or
6227
+ the catastrophic shell blocks.
6228
+ """
6229
+ sub = getattr(args, "override_cmd", None)
6230
+ if not sub:
6231
+ print("Usage: c3 override <policy|grant|list|revoke|check|sweep> ...")
6232
+ print(" c3 override policy # what is escalatable right now")
6233
+ return
6234
+
6235
+ from services import override_grants as og
6236
+ from services import override_policy as opol
6237
+
6238
+ project_path = str(Path(getattr(args, "project_path", ".") or ".").resolve())
6239
+ policy = opol.resolve(project_path)
6240
+ for warn in policy.warnings:
6241
+ print(f" [warn] {warn}")
6242
+
6243
+ if sub == "policy":
6244
+ _override_show(policy, project_path, opol)
6245
+ elif sub == "grant":
6246
+ _override_grant(args, policy, project_path, og, opol)
6247
+ elif sub == "list":
6248
+ _override_list(args, project_path, og)
6249
+ elif sub == "check":
6250
+ _override_check(args, policy, project_path, og, opol)
6251
+ elif sub == "revoke":
6252
+ gid = getattr(args, "grant_id", "")
6253
+ print(" Revoked." if og.revoke(project_path, gid)
6254
+ else f" No live grant with id '{gid}'.")
6255
+ elif sub == "sweep":
6256
+ print(f" Dropped {og.sweep_expired(project_path)} expired/spent grant(s).")
6257
+ elif sub == "requests":
6258
+ _override_requests(args, project_path)
6259
+ elif sub in ("approve", "deny"):
6260
+ _override_decide(args, sub, project_path)
6261
+
6262
+
6263
+ def _override_requests(args, project_path: str) -> None:
6264
+ from services import override_requests as orq
6265
+
6266
+ rows = orq.list_requests(
6267
+ project_path="" if getattr(args, "all_projects", False) else project_path,
6268
+ status=getattr(args, "status", "") or "")
6269
+ print_header("Override requests")
6270
+ if not rows:
6271
+ print(" (none)")
6272
+ return
6273
+ for r in rows:
6274
+ note = f" note: {r['decision_note']}" if r.get("decision_note") else ""
6275
+ print(f" {r['id']} {r['status']:<9} {r['tool']} {r['op']} {r['path']}")
6276
+ print(f" {'':18s} rule {r['rule']} ({r['rule_class']}) · session "
6277
+ f"{r['session_id']} · expires {r['expires_at']}{note}")
6278
+ if r.get("justification"):
6279
+ print(f" {'':18s} the agent wrote: \"{r['justification']}\"")
6280
+ print(f" {'':18s} (untrusted — it may be repeating text it read "
6281
+ f"from a file)")
6282
+ print("\n Approve: c3 override approve <id> Refuse: c3 override deny <id>")
6283
+
6284
+
6285
+ def _override_decide(args, decision: str, project_path: str) -> None:
6286
+ from services import override_requests as orq
6287
+
6288
+ try:
6289
+ row = orq.decide(getattr(args, "request_id", ""), decision,
6290
+ uses=getattr(args, "uses", None),
6291
+ ttl_s=getattr(args, "ttl_s", None),
6292
+ note=getattr(args, "note", "") or "",
6293
+ decided_by="cli",
6294
+ confirm=getattr(args, "confirm", None))
6295
+ except orq.OverrideError as exc:
6296
+ print(f" {exc}")
6297
+ return
6298
+ except ValueError as exc:
6299
+ print(f" Error: {exc}")
6300
+ return
6301
+
6302
+ if decision == "deny":
6303
+ print(f" Denied {row['id']}. The block stands.")
6304
+ return
6305
+ print_header("Override approved")
6306
+ print(f" Request : {row['id']} Grant: {row.get('grant_id', '?')}")
6307
+ print(f" Allows : {row['tool']} {row['op']} on {row['path']}")
6308
+ print(f" Session : {row['session_id']}")
6309
+ print(f"\n The rule {row['rule']} is still in force. One retry of this "
6310
+ "exact call, in")
6311
+ print(" that session, on that exact path — nothing else.")
6312
+
6313
+
6314
+ def _override_show(policy, project_path: str, opol) -> None:
6315
+ print_header("Override Requests")
6316
+ state = "on" if policy.enabled else "off"
6317
+ print(f" Enabled : {state} (channel: {policy.channel})")
6318
+ print(f" Project : {project_path}")
6319
+ print("\n Escalatable layers:")
6320
+ for key in opol.LAYER_KEYS:
6321
+ on = policy.escalatable(key)
6322
+ mark = "*" if on else " "
6323
+ extra = " (approval needs the rule typed by hand)" \
6324
+ if key in opol.TYPED_CONFIRM_LAYERS else ""
6325
+ print(f" {mark} {key:18s} {'yes' if on else 'no':3s}{extra}")
6326
+ print("\n Limits:")
6327
+ print(f" grant TTL : <= {policy.max_ttl_s}s (hard ceiling "
6328
+ f"{opol.HARD_MAX_TTL_S}s)")
6329
+ print(f" uses : {policy.default_uses} "
6330
+ f"(session grants: {'allowed' if policy.allow_session_grants else 'off'})")
6331
+ print(f" request TTL : {policy.request_ttl_s}s")
6332
+ print(f" rate : {policy.max_pending_per_session} pending/session, "
6333
+ f"{policy.max_requests_per_hour}/hour")
6334
+ if policy.corrupt_scopes:
6335
+ print(f"\n FAILING CLOSED — corrupt scope(s): "
6336
+ f"{', '.join(policy.corrupt_scopes)}")
6337
+ print("\n Never escalatable, at any setting: the credential vault, "
6338
+ ".c3/secrets.enc,")
6339
+ print(" .c3/cred_state.json, the dispatcher fail-closed deny, and the "
6340
+ "catastrophic")
6341
+ print(" shell blocks. Policy lives in .c3/config.json 'override' "
6342
+ "(human-edited).")
6343
+
6344
+
6345
+ def _override_identify(args, project_path: str, opol):
6346
+ """(gate, layer_key, rule, tool, op, error) for the call being approved.
6347
+
6348
+ Derived from the denial the path ACTUALLY produces, not from what the
6349
+ caller claims: a grant whose rule string does not match the rule that
6350
+ denies would never match at consume time, and a silently-useless grant is
6351
+ worse than a refusal.
6352
+ """
6353
+ op = getattr(args, "op", "read") or "read"
6354
+ tool = getattr(args, "tool", None) or _OVERRIDE_DEFAULT_TOOL[op]
6355
+ layer = getattr(args, "layer", None)
6356
+ target = getattr(args, "target", "")
6357
+
6358
+ if layer == "discipline":
6359
+ return (opol.GATE_DISCIPLINE, opol.LAYER_DISCIPLINE,
6360
+ opol.RULE_DISCIPLINE, tool, "write", None)
6361
+ if layer == "shell":
6362
+ return (opol.GATE_SHELL, opol.LAYER_SHELL_WARN,
6363
+ opol.RULE_SHELL_WARN, tool, op, None)
6364
+
6365
+ from services import access_guard as ag
6366
+ denial = ag.check(target, op, project_path)
6367
+ if denial is None:
6368
+ return (None, None, None, tool, op,
6369
+ f"`{target}` is not blocked for {op} — nothing to approve.")
6370
+ layer_key = opol.rule_class_for_denial(denial)
6371
+ if layer_key is None:
6372
+ return (None, None, None, tool, op,
6373
+ f"{opol.TAG_NOT_ESCALATABLE} `{denial.rule}` can never be "
6374
+ f"overridden — no config value and no approval reaches it.")
6375
+ gate = opol.GATE_FOR_LAYER_KEY[layer_key]
6376
+ if layer and layer != gate:
6377
+ return (None, None, None, tool, op,
6378
+ f"--layer {layer} does not match the layer that denied "
6379
+ f"({gate}, rule `{denial.rule}`).")
6380
+ return gate, layer_key, denial.rule, tool, op, None
6381
+
6382
+
6383
+ def _override_grant(args, policy, project_path: str, og, opol) -> None:
6384
+ gate, layer_key, rule, tool, op, err = _override_identify(
6385
+ args, project_path, opol)
6386
+ if err:
6387
+ print(f" {err}")
6388
+ return
6389
+ if not policy.enabled:
6390
+ print(" Overrides are disabled for this project.")
6391
+ print(" Enable by hand in .c3/config.json — the 'override' section, "
6392
+ "\"enabled\": true,")
6393
+ print(f" plus \"layers\": {{\"{layer_key}\": true}}. Nothing is "
6394
+ "escalatable by default.")
6395
+ return
6396
+ if not policy.escalatable(layer_key):
6397
+ print(f" The '{layer_key}' layer is not escalatable "
6398
+ f"(override.layers.{layer_key} is false).")
6399
+ print(" Opt in per layer, deliberately — that is the point of the "
6400
+ "default.")
6401
+ return
6402
+ if (layer_key in opol.TYPED_CONFIRM_LAYERS
6403
+ and getattr(args, "confirm", None) != rule):
6404
+ print(f" This approves ONE call against an {layer_key} rule "
6405
+ f"({rule}).")
6406
+ print(f" Retype the rule to confirm: --confirm '{rule}'")
6407
+ return
6408
+ try:
6409
+ grant = og.mint(project_path, session_id=getattr(args, "session_id", ""),
6410
+ layer=gate, rule=rule, tool=tool, op=op,
6411
+ path=getattr(args, "target", ""),
6412
+ ttl_s=getattr(args, "ttl_s", None),
6413
+ uses=getattr(args, "uses", None),
6414
+ granted_by="cli", policy=policy)
6415
+ except ValueError as exc:
6416
+ print(f" Error: {exc}")
6417
+ return
6418
+
6419
+ print_header("Override granted")
6420
+ print(f" Id : {grant['id']}")
6421
+ print(f" Allows : {tool} {op} on {grant['path_key']}")
6422
+ print(f" Layer : {gate} Rule: {rule}")
6423
+ print(f" Session : {grant['session_id']}")
6424
+ print(f" Expires : {grant['expires_at']} Uses: "
6425
+ f"{grant['uses_remaining']}")
6426
+ if getattr(args, "ttl_s", None) and grant.get("expires_at"):
6427
+ print(f" (TTL clamped to override.max_ttl_s = {policy.max_ttl_s}s "
6428
+ f"where it exceeded it)")
6429
+ print(f"\n The rule {rule} is still in force. This grant authorises one "
6430
+ "retry of")
6431
+ print(" this exact call, in this session, on this exact path, and "
6432
+ "nothing else.")
6433
+ _override_audit(grant, project_path)
6434
+
6435
+
6436
+ def _override_list(args, project_path: str, og) -> None:
6437
+ grants = og.active(project_path, getattr(args, "session_id", "") or "")
6438
+ print_header("Live override grants")
6439
+ if not grants:
6440
+ print(" (none)")
6441
+ for g in grants:
6442
+ print(f" {g.get('id', '?'):18s} {g.get('tool', '?')} "
6443
+ f"{g.get('op', '?')} {g.get('path_key', '?')}")
6444
+ print(f" {'':18s} rule {g.get('rule', '?')} · session "
6445
+ f"{g.get('session_id', '?')} · expires {g.get('expires_at', '?')}"
6446
+ f" · {g.get('uses_remaining', 0)} use(s) left")
6447
+ tail = int(getattr(args, "audit", 0) or 0)
6448
+ if tail:
6449
+ print("\n Audit (.c3/overrides.jsonl):")
6450
+ for line in og.read_audit(project_path, tail):
6451
+ print(f" {line.get('ts', '?')} {line.get('event', '?'):12s} "
6452
+ f"{line.get('grant_id', '')} {line.get('path', '')}")
6453
+
6454
+
6455
+ def _override_check(args, policy, project_path: str, og, opol) -> None:
6456
+ gate, layer_key, rule, tool, op, err = _override_identify(
6457
+ args, project_path, opol)
6458
+ if err:
6459
+ print(f" {err}")
6460
+ return
6461
+ if not policy.escalatable(layer_key):
6462
+ print(f" Denied — the '{layer_key}' layer is not escalatable, so no "
6463
+ "grant is even consulted.")
6464
+ return
6465
+ hit = og.find(project_path, session_id=getattr(args, "session_id", ""),
6466
+ layer=gate, rule=rule, tool=tool, op=op,
6467
+ path=getattr(args, "target", ""))
6468
+ if hit:
6469
+ print(f" Allowed by {hit['id']} — {hit['uses_remaining']} use(s) "
6470
+ f"left, expires {hit['expires_at']}.")
6471
+ print(" (`check` never consumes a use.)")
6472
+ else:
6473
+ print(" Denied — no live grant matches this exact "
6474
+ "session/layer/rule/tool/op/path.")
6475
+
6476
+
6477
+ def _override_audit(grant: dict, project_path: str) -> None:
6478
+ """Ledger + activity entry. The JSONL audit is written by the service."""
6479
+ try:
6480
+ from services.activity_log import ActivityLog
6481
+ ActivityLog(project_path).log("access_action", {
6482
+ "kind": "override", "action": "grant", "grant_id": grant["id"],
6483
+ "layer": grant["layer"], "rule": grant["rule"],
6484
+ "tool": grant["tool"], "op": grant["op"],
6485
+ "path": grant["path_key"], "session_id": grant["session_id"],
6486
+ "expires_at": grant["expires_at"],
6487
+ "uses": grant["uses_remaining"], "granted_by": grant["granted_by"],
6488
+ })
6489
+ except Exception:
6490
+ pass
6491
+ try:
6492
+ from services.edit_ledger import EditLedger
6493
+ EditLedger(project_path).log_edit(
6494
+ file=f"override://{grant['id']}",
6495
+ change_type="override_grant",
6496
+ summary=(f"granted {grant['tool']} {grant['op']} on "
6497
+ f"{grant['path_key']} once (rule {grant['rule']}, "
6498
+ f"expires {grant['expires_at']}) via `c3 override`"),
6499
+ tags=["override", "access"],
6500
+ detail={"kind": "override", **grant},
6501
+ )
6502
+ except Exception:
6503
+ pass
6504
+
6505
+
6212
6506
  def cmd_access(args):
6213
6507
  """Access Guard rule management — human-only mutation surface (spec §1)."""
6214
6508
  sub = getattr(args, "access_cmd", None)
@@ -7994,6 +8288,7 @@ def main():
7994
8288
  "creds": cmd_creds,
7995
8289
  "access": cmd_access,
7996
8290
  "enforce": cmd_enforce,
8291
+ "override": cmd_override,
7997
8292
  "locks": cmd_locks,
7998
8293
  "oracle": cmd_oracle,
7999
8294
  "upgrade": cmd_upgrade,
@@ -472,6 +472,108 @@ def build_parser(version: str, parse_cli_ide_arg):
472
472
  lk_sweep = locks_subs.add_parser("sweep", help="Drop expired leases now")
473
473
  lk_sweep.add_argument("--path", dest="project_path", default=".")
474
474
 
475
+ # ── Override Requests (v2.69.0, docs/override-requests.md) ──────────
476
+ # Human-only approval surface. A grant is single-use, session-bound,
477
+ # path-exact and TTL-capped, and it never edits policy: the rule that
478
+ # denied the call is still in force the moment the grant is spent.
479
+ # There is no agent-facing path to any verb in this command.
480
+ p_override = subparsers.add_parser(
481
+ "override",
482
+ help="Override Requests — allow one blocked call once, without weakening the rule",
483
+ )
484
+ override_subs = p_override.add_subparsers(dest="override_cmd")
485
+
486
+ ov_policy = override_subs.add_parser(
487
+ "policy", help="Show the effective `override` policy and which layers are escalatable"
488
+ )
489
+ ov_policy.add_argument("--path", dest="project_path", default=".",
490
+ help="Project directory (default: current)")
491
+
492
+ ov_grant = override_subs.add_parser(
493
+ "grant", help="Mint a single-use grant for one blocked call (human approval)"
494
+ )
495
+ ov_grant.add_argument("target", help="The exact path that was blocked")
496
+ ov_grant.add_argument("--session", dest="session_id", required=True,
497
+ help="Claude Code session id the grant is bound to — a grant never crosses sessions")
498
+ ov_grant.add_argument("--op", choices=["read", "write"], default="read",
499
+ help="Operation to allow (default: read)")
500
+ ov_grant.add_argument("--tool", default=None,
501
+ help="Exact tool name (default: Read for --op read, Edit for --op write)")
502
+ ov_grant.add_argument("--layer", choices=["access", "discipline", "mask", "shell"],
503
+ default=None,
504
+ help="Force a layer. Default: derive it from the denial the path actually produces")
505
+ ov_grant.add_argument("--ttl", type=int, default=None, dest="ttl_s",
506
+ help="Seconds until the grant expires (clamped to override.max_ttl_s, hard ceiling 900)")
507
+ ov_grant.add_argument("--uses", type=int, default=None,
508
+ help="Uses allowed (default 1; >1 needs override.allow_session_grants)")
509
+ ov_grant.add_argument("--confirm", default=None,
510
+ help="Required for deny/builtin rules: retype the rule glob by hand")
511
+ ov_grant.add_argument("--path", dest="project_path", default=".",
512
+ help="Project directory (default: current)")
513
+
514
+ ov_list = override_subs.add_parser("list", help="Live grants for this project")
515
+ ov_list.add_argument("--session", dest="session_id", default="",
516
+ help="Only grants bound to this session id")
517
+ ov_list.add_argument("--audit", type=int, default=0, metavar="N",
518
+ help="Also print the last N lines of .c3/overrides.jsonl")
519
+ ov_list.add_argument("--path", dest="project_path", default=".",
520
+ help="Project directory (default: current)")
521
+
522
+ ov_revoke = override_subs.add_parser("revoke", help="Drop a live grant before it is used")
523
+ ov_revoke.add_argument("grant_id", help="Grant id (grt_…)")
524
+ ov_revoke.add_argument("--path", dest="project_path", default=".",
525
+ help="Project directory (default: current)")
526
+
527
+ ov_check = override_subs.add_parser(
528
+ "check", help="Would a grant cover this call right now? (never consumes one)"
529
+ )
530
+ ov_check.add_argument("target", help="Path to test")
531
+ ov_check.add_argument("--session", dest="session_id", required=True)
532
+ ov_check.add_argument("--op", choices=["read", "write"], default="read")
533
+ ov_check.add_argument("--tool", default=None)
534
+ ov_check.add_argument("--layer", choices=["access", "discipline", "mask", "shell"],
535
+ default=None)
536
+ ov_check.add_argument("--path", dest="project_path", default=".",
537
+ help="Project directory (default: current)")
538
+
539
+ ov_sweep = override_subs.add_parser("sweep", help="Drop expired / spent grants now")
540
+ ov_sweep.add_argument("--path", dest="project_path", default=".",
541
+ help="Project directory (default: current)")
542
+
543
+ # Requests: what agents have ASKED for. Deciding is human-only and lives
544
+ # here (and, later, on the phone) — never on the c3_override agent tool.
545
+ ov_requests = override_subs.add_parser(
546
+ "requests", help="Override requests agents have asked for"
547
+ )
548
+ ov_requests.add_argument("--status", default="",
549
+ help="pending | approved | denied | expired | withdrawn")
550
+ ov_requests.add_argument("--all", dest="all_projects", action="store_true",
551
+ help="Every project, not just this one")
552
+ ov_requests.add_argument("--path", dest="project_path", default=".",
553
+ help="Project directory (default: current)")
554
+
555
+ ov_approve = override_subs.add_parser(
556
+ "approve", help="Approve one request — mints a single-use grant"
557
+ )
558
+ ov_approve.add_argument("request_id", help="Request id (ovr_…)")
559
+ ov_approve.add_argument("--ttl", type=int, default=None, dest="ttl_s",
560
+ help="Seconds until the grant expires (clamped to override.max_ttl_s)")
561
+ ov_approve.add_argument("--uses", type=int, default=None,
562
+ help="Uses allowed (default 1)")
563
+ ov_approve.add_argument("--note", default="",
564
+ help="Note recorded with the decision")
565
+ ov_approve.add_argument("--confirm", default=None,
566
+ help="Required for deny/builtin rules: retype the rule glob by hand")
567
+ ov_approve.add_argument("--path", dest="project_path", default=".",
568
+ help="Project directory (default: current)")
569
+
570
+ ov_deny = override_subs.add_parser("deny", help="Refuse one request")
571
+ ov_deny.add_argument("request_id", help="Request id (ovr_…)")
572
+ ov_deny.add_argument("--note", default="",
573
+ help="Note the agent will see with the refusal")
574
+ ov_deny.add_argument("--path", dest="project_path", default=".",
575
+ help="Project directory (default: current)")
576
+
475
577
  # ── Tool discipline / enforcement mode (v2.66.0) ────────────────────
476
578
  # LAYER C: how hard C3 pushes the agent toward c3_* tools. Distinct from
477
579
  # `c3 access` (path policy — a security boundary) and from