code-context-control 2.101.0__tar.gz → 2.102.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 (469) hide show
  1. {code_context_control-2.101.0/code_context_control.egg-info → code_context_control-2.102.0}/PKG-INFO +1 -1
  2. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/c3.py +51 -11
  3. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_access_guard.py +106 -9
  4. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_dispatch.py +91 -1
  5. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_pretool_enforce.py +24 -5
  6. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/mcp_server.py +1 -1
  7. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/_grants.py +26 -9
  8. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/artifacts.py +33 -2
  9. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/override.py +15 -5
  10. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/shell.py +82 -1
  11. {code_context_control-2.101.0 → code_context_control-2.102.0/code_context_control.egg-info}/PKG-INFO +1 -1
  12. {code_context_control-2.101.0 → code_context_control-2.102.0}/code_context_control.egg-info/SOURCES.txt +1 -0
  13. {code_context_control-2.101.0 → code_context_control-2.102.0}/pyproject.toml +1 -1
  14. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/access_guard.py +41 -5
  15. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/artifact_store.py +36 -9
  16. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/claude_md.py +22 -2
  17. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/override_grants.py +20 -9
  18. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/session_manager.py +18 -0
  19. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_artifact_tool.py +41 -4
  20. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_confirm_guard.py +46 -6
  21. code_context_control-2.102.0/tests/test_confirm_guard_wiring.py +482 -0
  22. code_context_control-2.102.0/tests/test_ide_workflow_adaptation.py +235 -0
  23. code_context_control-2.101.0/tests/test_ide_workflow_adaptation.py +0 -121
  24. {code_context_control-2.101.0 → code_context_control-2.102.0}/LICENSE +0 -0
  25. {code_context_control-2.101.0 → code_context_control-2.102.0}/README.md +0 -0
  26. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/__init__.py +0 -0
  27. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/_hook_utils.py +0 -0
  28. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/_shell_writes.py +0 -0
  29. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/commands/__init__.py +0 -0
  30. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/commands/common.py +0 -0
  31. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/commands/parser.py +0 -0
  32. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/docs.html +0 -0
  33. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/edits.html +0 -0
  34. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/access.html +0 -0
  35. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/bitbucket.html +0 -0
  36. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/credentials.html +0 -0
  37. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/getting-started.html +0 -0
  38. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/index.html +0 -0
  39. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/masking.html +0 -0
  40. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/oracle.html +0 -0
  41. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/shared.css +0 -0
  42. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/tools.html +0 -0
  43. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/guide/workflow.html +0 -0
  44. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_artifact.py +0 -0
  45. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_auto_snapshot.py +0 -0
  46. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_c3_signal.py +0 -0
  47. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_c3read.py +0 -0
  48. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_edit_ledger.py +0 -0
  49. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_edit_unlock.py +0 -0
  50. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_filter.py +0 -0
  51. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_ghost_files.py +0 -0
  52. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_prompt_recall.py +0 -0
  53. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_read.py +0 -0
  54. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_session_stats.py +0 -0
  55. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hook_terse_advisor.py +0 -0
  56. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_server.py +0 -0
  57. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/app.js +0 -0
  58. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/add_project.js +0 -0
  59. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/config_editor.js +0 -0
  60. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  61. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/drill_health.js +0 -0
  62. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/drill_panel.js +0 -0
  63. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  64. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  65. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/drill_views.js +0 -0
  66. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/global_search.js +0 -0
  67. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_access.js +0 -0
  68. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_ci.js +0 -0
  69. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  70. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  71. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  72. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_locks.js +0 -0
  73. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  74. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  75. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/modals.js +0 -0
  76. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/project_card.js +0 -0
  77. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/project_tree.js +0 -0
  78. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/session_drawer.js +0 -0
  79. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/settings_modal.js +0 -0
  80. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/sidebar.js +0 -0
  81. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/summary_bar.js +0 -0
  82. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/task_board.js +0 -0
  83. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/toasts.js +0 -0
  84. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/components/topbar.js +0 -0
  85. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui/state.js +0 -0
  86. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/hub_ui.html +0 -0
  87. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/mcp_proxy.py +0 -0
  88. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/progress.py +0 -0
  89. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/server.py +0 -0
  90. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/__init__.py +0 -0
  91. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/_helpers.py +0 -0
  92. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/agent.py +0 -0
  93. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/bitbucket.py +0 -0
  94. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/ci.py +0 -0
  95. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/compress.py +0 -0
  96. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/credentials.py +0 -0
  97. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/delegate.py +0 -0
  98. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/edit.py +0 -0
  99. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/edit_verify.py +0 -0
  100. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/edits.py +0 -0
  101. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/federate.py +0 -0
  102. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/filter.py +0 -0
  103. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/impact.py +0 -0
  104. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/jira.py +0 -0
  105. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/locks.py +0 -0
  106. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/memory.py +0 -0
  107. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/project.py +0 -0
  108. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/read.py +0 -0
  109. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/search.py +0 -0
  110. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/session.py +0 -0
  111. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/status.py +0 -0
  112. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/tasks.py +0 -0
  113. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/tools/validate.py +0 -0
  114. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/api.js +0 -0
  115. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/app.js +0 -0
  116. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/access.js +0 -0
  117. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/bitbucket.js +0 -0
  118. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/chat.js +0 -0
  119. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/credentials.js +0 -0
  120. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/dashboard.js +0 -0
  121. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/edits.js +0 -0
  122. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/enforcement.js +0 -0
  123. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/instructions.js +0 -0
  124. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/jira.js +0 -0
  125. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/memory.js +0 -0
  126. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/sessions.js +0 -0
  127. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/settings.js +0 -0
  128. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/sidebar.js +0 -0
  129. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/components/tasks.js +0 -0
  130. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/icons.js +0 -0
  131. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/pm_shared.js +0 -0
  132. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/shared.js +0 -0
  133. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui/theme.js +0 -0
  134. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui.html +0 -0
  135. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui_legacy.html +0 -0
  136. {code_context_control-2.101.0 → code_context_control-2.102.0}/cli/ui_nano.html +0 -0
  137. {code_context_control-2.101.0 → code_context_control-2.102.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  138. {code_context_control-2.101.0 → code_context_control-2.102.0}/code_context_control.egg-info/entry_points.txt +0 -0
  139. {code_context_control-2.101.0 → code_context_control-2.102.0}/code_context_control.egg-info/requires.txt +0 -0
  140. {code_context_control-2.101.0 → code_context_control-2.102.0}/code_context_control.egg-info/top_level.txt +0 -0
  141. {code_context_control-2.101.0 → code_context_control-2.102.0}/core/__init__.py +0 -0
  142. {code_context_control-2.101.0 → code_context_control-2.102.0}/core/config.py +0 -0
  143. {code_context_control-2.101.0 → code_context_control-2.102.0}/core/ide.py +0 -0
  144. {code_context_control-2.101.0 → code_context_control-2.102.0}/core/mcp_toml.py +0 -0
  145. {code_context_control-2.101.0 → code_context_control-2.102.0}/core/web_security.py +0 -0
  146. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/__init__.py +0 -0
  147. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/config.py +0 -0
  148. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/mcp_oracle.py +0 -0
  149. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/oracle_server.py +0 -0
  150. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/oracle_ui.html +0 -0
  151. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/__init__.py +0 -0
  152. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/activity_reporter.py +0 -0
  153. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/api_auth.py +0 -0
  154. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/c3_bridge.py +0 -0
  155. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/chat_engine.py +0 -0
  156. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/chat_poll.py +0 -0
  157. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/chat_store.py +0 -0
  158. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/cross_memory.py +0 -0
  159. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/discovery_audit.py +0 -0
  160. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/federated_graph.py +0 -0
  161. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/health_checker.py +0 -0
  162. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/insight_engine.py +0 -0
  163. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/local_session.py +0 -0
  164. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/memory_reader.py +0 -0
  165. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/memory_writer.py +0 -0
  166. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/mobile_api.py +0 -0
  167. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/project_scanner.py +0 -0
  168. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/review_agent.py +0 -0
  169. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/tool_executor.py +0 -0
  170. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/services/tool_registry.py +0 -0
  171. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/activity.js +0 -0
  172. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/agents.js +0 -0
  173. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/app.js +0 -0
  174. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/busy.js +0 -0
  175. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/chat/conversations.js +0 -0
  176. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/chat/input.js +0 -0
  177. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/chat/markdown.js +0 -0
  178. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/chat/send.js +0 -0
  179. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/chat/stream_renderer.js +0 -0
  180. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/chat/toolbar.js +0 -0
  181. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/core.js +0 -0
  182. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/crossgraph.js +0 -0
  183. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/header.js +0 -0
  184. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/insights.js +0 -0
  185. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/projects.js +0 -0
  186. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/qrcode.js +0 -0
  187. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/settings.js +0 -0
  188. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/suggestions.js +0 -0
  189. {code_context_control-2.101.0 → code_context_control-2.102.0}/oracle/ui/theme_tabs.js +0 -0
  190. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/__init__.py +0 -0
  191. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/access_telemetry.py +0 -0
  192. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/activity_log.py +0 -0
  193. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/agent_base.py +0 -0
  194. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/agent_locks.py +0 -0
  195. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/agents.py +0 -0
  196. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/artifact_defs.py +0 -0
  197. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/auto_memory.py +0 -0
  198. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/background_service.py +0 -0
  199. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/bench/__init__.py +0 -0
  200. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/bench/external/__init__.py +0 -0
  201. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/bench/external/aider_polyglot.py +0 -0
  202. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/bench/external/swe_bench.py +0 -0
  203. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/benchmark_dashboard.py +0 -0
  204. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/bitbucket_client.py +0 -0
  205. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/bitbucket_credentials.py +0 -0
  206. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_act.py +0 -0
  207. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_cache.py +0 -0
  208. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_expr.py +0 -0
  209. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_failures.py +0 -0
  210. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_github.py +0 -0
  211. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_impact.py +0 -0
  212. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_intel.py +0 -0
  213. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_runner.py +0 -0
  214. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ci_workflow.py +0 -0
  215. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/circuit_breaker.py +0 -0
  216. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/compressor.py +0 -0
  217. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/context_snapshot.py +0 -0
  218. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/conversation_store.py +0 -0
  219. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/cred_audit.py +0 -0
  220. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/cred_telemetry.py +0 -0
  221. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/credential_store.py +0 -0
  222. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/doc_index.py +0 -0
  223. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/e2e_benchmark.py +0 -0
  224. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/e2e_evaluator.py +0 -0
  225. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/e2e_tasks.py +0 -0
  226. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/edit_ledger.py +0 -0
  227. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/embedding_index.py +0 -0
  228. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/enforcement_policy.py +0 -0
  229. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/error_reporting.py +0 -0
  230. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/file_memory.py +0 -0
  231. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/git_context.py +0 -0
  232. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/hub_service.py +0 -0
  233. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/indexer.py +0 -0
  234. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/jira_client.py +0 -0
  235. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/jira_cloud.py +0 -0
  236. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/jira_credentials.py +0 -0
  237. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/jira_data_center.py +0 -0
  238. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/jira_links.py +0 -0
  239. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/mask_activation.py +0 -0
  240. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/mask_mirror.py +0 -0
  241. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/mask_presets.py +0 -0
  242. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory.py +0 -0
  243. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory_consolidator.py +0 -0
  244. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory_distiller.py +0 -0
  245. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory_graph.py +0 -0
  246. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory_grounder.py +0 -0
  247. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory_queue.py +0 -0
  248. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/memory_scorer.py +0 -0
  249. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/metrics.py +0 -0
  250. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/notifications.py +0 -0
  251. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ollama_bridge.py +0 -0
  252. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ollama_client.py +0 -0
  253. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/ollama_credentials.py +0 -0
  254. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/oracle_service.py +0 -0
  255. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/output_filter.py +0 -0
  256. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/override_policy.py +0 -0
  257. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/override_requests.py +0 -0
  258. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/override_wake.py +0 -0
  259. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/parser.py +0 -0
  260. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/project_manager.py +0 -0
  261. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/project_runtime.py +0 -0
  262. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/protocol.py +0 -0
  263. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/proxy_state.py +0 -0
  264. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/repo_map.py +0 -0
  265. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/repo_shape.py +0 -0
  266. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/retention.py +0 -0
  267. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/retrieval_broker.py +0 -0
  268. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/router.py +0 -0
  269. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/runtime.py +0 -0
  270. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/scanner.py +0 -0
  271. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/session_benchmark.py +0 -0
  272. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/session_preloader.py +0 -0
  273. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/subprojects.py +0 -0
  274. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/task_store.py +0 -0
  275. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/telemetry.py +0 -0
  276. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/text_index.py +0 -0
  277. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/time_tracker.py +0 -0
  278. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/tool_classifier.py +0 -0
  279. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/transcript_index.py +0 -0
  280. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/validation_cache.py +0 -0
  281. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/vector_store.py +0 -0
  282. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/watcher.py +0 -0
  283. {code_context_control-2.101.0 → code_context_control-2.102.0}/services/win_subprocess.py +0 -0
  284. {code_context_control-2.101.0 → code_context_control-2.102.0}/setup.cfg +0 -0
  285. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_access_builtin_optout.py +0 -0
  286. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_access_guard.py +0 -0
  287. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_access_guard_meta.py +0 -0
  288. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_access_guard_shell_project.py +0 -0
  289. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_access_guard_surfaces.py +0 -0
  290. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_access_guard_wiring.py +0 -0
  291. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_activity_reporter.py +0 -0
  292. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_agent_locks.py +0 -0
  293. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_aider_polyglot.py +0 -0
  294. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_antigravity_transition.py +0 -0
  295. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_artifact_store.py +0 -0
  296. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_background_service.py +0 -0
  297. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  298. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_bitbucket_client.py +0 -0
  299. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_bitbucket_config_fallback.py +0 -0
  300. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_bitbucket_credentials.py +0 -0
  301. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_bitbucket_tool.py +0 -0
  302. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_builtin_modes.py +0 -0
  303. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  304. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_c3_shell.py +0 -0
  305. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_chat_poll.py +0 -0
  306. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_act.py +0 -0
  307. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_cache.py +0 -0
  308. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_expr.py +0 -0
  309. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_impact.py +0 -0
  310. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_intel.py +0 -0
  311. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_runner.py +0 -0
  312. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ci_workflow.py +0 -0
  313. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_circuit_breaker.py +0 -0
  314. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_claude_md_merge.py +0 -0
  315. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_cli_smoke.py +0 -0
  316. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_compressor_large_file.py +0 -0
  317. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_cooccurrence_bounds.py +0 -0
  318. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_cred_audit.py +0 -0
  319. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_cred_telemetry.py +0 -0
  320. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_credential_login_kind.py +0 -0
  321. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_credential_store.py +0 -0
  322. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_credential_ui_parity.py +0 -0
  323. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_credentials_routes.py +0 -0
  324. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_credentials_tool.py +0 -0
  325. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_creds_cli_smoke.py +0 -0
  326. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_delegate_cascade.py +0 -0
  327. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_delegate_version_probe.py +0 -0
  328. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_e2e_benchmark.py +0 -0
  329. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_edit_ledger_deadline.py +0 -0
  330. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_edit_ledger_hook.py +0 -0
  331. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_edit_locking.py +0 -0
  332. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_edit_normalization.py +0 -0
  333. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_edit_verify.py +0 -0
  334. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_embedding_index_deadlock.py +0 -0
  335. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_enforcement_flip.py +0 -0
  336. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_enforcement_policy.py +0 -0
  337. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_enforcement_routes.py +0 -0
  338. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_env_parser.py +0 -0
  339. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  340. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_federated_graph.py +0 -0
  341. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_filter_backoff.py +0 -0
  342. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ghost_files.py +0 -0
  343. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ghost_generation.py +0 -0
  344. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_git_branch_awareness.py +0 -0
  345. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_access_guard.py +0 -0
  346. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_codex_compat.py +0 -0
  347. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_dispatch.py +0 -0
  348. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_pretool_enforce.py +0 -0
  349. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_prompt_recall.py +0 -0
  350. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_smoke.py +0 -0
  351. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_state.py +0 -0
  352. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hook_utf8_stdio.py +0 -0
  353. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_credentials_route.py +0 -0
  354. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_credentials_routes_write.py +0 -0
  355. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_enforcement_routes.py +0 -0
  356. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_inspect_api.py +0 -0
  357. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_locks_routes.py +0 -0
  358. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_oracle_service_routes.py +0 -0
  359. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_override_routes.py +0 -0
  360. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_server_smoke.py +0 -0
  361. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_hub_subproject_links.py +0 -0
  362. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  363. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_init_scan.py +0 -0
  364. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_install_mcp_entrypoint.py +0 -0
  365. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_cli_smoke.py +0 -0
  366. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_client.py +0 -0
  367. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_config_fallback.py +0 -0
  368. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_credentials.py +0 -0
  369. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_links.py +0 -0
  370. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_routes.py +0 -0
  371. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_jira_tool.py +0 -0
  372. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_lazy_store_init.py +0 -0
  373. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mask_guard.py +0 -0
  374. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mask_routes.py +0 -0
  375. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mask_surfaces.py +0 -0
  376. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mcp_host_guard.py +0 -0
  377. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mcp_memory_timeout.py +0 -0
  378. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mcp_server_smoke.py +0 -0
  379. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mcp_toml.py +0 -0
  380. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_memory_distiller.py +0 -0
  381. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_memory_graph_api.py +0 -0
  382. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_memory_queue.py +0 -0
  383. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_memory_system.py +0 -0
  384. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mobile_api.py +0 -0
  385. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mobile_extras.py +0 -0
  386. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mobile_override_routes.py +0 -0
  387. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_mobile_security_api.py +0 -0
  388. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_no_stale_model_pins.py +0 -0
  389. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_notification_dedup.py +0 -0
  390. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_notification_discipline.py +0 -0
  391. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ollama_credentials.py +0 -0
  392. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_api_auth.py +0 -0
  393. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_apikey_api.py +0 -0
  394. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_c3_bridge.py +0 -0
  395. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_chat_engine.py +0 -0
  396. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_chat_store.py +0 -0
  397. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_discovery_api.py +0 -0
  398. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_discovery_audit.py +0 -0
  399. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_health_probe.py +0 -0
  400. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_local_auth.py +0 -0
  401. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_ollama_bridge.py +0 -0
  402. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_scanner_cache.py +0 -0
  403. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_security_fixes.py +0 -0
  404. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_session_bootstrap.py +0 -0
  405. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_subproject_awareness.py +0 -0
  406. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_oracle_ui_bundle.py +0 -0
  407. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_output_filter.py +0 -0
  408. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_override_grants.py +0 -0
  409. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_override_grants_mcp.py +0 -0
  410. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_override_requests.py +0 -0
  411. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_override_wake.py +0 -0
  412. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_permissions.py +0 -0
  413. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_pm_api.py +0 -0
  414. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_project_manager.py +0 -0
  415. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_project_manager_merge.py +0 -0
  416. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_project_tool.py +0 -0
  417. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_read_coercion.py +0 -0
  418. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_read_edit_parity.py +0 -0
  419. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_related_facts.py +0 -0
  420. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_repo_map.py +0 -0
  421. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_repo_shape.py +0 -0
  422. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_response_boilerplate.py +0 -0
  423. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_retention.py +0 -0
  424. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_review_digest.py +0 -0
  425. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_service_durability.py +0 -0
  426. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_session_benchmark.py +0 -0
  427. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_session_budget.py +0 -0
  428. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_shell_creds.py +0 -0
  429. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_shell_robustness.py +0 -0
  430. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_shell_write_ledger.py +0 -0
  431. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_shell_writes.py +0 -0
  432. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_subproject_clear_scope.py +0 -0
  433. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_subproject_depth_consumers.py +0 -0
  434. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_subproject_exclusion.py +0 -0
  435. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_subproject_federation.py +0 -0
  436. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_subproject_linkage_api.py +0 -0
  437. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_subprojects.py +0 -0
  438. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_swe_bench.py +0 -0
  439. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_task_store.py +0 -0
  440. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_task_tool.py +0 -0
  441. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_time_tracker.py +0 -0
  442. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_token_telemetry.py +0 -0
  443. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_token_tracking.py +0 -0
  444. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_tool_registry.py +0 -0
  445. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_ui_jsx_syntax.py +0 -0
  446. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_upgrade_and_version.py +0 -0
  447. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_validate.py +0 -0
  448. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_vault_write_guard.py +0 -0
  449. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_version_sync.py +0 -0
  450. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_web_security.py +0 -0
  451. {code_context_control-2.101.0 → code_context_control-2.102.0}/tests/test_windows_reliability.py +0 -0
  452. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/__init__.py +0 -0
  453. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/backend.py +0 -0
  454. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/main.py +0 -0
  455. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/__init__.py +0 -0
  456. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/benchmark_view.py +0 -0
  457. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/claudemd_view.py +0 -0
  458. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/compress_view.py +0 -0
  459. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/index_view.py +0 -0
  460. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/init_view.py +0 -0
  461. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/mcp_view.py +0 -0
  462. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/optimize_view.py +0 -0
  463. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/pipe_view.py +0 -0
  464. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/projects_view.py +0 -0
  465. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/search_view.py +0 -0
  466. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/session_view.py +0 -0
  467. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/stats.py +0 -0
  468. {code_context_control-2.101.0 → code_context_control-2.102.0}/tui/screens/ui_view.py +0 -0
  469. {code_context_control-2.101.0 → code_context_control-2.102.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.101.0
3
+ Version: 2.102.0
4
4
  Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -92,7 +92,7 @@ console = Console() if HAS_RICH else None
92
92
  # Config
93
93
  CONFIG_DIR = ".c3"
94
94
  CONFIG_FILE = ".c3/config.json"
95
- __version__ = "2.101.0"
95
+ __version__ = "2.102.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -4212,6 +4212,7 @@ def cmd_pipe(args):
4212
4212
 
4213
4213
 
4214
4214
  from services.claude_md import C3_COMPACT_WORKFLOW as _SHARED_C3_COMPACT_WORKFLOW
4215
+ from services.claude_md import VSCODE_INSTRUCTIONS_FILE
4215
4216
  from services.claude_md import VSCODE_SESSION_INIT as _VSCODE_SESSION_INIT
4216
4217
  from services.claude_md import adapt_workflow_for_ide as _adapt_workflow_for_ide
4217
4218
 
@@ -4433,14 +4434,27 @@ def _toml_section_bool_value(toml_path: Path, section: str, key: str) -> bool |
4433
4434
  return None
4434
4435
 
4435
4436
 
4436
- def _ensure_instruction_workflow(instructions_path: Path, template: str, required_markers: list[str]) -> str:
4437
- """Ensure an instructions file has required C3 workflow markers.
4437
+ def _ensure_instruction_workflow(instructions_path: Path, template: str,
4438
+ required_markers: list[str],
4439
+ project_path: str = "") -> str:
4440
+ """Ensure an instructions file carries the CURRENT C3 workflow.
4438
4441
 
4439
4442
  Returns one of: "written", "updated", "kept".
4443
+
4444
+ Marker PRESENCE used to mean "up to date", which is why this function
4445
+ could report `kept` on a doc generated four releases ago: the markers are
4446
+ stable words like "SEARCH FIRST" and "c3_edit", so they survive every
4447
+ template change. A doc carrying the managed sentinels is now refreshed
4448
+ through ``merge_c3_block`` — the same non-destructive regeneration
4449
+ CLAUDE.md and AGENTS.md get — and `kept` means the block already matches
4450
+ byte for byte (v2.102.0). A marker-less legacy doc keeps the old
4451
+ prepend-and-preserve path, which ``write_c3_instruction_doc`` handles.
4440
4452
  """
4453
+ from services.claude_md import C3_BLOCK_BEGIN, merge_c3_block, wrap_c3_block, write_c3_instruction_doc
4454
+
4441
4455
  if not instructions_path.exists():
4442
- instructions_path.parent.mkdir(parents=True, exist_ok=True)
4443
- instructions_path.write_text(template, encoding="utf-8")
4456
+ write_c3_instruction_doc(instructions_path, template,
4457
+ project_path=project_path or None)
4444
4458
  return "written"
4445
4459
 
4446
4460
  try:
@@ -4448,6 +4462,14 @@ def _ensure_instruction_workflow(instructions_path: Path, template: str, require
4448
4462
  except Exception:
4449
4463
  existing = ""
4450
4464
 
4465
+ # Managed doc → regenerate the block in place, preserving user content.
4466
+ if C3_BLOCK_BEGIN in existing:
4467
+ if merge_c3_block(existing, wrap_c3_block(template)) == existing:
4468
+ return "kept"
4469
+ write_c3_instruction_doc(instructions_path, template,
4470
+ project_path=project_path or None)
4471
+ return "updated"
4472
+
4451
4473
  if all(marker in existing for marker in required_markers):
4452
4474
  return "kept"
4453
4475
 
@@ -4460,7 +4482,8 @@ def _ensure_instruction_workflow(instructions_path: Path, template: str, require
4460
4482
  return "updated"
4461
4483
 
4462
4484
 
4463
- def _ensure_codex_agents_workflow(agents_md_path: Path) -> str:
4485
+ def _ensure_codex_agents_workflow(agents_md_path: Path,
4486
+ project_path: str = "") -> str:
4464
4487
  """Ensure AGENTS.md contains the mandatory C3 workflow for Codex sessions."""
4465
4488
  required_markers = [
4466
4489
  "C3 Tools",
@@ -4474,10 +4497,12 @@ def _ensure_codex_agents_workflow(agents_md_path: Path) -> str:
4474
4497
  "c3_memory",
4475
4498
  "c3_filter",
4476
4499
  ]
4477
- return _ensure_instruction_workflow(agents_md_path, _AGENTS_MD_CONTENT, required_markers)
4500
+ return _ensure_instruction_workflow(agents_md_path, _AGENTS_MD_CONTENT,
4501
+ required_markers, project_path)
4478
4502
 
4479
4503
 
4480
- def _ensure_vscode_instructions_workflow(instructions_path: Path) -> str:
4504
+ def _ensure_vscode_instructions_workflow(instructions_path: Path,
4505
+ project_path: str = "") -> str:
4481
4506
  """Ensure VS Code Copilot instructions include the latest C3 workflow markers."""
4482
4507
  required_markers = [
4483
4508
  "C3 Tools",
@@ -4491,7 +4516,9 @@ def _ensure_vscode_instructions_workflow(instructions_path: Path) -> str:
4491
4516
  "c3_memory",
4492
4517
  "c3_filter",
4493
4518
  ]
4494
- return _ensure_instruction_workflow(instructions_path, _COPILOT_INSTRUCTIONS_CONTENT, required_markers)
4519
+ return _ensure_instruction_workflow(instructions_path,
4520
+ _COPILOT_INSTRUCTIONS_CONTENT,
4521
+ required_markers, project_path)
4495
4522
 
4496
4523
 
4497
4524
  def _safe_read_json(path: Path, label: str = "") -> dict:
@@ -4957,6 +4984,7 @@ back to native tools as the task progresses.
4957
4984
  - **Bitbucket** (v2.30.0+, when `c3 bitbucket login` has run): `c3_bitbucket(action='list_prs|get_pr|merge_pr|...')` — self-hosted Bitbucket Data Center / Server. Token in OS keyring; mutating actions auto-log to the edit ledger.
4958
4985
  - **Cross-project** (v2.31.0+): `c3_project(action='list|scan|search|read|edit|...', project='<name|path>')` — discover and operate on OTHER c3-installed projects. Reads run freely; writes (edit/shell/memory) need `allow_write=true`.
4959
4986
  - **Masked paths** (v2.63.0+): content prefixed `[c3-mask:transformed]` is a policy-transformed VIEW, not the file. Values may be synthetic, rows withheld, bodies stripped — don't copy literals out of it or infer completeness from it. Masked paths are read-only and refuse shell/git/validate/impact/filter/delegate (`[c3-mask:unsupported]`). That's policy, not a transient error — report the block instead of routing around it.
4987
+ - **Confirm holds** (v2.97.0+): `[c3-access:confirm]` is a PAUSE, not a block — a human must approve this exact write. Since v2.100.0 the agent-config files (`.mcp.json`, instruction docs, `.claude` hook/skill/agent/command bodies) hold by default in EVERY C3 project, reads stay open. Read the S8 tail: it names the filed request (`c3_override(action='wait', request_id='...', timeout_s=180)`, then retry the same call once if approved), or names a surface that files one, or gives a reason to obey. Never retry before a decision and never route around a hold via the shell.
4960
4988
 
4961
4989
  ## Self-Check
4962
4990
  If you haven't called a c3_* tool in several turns during active development, re-engage
@@ -5034,10 +5062,20 @@ def _instruction_documents_for_project() -> list[tuple[str, str]]:
5034
5062
 
5035
5063
  Used by uninstall/cleanup paths, so it must keep listing legacy docs
5036
5064
  (GEMINI.md — profile removed in v2.52; empty template, never generated).
5065
+
5066
+ ``.github/copilot-instructions.md`` was absent from this list until
5067
+ v2.102.0, and that absence was the whole bug: the managed-block sync
5068
+ that regenerates CLAUDE.md and AGENTS.md on every install-mcp never
5069
+ touched it, while ``_ensure_vscode_instructions_workflow`` reported
5070
+ `kept` on marker presence alone. In a vscode-primary project the third
5071
+ git-tracked instruction doc silently drifted a release behind on every
5072
+ template change — v2.100.0's agent-config confirm tier reached the
5073
+ Claude Code and Codex docs and never reached Copilot's.
5037
5074
  """
5038
5075
  return [
5039
5076
  ("CLAUDE.md", _CLAUDE_MD_CONTENT),
5040
5077
  ("AGENTS.md", _AGENTS_MD_CONTENT),
5078
+ (VSCODE_INSTRUCTIONS_FILE, _COPILOT_INSTRUCTIONS_CONTENT),
5041
5079
  ("GEMINI.md", ""),
5042
5080
  ]
5043
5081
 
@@ -5692,7 +5730,8 @@ def cmd_install_mcp(args):
5692
5730
  if profile.name == "vscode":
5693
5731
  # 1. Ensure copilot-instructions.md has the latest C3 workflow markers
5694
5732
  instructions_path = target / ".github" / "copilot-instructions.md"
5695
- vs_state = _ensure_vscode_instructions_workflow(instructions_path)
5733
+ vs_state = _ensure_vscode_instructions_workflow(instructions_path,
5734
+ str(target))
5696
5735
  if vs_state == "written":
5697
5736
  print(f"Wrote {instructions_path}")
5698
5737
  elif vs_state == "updated":
@@ -5731,7 +5770,8 @@ def cmd_install_mcp(args):
5731
5770
  # ── Codex AGENTS.md enforcement file ──────────────────────
5732
5771
  if profile.name == "codex":
5733
5772
  agents_md_path = target / "AGENTS.md"
5734
- agents_state = _ensure_codex_agents_workflow(agents_md_path)
5773
+ agents_state = _ensure_codex_agents_workflow(agents_md_path,
5774
+ str(target))
5735
5775
  if agents_state == "written":
5736
5776
  print(f"Wrote {agents_md_path}")
5737
5777
  elif agents_state == "updated":
@@ -6,9 +6,11 @@ dispatcher's deny-beats-allow merge makes a deny here final. hook_dispatch
6
6
  treats this module as _FAIL_CLOSED: if it fails to import or raises, write-
7
7
  class native tools are denied instead of sailing through.
8
8
 
9
- Bash coverage is a best-effort, existence-gated token scan — advisory by
10
- design (docs/access-guard.md §3/§6); it catches an agent naming a denied
11
- path, not an adversary hiding one.
9
+ Bash coverage is a best-effort token scan — advisory by design
10
+ (docs/access-guard.md §3/§6); it catches an agent naming a denied path, not
11
+ an adversary hiding one. Two passes: every path-shaped token as a READ
12
+ (existence-gated), then the command's WRITE targets as writes (v2.102.0), so
13
+ a redirect into a confirm-held or read-only file holds like any other write.
12
14
  """
13
15
  import os
14
16
  import re
@@ -29,6 +31,16 @@ try:
29
31
  except Exception: # pragma: no cover — telemetry is never load-bearing
30
32
  access_telemetry = None
31
33
 
34
+ # The same write-target extractor the edit ledger uses after the fact. Its
35
+ # absence degrades the shell branch to the read-only scan, never to a crash.
36
+ try:
37
+ from _shell_writes import shell_write_targets
38
+ except Exception: # pragma: no cover — package-style import (dispatcher, tests)
39
+ try:
40
+ from cli._shell_writes import shell_write_targets
41
+ except Exception:
42
+ shell_write_targets = None
43
+
32
44
  _WRITE_TOOLS = {"Edit", "Write", "MultiEdit", "NotebookEdit"}
33
45
  _READ_TOOLS = {"Read", "SearchText"}
34
46
  _SEARCH_TOOLS = {"Grep", "Glob", "FindFiles"}
@@ -206,21 +218,41 @@ def _record(denial, tool: str, operation: str, path: str,
206
218
 
207
219
 
208
220
  def _override_allows(base: str, denial, *, tool: str, op: str, path: str,
209
- session_id: str):
221
+ session_id: str, peek: bool = False):
210
222
  """A live override grant covering this exact call, or None (spec §5).
211
223
 
212
224
  Lazy import and policy-before-grants ordering keep the hot path free: a
213
225
  project that never turned overrides on pays one dict lookup and never
214
226
  opens the grants file. Any failure returns None — the denial stands.
227
+
228
+ ``peek`` looks without consuming; see ``_granted`` for why.
215
229
  """
216
230
  try:
217
231
  from services import override_grants as og # noqa: PLC0415 — lazy
218
232
  return og.gate_access(base, denial, tool=tool, op=op, path=path,
219
- session_id=session_id)
233
+ session_id=session_id, peek=peek)
220
234
  except Exception:
221
235
  return None # fail closed: no grant, ordinary denial
222
236
 
223
237
 
238
+ def _granted(line: str, defer: bool, consume) -> dict:
239
+ """The allow-by-grant output.
240
+
241
+ Under the dispatcher (``defer`` True) the use is NOT burned here: the
242
+ provisional line goes out with an ``_on_allow`` callable, and
243
+ hook_dispatch runs it only once every PreToolUse sub-hook has voted
244
+ allow. Before v2.102.0 this hook consumed first and a strict-mode
245
+ discipline deny from hook_pretool_enforce then won the merge — grant
246
+ spent, nothing written, and the user asked twice for one edit. Run
247
+ standalone (``defer`` False) the use was already consumed by the peek-
248
+ less lookup, so the line is final.
249
+ """
250
+ out = {"additionalContext": line}
251
+ if defer:
252
+ out["_on_allow"] = consume
253
+ return out
254
+
255
+
224
256
  def _confirm_request(base: str, denial, *, tool: str, op: str, path: str,
225
257
  session_id: str) -> tuple:
226
258
  """Auto-file an Override Request for a ``confirm`` denial (S8).
@@ -349,7 +381,68 @@ def _scan_shell(cmd: str, base: str):
349
381
  return None, ""
350
382
 
351
383
 
352
- def run(payload: dict, project_path: Path | None = None) -> dict | None:
384
+ def _scan_shell_writes(cmd: str, base: str, session_id: str,
385
+ defer: bool) -> dict | None:
386
+ """WRITE-class verdict for the files a shell command writes (v2.102.0).
387
+
388
+ ``_scan_shell`` evaluates every token as a READ. Confirm holds, read_only
389
+ rules and the builtin write-denies are all write-class, so ``cat >>
390
+ CLAUDE.md``, ``sed -i`` on .mcp.json or a heredoc into a hook body sailed
391
+ through with no hold — the one bypass docs/confirm-guard.md could only
392
+ ask the agent not to use. Targets come from the extractor the edit
393
+ ledger already trusts after the fact (cli/_shell_writes), with the same
394
+ best-effort caveat: a missed target costs a missed hold, a false one
395
+ costs a hold the human clears in one tap. Synthetic spelling denials are
396
+ skipped exactly as the read scan skips them (a token that is not a path
397
+ must not veto the command); the corrupt-config deny-all still refuses.
398
+
399
+ Grant identity is ``tool="Bash", op="write", path=<target>`` — bound to
400
+ the file, not the command text, the same stated limitation as the
401
+ shell_warn layer. Several held targets need several grants; the command
402
+ runs only when every one is covered, and the uses are burned together
403
+ (or not at all) by the dispatcher's settle step.
404
+ """
405
+ if shell_write_targets is None:
406
+ return None
407
+ lines, consumers = [], []
408
+ for target in shell_write_targets(cmd, base):
409
+ denial = ag.check(target, "write", base)
410
+ if denial is None:
411
+ continue
412
+ if denial.rule.startswith("<") and denial.rule != "<corrupt-config>":
413
+ continue
414
+ granted = _override_allows(base, denial, tool="Bash", op="write",
415
+ path=target, session_id=session_id,
416
+ peek=defer)
417
+ if granted:
418
+ lines.append(granted)
419
+ if defer:
420
+ consumers.append(lambda t=target, d=denial: _override_allows(
421
+ base, d, tool="Bash", op="write", path=t,
422
+ session_id=session_id))
423
+ continue
424
+ _record(denial, "Bash", "write", target, base, session_id)
425
+ if denial.kind == "confirm":
426
+ rid, note = _confirm_request(base, denial, tool="Bash", op="write",
427
+ path=target, session_id=session_id)
428
+ return _deny(ag.refusal(denial, target, "write", surface="hook",
429
+ tool="Bash", request_id=rid,
430
+ request_note=note)
431
+ + " (best-effort shell scan)")
432
+ return _deny(ag.refusal(denial, target, "write", surface="hook",
433
+ tool="Bash") + " (best-effort shell scan)")
434
+ if not lines:
435
+ return None
436
+
437
+ def _settle():
438
+ got = [fn() for fn in consumers]
439
+ return "\n".join(got) if all(got) else None
440
+
441
+ return _granted("\n".join(lines), bool(consumers), _settle)
442
+
443
+
444
+ def run(payload: dict, project_path: Path | None = None,
445
+ defer_consume: bool = False) -> dict | None:
353
446
  tool = normalize_tool_name(payload.get("tool_name", ""))
354
447
  tool_input = payload.get("tool_input", {}) or {}
355
448
  base = str(project_path if project_path is not None else Path.cwd())
@@ -363,9 +456,13 @@ def run(payload: dict, project_path: Path | None = None) -> dict | None:
363
456
  denial = ag.check(fp, op, base)
364
457
  if denial:
365
458
  granted = _override_allows(base, denial, tool=tool, op=op,
366
- path=fp, session_id=session_id)
459
+ path=fp, session_id=session_id,
460
+ peek=defer_consume)
367
461
  if granted:
368
- return {"additionalContext": granted}
462
+ return _granted(granted, defer_consume,
463
+ lambda: _override_allows(
464
+ base, denial, tool=tool, op=op, path=fp,
465
+ session_id=session_id))
369
466
  _record(denial, tool, op, fp, base, session_id)
370
467
  if denial.kind == "confirm":
371
468
  # A confirm hold files its own request (grants were consulted
@@ -401,6 +498,6 @@ def run(payload: dict, project_path: Path | None = None) -> dict | None:
401
498
  return _deny(ag.refusal(denial, tok, "read",
402
499
  surface="hook", tool=tool)
403
500
  + " (best-effort shell scan)")
404
- return None
501
+ return _scan_shell_writes(cmd, base, session_id, defer_consume)
405
502
 
406
503
  return None
@@ -36,6 +36,7 @@ emit a short "[c3:hook-error] ..." additionalContext warning so enforcement
36
36
  cannot silently stop enforcing.
37
37
  """
38
38
  import importlib
39
+ import inspect
39
40
  import json
40
41
  import sys
41
42
  from pathlib import Path
@@ -166,6 +167,16 @@ def _routes(event: str, raw_tool: str, norm_tool: str, host: str = HOST_CLAUDE):
166
167
 
167
168
  _RUN_CACHE: dict = {}
168
169
 
170
+ # PreToolUse sub-hooks that consult override grants. Under the dispatcher
171
+ # they are asked to DEFER consumption (`defer_consume=True`): each returns a
172
+ # provisional allow carrying an `_on_allow` callable, and `_settle_grants`
173
+ # runs those only once every sub-hook has voted allow. Before v2.102.0 the
174
+ # access guard consumed a grant first and a strict-mode discipline deny from
175
+ # the next sub-hook then won the merge — grant spent, nothing written, the
176
+ # user asked twice for one edit. The same hole ran the other way for a
177
+ # discipline grant followed by a policy deny.
178
+ _DEFER_CONSUME = {"hook_access_guard", "hook_pretool_enforce"}
179
+
169
180
  # Modules whose failure must DENY rather than fall through (fail closed).
170
181
  # Scoped to write-class tools + shell: a broken guard must not let mutations
171
182
  # sail through, but read-class fail-open avoids bricking whole sessions.
@@ -189,6 +200,77 @@ def _fail_closed_deny(module_name: str, err: str) -> dict:
189
200
  }
190
201
 
191
202
 
203
+ def _is_deny(out) -> bool:
204
+ hso = out.get("hookSpecificOutput") if isinstance(out, dict) else None
205
+ return isinstance(hso, dict) and hso.get("permissionDecision") == "deny"
206
+
207
+
208
+ def _call_run(run_fn, module_name: str, event: str, payload: dict,
209
+ project_path):
210
+ """Invoke a sub-hook, asking the grant-aware ones to defer consumption.
211
+
212
+ Signature-checked rather than assumed: tests stub these modules with
213
+ two-argument lambdas, and a legacy single-file hook must keep working.
214
+ """
215
+ if event == "pretool" and module_name in _DEFER_CONSUME:
216
+ try:
217
+ accepts = "defer_consume" in inspect.signature(run_fn).parameters
218
+ except (TypeError, ValueError):
219
+ accepts = False
220
+ if accepts:
221
+ return run_fn(payload, project_path, defer_consume=True)
222
+ return run_fn(payload, project_path)
223
+
224
+
225
+ def _grant_lost_deny() -> dict:
226
+ return {
227
+ "hookSpecificOutput": {
228
+ "hookEventName": "PreToolUse",
229
+ "permissionDecision": "deny",
230
+ "permissionDecisionReason": (
231
+ "[c3-override:spent] the override grant that would have "
232
+ "allowed this call was used up between check and use (a "
233
+ "concurrent call spent it). The rule stands — ask again "
234
+ "with c3_override if it is still needed."
235
+ ),
236
+ }
237
+ }
238
+
239
+
240
+ def _settle_grants(outputs: list) -> list:
241
+ """Burn deferred grant uses — only when the merged verdict is allow.
242
+
243
+ Any deny among the outputs means the call will not run, so every
244
+ `_on_allow` is dropped unrun and the grants stay live for the retry. On
245
+ an allow each consumer runs; the line it returns replaces the
246
+ provisional one (it carries the post-consumption uses count). A consumer
247
+ that finds nothing left — the grant was spent by a concurrent call
248
+ between peek and settle — turns its allow into a deny: the call must
249
+ not proceed on a grant that no longer exists.
250
+ """
251
+ denied = any(_is_deny(o) for o in outputs)
252
+ settled = []
253
+ for out in outputs:
254
+ if not isinstance(out, dict):
255
+ settled.append(out)
256
+ continue
257
+ consume = out.pop("_on_allow", None)
258
+ if consume is None or denied:
259
+ settled.append(out)
260
+ continue
261
+ try:
262
+ line = consume()
263
+ except Exception as exc:
264
+ log_hook_error("hook_dispatch:settle", exc)
265
+ line = None
266
+ if line:
267
+ out["additionalContext"] = str(line)
268
+ settled.append(out)
269
+ else:
270
+ settled.append(_grant_lost_deny())
271
+ return settled
272
+
273
+
192
274
  def _load_run(module_name: str):
193
275
  """Import cli.<module_name> and return its run() (cached).
194
276
 
@@ -353,7 +435,7 @@ def dispatch(event: str, payload: dict, project_path: Path | None = None) -> dic
353
435
  outputs.append(_fail_closed_deny(module_name, err))
354
436
  continue
355
437
  try:
356
- out = run_fn(payload, project_path)
438
+ out = _call_run(run_fn, module_name, event, payload, project_path)
357
439
  if out:
358
440
  outputs.append(out)
359
441
  except Exception as exc:
@@ -368,7 +450,15 @@ def dispatch(event: str, payload: dict, project_path: Path | None = None) -> dic
368
450
  # Critical state-layer warnings (corrupt enforcement_state.json)
369
451
  # become visible instead of silently disabling enforcement.
370
452
  warnings.extend(_hook_utils.drain_state_warnings())
453
+ # A PreToolUse deny is final — merge keeps the first deny and no
454
+ # allow readmits it — so the sub-hooks after it have nothing to add,
455
+ # and running them could only spend a grant or log a block for a
456
+ # call that will not happen.
457
+ if event == "pretool" and outputs and _is_deny(outputs[-1]):
458
+ break
371
459
 
460
+ if event == "pretool":
461
+ outputs = _settle_grants(outputs)
372
462
  return merge_outputs(outputs, warnings, event=event, host=host)
373
463
 
374
464
 
@@ -504,11 +504,12 @@ def _agent_cannot_comply(payload: dict, base: Path) -> str | None:
504
504
 
505
505
 
506
506
  def _override_allows(base: Path, tool_name: str, tool_input: dict,
507
- session_id: str) -> str | None:
507
+ session_id: str, peek: bool = False) -> str | None:
508
508
  """A live discipline grant for this exact write, or None (spec §5).
509
509
 
510
510
  Runs AFTER `_vault_denial`, which stays unconditional: no grant, no
511
511
  config value and no approval can open a native write path to the vault.
512
+ ``peek`` looks without consuming (see hook_dispatch's settle step).
512
513
  """
513
514
  target = str(tool_input.get("file_path")
514
515
  or tool_input.get("notebook_path") or "")
@@ -517,16 +518,23 @@ def _override_allows(base: Path, tool_name: str, tool_input: dict,
517
518
  try:
518
519
  from services import override_grants as og # noqa: PLC0415 — lazy
519
520
  return og.gate_discipline(base, tool=tool_name, path=target,
520
- session_id=session_id)
521
+ session_id=session_id, peek=peek)
521
522
  except Exception:
522
523
  return None # fail closed: no grant, ordinary block
523
524
 
524
525
 
525
- def run(payload: dict, project_path: Path | None = None) -> dict | None:
526
+ def run(payload: dict, project_path: Path | None = None,
527
+ defer_consume: bool = False) -> dict | None:
526
528
  """Core enforcement logic — importable by the dispatcher and tests.
527
529
 
528
530
  Returns a hook-output dict ({"additionalContext": ...} or a deny
529
531
  {"hookSpecificOutput": ...}) or None when the call passes silently.
532
+
533
+ ``defer_consume`` (dispatcher only): a discipline grant is peeked, not
534
+ burned, and the output carries an ``_on_allow`` callable the dispatcher
535
+ runs once the whole PreToolUse verdict is allow — so a policy deny from
536
+ hook_access_guard can no longer spend a grant on a write that never
537
+ happens (v2.102.0).
530
538
  """
531
539
  tool_name = normalize_tool_name(payload.get("tool_name", ""))
532
540
 
@@ -591,9 +599,20 @@ def run(payload: dict, project_path: Path | None = None) -> dict | None:
591
599
  # the ledger stays complete either way; what is lost is the pre-edit
592
600
  # snapshot c3_edit would have taken.
593
601
  if tool_name in blocked_tools and mode == "strict":
594
- granted = _override_allows(base, tool_name, tool_input, session_id)
602
+ granted = _override_allows(base, tool_name, tool_input, session_id,
603
+ peek=defer_consume)
595
604
  if granted:
596
- return _with_warnings({"additionalContext": granted}, policy)
605
+ out = _with_warnings({"additionalContext": granted}, policy)
606
+ if defer_consume:
607
+ def _consume():
608
+ line = _override_allows(base, tool_name, tool_input,
609
+ session_id)
610
+ if not line:
611
+ return None
612
+ return _with_warnings({"additionalContext": line},
613
+ policy)["additionalContext"]
614
+ out["_on_allow"] = _consume
615
+ return out
597
616
  cannot = _agent_cannot_comply(payload, base)
598
617
  if cannot:
599
618
  return _with_warnings({
@@ -832,7 +832,7 @@ async def c3_override(action: str = "list", path: str = "", tool: str = "",
832
832
  layer: str = "", timeout_s: int = 60,
833
833
  ctx: Context = None) -> str:
834
834
  """ASK A HUMAN to allow ONE blocked call — never a policy change.
835
- actions: request, status, wait (blocks <=180s), list, withdraw.
835
+ actions: request, status, wait (blocks timeout_s: default 60, max 180), list, withdraw.
836
836
  request: path = the exact blocked path; why = one concrete sentence for the human.
837
837
  A yes mints a single-use, session-bound, path-exact grant with a short TTL; the
838
838
  rule that blocked you stays in force. There is NO approve action here — only the
@@ -25,27 +25,43 @@ import os
25
25
  def session_id(svc) -> str:
26
26
  """This agent's identity, for leases and for grants.
27
27
 
28
- Falls back to the process id, never to "". Two agents that both resolved
29
- to "" would count as ONE session and stop blocking each other — the exact
30
- opposite of what a lock is for. Each Claude Code session runs its own
31
- c3-mcp process, so the pid is a faithful stand-in.
28
+ Prefers the HOST session id (``current_session["host_session_id"]``,
29
+ from ``CLAUDE_CODE_SESSION_ID`` services.session_manager) over C3's
30
+ own timestamp id. The PreToolUse hooks file requests and consume grants
31
+ under the id the host puts in every hook payload, which is that same
32
+ value; until v2.102.0 this surface used C3's own id instead, so every
33
+ hook-filed confirm hold answered ``c3_override(action='wait')`` with
34
+ "belongs to another session" and the flow CLAUDE.md mandates could not
35
+ complete. One id on both surfaces is what makes it true.
36
+
37
+ Falls back to C3's session id, then to the process id, never to "". Two
38
+ agents that both resolved to "" would count as ONE session and stop
39
+ blocking each other — the exact opposite of what a lock is for. Each
40
+ Claude Code session runs its own c3-mcp process, so the pid is a
41
+ faithful stand-in.
32
42
 
33
43
  It is also the right identity for c3_project: that proxy builds a runtime
34
44
  for the TARGET project but runs inside the CALLER's process, so the pid
35
45
  keeps the edit attributed to the agent that actually asked for it.
36
46
  """
37
47
  session = getattr(getattr(svc, "session_mgr", None), "current_session", None) or {}
48
+ host = str(session.get("host_session_id") or "").strip()
49
+ if host:
50
+ return host
38
51
  return str(session.get("id", "") or "") or f"pid-{os.getpid()}"
39
52
 
40
53
 
41
- def allow(svc, denial, *, tool: str, op: str, path) -> str | None:
54
+ def allow(svc, denial, *, tool: str, op: str, path,
55
+ peek: bool = False) -> str | None:
42
56
  """The `[c3-override:granted]` line when a live grant permits this exact
43
57
  call, or None to stay denied.
44
58
 
45
59
  Consumes a use on success — so callers must invoke this only when they
46
- are about to proceed, never speculatively. Ordering inside `gate_access`
47
- is policy first, grants second, so a grant is void the instant the policy
48
- or its layer is switched off (§12.8).
60
+ are about to proceed, never speculatively; ``peek=True`` is the
61
+ speculative form (a look, no burn) for a caller that has to check
62
+ several targets before it knows whether the call runs at all. Ordering
63
+ inside `gate_access` is policy first, grants second, so a grant is void
64
+ the instant the policy or its layer is switched off (§12.8).
49
65
 
50
66
  Fail-closed by construction: any error resolving the grant store leaves
51
67
  the caller on its ordinary refusal path. A grant that cannot be read is
@@ -56,7 +72,8 @@ def allow(svc, denial, *, tool: str, op: str, path) -> str | None:
56
72
  try:
57
73
  from services import override_grants as og # noqa: PLC0415 — lazy
58
74
  return og.gate_access(svc.project_path, denial, tool=tool, op=op,
59
- path=str(path), session_id=session_id(svc))
75
+ path=str(path), session_id=session_id(svc),
76
+ peek=peek)
60
77
  except Exception:
61
78
  return None
62
79
 
@@ -1,5 +1,8 @@
1
1
  """c3_artifacts — agent-config tracking: inventory, history, diff, restore."""
2
2
 
3
+ from pathlib import Path
4
+
5
+ from cli.tools import _grants
3
6
  from services import access_guard
4
7
 
5
8
  READ_ACTIONS = {"scan", "list", "history", "show", "diff", "status"}
@@ -139,8 +142,35 @@ def handle_artifacts(action, svc, finalize, *, artifact="", cls="", provider="",
139
142
  if not version:
140
143
  return done_resp("[artifacts:error] restore requires version.", "error")
141
144
  session = getattr(getattr(svc, "session_mgr", None), "current_session", None) or {}
145
+ sid = session.get("id", "")
146
+ granted = ""
142
147
  try:
143
- res = store.restore(artifact, version, session_id=session.get("id", ""))
148
+ try:
149
+ res = store.restore(artifact, version, session_id=sid,
150
+ confirm="hold")
151
+ except access_guard.AccessDenied as exc:
152
+ if exc.denial.kind != "confirm":
153
+ raise
154
+ # An agent-issued restore of a held file pauses like any
155
+ # other agent write to it (v2.102.0): the hold is filed
156
+ # against the artifact's ROOT so one approval covers a
157
+ # multi-file unit (a skill directory), and the retry — this
158
+ # same call — consumes that grant and restores under it.
159
+ entry = store.resolve(artifact) or {}
160
+ root = str(Path(str(svc.project_path))
161
+ / str(entry.get("root") or artifact))
162
+ granted = _grants.allow(svc, exc.denial, tool="c3_artifacts",
163
+ op="write", path=root) or ""
164
+ if not granted:
165
+ rid, note = _grants.confirm_request(
166
+ svc, exc.denial, tool="c3_artifacts", op="write",
167
+ path=root)
168
+ return done_resp(
169
+ access_guard.refusal(exc.denial, root, "write",
170
+ request_id=rid, request_note=note),
171
+ "access-confirm")
172
+ res = store.restore(artifact, version, session_id=sid,
173
+ confirm="approved")
144
174
  except access_guard.AccessDenied as exc:
145
175
  # Service layer raises; the tool boundary converts to the S1/S2
146
176
  # refusal string (docs/access-guard.md §3).
@@ -157,7 +187,8 @@ def handle_artifacts(action, svc, finalize, *, artifact="", cls="", provider="",
157
187
  session_id=session.get("id", ""))
158
188
  except Exception:
159
189
  pass
160
- lines = [f"[artifacts:restored] {res['id']} v{version} -> live as "
190
+ lines = ([granted] if granted else []) + [
191
+ f"[artifacts:restored] {res['id']} v{version} -> live as "
161
192
  f"v{res['new_version']} — {len(res['files_written'])} file(s) written"
162
193
  + (f", {len(res['files_removed'])} removed" if res["files_removed"] else "")]
163
194
  for w in res["warnings"]: