code-context-control 2.146.0__tar.gz → 2.152.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 (586) hide show
  1. {code_context_control-2.146.0 → code_context_control-2.152.0}/PKG-INFO +1 -1
  2. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/c3.py +1 -1
  3. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/tools.html +1 -0
  4. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_access_guard.py +1 -0
  5. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_server.py +231 -20
  6. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_access.js +119 -2
  7. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/modals.js +34 -12
  8. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/settings_modal.js +11 -0
  9. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/mcp_server.py +18 -5
  10. code_context_control-2.152.0/cli/tools/_edit_report.py +186 -0
  11. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/edit.py +196 -118
  12. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/edit_verify.py +7 -4
  13. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/edits.py +112 -2
  14. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/read.py +2 -1
  15. {code_context_control-2.146.0 → code_context_control-2.152.0}/code_context_control.egg-info/PKG-INFO +1 -1
  16. {code_context_control-2.146.0 → code_context_control-2.152.0}/code_context_control.egg-info/SOURCES.txt +11 -0
  17. {code_context_control-2.146.0 → code_context_control-2.152.0}/pyproject.toml +1 -1
  18. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/access_guard.py +61 -5
  19. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/atomic_json.py +25 -8
  20. code_context_control-2.152.0/services/builtin_leases.py +182 -0
  21. code_context_control-2.152.0/services/edit_blobs.py +192 -0
  22. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/project_manager.py +10 -2
  23. code_context_control-2.152.0/services/read_stamps.py +287 -0
  24. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_access_guard_meta.py +1 -0
  25. code_context_control-2.152.0/tests/test_builtin_leases.py +210 -0
  26. code_context_control-2.152.0/tests/test_edit_eol_atomic.py +223 -0
  27. code_context_control-2.152.0/tests/test_edit_report.py +142 -0
  28. code_context_control-2.152.0/tests/test_edit_revert.py +204 -0
  29. code_context_control-2.152.0/tests/test_edit_stale.py +214 -0
  30. code_context_control-2.152.0/tests/test_hub_builtin_routes.py +163 -0
  31. code_context_control-2.152.0/tests/test_hub_ide_cmd.py +125 -0
  32. {code_context_control-2.146.0 → code_context_control-2.152.0}/LICENSE +0 -0
  33. {code_context_control-2.146.0 → code_context_control-2.152.0}/README.md +0 -0
  34. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/__init__.py +0 -0
  35. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/_hook_utils.py +0 -0
  36. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/_shell_writes.py +0 -0
  37. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/commands/__init__.py +0 -0
  38. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/commands/common.py +0 -0
  39. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/commands/parser.py +0 -0
  40. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/docs.html +0 -0
  41. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/edits.html +0 -0
  42. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/access.html +0 -0
  43. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/bitbucket.html +0 -0
  44. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/ci.html +0 -0
  45. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/credentials.html +0 -0
  46. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/delegate.html +0 -0
  47. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/getting-started.html +0 -0
  48. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/index.html +0 -0
  49. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/jira.html +0 -0
  50. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/masking.html +0 -0
  51. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/oracle.html +0 -0
  52. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/sessions.html +0 -0
  53. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/shared.css +0 -0
  54. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/guide/workflow.html +0 -0
  55. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_agent_model.py +0 -0
  56. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_artifact.py +0 -0
  57. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_auto_snapshot.py +0 -0
  58. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_c3_signal.py +0 -0
  59. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_c3read.py +0 -0
  60. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_codex_lifecycle.py +0 -0
  61. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_dispatch.py +0 -0
  62. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_edit_ledger.py +0 -0
  63. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_edit_unlock.py +0 -0
  64. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_filter.py +0 -0
  65. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_ghost_files.py +0 -0
  66. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_pretool_enforce.py +0 -0
  67. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_prompt_recall.py +0 -0
  68. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_read.py +0 -0
  69. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_session_end.py +0 -0
  70. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_session_open.py +0 -0
  71. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_session_stats.py +0 -0
  72. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hook_terse_advisor.py +0 -0
  73. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/app.js +0 -0
  74. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/add_project.js +0 -0
  75. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/config_editor.js +0 -0
  76. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  77. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/drill_health.js +0 -0
  78. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/drill_panel.js +0 -0
  79. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
  80. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  81. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/drill_views.js +0 -0
  82. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/global_search.js +0 -0
  83. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_ci.js +0 -0
  84. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  85. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_credentials.js +0 -0
  86. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
  87. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_locks.js +0 -0
  88. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_sessions.js +0 -0
  89. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/hub_tokens.js +0 -0
  90. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  91. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/project_card.js +0 -0
  92. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/project_tree.js +0 -0
  93. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/session_drawer.js +0 -0
  94. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/sidebar.js +0 -0
  95. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/summary_bar.js +0 -0
  96. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/task_board.js +0 -0
  97. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/toasts.js +0 -0
  98. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/components/topbar.js +0 -0
  99. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui/state.js +0 -0
  100. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/hub_ui.html +0 -0
  101. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/mcp_proxy.py +0 -0
  102. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/progress.py +0 -0
  103. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/server.py +0 -0
  104. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/__init__.py +0 -0
  105. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/_grants.py +0 -0
  106. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/_helpers.py +0 -0
  107. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/agent.py +0 -0
  108. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/artifacts.py +0 -0
  109. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/bitbucket.py +0 -0
  110. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/ci.py +0 -0
  111. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/compress.py +0 -0
  112. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/credentials.py +0 -0
  113. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/delegate.py +0 -0
  114. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/federate.py +0 -0
  115. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/filter.py +0 -0
  116. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/impact.py +0 -0
  117. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/jira.py +0 -0
  118. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/locks.py +0 -0
  119. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/memory.py +0 -0
  120. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/override.py +0 -0
  121. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/project.py +0 -0
  122. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/search.py +0 -0
  123. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/session.py +0 -0
  124. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/shell.py +0 -0
  125. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/shell_job.py +0 -0
  126. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/shell_nudge.py +0 -0
  127. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/shell_parsers.py +0 -0
  128. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/shell_render.py +0 -0
  129. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/status.py +0 -0
  130. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/tasks.py +0 -0
  131. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/tools/validate.py +0 -0
  132. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/api.js +0 -0
  133. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/app.js +0 -0
  134. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/access.js +0 -0
  135. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/bitbucket.js +0 -0
  136. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/chat.js +0 -0
  137. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/credentials.js +0 -0
  138. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/dashboard.js +0 -0
  139. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/edits.js +0 -0
  140. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/enforcement.js +0 -0
  141. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/instructions.js +0 -0
  142. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/jira.js +0 -0
  143. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/memory.js +0 -0
  144. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/sessions.js +0 -0
  145. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/settings.js +0 -0
  146. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/sidebar.js +0 -0
  147. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/components/tasks.js +0 -0
  148. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/icons.js +0 -0
  149. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/pm_shared.js +0 -0
  150. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/shared.js +0 -0
  151. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui/theme.js +0 -0
  152. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui.html +0 -0
  153. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui_legacy.html +0 -0
  154. {code_context_control-2.146.0 → code_context_control-2.152.0}/cli/ui_nano.html +0 -0
  155. {code_context_control-2.146.0 → code_context_control-2.152.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  156. {code_context_control-2.146.0 → code_context_control-2.152.0}/code_context_control.egg-info/entry_points.txt +0 -0
  157. {code_context_control-2.146.0 → code_context_control-2.152.0}/code_context_control.egg-info/requires.txt +0 -0
  158. {code_context_control-2.146.0 → code_context_control-2.152.0}/code_context_control.egg-info/top_level.txt +0 -0
  159. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/__init__.py +0 -0
  160. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/codex_patch.py +0 -0
  161. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/config.py +0 -0
  162. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/grok_payload.py +0 -0
  163. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/hook_command.py +0 -0
  164. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/host.py +0 -0
  165. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/ide.py +0 -0
  166. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/mcp_toml.py +0 -0
  167. {code_context_control-2.146.0 → code_context_control-2.152.0}/core/web_security.py +0 -0
  168. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/__init__.py +0 -0
  169. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/config.py +0 -0
  170. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/listeners.py +0 -0
  171. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/mcp_oracle.py +0 -0
  172. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/oracle_server.py +0 -0
  173. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/oracle_ui.html +0 -0
  174. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/__init__.py +0 -0
  175. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/activity_reporter.py +0 -0
  176. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/api_auth.py +0 -0
  177. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/c3_bridge.py +0 -0
  178. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/chat_engine.py +0 -0
  179. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/chat_poll.py +0 -0
  180. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/chat_store.py +0 -0
  181. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/client_tokens.py +0 -0
  182. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/cross_memory.py +0 -0
  183. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/discovery_audit.py +0 -0
  184. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/federated_graph.py +0 -0
  185. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/health_checker.py +0 -0
  186. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/insight_engine.py +0 -0
  187. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/local_session.py +0 -0
  188. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/memory_reader.py +0 -0
  189. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/memory_writer.py +0 -0
  190. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/mobile_api.py +0 -0
  191. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/project_scanner.py +0 -0
  192. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/review_agent.py +0 -0
  193. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/tool_executor.py +0 -0
  194. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/services/tool_registry.py +0 -0
  195. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/activity.js +0 -0
  196. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/agents.js +0 -0
  197. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/app.js +0 -0
  198. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/busy.js +0 -0
  199. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/chat/conversations.js +0 -0
  200. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/chat/input.js +0 -0
  201. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/chat/markdown.js +0 -0
  202. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/chat/send.js +0 -0
  203. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/chat/stream_renderer.js +0 -0
  204. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/chat/toolbar.js +0 -0
  205. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/core.js +0 -0
  206. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/crossgraph.js +0 -0
  207. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/header.js +0 -0
  208. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/insights.js +0 -0
  209. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/projects.js +0 -0
  210. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/qrcode.js +0 -0
  211. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/settings.js +0 -0
  212. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/suggestions.js +0 -0
  213. {code_context_control-2.146.0 → code_context_control-2.152.0}/oracle/ui/theme_tabs.js +0 -0
  214. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/__init__.py +0 -0
  215. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/access_telemetry.py +0 -0
  216. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/activity_log.py +0 -0
  217. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/agent_base.py +0 -0
  218. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/agent_downshift.py +0 -0
  219. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/agent_locks.py +0 -0
  220. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/agents.py +0 -0
  221. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/agents_workflow.py +0 -0
  222. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/artifact_defs.py +0 -0
  223. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/artifact_store.py +0 -0
  224. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/auto_memory.py +0 -0
  225. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/background_service.py +0 -0
  226. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/__init__.py +0 -0
  227. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/delegate_eval.py +0 -0
  228. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/delegate_write_eval.py +0 -0
  229. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/external/__init__.py +0 -0
  230. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/external/aider_polyglot.py +0 -0
  231. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/external/swe_bench.py +0 -0
  232. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/map_eval.py +0 -0
  233. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/search_eval.py +0 -0
  234. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bench/shell_eval.py +0 -0
  235. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/benchmark_dashboard.py +0 -0
  236. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bitbucket_client.py +0 -0
  237. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/bitbucket_credentials.py +0 -0
  238. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_act.py +0 -0
  239. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_cache.py +0 -0
  240. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_expr.py +0 -0
  241. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_failures.py +0 -0
  242. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_github.py +0 -0
  243. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_impact.py +0 -0
  244. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_intel.py +0 -0
  245. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_runner.py +0 -0
  246. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ci_workflow.py +0 -0
  247. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/circuit_breaker.py +0 -0
  248. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/claude_md.py +0 -0
  249. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/codex_integration.py +0 -0
  250. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/codex_transcript.py +0 -0
  251. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/compressor.py +0 -0
  252. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/context_snapshot.py +0 -0
  253. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/conversation_store.py +0 -0
  254. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/cred_audit.py +0 -0
  255. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/cred_telemetry.py +0 -0
  256. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/credential_backup.py +0 -0
  257. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/credential_store.py +0 -0
  258. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/delegate_agents.py +0 -0
  259. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/delegate_hints.py +0 -0
  260. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/delegate_write.py +0 -0
  261. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/dir_map.py +0 -0
  262. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/doc_index.py +0 -0
  263. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/e2e_benchmark.py +0 -0
  264. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/e2e_evaluator.py +0 -0
  265. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/e2e_tasks.py +0 -0
  266. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/edit_ledger.py +0 -0
  267. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/embedding_index.py +0 -0
  268. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/enforcement_policy.py +0 -0
  269. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/error_reporting.py +0 -0
  270. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/file_map.py +0 -0
  271. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/file_memory.py +0 -0
  272. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/git_context.py +0 -0
  273. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/grok_integration.py +0 -0
  274. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/grok_transcript.py +0 -0
  275. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/host_sessions.py +0 -0
  276. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/hub_service.py +0 -0
  277. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/index_store.py +0 -0
  278. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/indexer.py +0 -0
  279. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/jira_client.py +0 -0
  280. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/jira_cloud.py +0 -0
  281. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/jira_credentials.py +0 -0
  282. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/jira_data_center.py +0 -0
  283. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/jira_links.py +0 -0
  284. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/lexical_index.py +0 -0
  285. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/mask_activation.py +0 -0
  286. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/mask_mirror.py +0 -0
  287. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/mask_presets.py +0 -0
  288. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory.py +0 -0
  289. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory_consolidator.py +0 -0
  290. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory_distiller.py +0 -0
  291. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory_graph.py +0 -0
  292. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory_grounder.py +0 -0
  293. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory_queue.py +0 -0
  294. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/memory_scorer.py +0 -0
  295. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/metrics.py +0 -0
  296. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/notifications.py +0 -0
  297. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ollama_bridge.py +0 -0
  298. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ollama_client.py +0 -0
  299. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/ollama_credentials.py +0 -0
  300. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/oracle_service.py +0 -0
  301. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/output_filter.py +0 -0
  302. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/override_costs.py +0 -0
  303. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/override_grants.py +0 -0
  304. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/override_policy.py +0 -0
  305. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/override_requests.py +0 -0
  306. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/override_wake.py +0 -0
  307. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/parser.py +0 -0
  308. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/project_runtime.py +0 -0
  309. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/protocol.py +0 -0
  310. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/proxy_state.py +0 -0
  311. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/repo_map.py +0 -0
  312. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/repo_shape.py +0 -0
  313. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/reranker.py +0 -0
  314. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/retention.py +0 -0
  315. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/retrieval.py +0 -0
  316. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/retrieval_broker.py +0 -0
  317. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/router.py +0 -0
  318. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/runtime.py +0 -0
  319. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/scanner.py +0 -0
  320. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/session_benchmark.py +0 -0
  321. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/session_catalog.py +0 -0
  322. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/session_live.py +0 -0
  323. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/session_manager.py +0 -0
  324. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/session_preloader.py +0 -0
  325. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/shell_jobs.py +0 -0
  326. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/shell_output.py +0 -0
  327. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/subprojects.py +0 -0
  328. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/task_store.py +0 -0
  329. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/telemetry.py +0 -0
  330. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/terminal_launch.py +0 -0
  331. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/text_index.py +0 -0
  332. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/time_tracker.py +0 -0
  333. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/tool_classifier.py +0 -0
  334. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/transcript_index.py +0 -0
  335. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/validation_cache.py +0 -0
  336. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/vector_store.py +0 -0
  337. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/watcher.py +0 -0
  338. {code_context_control-2.146.0 → code_context_control-2.152.0}/services/win_subprocess.py +0 -0
  339. {code_context_control-2.146.0 → code_context_control-2.152.0}/setup.cfg +0 -0
  340. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_access_builtin_optout.py +0 -0
  341. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_access_guard.py +0 -0
  342. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_access_guard_shell_project.py +0 -0
  343. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_access_guard_surfaces.py +0 -0
  344. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_access_guard_wiring.py +0 -0
  345. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_activity_log_find_last.py +0 -0
  346. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_activity_reporter.py +0 -0
  347. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_agent_locks.py +0 -0
  348. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_aider_polyglot.py +0 -0
  349. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_antigravity_transition.py +0 -0
  350. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_artifact_store.py +0 -0
  351. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_artifact_tool.py +0 -0
  352. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_atomic_json.py +0 -0
  353. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_background_service.py +0 -0
  354. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  355. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_bitbucket_client.py +0 -0
  356. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_bitbucket_config_fallback.py +0 -0
  357. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_bitbucket_credentials.py +0 -0
  358. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_bitbucket_tool.py +0 -0
  359. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_builtin_modes.py +0 -0
  360. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  361. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_c3_shell.py +0 -0
  362. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_chat_poll.py +0 -0
  363. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_act.py +0 -0
  364. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_cache.py +0 -0
  365. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_dag_ownership.py +0 -0
  366. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_expr.py +0 -0
  367. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_impact.py +0 -0
  368. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_intel.py +0 -0
  369. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_runner.py +0 -0
  370. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ci_workflow.py +0 -0
  371. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_circuit_breaker.py +0 -0
  372. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_claude_md_merge.py +0 -0
  373. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_claude_md_updater.py +0 -0
  374. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_cli_smoke.py +0 -0
  375. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_codex_mcp_transport.py +0 -0
  376. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_codex_native.py +0 -0
  377. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_compressor_large_file.py +0 -0
  378. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_confirm_guard.py +0 -0
  379. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_confirm_guard_wiring.py +0 -0
  380. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_cooccurrence_bounds.py +0 -0
  381. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_cred_audit.py +0 -0
  382. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_cred_telemetry.py +0 -0
  383. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_credential_backup.py +0 -0
  384. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_credential_login_kind.py +0 -0
  385. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_credential_store.py +0 -0
  386. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_credential_ui_parity.py +0 -0
  387. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_credentials_routes.py +0 -0
  388. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_credentials_tool.py +0 -0
  389. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_creds_cli_smoke.py +0 -0
  390. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_adoption_d4.py +0 -0
  391. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_auto.py +0 -0
  392. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_cascade.py +0 -0
  393. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_claude_d1.py +0 -0
  394. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_eval.py +0 -0
  395. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_grok.py +0 -0
  396. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_host_d2.py +0 -0
  397. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_scout_d3.py +0 -0
  398. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_telemetry_d0.py +0 -0
  399. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_thinking_cap.py +0 -0
  400. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_tuning.py +0 -0
  401. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_version_probe.py +0 -0
  402. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_write.py +0 -0
  403. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_delegate_write_eval.py +0 -0
  404. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_dir_map_c3.py +0 -0
  405. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_e2e_benchmark.py +0 -0
  406. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_edit_ledger_deadline.py +0 -0
  407. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_edit_ledger_hook.py +0 -0
  408. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_edit_locking.py +0 -0
  409. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_edit_normalization.py +0 -0
  410. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_edit_verify.py +0 -0
  411. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_embedding_index_corrupt_store.py +0 -0
  412. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_embedding_index_deadlock.py +0 -0
  413. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_enforcement_flip.py +0 -0
  414. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_enforcement_policy.py +0 -0
  415. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_enforcement_routes.py +0 -0
  416. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_env_parser.py +0 -0
  417. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
  418. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_federated_graph.py +0 -0
  419. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_file_map_c1.py +0 -0
  420. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_filter_backoff.py +0 -0
  421. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ghost_files.py +0 -0
  422. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ghost_generation.py +0 -0
  423. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_git_branch_awareness.py +0 -0
  424. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_grok_native.py +0 -0
  425. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_access_guard.py +0 -0
  426. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_codex_compat.py +0 -0
  427. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_dispatch.py +0 -0
  428. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_grok_compat.py +0 -0
  429. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_pretool_enforce.py +0 -0
  430. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_prompt_recall.py +0 -0
  431. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_session_end.py +0 -0
  432. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_smoke.py +0 -0
  433. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_state.py +0 -0
  434. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hook_utf8_stdio.py +0 -0
  435. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_access_routes.py +0 -0
  436. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_credentials_route.py +0 -0
  437. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_credentials_routes_write.py +0 -0
  438. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_enforcement_routes.py +0 -0
  439. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_hook_migration.py +0 -0
  440. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_inspect_api.py +0 -0
  441. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_locks_routes.py +0 -0
  442. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_oracle_service_routes.py +0 -0
  443. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_override_policy_routes.py +0 -0
  444. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_override_routes.py +0 -0
  445. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_rules_routes.py +0 -0
  446. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_server_smoke.py +0 -0
  447. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_sessions_routes.py +0 -0
  448. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_hub_subproject_links.py +0 -0
  449. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ide_workflow_adaptation.py +0 -0
  450. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_index_cap_not_hardcoded.py +0 -0
  451. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_init_clear_preserves_user_content.py +0 -0
  452. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_init_scan.py +0 -0
  453. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_install_mcp_entrypoint.py +0 -0
  454. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_cli_smoke.py +0 -0
  455. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_client.py +0 -0
  456. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_config_fallback.py +0 -0
  457. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_credentials.py +0 -0
  458. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_links.py +0 -0
  459. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_routes.py +0 -0
  460. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_jira_tool.py +0 -0
  461. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_large_files_c4.py +0 -0
  462. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_lazy_store_init.py +0 -0
  463. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_map_eval.py +0 -0
  464. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_map_telemetry_c0.py +0 -0
  465. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mask_guard.py +0 -0
  466. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mask_routes.py +0 -0
  467. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mask_surfaces.py +0 -0
  468. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mcp_host_guard.py +0 -0
  469. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mcp_memory_timeout.py +0 -0
  470. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mcp_server_smoke.py +0 -0
  471. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mcp_toml.py +0 -0
  472. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_memory_distiller.py +0 -0
  473. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_memory_graph_api.py +0 -0
  474. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_memory_queue.py +0 -0
  475. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_memory_system.py +0 -0
  476. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_api.py +0 -0
  477. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_clients.py +0 -0
  478. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_extras.py +0 -0
  479. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_jobs_ci.py +0 -0
  480. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_override_costs.py +0 -0
  481. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_override_routes.py +0 -0
  482. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_mobile_security_api.py +0 -0
  483. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_no_stale_model_pins.py +0 -0
  484. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_notification_dedup.py +0 -0
  485. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_notification_discipline.py +0 -0
  486. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_notification_kinds.py +0 -0
  487. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ollama_credentials.py +0 -0
  488. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_api_auth.py +0 -0
  489. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_apikey_api.py +0 -0
  490. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_c3_bridge.py +0 -0
  491. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_chat_engine.py +0 -0
  492. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_chat_store.py +0 -0
  493. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_discovery_api.py +0 -0
  494. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_discovery_audit.py +0 -0
  495. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_health_probe.py +0 -0
  496. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_local_auth.py +0 -0
  497. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_loopback.py +0 -0
  498. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_ollama_bridge.py +0 -0
  499. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_scanner_cache.py +0 -0
  500. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_security_fixes.py +0 -0
  501. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_session_bootstrap.py +0 -0
  502. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_subproject_awareness.py +0 -0
  503. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_oracle_ui_bundle.py +0 -0
  504. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_output_filter.py +0 -0
  505. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_override_costs.py +0 -0
  506. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_override_grants.py +0 -0
  507. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_override_grants_mcp.py +0 -0
  508. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_override_requests.py +0 -0
  509. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_override_wake.py +0 -0
  510. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_permissions.py +0 -0
  511. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_pm_api.py +0 -0
  512. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_project_manager.py +0 -0
  513. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_project_manager_merge.py +0 -0
  514. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_project_tool.py +0 -0
  515. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_read_coercion.py +0 -0
  516. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_read_edit_parity.py +0 -0
  517. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_read_outside_root.py +0 -0
  518. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_related_facts.py +0 -0
  519. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_repo_map.py +0 -0
  520. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_repo_shape.py +0 -0
  521. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_response_boilerplate.py +0 -0
  522. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_retention.py +0 -0
  523. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_review_digest.py +0 -0
  524. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_eval.py +0 -0
  525. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_p1.py +0 -0
  526. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_p2.py +0 -0
  527. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_p2b.py +0 -0
  528. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_p3.py +0 -0
  529. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_p4.py +0 -0
  530. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_search_p5.py +0 -0
  531. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_service_durability.py +0 -0
  532. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_session_benchmark.py +0 -0
  533. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_session_budget.py +0 -0
  534. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_session_catalog.py +0 -0
  535. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_session_live.py +0 -0
  536. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_session_tool.py +0 -0
  537. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_budget_s1.py +0 -0
  538. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_creds.py +0 -0
  539. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_eval.py +0 -0
  540. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_jobs_s3.py +0 -0
  541. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_nudge_s4.py +0 -0
  542. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_output.py +0 -0
  543. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_parsers_s2.py +0 -0
  544. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_robustness.py +0 -0
  545. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_telemetry_s0.py +0 -0
  546. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_write_ledger.py +0 -0
  547. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_shell_writes.py +0 -0
  548. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_subproject_clear_scope.py +0 -0
  549. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_subproject_depth_consumers.py +0 -0
  550. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_subproject_exclusion.py +0 -0
  551. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_subproject_federation.py +0 -0
  552. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_subproject_linkage_api.py +0 -0
  553. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_subprojects.py +0 -0
  554. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_swe_bench.py +0 -0
  555. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_task_store.py +0 -0
  556. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_task_tool.py +0 -0
  557. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_time_tracker.py +0 -0
  558. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_token_telemetry.py +0 -0
  559. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_token_tracking.py +0 -0
  560. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_tool_registry.py +0 -0
  561. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_ui_jsx_syntax.py +0 -0
  562. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_upgrade_and_version.py +0 -0
  563. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_validate.py +0 -0
  564. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_vault_write_guard.py +0 -0
  565. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_version_sync.py +0 -0
  566. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_watcher_pruning.py +0 -0
  567. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_web_security.py +0 -0
  568. {code_context_control-2.146.0 → code_context_control-2.152.0}/tests/test_windows_reliability.py +0 -0
  569. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/__init__.py +0 -0
  570. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/backend.py +0 -0
  571. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/main.py +0 -0
  572. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/__init__.py +0 -0
  573. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/benchmark_view.py +0 -0
  574. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/claudemd_view.py +0 -0
  575. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/compress_view.py +0 -0
  576. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/index_view.py +0 -0
  577. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/init_view.py +0 -0
  578. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/mcp_view.py +0 -0
  579. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/optimize_view.py +0 -0
  580. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/pipe_view.py +0 -0
  581. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/projects_view.py +0 -0
  582. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/search_view.py +0 -0
  583. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/session_view.py +0 -0
  584. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/stats.py +0 -0
  585. {code_context_control-2.146.0 → code_context_control-2.152.0}/tui/screens/ui_view.py +0 -0
  586. {code_context_control-2.146.0 → code_context_control-2.152.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.146.0
3
+ Version: 2.152.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
@@ -91,7 +91,7 @@ console = Console() if HAS_RICH else None
91
91
  # Config
92
92
  CONFIG_DIR = ".c3"
93
93
  CONFIG_FILE = ".c3/config.json"
94
- __version__ = "2.146.0"
94
+ __version__ = "2.152.0"
95
95
 
96
96
  # The PreToolUse matcher for native subagent calls (installer and hub migration).
97
97
  AGENT_MATCHER = "Agent|Task"
@@ -1507,6 +1507,7 @@ c3_agent(workflow=<span class="str">'investigate'</span>, context=<span class="s
1507
1507
  <tr><td class="param-name">stats</td><td class="param-desc">Aggregate stats: most-edited files, change frequency</td></tr>
1508
1508
  <tr><td class="param-name">tag</td><td class="param-desc">Tag an edit entry with a label</td></tr>
1509
1509
  <tr><td class="param-name">verify</td><td class="param-desc"><strong>v2.75.0</strong> — did an edit land? Pass the SAME <code>file</code>/<code>old_string</code>/<code>new_string</code> (or <code>edits</code>) a failed or timed-out <code>c3_edit</code> call took. Answers APPLIED (do not retry) / NOT_APPLIED (safe to retry) / INCONCLUSIVE (read the file)</td></tr>
1510
+ <tr><td class="param-name">revert</td><td class="param-desc">Put a file back to the bytes it had before <code>edit_id</code>, from the pre-image <code>c3_edit</code> stored in <code>.c3/edit_blobs/</code>. Refuses if the file changed since (lists the later edit ids to revert first) or if no pre-image was kept (native Edit/shell rows, files over 5 MB, read-denied or masked paths, evicted blobs). Logs its own <code>reverted</code> row, which can itself be reverted.</td></tr>
1510
1511
  </tbody>
1511
1512
  </table>
1512
1513
 
@@ -447,6 +447,7 @@ def run(payload: dict, project_path: Path | None = None,
447
447
  tool_input = payload.get("tool_input", {}) or {}
448
448
  base = str(project_path if project_path is not None else Path.cwd())
449
449
  session_id = str(payload.get("session_id") or "")
450
+ ag.bind_session(session_id)
450
451
 
451
452
  if tool in _WRITE_TOOLS or tool in _READ_TOOLS:
452
453
  fp = _target(tool_input)
@@ -72,6 +72,10 @@ _HUB_CONFIG_DEFAULTS = {
72
72
  "projects_view": "list",
73
73
  "main_view": "projects",
74
74
  "oracle_url": "",
75
+ # Per-IDE launch command overrides, e.g. {"claude-code": "yep"} when the
76
+ # agent is started through a wrapper script instead of the stock binary.
77
+ # A project's own `ide_cmd` (projects.json) wins over this.
78
+ "ide_cmds": {},
75
79
  # Minutes of project silence, with no session heartbeat left, before a UI
76
80
  # server that an IDE session launched is stopped. 0 = never reap.
77
81
  "ui_reap_minutes": 30,
@@ -81,6 +85,48 @@ _HUB_CONFIG_DEFAULTS = {
81
85
  _MAIN_VIEWS = {"projects", "board", "ci", "creds", "tokens", "locks", "access",
82
86
  "enforce", "sessions"}
83
87
 
88
+ # Stock command per IDE id, and whether it takes the project path as an
89
+ # argument (GUI) instead of being run inside a terminal at that path.
90
+ _IDE_DEFAULT_CMDS = {
91
+ "claude-code": ("claude", False),
92
+ "claude-app": ("claude-app", True),
93
+ "codex": ("codex", False),
94
+ "grok": ("grok", False),
95
+ "gemini": ("gemini", False),
96
+ "antigravity": ("antigravity", False),
97
+ "vscode": ("code", True),
98
+ "cursor": ("cursor", True),
99
+ }
100
+
101
+
102
+ def _resolve_ide_cmd(ide: str, path: str, custom_cmd: str = "") -> tuple:
103
+ """Resolve the command to launch for `ide` in `path`.
104
+
105
+ Precedence: explicit custom_cmd (this launch) → the project's stored
106
+ `ide_cmd` → hub config `ide_cmds[ide]` → the stock command.
107
+ Returns (cmd, is_gui, error) — error is a message when nothing resolves.
108
+ """
109
+ default_cmd, is_gui = _IDE_DEFAULT_CMDS.get(ide, ("", False))
110
+ if ide != "custom" and not default_cmd:
111
+ return "", False, f"Unknown IDE: {ide}"
112
+ cmd = (custom_cmd or "").strip()
113
+ if not cmd:
114
+ try:
115
+ cmd = _pm().get_ide_cmd(path)
116
+ except Exception:
117
+ cmd = ""
118
+ if not cmd:
119
+ overrides = _read_hub_config().get("ide_cmds") or {}
120
+ if isinstance(overrides, dict):
121
+ cmd = str(overrides.get(ide) or "").strip()
122
+ if not cmd:
123
+ cmd = default_cmd
124
+ if not cmd:
125
+ return "", False, "custom_cmd is required for custom IDE"
126
+ # An overridden command replaces the binary, not the launch style: a
127
+ # wrapper for a terminal CLI is still a terminal CLI.
128
+ return cmd, is_gui, ""
129
+
84
130
  # How often the hub reaps orphaned UI servers and chases autostart projects.
85
131
  _UI_SWEEP_INTERVAL_S = 60
86
132
 
@@ -460,6 +506,15 @@ def api_hub_config_set():
460
506
  cfg["main_view"] = main_view
461
507
  if "oracle_url" in data:
462
508
  cfg["oracle_url"] = str(data["oracle_url"]).strip()
509
+ if "ide_cmds" in data:
510
+ raw = data["ide_cmds"]
511
+ if not isinstance(raw, dict):
512
+ return jsonify({"error": "ide_cmds must be an object"}), 400
513
+ # Empty string = "back to the stock command", so the key is dropped
514
+ # rather than stored blank.
515
+ cfg["ide_cmds"] = {
516
+ str(k): str(v).strip() for k, v in raw.items() if str(v or "").strip()
517
+ }
463
518
  if "sidebar_group" in data:
464
519
  cfg["sidebar_group"] = str(data["sidebar_group"]).strip()
465
520
  if "sidebar_collapsed" in data:
@@ -718,17 +773,11 @@ def api_projects_open():
718
773
 
719
774
  @app.route("/api/projects/launch-ide", methods=["POST"])
720
775
  def api_launch_ide():
721
- """Launch an IDE or CLI tool in the project directory. Body: {path, ide, custom_cmd?}"""
722
- _IDE_CMDS = {
723
- "claude-code": ("claude", False),
724
- "claude-app": ("claude-app", True),
725
- "codex": ("codex", False),
726
- "grok": ("grok", False),
727
- "gemini": ("gemini", False),
728
- "antigravity": ("antigravity", False),
729
- "vscode": ("code", True),
730
- "cursor": ("cursor", True),
731
- }
776
+ """Launch an IDE or CLI tool in the project directory. Body: {path, ide, custom_cmd?}
777
+
778
+ The command itself is resolved by _resolve_ide_cmd, so a project or the
779
+ hub can point `claude-code` at a wrapper such as `yep`.
780
+ """
732
781
  try:
733
782
  data = request.get_json(force=True) or {}
734
783
  path_str = (data.get("path") or "").strip()
@@ -744,14 +793,9 @@ def api_launch_ide():
744
793
  if not path.exists():
745
794
  return jsonify({"error": f"Path does not exist: {path_str}"}), 404
746
795
 
747
- if ide == "custom":
748
- if not custom_cmd:
749
- return jsonify({"error": "custom_cmd is required for custom IDE"}), 400
750
- cmd, is_gui = custom_cmd, False
751
- elif ide in _IDE_CMDS:
752
- cmd, is_gui = _IDE_CMDS[ide]
753
- else:
754
- return jsonify({"error": f"Unknown IDE: {ide}"}), 400
796
+ cmd, is_gui, err = _resolve_ide_cmd(ide, str(path), custom_cmd)
797
+ if err:
798
+ return jsonify({"error": err}), 400
755
799
 
756
800
  if is_gui:
757
801
  # GUI IDEs (VS Code, Cursor) accept a path argument directly
@@ -795,7 +839,7 @@ def api_launch_ide():
795
839
 
796
840
  @app.route("/api/projects/update", methods=["POST"])
797
841
  def api_projects_update():
798
- """Update editable project fields (name, tags, notes). Body: {path, name?, tags?, notes?}"""
842
+ """Update editable project fields. Body: {path, name?, tags?, notes?, ide_cmd?}"""
799
843
  data = request.get_json(force=True) or {}
800
844
  path = (data.get("path") or "").strip()
801
845
  if not path:
@@ -812,6 +856,9 @@ def api_projects_update():
812
856
  fields["tags"] = [t.strip() for t in str(raw).split(",") if t.strip()]
813
857
  if "notes" in data:
814
858
  fields["notes"] = str(data["notes"])
859
+ if "ide_cmd" in data:
860
+ # Blank clears the override and falls back to the stock command.
861
+ fields["ide_cmd"] = str(data["ide_cmd"] or "").strip()
815
862
  try:
816
863
  ok = _pm().update_project(path, **fields)
817
864
  return jsonify({"updated": ok})
@@ -3839,6 +3886,170 @@ def api_hub_access_rule_remove():
3839
3886
  return jsonify(result)
3840
3887
 
3841
3888
 
3889
+ # ── Builtin guard modes (v2.147.0) ─────────────────────────────────────────
3890
+
3891
+ def _hub_human():
3892
+ """The Desk client behind ``Authorization: Bearer``, else ``None``.
3893
+
3894
+ Loopback and the CSRF guard stop a browser, not a process on this
3895
+ machine: an agent can POST here as easily as a person. A Desk client
3896
+ token is minted only with ``bootstrap.key``, which Access Guard denies
3897
+ to agents, and is stored as a hash, so presenting one is the proof a
3898
+ loosening builtin change needs. A phone token does not count: builtin
3899
+ guards are not loosened from a device that leaves the building.
3900
+ """
3901
+ from oracle.services import client_tokens
3902
+ scheme, _, token = (request.headers.get("Authorization") or "").partition(" ")
3903
+ if scheme.lower() != "bearer" or not token.strip():
3904
+ return None
3905
+ who = client_tokens.principal_for(token.strip())
3906
+ return who if who and who.get("kind") == "desk" else None
3907
+
3908
+
3909
+ @app.route("/api/hub/access/builtin", methods=["GET"])
3910
+ def api_hub_access_builtin():
3911
+ """Every Tier-1 builtin with its live global mode, and each project's own.
3912
+
3913
+ Modes are read through the keyring (config and attestation must agree),
3914
+ so a mode listed here is one evaluation honours. ``projects`` holds only
3915
+ projects that set a mode of their own, or could not be read.
3916
+ ``strictness`` ranks each mode 0 (allow) to 3 (full deny), so a client
3917
+ can tell loosening from tightening without knowing the tiers.
3918
+ """
3919
+ try:
3920
+ global_modes = access_guard._scope_modes("global")
3921
+ except Exception as e:
3922
+ return jsonify({"error": str(e)}), 500
3923
+ home = access_guard._global_base()
3924
+ guards = []
3925
+ for glob in access_guard.DISABLEABLE_BUILTINS:
3926
+ canon = access_guard._norm_builtin(glob)
3927
+ guards.append({
3928
+ "glob": canon,
3929
+ "global": global_modes.get(canon, "default"),
3930
+ "strictness": {m: access_guard.builtin_strictness(canon, m)
3931
+ for m in (*access_guard.BUILTIN_MODES, "default")},
3932
+ })
3933
+ projects = []
3934
+ for p in _pm().list_registered():
3935
+ ppath = str(p.get("path") or "")
3936
+ row = {"name": p.get("name") or Path(ppath).name, "path": ppath,
3937
+ "modes": {}, "error": None}
3938
+ try:
3939
+ base = Path(ppath).resolve()
3940
+ if (base / ".c3").is_dir() and base != home:
3941
+ row["modes"] = access_guard._scope_modes("project", ppath)
3942
+ except Exception as e:
3943
+ row["error"] = str(e)
3944
+ if row["modes"] or row["error"]:
3945
+ projects.append(row)
3946
+ from services import builtin_leases
3947
+ return jsonify({"guards": guards, "projects": projects,
3948
+ "leases": builtin_leases.active()})
3949
+
3950
+
3951
+ @app.route("/api/hub/access/builtin/mode", methods=["POST"])
3952
+ def api_hub_access_builtin_mode():
3953
+ """Set a Tier-1 builtin's mode in the global realm or one project's.
3954
+
3955
+ Body: ``{scope, path?, glob, mode, confirm?}``. A mode at least as strict
3956
+ as the live one needs nothing more. A looser one needs a Desk client
3957
+ token (403 ``needs_human`` without it) and ``confirm`` equal to the glob
3958
+ (400 ``needs_confirmation``). A project is judged against what its
3959
+ agents see today, which is the global mode unless it set its own.
3960
+ """
3961
+ data = request.get_json(silent=True) or {}
3962
+ scope, project, err = _hub_access_target(data)
3963
+ if err:
3964
+ return err
3965
+ glob = str(data.get("glob") or "")
3966
+ mode = str(data.get("mode") or "").strip()
3967
+ canon = access_guard._norm_builtin(glob)
3968
+ try:
3969
+ new = access_guard.builtin_strictness(canon, mode)
3970
+ live = (access_guard._scope_modes("global") if scope == "global"
3971
+ else access_guard.effective_builtin_modes(project))
3972
+ old = access_guard.builtin_strictness(canon, live.get(canon, "default"))
3973
+ except ValueError as exc:
3974
+ return jsonify({"error": str(exc)}), 400
3975
+ if new < old:
3976
+ if _hub_human() is None:
3977
+ return jsonify({
3978
+ "error": "loosening a built-in guard needs C3 Desk",
3979
+ "needs_human": True,
3980
+ "command": f'c3 access builtin mode "{canon}" {mode}'
3981
+ + (f' --project --path "{project}"'
3982
+ if scope == "project" else ""),
3983
+ }), 403
3984
+ if access_guard._norm_builtin(data.get("confirm")) != canon:
3985
+ return jsonify({
3986
+ "error": "loosening a built-in guard weakens protection",
3987
+ "needs_confirmation": True, "confirm_with": canon,
3988
+ }), 400
3989
+ try:
3990
+ result = access_guard.set_builtin_mode(canon, mode, scope,
3991
+ project or ".")
3992
+ except ValueError as exc:
3993
+ return jsonify({"error": str(exc)}), 400
3994
+ if result["changed"]:
3995
+ _hub_access_audit(f"builtin_mode_{result['mode']}", result["glob"],
3996
+ "builtin", scope, project)
3997
+ return jsonify({**result, "loosened": new < old})
3998
+
3999
+
4000
+ @app.route("/api/hub/access/builtin/lease", methods=["POST"])
4001
+ def api_hub_access_builtin_lease():
4002
+ """Loosen a builtin for a while: every session, or one.
4003
+
4004
+ Body: ``{scope, path?, glob, mode, ttl_s, session_id?, confirm}``. A
4005
+ lease only ever loosens, so it always needs a Desk client token and the
4006
+ glob typed back, and a lease no looser than the live mode is a 400
4007
+ rather than a row that would never apply. ``ttl_s`` is 60 s to 8 h.
4008
+ """
4009
+ from services import builtin_leases
4010
+ data = request.get_json(silent=True) or {}
4011
+ scope, project, err = _hub_access_target(data)
4012
+ if err:
4013
+ return err
4014
+ who = _hub_human()
4015
+ if who is None:
4016
+ return jsonify({"error": "a temporary built-in change needs C3 Desk",
4017
+ "needs_human": True}), 403
4018
+ canon = access_guard._norm_builtin(data.get("glob"))
4019
+ mode = str(data.get("mode") or "").strip()
4020
+ if access_guard._norm_builtin(data.get("confirm")) != canon or not canon:
4021
+ return jsonify({"error": "a temporary change weakens protection",
4022
+ "needs_confirmation": True, "confirm_with": canon}), 400
4023
+ try:
4024
+ live = access_guard.effective_builtin_modes(project or ".") \
4025
+ if scope == "project" else access_guard._scope_modes("global")
4026
+ if access_guard.builtin_strictness(canon, mode) >= \
4027
+ access_guard.builtin_strictness(canon, live.get(canon, "default")):
4028
+ return jsonify({"error": f"{canon} is already {live.get(canon, 'default')}"
4029
+ f" — a {mode} lease would change nothing"}), 400
4030
+ row = builtin_leases.mint(
4031
+ canon, mode, scope=scope, project_path=project or ".",
4032
+ session_id=str(data.get("session_id") or ""),
4033
+ ttl_s=data.get("ttl_s"), created_by=who["client_id"])
4034
+ except ValueError as exc:
4035
+ return jsonify({"error": str(exc)}), 400
4036
+ _hub_access_audit(f"builtin_lease_{mode}", canon, "builtin", scope, project)
4037
+ return jsonify({"lease": row})
4038
+
4039
+
4040
+ @app.route("/api/hub/access/builtin/lease/revoke", methods=["POST"])
4041
+ def api_hub_access_builtin_lease_revoke():
4042
+ """End a lease now. Tightening, so no token. Body: ``{id}``."""
4043
+ from services import builtin_leases
4044
+ lease_id = str((request.get_json(silent=True) or {}).get("id") or "")
4045
+ gone = builtin_leases.revoke(lease_id) if lease_id else None
4046
+ if gone is None:
4047
+ return jsonify({"error": "unknown or already-expired lease"}), 404
4048
+ _hub_access_audit("builtin_lease_revoke", gone["glob"], "builtin",
4049
+ gone["scope"], gone.get("project_path") or None)
4050
+ return jsonify({"revoked": lease_id})
4051
+
4052
+
3842
4053
  # ── Project management: tasks / milestones / notes (v2.45.0) ──────────────
3843
4054
  # Direct TaskStore per request (reload-per-op store — no runtime build needed
3844
4055
  # for mutations); every write audited to the target project's activity log.
@@ -19,8 +19,9 @@
19
19
  // hand — the server enforces that regardless of what this UI believes);
20
20
  // - a request that lapsed while the page showed it refreshes to its real
21
21
  // status (the decide route answers 409), never silently mints a grant;
22
- // - rule MUTATION stays on the per-project server and `c3 access` — this
23
- // tab approves and reads, it does not edit policy.
22
+ // - path-rule MUTATION stays on the per-project server and `c3 access`.
23
+ // Builtin guards may be TIGHTENED here; loosening one needs C3 Desk or
24
+ // the CLI, because this page holds no proof that a person clicked.
24
25
 
25
26
  const ACC_KIND_COLOR = (kind) => (
26
27
  kind === 'deny' ? T.error : kind === 'mask' ? T.blue
@@ -387,6 +388,121 @@ function AccRulesPanel({ projects, focus }) {
387
388
  );
388
389
  }
389
390
 
391
+ // ── Builtin guards ─────────────────────────────────────────────────────────
392
+ // The global mode of every Tier-1 builtin. Picking a stricter mode applies
393
+ // it; picking a looser one is refused by the server without a Desk token,
394
+ // and the panel shows the CLI command instead.
395
+ const ACC_BUILTIN_MODES = ['default', 'deny', 'confirm', 'allow'];
396
+
397
+ function AccBuiltinPanel() {
398
+ const [data, setData] = useState(null);
399
+ const [err, setErr] = useState('');
400
+ const [busy, setBusy] = useState('');
401
+ const [needs, setNeeds] = useState(null);
402
+
403
+ const load = useCallback(async () => {
404
+ try { setData(await api.get('/api/hub/access/builtin')); setErr(''); }
405
+ catch (e) { setErr(apiErr(e)); }
406
+ }, []);
407
+ useEffect(() => { load(); }, [load]);
408
+ usePoll(load, 15000);
409
+
410
+ const setMode = async (g, mode) => {
411
+ setBusy(g.glob); setNeeds(null);
412
+ try {
413
+ await api.post('/api/hub/access/builtin/mode', { scope: 'global', glob: g.glob, mode });
414
+ notify(`${g.glob} is now ${mode} everywhere`, 'ok');
415
+ } catch (e) {
416
+ const body = e && e.payload;
417
+ if (body && body.needs_human) setNeeds({ glob: g.glob, command: body.command });
418
+ else notify(apiErr(e), 'err');
419
+ }
420
+ setBusy('');
421
+ load();
422
+ };
423
+
424
+ const revoke = async (l) => {
425
+ setBusy(l.id);
426
+ try {
427
+ await api.post('/api/hub/access/builtin/lease/revoke', { id: l.id });
428
+ notify(`Ended the ${l.mode} lease on ${l.glob}`, 'ok');
429
+ } catch (e) { notify(apiErr(e), 'err'); }
430
+ setBusy('');
431
+ load();
432
+ };
433
+
434
+ const projects = (data && data.projects) || [];
435
+ const leases = (data && data.leases) || [];
436
+ return (
437
+ <div style={{
438
+ background: T.surface, border: `1px solid ${T.border}`, borderRadius: 10,
439
+ padding: '12px 16px',
440
+ }}>
441
+ <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
442
+ <div style={{ fontSize: 12.5, fontWeight: 700, color: T.text }}>Built-in guards</div>
443
+ <span style={{ fontSize: 10.5, color: T.textDim }}>
444
+ global mode — a stricter mode applies here; loosening one needs C3 Desk or the CLI
445
+ </span>
446
+ </div>
447
+ {err && <div style={{ marginTop: 8, fontSize: 11.5, color: T.error }}>{err}</div>}
448
+ {needs && (
449
+ <div style={{ marginTop: 8, fontSize: 11.5, color: T.warn }}>
450
+ Loosening <span className="mono">{needs.glob}</span> needs C3 Desk, or run:{' '}
451
+ <span className="mono" style={{ color: T.text }}>{needs.command}</span>
452
+ </div>
453
+ )}
454
+ {data && (
455
+ <div style={{ marginTop: 10, display: 'grid', gridTemplateColumns: 'minmax(0,1fr) auto', gap: '4px 12px', alignItems: 'center' }}>
456
+ {data.guards.map(g => (
457
+ <React.Fragment key={g.glob}>
458
+ <span className="mono" style={{ fontSize: 11.5, color: T.text, overflow: 'hidden', textOverflow: 'ellipsis' }}>{g.glob}</span>
459
+ <select value={g.global} disabled={busy === g.glob}
460
+ onChange={e => setMode(g, e.target.value)}
461
+ style={{
462
+ background: T.surfaceAlt, color: g.global === 'default' ? T.textMuted : T.warn,
463
+ border: `1px solid ${T.border}`, borderRadius: 6, fontSize: 11.5, padding: '2px 6px',
464
+ }}>
465
+ {ACC_BUILTIN_MODES.map(m => <option key={m} value={m}>{m}</option>)}
466
+ </select>
467
+ </React.Fragment>
468
+ ))}
469
+ </div>
470
+ )}
471
+ {leases.length > 0 && (
472
+ <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 4 }}>
473
+ <div style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', color: T.warn }}>
474
+ temporary changes
475
+ </div>
476
+ {leases.map(l => (
477
+ <div key={l.id} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11.5 }}>
478
+ <span className="mono" style={{ color: T.text }}>{l.glob}</span>
479
+ <span style={{ color: T.warn }}>{l.mode}</span>
480
+ <span style={{ color: T.textMuted }}>
481
+ {l.scope === 'project' ? l.project_path : 'all projects'}
482
+ {' · '}{l.session_id ? <>session <span className="mono">{l.session_id.slice(0, 8)}</span></> : 'all sessions'}
483
+ {' · '}ends in {accExpiresIn(l.expires_at) || '—'}
484
+ </span>
485
+ <div style={{ flex: 1 }} />
486
+ <Btn variant="ghost" disabled={busy === l.id} onClick={() => revoke(l)}>Revoke</Btn>
487
+ </div>
488
+ ))}
489
+ </div>
490
+ )}
491
+ {projects.length > 0 && (
492
+ <div style={{ marginTop: 8, fontSize: 11, color: T.textDim }}>
493
+ Set per project:{' '}
494
+ {projects.map(p => (
495
+ <span key={p.path} className="mono" style={{ marginRight: 10 }}>
496
+ {p.name}: {p.error ? `unreadable (${p.error})`
497
+ : Object.entries(p.modes).map(([g, m]) => `${g}=${m}`).join(', ')}
498
+ </span>
499
+ ))}
500
+ </div>
501
+ )}
502
+ </div>
503
+ );
504
+ }
505
+
390
506
  // ── Active grants ──────────────────────────────────────────────────────────
391
507
  // A rule grant can authorise many calls over hours. The only other place it
392
508
  // shows up is a line in the agent's own transcript, which is the wrong place
@@ -762,6 +878,7 @@ function HubAccess({ projects }) {
762
878
  )}
763
879
 
764
880
  <AccGrantsPanel projects={projects} />
881
+ <AccBuiltinPanel />
765
882
  <AccRulesPanel projects={projects} focus={ruleFocus} />
766
883
  {confirmSpec && (
767
884
  <CredConfirm spec={confirmSpec} onClose={() => setConfirmSpec(null)} />
@@ -528,24 +528,41 @@ function ReparentModal({ project, projects, onClose, onChanged }) {
528
528
 
529
529
  // ── IDE picker ─────────────────────────────────────────────────
530
530
  // POST /api/projects/launch-ide {path, ide, custom_cmd?}
531
+ // The command is editable per project (stored as ide_cmd via
532
+ // /api/projects/update) so an agent started through a wrapper — `yep` rather
533
+ // than `claude` — launches from the card too, not just from here.
531
534
  function IdePickerModal({ project, onClose, onChanged }) {
532
535
  const known = IDE_OPTIONS.some(o => o.id === (project && project.ide));
533
536
  const [selected, setSelected] = useState(known ? project.ide : 'claude-code');
534
- const [customCmd, setCustomCmd] = useState('');
537
+ const savedCmd = ((project && project.ide_cmd) || '').trim();
538
+ const [customCmd, setCustomCmd] = useState(savedCmd);
539
+ const [remember, setRemember] = useState(!!savedCmd);
535
540
  const [busy, setBusy] = useState(false);
536
541
 
542
+ const stockCmd = (id) => {
543
+ const opt = IDE_OPTIONS.find(o => o.id === id);
544
+ return opt && opt.cmd !== '...' ? opt.cmd : '';
545
+ };
546
+ const effectiveCmd = customCmd.trim() || stockCmd(selected);
547
+
537
548
  const launch = async () => {
538
- if (selected === 'custom' && !customCmd.trim()) {
549
+ const cmd = customCmd.trim();
550
+ if (selected === 'custom' && !cmd) {
539
551
  notify('Enter a custom command first.', 'err');
540
552
  return;
541
553
  }
542
554
  setBusy(true);
543
555
  try {
556
+ // Persist first: the project card launches with no command of its own.
557
+ const wanted = remember ? cmd : '';
558
+ if (wanted !== savedCmd) {
559
+ await api.post('/api/projects/update', { path: project.path, ide_cmd: wanted });
560
+ }
544
561
  await api.post('/api/projects/launch-ide', {
545
- path: project.path, ide: selected,
546
- custom_cmd: selected === 'custom' ? customCmd.trim() : '',
562
+ path: project.path, ide: selected, custom_cmd: cmd,
547
563
  });
548
- notify(`Launched ${ideLabel(selected)} in ${project.name || project.path}`);
564
+ notify(`Launched ${ideLabel(selected)} (${effectiveCmd}) in ${project.name || project.path}`);
565
+ if (onChanged) onChanged();
549
566
  onClose();
550
567
  } catch (e) {
551
568
  notify(`Launch failed: ${e.message}`, 'err');
@@ -575,13 +592,18 @@ function IdePickerModal({ project, onClose, onChanged }) {
575
592
  );
576
593
  })}
577
594
  </div>
578
- {selected === 'custom' && (
579
- <div>
580
- <MdlLabel>Custom command</MdlLabel>
581
- <input value={customCmd} onChange={e => setCustomCmd(e.target.value)}
582
- placeholder="e.g. nvim ." className="mono" style={mdlInputStyle()} />
583
- </div>
584
- )}
595
+ <div>
596
+ <MdlLabel>Command{selected === 'custom' ? '' : ' (leave blank for the default)'}</MdlLabel>
597
+ <input value={customCmd} onChange={e => setCustomCmd(e.target.value)}
598
+ placeholder={selected === 'custom' ? 'e.g. nvim .' : `${stockCmd(selected)} — override with e.g. yep`}
599
+ className="mono" style={mdlInputStyle()} />
600
+ <label style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 6,
601
+ fontSize: 11, color: T.textMuted, cursor: 'pointer' }}>
602
+ <input type="checkbox" checked={remember}
603
+ onChange={e => setRemember(e.target.checked)} />
604
+ Remember for this project (used by the card's launch button too)
605
+ </label>
606
+ </div>
585
607
  <MdlFooter>
586
608
  <Btn variant="ghost" onClick={onClose}>Cancel</Btn>
587
609
  <Btn onClick={launch} disabled={busy}>
@@ -14,6 +14,7 @@ function SettingsModal({ onClose, onChanged }) {
14
14
  const [port, setPort] = useState('');
15
15
  const [autoBrowser, setAutoBrowser] = useState(true);
16
16
  const [oracleUrl, setOracleUrl] = useState('');
17
+ const [agentCmd, setAgentCmd] = useState(''); // ide_cmds['claude-code']
17
18
  const [cacheSize, setCacheSize] = useState(8);
18
19
  const [saving, setSaving] = useState(false);
19
20
  const [svc, setSvc] = useState(null); // hub: {installed, running, method, port, log_path} | {error}
@@ -51,6 +52,7 @@ function SettingsModal({ onClose, onChanged }) {
51
52
  setPort(d.port || 3330);
52
53
  setAutoBrowser(!!d.auto_open_browser);
53
54
  setOracleUrl(d.oracle_url || '');
55
+ setAgentCmd(((d.ide_cmds || {})['claude-code']) || '');
54
56
  setCacheSize(d.runtime_cache_size || 8);
55
57
  } catch {
56
58
  notify('Could not load hub config', 'err');
@@ -70,9 +72,14 @@ function SettingsModal({ onClose, onChanged }) {
70
72
  const cache = Math.max(1, parseInt(cacheSize, 10) || 8);
71
73
  setSaving(true);
72
74
  try {
75
+ // Blank restores the stock `claude`; other IDE overrides are left alone.
76
+ const ideCmds = { ...((cfg && cfg.ide_cmds) || {}) };
77
+ if (agentCmd.trim()) ideCmds['claude-code'] = agentCmd.trim();
78
+ else delete ideCmds['claude-code'];
73
79
  await api.post('/api/hub/config', {
74
80
  port: portVal, auto_open_browser: autoBrowser,
75
81
  oracle_url: oracleUrl.trim(), runtime_cache_size: cache,
82
+ ide_cmds: ideCmds,
76
83
  });
77
84
  const portChanged = cfg && portVal !== cfg.port;
78
85
  notify(portChanged
@@ -222,6 +229,10 @@ function SettingsModal({ onClose, onChanged }) {
222
229
  <input type="number" value={cacheSize} onChange={e => setCacheSize(e.target.value)}
223
230
  min={1} className="mono" style={inputStyle(110)} />
224
231
  ), 'Max project runtimes kept warm for drill-in and global search.')}
232
+ {fieldRow('Agent command', (
233
+ <input value={agentCmd} onChange={e => setAgentCmd(e.target.value)}
234
+ placeholder="claude" className="mono" style={inputStyle(220)} />
235
+ ), 'Default command for "Claude Code CLI" launches — e.g. yep for a wrapper script. A project can override it in Open in IDE.')}
225
236
  {fieldRow('Oracle URL', (
226
237
  <input value={oracleUrl} onChange={e => setOracleUrl(e.target.value)}
227
238
  placeholder="http://localhost:3331" className="mono" style={inputStyle(220)} />