code-context-control 2.111.0__tar.gz → 2.113.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 (491) hide show
  1. {code_context_control-2.111.0 → code_context_control-2.113.0}/PKG-INFO +1 -1
  2. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/c3.py +1 -1
  3. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/mcp_server.py +28 -5
  4. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/shell.py +397 -62
  5. code_context_control-2.113.0/cli/tools/shell_parsers.py +683 -0
  6. code_context_control-2.113.0/cli/tools/shell_render.py +481 -0
  7. {code_context_control-2.111.0 → code_context_control-2.113.0}/code_context_control.egg-info/PKG-INFO +1 -1
  8. {code_context_control-2.111.0 → code_context_control-2.113.0}/code_context_control.egg-info/SOURCES.txt +6 -0
  9. {code_context_control-2.111.0 → code_context_control-2.113.0}/pyproject.toml +1 -1
  10. code_context_control-2.113.0/services/shell_output.py +1051 -0
  11. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_c3_shell.py +37 -12
  12. code_context_control-2.113.0/tests/test_shell_budget_s1.py +334 -0
  13. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_shell_eval.py +18 -9
  14. code_context_control-2.113.0/tests/test_shell_output.py +727 -0
  15. code_context_control-2.113.0/tests/test_shell_parsers_s2.py +566 -0
  16. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_shell_telemetry_s0.py +12 -5
  17. {code_context_control-2.111.0 → code_context_control-2.113.0}/LICENSE +0 -0
  18. {code_context_control-2.111.0 → code_context_control-2.113.0}/README.md +0 -0
  19. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/__init__.py +0 -0
  20. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/_hook_utils.py +0 -0
  21. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/_shell_writes.py +0 -0
  22. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/commands/__init__.py +0 -0
  23. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/commands/common.py +0 -0
  24. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/commands/parser.py +0 -0
  25. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/docs.html +0 -0
  26. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/edits.html +0 -0
  27. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/access.html +0 -0
  28. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/bitbucket.html +0 -0
  29. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/credentials.html +0 -0
  30. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/getting-started.html +0 -0
  31. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/index.html +0 -0
  32. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/masking.html +0 -0
  33. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/oracle.html +0 -0
  34. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/shared.css +0 -0
  35. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/tools.html +0 -0
  36. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/guide/workflow.html +0 -0
  37. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_access_guard.py +0 -0
  38. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_artifact.py +0 -0
  39. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_auto_snapshot.py +0 -0
  40. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_c3_signal.py +0 -0
  41. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_c3read.py +0 -0
  42. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_dispatch.py +0 -0
  43. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_edit_ledger.py +0 -0
  44. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_edit_unlock.py +0 -0
  45. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_filter.py +0 -0
  46. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_ghost_files.py +0 -0
  47. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_pretool_enforce.py +0 -0
  48. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_prompt_recall.py +0 -0
  49. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_read.py +0 -0
  50. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_session_stats.py +0 -0
  51. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hook_terse_advisor.py +0 -0
  52. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_server.py +0 -0
  53. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/app.js +0 -0
  54. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/add_project.js +0 -0
  55. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/config_editor.js +0 -0
  56. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  57. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/drill_health.js +0 -0
  58. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/drill_panel.js +0 -0
  59. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  60. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  61. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/drill_views.js +0 -0
  62. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/global_search.js +0 -0
  63. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_access.js +0 -0
  64. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_ci.js +0 -0
  65. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  66. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  67. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  68. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_locks.js +0 -0
  69. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  70. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  71. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/modals.js +0 -0
  72. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/project_card.js +0 -0
  73. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/project_tree.js +0 -0
  74. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/session_drawer.js +0 -0
  75. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/settings_modal.js +0 -0
  76. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/sidebar.js +0 -0
  77. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/summary_bar.js +0 -0
  78. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/task_board.js +0 -0
  79. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/toasts.js +0 -0
  80. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/components/topbar.js +0 -0
  81. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui/state.js +0 -0
  82. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/hub_ui.html +0 -0
  83. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/mcp_proxy.py +0 -0
  84. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/progress.py +0 -0
  85. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/server.py +0 -0
  86. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/__init__.py +0 -0
  87. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/_grants.py +0 -0
  88. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/_helpers.py +0 -0
  89. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/agent.py +0 -0
  90. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/artifacts.py +0 -0
  91. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/bitbucket.py +0 -0
  92. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/ci.py +0 -0
  93. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/compress.py +0 -0
  94. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/credentials.py +0 -0
  95. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/delegate.py +0 -0
  96. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/edit.py +0 -0
  97. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/edit_verify.py +0 -0
  98. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/edits.py +0 -0
  99. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/federate.py +0 -0
  100. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/filter.py +0 -0
  101. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/impact.py +0 -0
  102. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/jira.py +0 -0
  103. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/locks.py +0 -0
  104. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/memory.py +0 -0
  105. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/override.py +0 -0
  106. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/project.py +0 -0
  107. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/read.py +0 -0
  108. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/search.py +0 -0
  109. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/session.py +0 -0
  110. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/status.py +0 -0
  111. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/tasks.py +0 -0
  112. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/tools/validate.py +0 -0
  113. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/api.js +0 -0
  114. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/app.js +0 -0
  115. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/access.js +0 -0
  116. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/bitbucket.js +0 -0
  117. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/chat.js +0 -0
  118. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/credentials.js +0 -0
  119. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/dashboard.js +0 -0
  120. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/edits.js +0 -0
  121. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/enforcement.js +0 -0
  122. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/instructions.js +0 -0
  123. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/jira.js +0 -0
  124. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/memory.js +0 -0
  125. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/sessions.js +0 -0
  126. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/settings.js +0 -0
  127. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/sidebar.js +0 -0
  128. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/components/tasks.js +0 -0
  129. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/icons.js +0 -0
  130. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/pm_shared.js +0 -0
  131. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/shared.js +0 -0
  132. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui/theme.js +0 -0
  133. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui.html +0 -0
  134. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui_legacy.html +0 -0
  135. {code_context_control-2.111.0 → code_context_control-2.113.0}/cli/ui_nano.html +0 -0
  136. {code_context_control-2.111.0 → code_context_control-2.113.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  137. {code_context_control-2.111.0 → code_context_control-2.113.0}/code_context_control.egg-info/entry_points.txt +0 -0
  138. {code_context_control-2.111.0 → code_context_control-2.113.0}/code_context_control.egg-info/requires.txt +0 -0
  139. {code_context_control-2.111.0 → code_context_control-2.113.0}/code_context_control.egg-info/top_level.txt +0 -0
  140. {code_context_control-2.111.0 → code_context_control-2.113.0}/core/__init__.py +0 -0
  141. {code_context_control-2.111.0 → code_context_control-2.113.0}/core/config.py +0 -0
  142. {code_context_control-2.111.0 → code_context_control-2.113.0}/core/ide.py +0 -0
  143. {code_context_control-2.111.0 → code_context_control-2.113.0}/core/mcp_toml.py +0 -0
  144. {code_context_control-2.111.0 → code_context_control-2.113.0}/core/web_security.py +0 -0
  145. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/__init__.py +0 -0
  146. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/config.py +0 -0
  147. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/mcp_oracle.py +0 -0
  148. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/oracle_server.py +0 -0
  149. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/oracle_ui.html +0 -0
  150. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/__init__.py +0 -0
  151. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/activity_reporter.py +0 -0
  152. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/api_auth.py +0 -0
  153. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/c3_bridge.py +0 -0
  154. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/chat_engine.py +0 -0
  155. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/chat_poll.py +0 -0
  156. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/chat_store.py +0 -0
  157. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/cross_memory.py +0 -0
  158. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/discovery_audit.py +0 -0
  159. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/federated_graph.py +0 -0
  160. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/health_checker.py +0 -0
  161. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/insight_engine.py +0 -0
  162. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/local_session.py +0 -0
  163. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/memory_reader.py +0 -0
  164. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/memory_writer.py +0 -0
  165. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/mobile_api.py +0 -0
  166. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/project_scanner.py +0 -0
  167. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/review_agent.py +0 -0
  168. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/tool_executor.py +0 -0
  169. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/services/tool_registry.py +0 -0
  170. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/activity.js +0 -0
  171. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/agents.js +0 -0
  172. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/app.js +0 -0
  173. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/busy.js +0 -0
  174. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/chat/conversations.js +0 -0
  175. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/chat/input.js +0 -0
  176. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/chat/markdown.js +0 -0
  177. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/chat/send.js +0 -0
  178. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/chat/stream_renderer.js +0 -0
  179. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/chat/toolbar.js +0 -0
  180. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/core.js +0 -0
  181. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/crossgraph.js +0 -0
  182. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/header.js +0 -0
  183. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/insights.js +0 -0
  184. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/projects.js +0 -0
  185. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/qrcode.js +0 -0
  186. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/settings.js +0 -0
  187. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/suggestions.js +0 -0
  188. {code_context_control-2.111.0 → code_context_control-2.113.0}/oracle/ui/theme_tabs.js +0 -0
  189. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/__init__.py +0 -0
  190. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/access_guard.py +0 -0
  191. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/access_telemetry.py +0 -0
  192. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/activity_log.py +0 -0
  193. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/agent_base.py +0 -0
  194. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/agent_locks.py +0 -0
  195. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/agents.py +0 -0
  196. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/artifact_defs.py +0 -0
  197. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/artifact_store.py +0 -0
  198. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/auto_memory.py +0 -0
  199. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/background_service.py +0 -0
  200. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bench/__init__.py +0 -0
  201. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bench/external/__init__.py +0 -0
  202. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bench/external/aider_polyglot.py +0 -0
  203. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bench/external/swe_bench.py +0 -0
  204. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bench/search_eval.py +0 -0
  205. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bench/shell_eval.py +0 -0
  206. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/benchmark_dashboard.py +0 -0
  207. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bitbucket_client.py +0 -0
  208. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/bitbucket_credentials.py +0 -0
  209. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_act.py +0 -0
  210. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_cache.py +0 -0
  211. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_expr.py +0 -0
  212. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_failures.py +0 -0
  213. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_github.py +0 -0
  214. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_impact.py +0 -0
  215. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_intel.py +0 -0
  216. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_runner.py +0 -0
  217. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ci_workflow.py +0 -0
  218. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/circuit_breaker.py +0 -0
  219. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/claude_md.py +0 -0
  220. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/compressor.py +0 -0
  221. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/context_snapshot.py +0 -0
  222. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/conversation_store.py +0 -0
  223. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/cred_audit.py +0 -0
  224. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/cred_telemetry.py +0 -0
  225. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/credential_store.py +0 -0
  226. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/doc_index.py +0 -0
  227. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/e2e_benchmark.py +0 -0
  228. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/e2e_evaluator.py +0 -0
  229. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/e2e_tasks.py +0 -0
  230. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/edit_ledger.py +0 -0
  231. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/embedding_index.py +0 -0
  232. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/enforcement_policy.py +0 -0
  233. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/error_reporting.py +0 -0
  234. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/file_memory.py +0 -0
  235. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/git_context.py +0 -0
  236. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/hub_service.py +0 -0
  237. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/index_store.py +0 -0
  238. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/indexer.py +0 -0
  239. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/jira_client.py +0 -0
  240. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/jira_cloud.py +0 -0
  241. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/jira_credentials.py +0 -0
  242. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/jira_data_center.py +0 -0
  243. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/jira_links.py +0 -0
  244. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/lexical_index.py +0 -0
  245. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/mask_activation.py +0 -0
  246. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/mask_mirror.py +0 -0
  247. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/mask_presets.py +0 -0
  248. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory.py +0 -0
  249. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory_consolidator.py +0 -0
  250. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory_distiller.py +0 -0
  251. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory_graph.py +0 -0
  252. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory_grounder.py +0 -0
  253. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory_queue.py +0 -0
  254. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/memory_scorer.py +0 -0
  255. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/metrics.py +0 -0
  256. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/notifications.py +0 -0
  257. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ollama_bridge.py +0 -0
  258. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ollama_client.py +0 -0
  259. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/ollama_credentials.py +0 -0
  260. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/oracle_service.py +0 -0
  261. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/output_filter.py +0 -0
  262. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/override_grants.py +0 -0
  263. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/override_policy.py +0 -0
  264. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/override_requests.py +0 -0
  265. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/override_wake.py +0 -0
  266. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/parser.py +0 -0
  267. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/project_manager.py +0 -0
  268. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/project_runtime.py +0 -0
  269. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/protocol.py +0 -0
  270. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/proxy_state.py +0 -0
  271. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/repo_map.py +0 -0
  272. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/repo_shape.py +0 -0
  273. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/reranker.py +0 -0
  274. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/retention.py +0 -0
  275. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/retrieval.py +0 -0
  276. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/retrieval_broker.py +0 -0
  277. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/router.py +0 -0
  278. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/runtime.py +0 -0
  279. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/scanner.py +0 -0
  280. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/session_benchmark.py +0 -0
  281. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/session_manager.py +0 -0
  282. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/session_preloader.py +0 -0
  283. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/subprojects.py +0 -0
  284. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/task_store.py +0 -0
  285. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/telemetry.py +0 -0
  286. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/text_index.py +0 -0
  287. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/time_tracker.py +0 -0
  288. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/tool_classifier.py +0 -0
  289. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/transcript_index.py +0 -0
  290. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/validation_cache.py +0 -0
  291. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/vector_store.py +0 -0
  292. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/watcher.py +0 -0
  293. {code_context_control-2.111.0 → code_context_control-2.113.0}/services/win_subprocess.py +0 -0
  294. {code_context_control-2.111.0 → code_context_control-2.113.0}/setup.cfg +0 -0
  295. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_access_builtin_optout.py +0 -0
  296. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_access_guard.py +0 -0
  297. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_access_guard_meta.py +0 -0
  298. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_access_guard_shell_project.py +0 -0
  299. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_access_guard_surfaces.py +0 -0
  300. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_access_guard_wiring.py +0 -0
  301. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_activity_reporter.py +0 -0
  302. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_agent_locks.py +0 -0
  303. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_aider_polyglot.py +0 -0
  304. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_antigravity_transition.py +0 -0
  305. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_artifact_store.py +0 -0
  306. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_artifact_tool.py +0 -0
  307. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_background_service.py +0 -0
  308. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  309. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_bitbucket_client.py +0 -0
  310. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_bitbucket_config_fallback.py +0 -0
  311. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_bitbucket_credentials.py +0 -0
  312. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_bitbucket_tool.py +0 -0
  313. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_builtin_modes.py +0 -0
  314. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  315. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_chat_poll.py +0 -0
  316. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_act.py +0 -0
  317. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_cache.py +0 -0
  318. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_expr.py +0 -0
  319. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_impact.py +0 -0
  320. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_intel.py +0 -0
  321. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_runner.py +0 -0
  322. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ci_workflow.py +0 -0
  323. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_circuit_breaker.py +0 -0
  324. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_claude_md_merge.py +0 -0
  325. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_claude_md_updater.py +0 -0
  326. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_cli_smoke.py +0 -0
  327. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_compressor_large_file.py +0 -0
  328. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_confirm_guard.py +0 -0
  329. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_confirm_guard_wiring.py +0 -0
  330. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_cooccurrence_bounds.py +0 -0
  331. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_cred_audit.py +0 -0
  332. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_cred_telemetry.py +0 -0
  333. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_credential_login_kind.py +0 -0
  334. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_credential_store.py +0 -0
  335. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_credential_ui_parity.py +0 -0
  336. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_credentials_routes.py +0 -0
  337. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_credentials_tool.py +0 -0
  338. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_creds_cli_smoke.py +0 -0
  339. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_delegate_cascade.py +0 -0
  340. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_delegate_version_probe.py +0 -0
  341. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_e2e_benchmark.py +0 -0
  342. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_edit_ledger_deadline.py +0 -0
  343. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_edit_ledger_hook.py +0 -0
  344. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_edit_locking.py +0 -0
  345. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_edit_normalization.py +0 -0
  346. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_edit_verify.py +0 -0
  347. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_embedding_index_deadlock.py +0 -0
  348. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_enforcement_flip.py +0 -0
  349. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_enforcement_policy.py +0 -0
  350. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_enforcement_routes.py +0 -0
  351. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_env_parser.py +0 -0
  352. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  353. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_federated_graph.py +0 -0
  354. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_filter_backoff.py +0 -0
  355. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ghost_files.py +0 -0
  356. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ghost_generation.py +0 -0
  357. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_git_branch_awareness.py +0 -0
  358. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_access_guard.py +0 -0
  359. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_codex_compat.py +0 -0
  360. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_dispatch.py +0 -0
  361. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_pretool_enforce.py +0 -0
  362. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_prompt_recall.py +0 -0
  363. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_smoke.py +0 -0
  364. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_state.py +0 -0
  365. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hook_utf8_stdio.py +0 -0
  366. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_credentials_route.py +0 -0
  367. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_credentials_routes_write.py +0 -0
  368. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_enforcement_routes.py +0 -0
  369. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_inspect_api.py +0 -0
  370. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_locks_routes.py +0 -0
  371. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_oracle_service_routes.py +0 -0
  372. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_override_routes.py +0 -0
  373. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_server_smoke.py +0 -0
  374. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_hub_subproject_links.py +0 -0
  375. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ide_workflow_adaptation.py +0 -0
  376. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  377. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  378. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_init_scan.py +0 -0
  379. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_install_mcp_entrypoint.py +0 -0
  380. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_cli_smoke.py +0 -0
  381. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_client.py +0 -0
  382. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_config_fallback.py +0 -0
  383. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_credentials.py +0 -0
  384. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_links.py +0 -0
  385. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_routes.py +0 -0
  386. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_jira_tool.py +0 -0
  387. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_lazy_store_init.py +0 -0
  388. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mask_guard.py +0 -0
  389. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mask_routes.py +0 -0
  390. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mask_surfaces.py +0 -0
  391. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mcp_host_guard.py +0 -0
  392. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mcp_memory_timeout.py +0 -0
  393. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mcp_server_smoke.py +0 -0
  394. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mcp_toml.py +0 -0
  395. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_memory_distiller.py +0 -0
  396. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_memory_graph_api.py +0 -0
  397. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_memory_queue.py +0 -0
  398. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_memory_system.py +0 -0
  399. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mobile_api.py +0 -0
  400. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mobile_extras.py +0 -0
  401. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mobile_override_routes.py +0 -0
  402. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_mobile_security_api.py +0 -0
  403. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_no_stale_model_pins.py +0 -0
  404. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_notification_dedup.py +0 -0
  405. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_notification_discipline.py +0 -0
  406. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ollama_credentials.py +0 -0
  407. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_api_auth.py +0 -0
  408. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_apikey_api.py +0 -0
  409. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_c3_bridge.py +0 -0
  410. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_chat_engine.py +0 -0
  411. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_chat_store.py +0 -0
  412. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_discovery_api.py +0 -0
  413. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_discovery_audit.py +0 -0
  414. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_health_probe.py +0 -0
  415. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_local_auth.py +0 -0
  416. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_ollama_bridge.py +0 -0
  417. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_scanner_cache.py +0 -0
  418. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_security_fixes.py +0 -0
  419. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_session_bootstrap.py +0 -0
  420. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_subproject_awareness.py +0 -0
  421. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_oracle_ui_bundle.py +0 -0
  422. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_output_filter.py +0 -0
  423. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_override_grants.py +0 -0
  424. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_override_grants_mcp.py +0 -0
  425. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_override_requests.py +0 -0
  426. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_override_wake.py +0 -0
  427. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_permissions.py +0 -0
  428. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_pm_api.py +0 -0
  429. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_project_manager.py +0 -0
  430. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_project_manager_merge.py +0 -0
  431. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_project_tool.py +0 -0
  432. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_read_coercion.py +0 -0
  433. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_read_edit_parity.py +0 -0
  434. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_related_facts.py +0 -0
  435. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_repo_map.py +0 -0
  436. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_repo_shape.py +0 -0
  437. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_response_boilerplate.py +0 -0
  438. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_retention.py +0 -0
  439. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_review_digest.py +0 -0
  440. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_eval.py +0 -0
  441. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_p1.py +0 -0
  442. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_p2.py +0 -0
  443. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_p2b.py +0 -0
  444. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_p3.py +0 -0
  445. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_p4.py +0 -0
  446. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_search_p5.py +0 -0
  447. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_service_durability.py +0 -0
  448. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_session_benchmark.py +0 -0
  449. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_session_budget.py +0 -0
  450. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_shell_creds.py +0 -0
  451. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_shell_robustness.py +0 -0
  452. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_shell_write_ledger.py +0 -0
  453. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_shell_writes.py +0 -0
  454. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_subproject_clear_scope.py +0 -0
  455. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_subproject_depth_consumers.py +0 -0
  456. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_subproject_exclusion.py +0 -0
  457. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_subproject_federation.py +0 -0
  458. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_subproject_linkage_api.py +0 -0
  459. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_subprojects.py +0 -0
  460. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_swe_bench.py +0 -0
  461. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_task_store.py +0 -0
  462. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_task_tool.py +0 -0
  463. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_time_tracker.py +0 -0
  464. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_token_telemetry.py +0 -0
  465. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_token_tracking.py +0 -0
  466. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_tool_registry.py +0 -0
  467. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_ui_jsx_syntax.py +0 -0
  468. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_upgrade_and_version.py +0 -0
  469. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_validate.py +0 -0
  470. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_vault_write_guard.py +0 -0
  471. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_version_sync.py +0 -0
  472. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_web_security.py +0 -0
  473. {code_context_control-2.111.0 → code_context_control-2.113.0}/tests/test_windows_reliability.py +0 -0
  474. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/__init__.py +0 -0
  475. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/backend.py +0 -0
  476. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/main.py +0 -0
  477. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/__init__.py +0 -0
  478. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/benchmark_view.py +0 -0
  479. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/claudemd_view.py +0 -0
  480. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/compress_view.py +0 -0
  481. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/index_view.py +0 -0
  482. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/init_view.py +0 -0
  483. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/mcp_view.py +0 -0
  484. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/optimize_view.py +0 -0
  485. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/pipe_view.py +0 -0
  486. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/projects_view.py +0 -0
  487. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/search_view.py +0 -0
  488. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/session_view.py +0 -0
  489. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/stats.py +0 -0
  490. {code_context_control-2.111.0 → code_context_control-2.113.0}/tui/screens/ui_view.py +0 -0
  491. {code_context_control-2.111.0 → code_context_control-2.113.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.111.0
3
+ Version: 2.113.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.111.0"
95
+ __version__ = "2.113.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -883,16 +883,36 @@ async def c3_impact(target: str, file_path: str = "", mode: str = "symbol",
883
883
 
884
884
 
885
885
  @mcp.tool()
886
- async def c3_shell(cmd: str, cwd: str = "", timeout: int = 60,
886
+ async def c3_shell(cmd: str = "", cwd: str = "", timeout: int = 60,
887
887
  filter_output: bool = True, log: bool = True,
888
888
  env_creds: str = "",
889
+ output_id: str = "", output_action: str = "",
890
+ pattern: str = "", lines: str = "", stream: str = "stdout",
891
+ max_bytes: int = 0,
889
892
  ctx: Context = None) -> str:
890
- """EXECUTE shell command — structured returns, auto-filter, ledger-aware.
893
+ """EXECUTE shell command — structured returns, budgeted output, ledger-aware.
891
894
  Use for tests, git, build, scripts. Returns exit_code/stdout/stderr/duration_ms.
892
- Auto-filters stdout >30 lines; auto-logs git mutations to the edit ledger.
895
+ The response never exceeds 18 KiB (max_bytes may only lower it): a stream that does
896
+ not fit is clipped line by line and windowed head/tail with a note naming an
897
+ output_id; the raw streams are kept for 3 days OUTSIDE the project. Page them back
898
+ with output_id='o-…' and output_action='read' (lines='120-180'), 'search'
899
+ (pattern=regex), 'tail' (lines='80') or 'delete', stream='stdout'|'stderr'; an id
900
+ resolves only for this project and this session under the current access rules.
901
+ The echoed command is its first line; a multi-line command shows (N lines, M chars,
902
+ sha256). Under budget the output is complete: ANSI/control sequences stripped, each
903
+ line's \\r progress rewrites reduced to their final state, runs of 3+ identical
904
+ consecutive lines folded to one plus ` [x N]` (the header says `[collapsed …]` and the
905
+ raw streams are kept by id). Over budget, pytest/unittest/cargo/tsc/jest/vitest
906
+ failure blocks, error lines and totals are kept first (each announced by `[La-b: why]`),
907
+ then a bounded head/tail; a recognised runner past 30 lines gets a `--- summary ---`
908
+ (totals + one line per failing test). filter_output=False skips the two collapses so
909
+ every line comes through as written — escapes are still stripped, summaries and
910
+ priority regions still apply, and the cap is never lifted. Auto-logs git mutations
911
+ to the edit ledger.
893
912
  Credentials: env_creds='NAME1,NAME2' injects vault entries as env vars, and
894
913
  {{cred:NAME}} inside cmd expands server-side — decoded values never enter
895
- model context (see c3_credentials; echoed values are auto-redacted).
914
+ model context (see c3_credentials; echoed values are auto-redacted, in the
915
+ response and in the kept output).
896
916
  Best-effort block of catastrophic commands (rm -rf of /, a top-level system dir, or
897
917
  $HOME/~; fork bombs; whole-drive wipes) — a guard, NOT a sandbox. Soft-warns on
898
918
  --force, --no-verify, reset --hard.
@@ -904,7 +924,10 @@ async def c3_shell(cmd: str, cwd: str = "", timeout: int = 60,
904
924
  return _finalize_response(ctx, name, args, resp, summ, **kw)
905
925
 
906
926
  return await handle_shell(cmd, cwd, timeout, filter_output, log, svc, finalize,
907
- env_creds=env_creds)
927
+ env_creds=env_creds, output_id=output_id,
928
+ output_action=output_action, pattern=pattern,
929
+ lines=lines or None, stream=stream or "stdout",
930
+ max_bytes=max_bytes or None)
908
931
 
909
932
 
910
933
  @mcp.tool()
@@ -2,8 +2,10 @@
2
2
 
3
3
  Wraps subprocess.Popen with the Windows-safe pattern from
4
4
  services/edit_ledger.py::_git_combined (Popen + taskkill /F /T + stdin=DEVNULL).
5
- Auto-filters long stdout via handle_filter, auto-logs git mutations
6
- to the edit ledger, and accounts stdout tokens against session budget.
5
+ Budgets and shapes the response (cli/tools/shell_render.py + shell_parsers.py:
6
+ normalised streams, runner-aware priority regions, omission only over budget),
7
+ auto-logs git mutations to the edit ledger, and accounts stdout tokens against
8
+ session budget.
7
9
 
8
10
  Shell selection: on Windows, commands are run through Git Bash (bash.exe) when
9
11
  it is available, so c3_shell speaks the same POSIX dialect as the native Bash
@@ -28,9 +30,31 @@ from pathlib import Path
28
30
  from cli._shell_writes import shell_write_targets
29
31
  from cli.tools import _grants
30
32
  from cli.tools._helpers import finalize_with_tokens
31
- from cli.tools.filter import handle_filter
33
+ from cli.tools.shell_parsers import (
34
+ SUMMARY_MIN_LINES,
35
+ detect_runner,
36
+ normalize_stream,
37
+ priority_regions,
38
+ structured_tail,
39
+ )
40
+ from cli.tools.shell_render import (
41
+ allocate,
42
+ cmd_display,
43
+ effective_budget,
44
+ grep_pattern,
45
+ human_bytes,
46
+ shape_stream,
47
+ split_lines,
48
+ split_preview,
49
+ )
32
50
  from core import count_tokens
33
51
  from services import access_guard
52
+ from services.shell_output import (
53
+ TEXT_MAX_BYTES,
54
+ OutputAccessError,
55
+ ShellCapture,
56
+ ShellOutputStore,
57
+ )
34
58
 
35
59
  # Commands that mutate repo state — trigger edit-ledger refresh after success.
36
60
  _GIT_MUTATING = re.compile(
@@ -211,9 +235,28 @@ def _kill_tree(proc: subprocess.Popen) -> None:
211
235
  pass
212
236
 
213
237
 
238
+ def _default_redact(text: str) -> str:
239
+ """Scrub vault values from a piece of output; never raises."""
240
+ try:
241
+ from services import credential_store as _creds
242
+ return _creds.redact_text(text)
243
+ except Exception:
244
+ return text
245
+
246
+
214
247
  def _run_sync(cmd: str, cwd: str, timeout: int,
215
- extra_env: dict | None = None) -> dict:
216
- """Blocking subprocess run with hard kill on timeout. Returns structured dict."""
248
+ extra_env: dict | None = None, *, redact=_default_redact,
249
+ spool_dir=None) -> dict:
250
+ """Blocking subprocess run with hard kill on timeout. Returns structured dict.
251
+
252
+ Since 2.112.0 the child's streams are not buffered in memory: a
253
+ ShellCapture pumps them to spool files with bounded head/tail previews
254
+ (services/shell_output.py). ``stdout``/``stderr`` in the result carry the
255
+ whole text when the stream is at most TEXT_MAX_BYTES and None otherwise;
256
+ ``capture`` carries the stats and previews either way. Callers that
257
+ patch this function with a plain dict (tests) still work: no ``capture``
258
+ means "small output, text present".
259
+ """
217
260
  start = time.time()
218
261
  bash = _select_bash()
219
262
  if bash:
@@ -229,7 +272,6 @@ def _run_sync(cmd: str, cwd: str, timeout: int,
229
272
  proc = subprocess.Popen(
230
273
  popen_target, shell=use_shell, cwd=cwd,
231
274
  stdout=subprocess.PIPE, stderr=subprocess.PIPE,
232
- text=True, encoding="utf-8", errors="replace",
233
275
  **_popen_kwargs(extra_env),
234
276
  )
235
277
  except (OSError, ValueError) as exc:
@@ -241,24 +283,27 @@ def _run_sync(cmd: str, cwd: str, timeout: int,
241
283
  "timed_out": False,
242
284
  "shell": shell_name,
243
285
  }
244
- timed_out = False
245
- try:
246
- stdout, stderr = proc.communicate(timeout=timeout)
247
- except subprocess.TimeoutExpired:
248
- _kill_tree(proc)
286
+ if spool_dir is None:
287
+ spool_dir = ShellOutputStore().spool_dir()
288
+ capture = ShellCapture(proc, spool_dir, redact=redact, kill_tree=_kill_tree)
289
+ timed_out = capture.wait(timeout)
290
+
291
+ def _text(stream: str):
292
+ if getattr(capture.stats, stream).bytes > TEXT_MAX_BYTES:
293
+ return None
249
294
  try:
250
- stdout, stderr = proc.communicate(timeout=2)
295
+ return capture.text(stream)
251
296
  except Exception:
252
- stdout, stderr = "", ""
253
- timed_out = True
297
+ return None
254
298
 
255
299
  return {
256
300
  "exit_code": -1 if timed_out else (proc.returncode or 0),
257
- "stdout": stdout or "",
258
- "stderr": stderr or "",
259
- "duration_ms": round((time.time() - start) * 1000),
301
+ "stdout": _text("stdout"),
302
+ "stderr": _text("stderr"),
303
+ "duration_ms": capture.duration_ms or round((time.time() - start) * 1000),
260
304
  "timed_out": timed_out,
261
305
  "shell": shell_name,
306
+ "capture": capture,
262
307
  }
263
308
 
264
309
 
@@ -428,54 +473,195 @@ def _longest_line(*texts: str) -> int:
428
473
  return longest
429
474
 
430
475
 
476
+ def _stream_facts(result: dict, stream: str) -> dict:
477
+ """Size, line count, longest line and available text/previews of one stream."""
478
+ text = result.get(stream)
479
+ capture = result.get("capture")
480
+ if capture is not None:
481
+ st = getattr(capture.stats, stream)
482
+ return {"text": text, "head": st.head, "tail": st.tail, "bytes": st.bytes,
483
+ "lines": st.lines, "longest": st.longest_line}
484
+ text = text or ""
485
+ return {"text": text, "head": "", "tail": "", "bytes": len(text.encode("utf-8", errors="replace")),
486
+ "lines": text.count("\n") + (1 if text and not text.endswith("\n") else 0),
487
+ "longest": _longest_line(text)}
488
+
489
+
490
+ _ZERO_NORM = {"ansi_stripped": 0, "cr_collapsed": 0, "dup_collapsed": 0}
491
+
492
+
493
+ def _normalised(facts: dict, *, collapse: bool, fuzzy: bool) -> dict:
494
+ """One stream after ``normalize_stream``: the text (or previews), its
495
+ logical lines in the index space ``shape_stream`` uses, the raw line
496
+ numbers of those lines when folding shifted them, and the counters."""
497
+ info = dict(_ZERO_NORM)
498
+ if facts["text"] is not None:
499
+ text, ni = normalize_stream(facts["text"], collapse=collapse, fuzzy_dups=fuzzy)
500
+ for k in _ZERO_NORM:
501
+ info[k] = ni[k]
502
+ return {"text": text, "head": "", "tail": "", "lines": split_lines(text),
503
+ "bytes": len(text.encode("utf-8", errors="replace")),
504
+ "total_lines": facts["lines"], "numbers": ni["line_numbers"],
505
+ "tail_numbers": None, "info": info}
506
+ head, hi = normalize_stream(facts["head"], collapse=collapse, fuzzy_dups=fuzzy)
507
+ tail, ti = normalize_stream(facts["tail"], collapse=collapse, fuzzy_dups=fuzzy)
508
+ for k in _ZERO_NORM:
509
+ info[k] = hi[k] + ti[k]
510
+ h_lines, t_lines, _torn = split_preview(head, tail)
511
+ return {"text": None, "head": head, "tail": tail, "lines": h_lines + t_lines,
512
+ "bytes": facts["bytes"], "total_lines": facts["lines"],
513
+ "numbers": hi["line_numbers"], "tail_numbers": ti["line_numbers"], "info": info}
514
+
515
+
516
+ def _region_line_count(regions) -> int:
517
+ seen: set[int] = set()
518
+ for a, b, _why in regions:
519
+ seen.update(range(a, b + 1))
520
+ return len(seen)
521
+
522
+
431
523
  def render_shell_response(cmd: str, result: dict, svc, *,
432
524
  filter_output: bool = True, warn: str = "",
433
525
  capped_note: str = "", touched_files=(),
434
- cred_names=(), swept_ghosts=()) -> tuple[str, dict]:
526
+ cred_names=(), swept_ghosts=(),
527
+ max_bytes: int | None = None) -> tuple[str, dict]:
435
528
  """Turn a finished command into the body the agent reads, plus its stats.
436
529
 
437
530
  Pure with respect to the subprocess: ``result`` is the dict _run_sync
438
- returns (exit_code, stdout, stderr, duration_ms, timed_out, shell), so
439
- the shell-eval harness (services/bench/shell_eval.py) can feed captured
440
- or synthetic streams through exactly the code path a live call uses.
441
- Runs the same auto-filter handle_shell always ran — only stdout, only when
442
- it has more than _FILTER_THRESHOLD_LINES newlines, never for a git
443
- diagnostic — and assembles the same sections in the same order.
531
+ returns (exit_code, stdout, stderr, duration_ms, timed_out, shell, and
532
+ since 2.112.0 an optional ``capture`` with stats and head/tail previews
533
+ for a stream too large to hold), so the shell-eval harness
534
+ (services/bench/shell_eval.py) can feed captured or synthetic streams
535
+ through exactly the code path a live call uses.
536
+
537
+ Budget (2.112.0, cli/tools/shell_render.py): the rendered body never
538
+ exceeds ``effective_budget()`` bytes — 18 KiB by default, 22 KiB ceiling,
539
+ ``max_bytes`` and ``hybrid.shell_budget_bytes`` may only lower it,
540
+ ``filter_output=False`` never lifts it.
541
+
542
+ Shaping (2.113.0, S2, cli/tools/shell_parsers.py) — the Cod rule: strip
543
+ ANSI and collapse ``\\r`` rewrites always, otherwise preserve complete
544
+ under-budget output, run the parsers always but omit only over budget:
545
+
546
+ 1. Both streams are normalised: ``\\r\\n`` → ``\\n``, ANSI escape and
547
+ control sequences stripped (not a loss), each line's ``\\r``
548
+ progress rewrites collapsed to the final state, runs of three or
549
+ more identical consecutive lines folded to the first plus
550
+ `` [x N]``. The last two are a loss: the header says
551
+ ``[collapsed …]``, ``stats["filtered"]`` is set and the raw streams
552
+ are spilled. ``filter_output=False`` skips the two collapses (ANSI
553
+ is always stripped) and, as before, never lifts the byte cap.
554
+ 2. The runner is detected (pytest, unittest, cargo/rustc, tsc, jest,
555
+ vitest) and its priority regions computed on every call; a stream
556
+ that fits its allocation passes through whole regardless. A stream
557
+ that does not is re-normalised with the duplicate test widened to
558
+ lines differing only in digits / hex / timestamps, then shaped:
559
+ priority regions first (each announced by ``[La-b: why]`` when not
560
+ contiguous), head/tail with the rest, one omission note naming the
561
+ output id. Unrecognised output uses generic error anchors, capped at
562
+ 60% of the allocation so head and tail always keep something.
563
+ 3. For a recognised runner with more than SUMMARY_MIN_LINES lines a
564
+ ``--- summary ---`` section (totals + one line per failing test,
565
+ cap 20) is appended and counted inside the budget.
566
+
567
+ The legacy ``>30 newlines → handle_filter`` path is gone from this
568
+ function (``c3_filter`` the tool is untouched). Whenever anything was
569
+ dropped — collapsed, clipped or windowed — ``stats["needs_spill"]``
570
+ tells handle_shell to keep the raw streams.
444
571
 
445
572
  ``stats`` is what telemetry records about the call (see
446
573
  SessionManager.record_tool_tokens ``detail``): stdout_bytes and
447
- stderr_bytes are measured BEFORE filtering so a later budget phase can
448
- size the band it changes; longest_line is the tell for the single-line
449
- monsters (minified bundles, JSONL) that a newline-count trigger never
450
- sees; filtered/spilled/output_id describe what this renderer did.
574
+ stderr_bytes are measured on the RAW streams; longest_line is the tell
575
+ for the single-line monsters (minified bundles, JSONL) that a
576
+ newline-count trigger never sees; filtered (a lossy collapse happened),
577
+ spilled, output_id, runner, ansi_stripped, cr_collapsed, dup_collapsed
578
+ and priority_lines (lines the parsers marked, both streams) describe
579
+ what this renderer did.
451
580
  """
452
- raw_stdout = result.get("stdout") or ""
453
- raw_stderr = result.get("stderr") or ""
581
+ out = _stream_facts(result, "stdout")
582
+ err = _stream_facts(result, "stderr")
583
+ capture = result.get("capture")
454
584
  stats: dict = {
455
585
  "exit_code": result.get("exit_code"),
456
586
  "timed_out": bool(result.get("timed_out")),
457
- "stdout_bytes": len(raw_stdout.encode("utf-8", errors="replace")),
458
- "stderr_bytes": len(raw_stderr.encode("utf-8", errors="replace")),
459
- "longest_line": _longest_line(raw_stdout, raw_stderr),
587
+ "stdout_bytes": out["bytes"],
588
+ "stderr_bytes": err["bytes"],
589
+ "longest_line": max(out["longest"], err["longest"]),
460
590
  "filtered": False,
461
591
  "spilled": False,
462
592
  "output_id": None,
463
593
  "cmd_class": _cmd_class(cmd),
594
+ "needs_spill": False,
595
+ "runner": None,
596
+ "ansi_stripped": 0,
597
+ "cr_collapsed": 0,
598
+ "dup_collapsed": 0,
599
+ "priority_lines": 0,
464
600
  }
465
601
 
466
- stdout = raw_stdout
467
- filtered_note = ""
468
- if (filter_output and raw_stdout.count("\n") > _FILTER_THRESHOLD_LINES
469
- and not _GIT_DIAGNOSTIC.search(cmd)):
470
- try:
471
- stdout = handle_filter(
472
- "", raw_stdout, "", 50, "smart", True,
473
- svc, lambda *a, **kw: a[2],
474
- )
475
- filtered_note = " [stdout filtered]"
476
- stats["filtered"] = True
477
- except Exception:
478
- stdout = raw_stdout
602
+ config_default = None
603
+ try:
604
+ config_default = (getattr(svc, "hybrid_config", None) or {}).get("shell_budget_bytes")
605
+ except Exception:
606
+ config_default = None
607
+ budget = effective_budget(max_bytes, config_default=config_default)
608
+ stats["budget_bytes"] = budget
609
+ output_id = getattr(capture, "output_id", None) if capture is not None else None
610
+ focus = grep_pattern(cmd)
611
+ collapse = bool(filter_output)
612
+
613
+ # 1. Normalise. A stream with previews only (over TEXT_MAX_BYTES) is over
614
+ # budget by definition, so it gets the widened duplicate test at once.
615
+ norm_out = _normalised(out, collapse=collapse, fuzzy=out["text"] is None)
616
+ norm_err = _normalised(err, collapse=collapse, fuzzy=err["text"] is None)
617
+
618
+ # 2. Runner, summary, allocation. The summary is measured first so the
619
+ # streams are allocated what is left of the budget.
620
+ runner = detect_runner(cmd, norm_out["text"] or norm_out["head"] + norm_out["tail"],
621
+ norm_err["text"] or norm_err["head"] + norm_err["tail"])
622
+ stats["runner"] = runner
623
+ summary = ""
624
+ if runner and (out["lines"] + err["lines"]) > SUMMARY_MIN_LINES:
625
+ summary = structured_tail(runner, norm_out["lines"] + norm_err["lines"], result.get("exit_code"))
626
+ summary_bytes = len(summary.encode("utf-8", errors="replace"))
627
+ out_alloc, err_alloc = allocate(budget - summary_bytes, norm_out["bytes"], norm_err["bytes"])
628
+
629
+ # 3. A stream that does not fit is re-normalised with fuzzy duplicates,
630
+ # then shaped around its priority regions.
631
+ if collapse and norm_out["text"] is not None and norm_out["bytes"] > out_alloc:
632
+ norm_out = _normalised(out, collapse=True, fuzzy=True)
633
+ if collapse and norm_err["text"] is not None and norm_err["bytes"] > err_alloc:
634
+ norm_err = _normalised(err, collapse=True, fuzzy=True)
635
+ for key in ("ansi_stripped", "cr_collapsed", "dup_collapsed"):
636
+ stats[key] = norm_out["info"][key] + norm_err["info"][key]
637
+ stats["filtered"] = bool(stats["cr_collapsed"] or stats["dup_collapsed"])
638
+
639
+ prio_out = priority_regions(runner, norm_out["lines"]) if norm_out["lines"] else []
640
+ prio_err = priority_regions(runner, norm_err["lines"]) if norm_err["lines"] else []
641
+ stats["priority_lines"] = _region_line_count(prio_out) + _region_line_count(prio_err)
642
+ share = 1.0 if runner else 0.6
643
+
644
+ shaped_out, info_out = shape_stream(
645
+ full_text=norm_out["text"], head=norm_out["head"], tail=norm_out["tail"],
646
+ total_bytes=norm_out["bytes"], total_lines=norm_out["total_lines"], alloc=out_alloc,
647
+ output_id=output_id, focus=focus, priority=prio_out, priority_share=share,
648
+ line_numbers=norm_out["numbers"], tail_line_numbers=norm_out["tail_numbers"])
649
+ if err["bytes"] > 0 or (err["text"] or "").strip():
650
+ shaped_err, info_err = shape_stream(
651
+ full_text=norm_err["text"], head=norm_err["head"], tail=norm_err["tail"],
652
+ total_bytes=norm_err["bytes"], total_lines=norm_err["total_lines"], alloc=err_alloc,
653
+ output_id=output_id, focus=focus, priority=prio_err, priority_share=share,
654
+ line_numbers=norm_err["numbers"], tail_line_numbers=norm_err["tail_numbers"])
655
+ else:
656
+ shaped_err, info_err = "", {"cut": False, "omitted_lines": 0, "omitted_bytes": 0,
657
+ "clipped_lines": 0, "rendered_bytes": 0, "priority_kept": 0}
658
+
659
+ cut = bool(info_out["cut"] or info_err["cut"])
660
+ stats["needs_spill"] = bool(stats["filtered"] or cut)
661
+ stats["spilled"] = bool(stats["needs_spill"] and output_id)
662
+ stats["output_id"] = output_id if stats["spilled"] else None
663
+ stats["omitted_lines"] = info_out["omitted_lines"] + info_err["omitted_lines"]
664
+ stats["clipped_lines"] = info_out["clipped_lines"] + info_err["clipped_lines"]
479
665
 
480
666
  if result.get("timed_out"):
481
667
  status = "TIMEOUT"
@@ -484,14 +670,30 @@ def render_shell_response(cmd: str, result: dict, svc, *,
484
670
  else:
485
671
  status = f"FAIL({result.get('exit_code')})"
486
672
 
673
+ collapsed_note = ""
674
+ if stats["filtered"]:
675
+ parts = []
676
+ if stats["cr_collapsed"]:
677
+ parts.append(f"{stats['cr_collapsed']} cr rewrites")
678
+ if stats["dup_collapsed"]:
679
+ parts.append(f"{stats['dup_collapsed']} dup lines")
680
+ collapsed_note = f" [collapsed {', '.join(parts)}]"
681
+ size_note = ""
682
+ if cut or stats["spilled"]:
683
+ size_note = (f" (stdout {human_bytes(out['bytes'])}/{out['lines']} lines,"
684
+ f" stderr {human_bytes(err['bytes'])}/{err['lines']} lines,")
685
+ size_note += f" output_id={output_id})" if stats["spilled"] else " not spilled)"
686
+
487
687
  body = (
488
688
  f"{warn}{capped_note}"
489
- f"[c3_shell:{status}] {result.get('duration_ms', 0)}ms{filtered_note}\n"
490
- f"$ {cmd}\n"
491
- f"--- stdout ---\n{stdout.rstrip()}\n"
689
+ f"[c3_shell:{status}] {result.get('duration_ms', 0)}ms{collapsed_note}{size_note}\n"
690
+ f"$ {cmd_display(cmd)}\n"
691
+ f"--- stdout ---\n{shaped_out.rstrip()}\n"
492
692
  )
493
- if raw_stderr.strip():
494
- body += f"--- stderr ---\n{raw_stderr.rstrip()}\n"
693
+ if shaped_err.strip():
694
+ body += f"--- stderr ---\n{shaped_err.rstrip()}\n"
695
+ if summary:
696
+ body += summary
495
697
  dependency_hint = _dependency_hint(cmd, result)
496
698
  if dependency_hint:
497
699
  body += f"--- hint ---\n{dependency_hint}\n"
@@ -724,9 +926,102 @@ def _shell_warn_grant(svc, work_cwd: str) -> str | None:
724
926
  return None
725
927
 
726
928
 
929
+ _OUTPUT_ACTIONS = ("read", "search", "tail", "delete")
930
+
931
+
932
+ def _handle_output(output_id: str, action: str, pattern: str, lines, stream: str,
933
+ max_bytes, svc, finalize) -> str:
934
+ """Page a spilled output back by id (2.112.0).
935
+
936
+ The id is resolved for THIS project and THIS host session under the
937
+ CURRENT Access Guard rules — services/shell_output.py documents why a
938
+ spill from a more privileged call must not be readable by a later, less
939
+ privileged one. Every read is re-budgeted like a live response.
940
+ """
941
+ action = (action or "read").strip().lower()
942
+ if action not in _OUTPUT_ACTIONS:
943
+ return (f"[c3_shell:error] output_action must be one of {', '.join(_OUTPUT_ACTIONS)}; "
944
+ f"got {action!r}")
945
+ stream = (stream or "stdout").strip().lower()
946
+ if stream not in ("stdout", "stderr"):
947
+ return "[c3_shell:error] stream must be 'stdout' or 'stderr'"
948
+ project_path = str(svc.project_path)
949
+
950
+ def guard_check(path: str):
951
+ try:
952
+ return access_guard.check(path, "read", project_path)
953
+ except Exception as exc: # evaluator error fails closed
954
+ return access_guard.Denial("<evaluator-error>", "deny", "builtin",
955
+ f"evaluator error: {type(exc).__name__}")
956
+
957
+ store = ShellOutputStore()
958
+ try:
959
+ meta = store.resolve(output_id, project_path=project_path,
960
+ session_id=_grants.session_id(svc), guard_check=guard_check)
961
+ except OutputAccessError as exc:
962
+ return f"[c3_shell:error] {exc}"
963
+ config_default = None
964
+ try:
965
+ config_default = (getattr(svc, "hybrid_config", None) or {}).get("shell_budget_bytes")
966
+ except Exception:
967
+ pass
968
+ budget = effective_budget(max_bytes, config_default=config_default) - 512
969
+ facts = getattr(meta, stream) or {}
970
+ header = (f"[c3_shell:output] {meta.id} {action} {stream} · $ {meta.cmd_display} · "
971
+ f"exit {meta.exit_code}{' (timed out)' if meta.timed_out else ''} · "
972
+ f"{human_bytes(facts.get('bytes', 0))}/{facts.get('lines', 0)} lines · "
973
+ f"captured {meta.created_at[:19]}\n")
974
+ try:
975
+ if action == "delete":
976
+ store.delete(meta)
977
+ body = header + "deleted\n"
978
+ elif action == "search":
979
+ if not pattern:
980
+ return "[c3_shell:error] output_action='search' needs a pattern"
981
+ body = header + store.search(meta, pattern, stream, max_bytes=budget)
982
+ elif action == "tail":
983
+ n = 50
984
+ if isinstance(lines, int) and lines > 0:
985
+ n = lines
986
+ body = header + store.tail(meta, stream, lines=n, max_bytes=budget)
987
+ else:
988
+ window = None
989
+ if isinstance(lines, (list, tuple)) and len(lines) == 2:
990
+ window = (int(lines[0]), int(lines[1]))
991
+ elif isinstance(lines, int) and lines > 0:
992
+ window = (lines, lines)
993
+ elif isinstance(lines, str) and lines.strip():
994
+ parts = [p for p in re.split(r"[,\-:\s]+", lines.strip()) if p]
995
+ if len(parts) == 1:
996
+ window = (int(parts[0]), int(parts[0]))
997
+ elif len(parts) >= 2:
998
+ window = (int(parts[0]), int(parts[1]))
999
+ body = header + store.read(meta, stream, lines=window, max_bytes=budget)
1000
+ except (ValueError, re.error) as exc:
1001
+ return f"[c3_shell:error] {exc}"
1002
+ if not body.endswith("\n"):
1003
+ body += "\n"
1004
+ return finalize_with_tokens(
1005
+ finalize, svc, "c3_shell",
1006
+ {"output_id": output_id, "output_action": action},
1007
+ body, f"shell output {action} {output_id}",
1008
+ detail={"cmd_class": "output", "output_id": output_id, "output_action": action,
1009
+ "response_bytes": len(body.encode("utf-8", errors="replace")),
1010
+ "response_tokens": count_tokens(body)},
1011
+ response_tokens=count_tokens(body),
1012
+ )
1013
+
1014
+
727
1015
  async def handle_shell(cmd: str, cwd: str, timeout: int, filter_output: bool,
728
1016
  log: bool, svc, finalize, env_creds: str = "",
729
- enable_creds: bool = True) -> str:
1017
+ enable_creds: bool = True, *, output_id: str = "",
1018
+ output_action: str = "", pattern: str = "",
1019
+ lines=None, stream: str = "stdout",
1020
+ max_bytes: int | None = None) -> str:
1021
+ if output_id:
1022
+ return await asyncio.to_thread(
1023
+ _handle_output, output_id, output_action, pattern, lines, stream,
1024
+ max_bytes, svc, finalize)
730
1025
  if not cmd or not cmd.strip():
731
1026
  return "[c3_shell:error] empty command"
732
1027
  # _BLOCKED never consults grants — no approval flow reaches the
@@ -860,16 +1155,23 @@ async def handle_shell(cmd: str, cwd: str, timeout: int, filter_output: bool,
860
1155
  else None
861
1156
  )
862
1157
 
1158
+ # Decoded values a child process may echo (env dumps, set, crash output)
1159
+ # are scrubbed IN THE STREAM by _run_sync's redactor, before a byte
1160
+ # reaches the spool or any preview, so neither the response nor a spill
1161
+ # ever holds one.
863
1162
  result = await asyncio.to_thread(
864
1163
  _run_sync, exec_cmd, work_cwd, timeout, extra_env or None)
1164
+ capture = result.get("capture")
865
1165
 
866
1166
  swept_ghosts = _sweep_new_ghost_files(ghost_root, _ghosts_before)
867
1167
 
868
- # Scrub decoded values a child process may have echoed (env dumps, set,
869
- # crash output) BEFORE filtering/ledger/logging see the text.
1168
+ # Belt over the in-stream scrub for the small-output text (and for a
1169
+ # result a test handed in without a capture).
870
1170
  if enable_creds:
871
- result["stdout"] = _creds.redact_text(result["stdout"])
872
- result["stderr"] = _creds.redact_text(result["stderr"])
1171
+ if result.get("stdout") is not None:
1172
+ result["stdout"] = _creds.redact_text(result["stdout"])
1173
+ if result.get("stderr") is not None:
1174
+ result["stderr"] = _creds.redact_text(result["stderr"])
873
1175
  if cred_names:
874
1176
  # Usage state is keyed by entry NAME; collapse dotted field refs.
875
1177
  _creds.touch_last_used(
@@ -936,12 +1238,45 @@ async def handle_shell(cmd: str, cwd: str, timeout: int, filter_output: bool,
936
1238
  f"run_in_background, which is not bound by this limit.\n"
937
1239
  )
938
1240
 
939
- body, stats = await asyncio.to_thread(
940
- render_shell_response, cmd, result, svc,
941
- filter_output=filter_output, warn=warn, capped_note=capped_note,
942
- touched_files=touched_files, cred_names=cred_names,
943
- swept_ghosts=swept_ghosts,
944
- )
1241
+ try:
1242
+ body, stats = await asyncio.to_thread(
1243
+ render_shell_response, cmd, result, svc,
1244
+ filter_output=filter_output, warn=warn, capped_note=capped_note,
1245
+ touched_files=touched_files, cred_names=cred_names,
1246
+ swept_ghosts=swept_ghosts, max_bytes=max_bytes,
1247
+ )
1248
+ # Keep the raw streams only when the body dropped something: the
1249
+ # spill is what makes a clipped or filtered response recoverable
1250
+ # without re-running the command (docs/shell-output.md).
1251
+ if capture is not None:
1252
+ if stats.get("needs_spill"):
1253
+ try:
1254
+ ShellOutputStore().promote(
1255
+ capture, project_path=str(svc.project_path),
1256
+ session_id=_grants.session_id(svc), cmd=cmd, cwd=work_cwd,
1257
+ guard_paths=_scan_candidates(cmd, work_cwd),
1258
+ exit_code=result.get("exit_code", -1),
1259
+ timed_out=bool(result.get("timed_out")),
1260
+ duration_ms=result.get("duration_ms", 0))
1261
+ except Exception as exc:
1262
+ stats["spilled"] = False
1263
+ stats["output_id"] = None
1264
+ body += (f"[c3_shell:note] the raw output could not be kept "
1265
+ f"({type(exc).__name__}: {exc}); the output id above is not retrievable\n")
1266
+ try:
1267
+ capture.discard()
1268
+ except Exception:
1269
+ pass
1270
+ else:
1271
+ capture.discard()
1272
+ except Exception:
1273
+ if capture is not None:
1274
+ try:
1275
+ capture.discard()
1276
+ except Exception:
1277
+ pass
1278
+ raise
1279
+ stats.pop("needs_spill", None)
945
1280
 
946
1281
  if result["timed_out"]:
947
1282
  status = "TIMEOUT"