code-context-control 2.95.0__tar.gz → 2.96.1__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 (464) hide show
  1. {code_context_control-2.95.0/code_context_control.egg-info → code_context_control-2.96.1}/PKG-INFO +3 -3
  2. {code_context_control-2.95.0 → code_context_control-2.96.1}/README.md +2 -2
  3. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/c3.py +117 -9
  4. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/commands/parser.py +9 -2
  5. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/tools.html +16 -1
  6. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/workflow.html +40 -10
  7. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_server.py +123 -3
  8. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/drill_panel.js +4 -2
  9. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/modals.js +309 -16
  10. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/project_card.js +26 -24
  11. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/project_tree.js +44 -40
  12. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/state.js +46 -11
  13. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/mcp_server.py +10 -2
  14. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/federate.py +7 -3
  15. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/project.py +77 -20
  16. {code_context_control-2.95.0 → code_context_control-2.96.1/code_context_control.egg-info}/PKG-INFO +3 -3
  17. {code_context_control-2.95.0 → code_context_control-2.96.1}/code_context_control.egg-info/SOURCES.txt +3 -0
  18. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/c3_bridge.py +31 -5
  19. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/federated_graph.py +21 -2
  20. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/project_scanner.py +19 -9
  21. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/tool_registry.py +10 -5
  22. {code_context_control-2.95.0 → code_context_control-2.96.1}/pyproject.toml +1 -1
  23. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/claude_md.py +1 -1
  24. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/project_manager.py +30 -2
  25. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/repo_map.py +6 -0
  26. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/subprojects.py +431 -57
  27. code_context_control-2.96.1/tests/test_subproject_clear_scope.py +118 -0
  28. code_context_control-2.96.1/tests/test_subproject_depth_consumers.py +205 -0
  29. code_context_control-2.96.1/tests/test_subproject_linkage_api.py +305 -0
  30. code_context_control-2.96.1/tests/test_subprojects.py +651 -0
  31. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ui_jsx_syntax.py +49 -3
  32. code_context_control-2.95.0/tests/test_subprojects.py +0 -294
  33. {code_context_control-2.95.0 → code_context_control-2.96.1}/LICENSE +0 -0
  34. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/__init__.py +0 -0
  35. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/_hook_utils.py +0 -0
  36. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/_shell_writes.py +0 -0
  37. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/commands/__init__.py +0 -0
  38. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/commands/common.py +0 -0
  39. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/docs.html +0 -0
  40. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/edits.html +0 -0
  41. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/access.html +0 -0
  42. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/bitbucket.html +0 -0
  43. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/credentials.html +0 -0
  44. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/getting-started.html +0 -0
  45. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/index.html +0 -0
  46. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/masking.html +0 -0
  47. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/oracle.html +0 -0
  48. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/guide/shared.css +0 -0
  49. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_access_guard.py +0 -0
  50. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_artifact.py +0 -0
  51. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_auto_snapshot.py +0 -0
  52. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_c3_signal.py +0 -0
  53. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_c3read.py +0 -0
  54. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_dispatch.py +0 -0
  55. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_edit_ledger.py +0 -0
  56. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_edit_unlock.py +0 -0
  57. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_filter.py +0 -0
  58. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_ghost_files.py +0 -0
  59. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_pretool_enforce.py +0 -0
  60. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_prompt_recall.py +0 -0
  61. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_read.py +0 -0
  62. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_session_stats.py +0 -0
  63. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hook_terse_advisor.py +0 -0
  64. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/app.js +0 -0
  65. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/add_project.js +0 -0
  66. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/config_editor.js +0 -0
  67. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/drill_artifacts.js +0 -0
  68. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/drill_health.js +0 -0
  69. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/drill_subprojects.js +0 -0
  70. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/drill_tasks.js +0 -0
  71. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/drill_views.js +0 -0
  72. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/global_search.js +0 -0
  73. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/hub_ci.js +0 -0
  74. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/hub_cred_audit.js +0 -0
  75. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/hub_credentials.js +0 -0
  76. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/hub_enforcement.js +0 -0
  77. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/hub_locks.js +0 -0
  78. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/hub_tokens.js +0 -0
  79. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/mcp_manager.js +0 -0
  80. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/session_drawer.js +0 -0
  81. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/settings_modal.js +0 -0
  82. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/sidebar.js +0 -0
  83. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/summary_bar.js +0 -0
  84. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/task_board.js +0 -0
  85. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/toasts.js +0 -0
  86. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui/components/topbar.js +0 -0
  87. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/hub_ui.html +0 -0
  88. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/mcp_proxy.py +0 -0
  89. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/progress.py +0 -0
  90. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/server.py +0 -0
  91. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/__init__.py +0 -0
  92. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/_grants.py +0 -0
  93. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/_helpers.py +0 -0
  94. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/agent.py +0 -0
  95. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/artifacts.py +0 -0
  96. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/bitbucket.py +0 -0
  97. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/ci.py +0 -0
  98. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/compress.py +0 -0
  99. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/credentials.py +0 -0
  100. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/delegate.py +0 -0
  101. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/edit.py +0 -0
  102. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/edit_verify.py +0 -0
  103. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/edits.py +0 -0
  104. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/filter.py +0 -0
  105. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/impact.py +0 -0
  106. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/jira.py +0 -0
  107. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/locks.py +0 -0
  108. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/memory.py +0 -0
  109. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/override.py +0 -0
  110. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/read.py +0 -0
  111. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/search.py +0 -0
  112. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/session.py +0 -0
  113. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/shell.py +0 -0
  114. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/status.py +0 -0
  115. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/tasks.py +0 -0
  116. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/tools/validate.py +0 -0
  117. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/api.js +0 -0
  118. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/app.js +0 -0
  119. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/access.js +0 -0
  120. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/bitbucket.js +0 -0
  121. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/chat.js +0 -0
  122. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/credentials.js +0 -0
  123. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/dashboard.js +0 -0
  124. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/edits.js +0 -0
  125. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/enforcement.js +0 -0
  126. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/instructions.js +0 -0
  127. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/jira.js +0 -0
  128. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/memory.js +0 -0
  129. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/sessions.js +0 -0
  130. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/settings.js +0 -0
  131. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/sidebar.js +0 -0
  132. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/components/tasks.js +0 -0
  133. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/icons.js +0 -0
  134. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/pm_shared.js +0 -0
  135. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/shared.js +0 -0
  136. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui/theme.js +0 -0
  137. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui.html +0 -0
  138. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui_legacy.html +0 -0
  139. {code_context_control-2.95.0 → code_context_control-2.96.1}/cli/ui_nano.html +0 -0
  140. {code_context_control-2.95.0 → code_context_control-2.96.1}/code_context_control.egg-info/dependency_links.txt +0 -0
  141. {code_context_control-2.95.0 → code_context_control-2.96.1}/code_context_control.egg-info/entry_points.txt +0 -0
  142. {code_context_control-2.95.0 → code_context_control-2.96.1}/code_context_control.egg-info/requires.txt +0 -0
  143. {code_context_control-2.95.0 → code_context_control-2.96.1}/code_context_control.egg-info/top_level.txt +0 -0
  144. {code_context_control-2.95.0 → code_context_control-2.96.1}/core/__init__.py +0 -0
  145. {code_context_control-2.95.0 → code_context_control-2.96.1}/core/config.py +0 -0
  146. {code_context_control-2.95.0 → code_context_control-2.96.1}/core/ide.py +0 -0
  147. {code_context_control-2.95.0 → code_context_control-2.96.1}/core/mcp_toml.py +0 -0
  148. {code_context_control-2.95.0 → code_context_control-2.96.1}/core/web_security.py +0 -0
  149. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/__init__.py +0 -0
  150. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/config.py +0 -0
  151. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/mcp_oracle.py +0 -0
  152. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/oracle_server.py +0 -0
  153. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/oracle_ui.html +0 -0
  154. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/__init__.py +0 -0
  155. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/activity_reporter.py +0 -0
  156. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/api_auth.py +0 -0
  157. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/chat_engine.py +0 -0
  158. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/chat_poll.py +0 -0
  159. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/chat_store.py +0 -0
  160. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/cross_memory.py +0 -0
  161. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/discovery_audit.py +0 -0
  162. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/health_checker.py +0 -0
  163. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/insight_engine.py +0 -0
  164. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/local_session.py +0 -0
  165. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/memory_reader.py +0 -0
  166. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/memory_writer.py +0 -0
  167. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/mobile_api.py +0 -0
  168. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/review_agent.py +0 -0
  169. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/services/tool_executor.py +0 -0
  170. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/activity.js +0 -0
  171. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/agents.js +0 -0
  172. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/app.js +0 -0
  173. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/busy.js +0 -0
  174. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/chat/conversations.js +0 -0
  175. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/chat/input.js +0 -0
  176. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/chat/markdown.js +0 -0
  177. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/chat/send.js +0 -0
  178. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/chat/stream_renderer.js +0 -0
  179. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/chat/toolbar.js +0 -0
  180. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/core.js +0 -0
  181. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/crossgraph.js +0 -0
  182. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/header.js +0 -0
  183. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/insights.js +0 -0
  184. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/projects.js +0 -0
  185. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/qrcode.js +0 -0
  186. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/settings.js +0 -0
  187. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/suggestions.js +0 -0
  188. {code_context_control-2.95.0 → code_context_control-2.96.1}/oracle/ui/theme_tabs.js +0 -0
  189. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/__init__.py +0 -0
  190. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/access_guard.py +0 -0
  191. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/access_telemetry.py +0 -0
  192. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/activity_log.py +0 -0
  193. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/agent_base.py +0 -0
  194. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/agent_locks.py +0 -0
  195. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/agents.py +0 -0
  196. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/artifact_defs.py +0 -0
  197. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/artifact_store.py +0 -0
  198. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/auto_memory.py +0 -0
  199. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/background_service.py +0 -0
  200. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/bench/__init__.py +0 -0
  201. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/bench/external/__init__.py +0 -0
  202. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/bench/external/aider_polyglot.py +0 -0
  203. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/bench/external/swe_bench.py +0 -0
  204. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/benchmark_dashboard.py +0 -0
  205. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/bitbucket_client.py +0 -0
  206. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/bitbucket_credentials.py +0 -0
  207. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_act.py +0 -0
  208. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_cache.py +0 -0
  209. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_expr.py +0 -0
  210. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_failures.py +0 -0
  211. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_github.py +0 -0
  212. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_impact.py +0 -0
  213. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_intel.py +0 -0
  214. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_runner.py +0 -0
  215. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ci_workflow.py +0 -0
  216. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/circuit_breaker.py +0 -0
  217. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/compressor.py +0 -0
  218. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/context_snapshot.py +0 -0
  219. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/conversation_store.py +0 -0
  220. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/cred_audit.py +0 -0
  221. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/cred_telemetry.py +0 -0
  222. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/credential_store.py +0 -0
  223. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/doc_index.py +0 -0
  224. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/e2e_benchmark.py +0 -0
  225. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/e2e_evaluator.py +0 -0
  226. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/e2e_tasks.py +0 -0
  227. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/edit_ledger.py +0 -0
  228. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/embedding_index.py +0 -0
  229. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/enforcement_policy.py +0 -0
  230. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/error_reporting.py +0 -0
  231. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/file_memory.py +0 -0
  232. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/git_context.py +0 -0
  233. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/hub_service.py +0 -0
  234. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/indexer.py +0 -0
  235. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/jira_client.py +0 -0
  236. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/jira_cloud.py +0 -0
  237. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/jira_credentials.py +0 -0
  238. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/jira_data_center.py +0 -0
  239. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/jira_links.py +0 -0
  240. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/mask_activation.py +0 -0
  241. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/mask_mirror.py +0 -0
  242. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/mask_presets.py +0 -0
  243. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory.py +0 -0
  244. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory_consolidator.py +0 -0
  245. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory_distiller.py +0 -0
  246. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory_graph.py +0 -0
  247. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory_grounder.py +0 -0
  248. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory_queue.py +0 -0
  249. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/memory_scorer.py +0 -0
  250. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/metrics.py +0 -0
  251. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/notifications.py +0 -0
  252. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ollama_bridge.py +0 -0
  253. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ollama_client.py +0 -0
  254. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/ollama_credentials.py +0 -0
  255. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/oracle_service.py +0 -0
  256. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/output_filter.py +0 -0
  257. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/override_grants.py +0 -0
  258. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/override_policy.py +0 -0
  259. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/override_requests.py +0 -0
  260. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/override_wake.py +0 -0
  261. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/parser.py +0 -0
  262. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/project_runtime.py +0 -0
  263. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/protocol.py +0 -0
  264. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/proxy_state.py +0 -0
  265. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/repo_shape.py +0 -0
  266. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/retention.py +0 -0
  267. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/retrieval_broker.py +0 -0
  268. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/router.py +0 -0
  269. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/runtime.py +0 -0
  270. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/scanner.py +0 -0
  271. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/session_benchmark.py +0 -0
  272. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/session_manager.py +0 -0
  273. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/session_preloader.py +0 -0
  274. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/task_store.py +0 -0
  275. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/telemetry.py +0 -0
  276. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/text_index.py +0 -0
  277. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/time_tracker.py +0 -0
  278. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/tool_classifier.py +0 -0
  279. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/transcript_index.py +0 -0
  280. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/validation_cache.py +0 -0
  281. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/vector_store.py +0 -0
  282. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/watcher.py +0 -0
  283. {code_context_control-2.95.0 → code_context_control-2.96.1}/services/win_subprocess.py +0 -0
  284. {code_context_control-2.95.0 → code_context_control-2.96.1}/setup.cfg +0 -0
  285. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_access_builtin_optout.py +0 -0
  286. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_access_guard.py +0 -0
  287. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_access_guard_meta.py +0 -0
  288. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_access_guard_shell_project.py +0 -0
  289. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_access_guard_surfaces.py +0 -0
  290. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_access_guard_wiring.py +0 -0
  291. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_activity_reporter.py +0 -0
  292. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_agent_locks.py +0 -0
  293. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_aider_polyglot.py +0 -0
  294. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_antigravity_transition.py +0 -0
  295. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_artifact_store.py +0 -0
  296. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_artifact_tool.py +0 -0
  297. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_background_service.py +0 -0
  298. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_bitbucket_cli_smoke.py +0 -0
  299. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_bitbucket_client.py +0 -0
  300. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_bitbucket_config_fallback.py +0 -0
  301. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_bitbucket_credentials.py +0 -0
  302. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_bitbucket_tool.py +0 -0
  303. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_c3_bridge_project_artifacts.py +0 -0
  304. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_c3_shell.py +0 -0
  305. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_chat_poll.py +0 -0
  306. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_act.py +0 -0
  307. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_cache.py +0 -0
  308. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_expr.py +0 -0
  309. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_impact.py +0 -0
  310. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_intel.py +0 -0
  311. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_runner.py +0 -0
  312. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ci_workflow.py +0 -0
  313. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_circuit_breaker.py +0 -0
  314. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_claude_md_merge.py +0 -0
  315. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_cli_smoke.py +0 -0
  316. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_compressor_large_file.py +0 -0
  317. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_cooccurrence_bounds.py +0 -0
  318. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_cred_audit.py +0 -0
  319. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_cred_telemetry.py +0 -0
  320. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_credential_login_kind.py +0 -0
  321. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_credential_store.py +0 -0
  322. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_credential_ui_parity.py +0 -0
  323. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_credentials_routes.py +0 -0
  324. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_credentials_tool.py +0 -0
  325. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_creds_cli_smoke.py +0 -0
  326. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_delegate_cascade.py +0 -0
  327. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_delegate_version_probe.py +0 -0
  328. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_e2e_benchmark.py +0 -0
  329. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_edit_ledger_deadline.py +0 -0
  330. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_edit_ledger_hook.py +0 -0
  331. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_edit_locking.py +0 -0
  332. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_edit_normalization.py +0 -0
  333. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_edit_verify.py +0 -0
  334. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_embedding_index_deadlock.py +0 -0
  335. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_enforcement_flip.py +0 -0
  336. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_enforcement_policy.py +0 -0
  337. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_enforcement_routes.py +0 -0
  338. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_env_parser.py +0 -0
  339. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_failed_calls_do_not_unlock.py +0 -0
  340. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_federated_graph.py +0 -0
  341. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_filter_backoff.py +0 -0
  342. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ghost_files.py +0 -0
  343. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ghost_generation.py +0 -0
  344. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_git_branch_awareness.py +0 -0
  345. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_access_guard.py +0 -0
  346. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_codex_compat.py +0 -0
  347. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_dispatch.py +0 -0
  348. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_pretool_enforce.py +0 -0
  349. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_prompt_recall.py +0 -0
  350. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_smoke.py +0 -0
  351. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_state.py +0 -0
  352. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hook_utf8_stdio.py +0 -0
  353. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_credentials_route.py +0 -0
  354. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_credentials_routes_write.py +0 -0
  355. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_enforcement_routes.py +0 -0
  356. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_inspect_api.py +0 -0
  357. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_locks_routes.py +0 -0
  358. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_oracle_service_routes.py +0 -0
  359. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_server_smoke.py +0 -0
  360. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_hub_subproject_links.py +0 -0
  361. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ide_workflow_adaptation.py +0 -0
  362. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_init_clear_preserves_user_content.py +0 -0
  363. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_init_scan.py +0 -0
  364. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_install_mcp_entrypoint.py +0 -0
  365. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_cli_smoke.py +0 -0
  366. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_client.py +0 -0
  367. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_config_fallback.py +0 -0
  368. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_credentials.py +0 -0
  369. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_links.py +0 -0
  370. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_routes.py +0 -0
  371. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_jira_tool.py +0 -0
  372. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_lazy_store_init.py +0 -0
  373. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mask_guard.py +0 -0
  374. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mask_routes.py +0 -0
  375. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mask_surfaces.py +0 -0
  376. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mcp_host_guard.py +0 -0
  377. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mcp_memory_timeout.py +0 -0
  378. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mcp_server_smoke.py +0 -0
  379. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mcp_toml.py +0 -0
  380. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_memory_distiller.py +0 -0
  381. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_memory_graph_api.py +0 -0
  382. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_memory_queue.py +0 -0
  383. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_memory_system.py +0 -0
  384. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mobile_api.py +0 -0
  385. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mobile_extras.py +0 -0
  386. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mobile_override_routes.py +0 -0
  387. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_mobile_security_api.py +0 -0
  388. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_no_stale_model_pins.py +0 -0
  389. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_notification_dedup.py +0 -0
  390. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_notification_discipline.py +0 -0
  391. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_ollama_credentials.py +0 -0
  392. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_api_auth.py +0 -0
  393. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_apikey_api.py +0 -0
  394. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_c3_bridge.py +0 -0
  395. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_chat_engine.py +0 -0
  396. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_chat_store.py +0 -0
  397. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_discovery_api.py +0 -0
  398. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_discovery_audit.py +0 -0
  399. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_health_probe.py +0 -0
  400. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_local_auth.py +0 -0
  401. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_ollama_bridge.py +0 -0
  402. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_scanner_cache.py +0 -0
  403. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_security_fixes.py +0 -0
  404. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_session_bootstrap.py +0 -0
  405. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_subproject_awareness.py +0 -0
  406. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_oracle_ui_bundle.py +0 -0
  407. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_output_filter.py +0 -0
  408. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_override_grants.py +0 -0
  409. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_override_grants_mcp.py +0 -0
  410. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_override_requests.py +0 -0
  411. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_override_wake.py +0 -0
  412. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_permissions.py +0 -0
  413. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_pm_api.py +0 -0
  414. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_project_manager.py +0 -0
  415. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_project_manager_merge.py +0 -0
  416. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_project_tool.py +0 -0
  417. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_read_coercion.py +0 -0
  418. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_read_edit_parity.py +0 -0
  419. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_related_facts.py +0 -0
  420. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_repo_map.py +0 -0
  421. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_repo_shape.py +0 -0
  422. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_response_boilerplate.py +0 -0
  423. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_retention.py +0 -0
  424. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_review_digest.py +0 -0
  425. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_service_durability.py +0 -0
  426. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_session_benchmark.py +0 -0
  427. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_session_budget.py +0 -0
  428. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_shell_creds.py +0 -0
  429. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_shell_robustness.py +0 -0
  430. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_shell_write_ledger.py +0 -0
  431. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_shell_writes.py +0 -0
  432. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_subproject_exclusion.py +0 -0
  433. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_subproject_federation.py +0 -0
  434. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_swe_bench.py +0 -0
  435. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_task_store.py +0 -0
  436. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_task_tool.py +0 -0
  437. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_time_tracker.py +0 -0
  438. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_token_telemetry.py +0 -0
  439. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_token_tracking.py +0 -0
  440. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_tool_registry.py +0 -0
  441. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_upgrade_and_version.py +0 -0
  442. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_validate.py +0 -0
  443. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_vault_write_guard.py +0 -0
  444. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_version_sync.py +0 -0
  445. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_web_security.py +0 -0
  446. {code_context_control-2.95.0 → code_context_control-2.96.1}/tests/test_windows_reliability.py +0 -0
  447. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/__init__.py +0 -0
  448. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/backend.py +0 -0
  449. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/main.py +0 -0
  450. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/__init__.py +0 -0
  451. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/benchmark_view.py +0 -0
  452. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/claudemd_view.py +0 -0
  453. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/compress_view.py +0 -0
  454. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/index_view.py +0 -0
  455. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/init_view.py +0 -0
  456. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/mcp_view.py +0 -0
  457. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/optimize_view.py +0 -0
  458. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/pipe_view.py +0 -0
  459. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/projects_view.py +0 -0
  460. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/search_view.py +0 -0
  461. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/session_view.py +0 -0
  462. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/stats.py +0 -0
  463. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/screens/ui_view.py +0 -0
  464. {code_context_control-2.95.0 → code_context_control-2.96.1}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.95.0
3
+ Version: 2.96.1
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
@@ -149,7 +149,7 @@ Every C3-initialized project registers itself here on `c3 init`. Filter by activ
149
149
 
150
150
  Three top-level views — **Projects**, **Tasks** (a cross-project kanban), and **Credentials** — plus a drill-in panel per project covering Overview, Sub-projects, Tasks, Artifacts, Memory, Ledger, Sessions, Health, Budget, Credentials, Config, and MCP.
151
151
 
152
- **Sub-projects** make a nested repo a first-class child with its own `.c3`: the parent's index excludes the child's subtree, and `c3_search` / `c3_memory` fan out on demand (`scope='all'`, or one child by name). → [Sub-projects guide](https://github.com/drknowhow/code-context-control/blob/main/docs/sub-projects.md)
152
+ **Sub-projects** make one project a first-class child of another, with its own `.c3`. It can be a nested repo — the parent's index then excludes its subtree — or a project anywhere else on disk, linked by path. Hierarchies nest up to 8 levels, and `c3_search` / `c3_memory` fan out on demand (`scope='all'`, or one by name). → [Sub-projects guide](https://github.com/drknowhow/code-context-control/blob/main/docs/sub-projects.md)
153
153
 
154
154
  ### Per-project UI
155
155
 
@@ -211,7 +211,7 @@ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
211
211
  | `c3_credentials` | Named-secret vault; values never enter model context (v2.58.0). `import_env` bulk-imports a `.env` the agent still cannot read (v2.93.0) |
212
212
  | `c3_bitbucket` | Bitbucket Data Center — PRs, branches, builds, admin (v2.30.0) |
213
213
  | `c3_jira` | Jira Cloud + Data Center — JQL, issues, transitions (v2.56.0) |
214
- | `c3_project` | Cross-project discovery & operations; guarded writes (v2.31.0) |
214
+ | `c3_project` | Cross-project discovery & operations; sub-project hierarchy (link by path, inspect, multi-level tree); guarded writes (v2.31.0) |
215
215
 
216
216
  Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_ci(action='run'|'rerun')`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task` / `c3_locks`.
217
217
 
@@ -86,7 +86,7 @@ Every C3-initialized project registers itself here on `c3 init`. Filter by activ
86
86
 
87
87
  Three top-level views — **Projects**, **Tasks** (a cross-project kanban), and **Credentials** — plus a drill-in panel per project covering Overview, Sub-projects, Tasks, Artifacts, Memory, Ledger, Sessions, Health, Budget, Credentials, Config, and MCP.
88
88
 
89
- **Sub-projects** make a nested repo a first-class child with its own `.c3`: the parent's index excludes the child's subtree, and `c3_search` / `c3_memory` fan out on demand (`scope='all'`, or one child by name). → [Sub-projects guide](https://github.com/drknowhow/code-context-control/blob/main/docs/sub-projects.md)
89
+ **Sub-projects** make one project a first-class child of another, with its own `.c3`. It can be a nested repo — the parent's index then excludes its subtree — or a project anywhere else on disk, linked by path. Hierarchies nest up to 8 levels, and `c3_search` / `c3_memory` fan out on demand (`scope='all'`, or one by name). → [Sub-projects guide](https://github.com/drknowhow/code-context-control/blob/main/docs/sub-projects.md)
90
90
 
91
91
  ### Per-project UI
92
92
 
@@ -148,7 +148,7 @@ C3 exposes **21 tools** as a native MCP server. Your IDE calls them directly:
148
148
  | `c3_credentials` | Named-secret vault; values never enter model context (v2.58.0). `import_env` bulk-imports a `.env` the agent still cannot read (v2.93.0) |
149
149
  | `c3_bitbucket` | Bitbucket Data Center — PRs, branches, builds, admin (v2.30.0) |
150
150
  | `c3_jira` | Jira Cloud + Data Center — JQL, issues, transitions (v2.56.0) |
151
- | `c3_project` | Cross-project discovery & operations; guarded writes (v2.31.0) |
151
+ | `c3_project` | Cross-project discovery & operations; sub-project hierarchy (link by path, inspect, multi-level tree); guarded writes (v2.31.0) |
152
152
 
153
153
  Every tool is **read-only safe in plan mode** except `c3_edit`, `c3_shell`, `c3_ci(action='run'|'rerun')`, `c3_artifacts(action='restore')`, `c3_delegate` with write delegation enabled, and write actions on `c3_bitbucket` / `c3_jira` / `c3_credentials` / `c3_project` / `c3_task` / `c3_locks`.
154
154
 
@@ -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.95.0"
95
+ __version__ = "2.96.1"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -7986,14 +7986,117 @@ def cmd_sub(args):
7986
7986
  print(f"No .c3 found in {parent}. Run 'c3 init' there first.")
7987
7987
  return
7988
7988
  # Import after the cheap guard — the registry module needs a resolvable home.
7989
- from services.subprojects import VALID_CASCADE_OPS, SubprojectManager
7989
+ from services.subprojects import (
7990
+ MAX_DEPTH,
7991
+ VALID_CASCADE_OPS,
7992
+ SubprojectManager,
7993
+ inspect_path,
7994
+ )
7990
7995
 
7991
7996
  sm = SubprojectManager(parent)
7992
7997
  sub = getattr(args, "sub_cmd", "list") or "list"
7993
7998
  target = getattr(args, "target", None)
7994
7999
  as_json = getattr(args, "json", False)
8000
+ depth = getattr(args, "depth", None) or MAX_DEPTH
7995
8001
 
7996
- if sub == "add":
8002
+ if sub == "inspect":
8003
+ if not target:
8004
+ print("Usage: c3 sub inspect <path> [--json]")
8005
+ return
8006
+ rep = inspect_path(target, detect=not getattr(args, "no_detect", False))
8007
+ if as_json:
8008
+ print(json.dumps(rep, indent=2))
8009
+ return
8010
+ print(f"\n{rep['path']}")
8011
+ if not rep["is_dir"]:
8012
+ print(" not a folder")
8013
+ return
8014
+ proj = rep.get("project")
8015
+ if proj:
8016
+ reg = "registered" if rep["registered"] else "NOT registered"
8017
+ print(f" C3 project: {proj['name']} (v{proj.get('c3_version') or '?'}, "
8018
+ f"ide={proj.get('ide') or '?'}, {reg})")
8019
+ print(f" {proj['facts_count']} facts, {proj['sessions']} sessions, "
8020
+ f"{proj['edit_ledger_entries']} ledger entries, "
8021
+ f"{proj['notification_count']} alerts")
8022
+ else:
8023
+ print(" no .c3 — not a C3 project yet")
8024
+ if rep["ancestors"]:
8025
+ chain = " < ".join(a["name"] for a in rep["ancestors"])
8026
+ print(f" parent chain (depth {rep['depth']}): {chain}")
8027
+ else:
8028
+ print(" top-level (no parent)")
8029
+ if rep["children"]:
8030
+ print(f" {len(rep['children'])} sub-project(s):")
8031
+ for c in rep["children"]:
8032
+ print(f" - {c['name']:<22} [{c['link_kind']}/{c['status']}] {c['path']}")
8033
+ if rep["detected"]:
8034
+ print(f" {len(rep['detected'])} unlinked project(s) detected inside:")
8035
+ for d in rep["detected"]:
8036
+ print(f" ? {d['name']:<22} {d['path']}")
8037
+ print(" (suggestions only — link with `c3 sub link <path>`)")
8038
+ for w in rep["warnings"]:
8039
+ print(f" warning: {w}")
8040
+
8041
+ elif sub == "tree":
8042
+ t = sm.tree(depth=depth)
8043
+ if as_json:
8044
+ print(json.dumps(t, indent=2))
8045
+ return
8046
+ r = t["rollup"]
8047
+ print(f"\n{t['parent']['name']} ({t['parent']['path']})")
8048
+
8049
+ def _render(rows, indent=" "):
8050
+ for row in rows:
8051
+ flag = "" if row["status"] == "ok" else f" [{row['status']}]"
8052
+ kind = "" if row["link_kind"] == "nested" else " (linked)"
8053
+ print(f"{indent}- {row['name']}{kind}{flag}")
8054
+ _render(row.get("children") or [], indent + " ")
8055
+
8056
+ _render(t["children"])
8057
+ if not t["children"]:
8058
+ print(" No sub-projects designated. Use `c3 sub link <path>`.")
8059
+ return
8060
+ line = f"\n{r['children']} descendant(s), {r['direct_children']} direct"
8061
+ if r["issues"]:
8062
+ line += f" -- {r['issues']} with issues (run `c3 sub check --fix`)"
8063
+ print(line)
8064
+
8065
+ elif sub == "link":
8066
+ if not target:
8067
+ print("Usage: c3 sub link <path> [--parent PATH] [--name NAME] [--init]")
8068
+ return
8069
+ # link is designation for a project that already exists anywhere on
8070
+ # disk; add is for a folder this parent should initialize.
8071
+ want_init = getattr(args, "init", False)
8072
+ candidate = target if Path(target).is_absolute() else str(Path(parent) / target)
8073
+ probe = inspect_path(candidate, detect=False)
8074
+ if probe["is_dir"] and not probe["has_c3"] and not want_init:
8075
+ err = (f"not a C3 project: {probe['path']} — "
8076
+ "pass --init to initialize it, or run `c3 init` there first")
8077
+ if as_json:
8078
+ print(json.dumps({"added": False, "error": err}, indent=2))
8079
+ else:
8080
+ print(f"Failed: {err}")
8081
+ return
8082
+ result = sm.add(
8083
+ target,
8084
+ name=getattr(args, "name", None),
8085
+ ide=getattr(args, "ide", None),
8086
+ run_init=want_init,
8087
+ reindex_parent=not getattr(args, "no_reindex_parent", False),
8088
+ )
8089
+ if as_json:
8090
+ print(json.dumps(result, indent=2))
8091
+ return
8092
+ if not result.get("added"):
8093
+ print(f"Failed: {result.get('error')}")
8094
+ return
8095
+ where = "by path" if result["link_kind"] == "external" else f"at {result['rel_path']}"
8096
+ print(f"\n[OK] Linked {result['name']} {where} (depth {result['depth']})")
8097
+ print(f" {result['path']}")
8098
+
8099
+ elif sub == "add":
7997
8100
  if not target:
7998
8101
  print("Usage: c3 sub add <folder> [--parent PATH] [--name NAME]")
7999
8102
  return
@@ -8026,16 +8129,19 @@ def cmd_sub(args):
8026
8129
  if not children:
8027
8130
  print("No sub-projects designated. Use `c3 sub add <folder>`.")
8028
8131
  return
8029
- fmt = "{:<22} {:<16} {:>6} {:>7} {}"
8030
- print(fmt.format("NAME", "STATUS", "FACTS", "ALERTS", "REL PATH"))
8031
- print("-" * 76)
8132
+ fmt = "{:<22} {:<16} {:<9} {:>6} {:>7} {}"
8133
+ print(fmt.format("NAME", "STATUS", "LINK", "FACTS", "ALERTS", "LOCATION"))
8134
+ print("-" * 90)
8032
8135
  for c in children:
8136
+ # An external child has no rel_path — show where it actually lives.
8137
+ location = c.get("rel_path") or c.get("path", "")
8033
8138
  print(fmt.format(
8034
8139
  (c.get("name") or "?")[:21],
8035
8140
  c.get("status", "?"),
8141
+ c.get("link_kind", "nested"),
8036
8142
  c.get("facts_count", 0),
8037
8143
  c.get("notification_count", 0),
8038
- c.get("rel_path", ""),
8144
+ location,
8039
8145
  ))
8040
8146
  issues = sum(1 for c in children if c["status"] != "ok")
8041
8147
  line = f"\n{len(children)} sub-project(s)"
@@ -8075,7 +8181,8 @@ def cmd_sub(args):
8075
8181
  return
8076
8182
  result = sm.cascade(target,
8077
8183
  include_parent=getattr(args, "include_parent", False),
8078
- mcp=getattr(args, "mcp", False))
8184
+ mcp=getattr(args, "mcp", False),
8185
+ depth=depth)
8079
8186
  if as_json:
8080
8187
  print(json.dumps(result, indent=2))
8081
8188
  return
@@ -8096,7 +8203,8 @@ def cmd_sub(args):
8096
8203
  print("No sub-projects designated.")
8097
8204
  return
8098
8205
  for c in result["children"]:
8099
- print(f" [{c['status']:<16}] {c.get('name') or '?':<22} {c.get('rel_path', '')}")
8206
+ location = c.get("rel_path") or c.get("path", "")
8207
+ print(f" [{c['status']:<16}] {c.get('name') or '?':<22} {location}")
8100
8208
  for o in result["orphans"]:
8101
8209
  print(f" [orphan_registry ] {o}")
8102
8210
  for f in result.get("fixed", []):
@@ -152,7 +152,7 @@ def build_parser(version: str, parse_cli_ide_arg):
152
152
  p_sub.add_argument(
153
153
  "sub_cmd",
154
154
  nargs="?",
155
- choices=["add", "list", "remove", "run", "check"],
155
+ choices=["add", "link", "inspect", "list", "tree", "remove", "run", "check"],
156
156
  default="list",
157
157
  help="Sub-command (default: list)",
158
158
  )
@@ -160,7 +160,8 @@ def build_parser(version: str, parse_cli_ide_arg):
160
160
  "target",
161
161
  nargs="?",
162
162
  default=None,
163
- help="Folder (add), sub-project name/path (remove), or operation update|reindex|health (run)",
163
+ help="Folder (add), path to an existing project (link|inspect), "
164
+ "sub-project name/path (remove), or operation update|reindex|health (run)",
164
165
  )
165
166
  p_sub.add_argument("--parent", default=".", help="Parent project path (default: current directory)")
166
167
  p_sub.add_argument("--name", default=None, help="Display name for the sub-project (add)")
@@ -173,6 +174,12 @@ def build_parser(version: str, parse_cli_ide_arg):
173
174
  p_sub.add_argument("--mcp", action="store_true", help="Also reinstall MCP config on update (run update)")
174
175
  p_sub.add_argument("--fix", action="store_true", help="Repair links from the parent config (check)")
175
176
  p_sub.add_argument("--prune", action="store_true", help="With --fix: drop entries whose folder is gone (check)")
177
+ p_sub.add_argument("--depth", default=None, type=int,
178
+ help="Levels to descend (tree|run); default: the full hierarchy")
179
+ p_sub.add_argument("--init", action="store_true",
180
+ help="Initialize the folder if it is not a C3 project yet (link)")
181
+ p_sub.add_argument("--no-detect", action="store_true",
182
+ help="Skip the scan for unlinked nested projects (inspect)")
176
183
  p_sub.add_argument("--json", action="store_true", help="Emit JSON output")
177
184
 
178
185
  p_perms = subparsers.add_parser("permissions",
@@ -1394,10 +1394,14 @@ c3_jira(action=<span class="str">'comment'</span>, issue=<span class="str">'PROJ
1394
1394
  <tr><td class="param-name">scan</td><td>Discover</td><td class="param-desc">Registry + bounded filesystem scan for unregistered <code>.c3</code> projects nearby</td></tr>
1395
1395
  <tr><td class="param-name">info, register, unregister</td><td>Discover</td><td class="param-desc">Project details / add to / remove from the registry</td></tr>
1396
1396
  <tr><td class="param-name">search, read, compress, status, memory, impact, edits, validate, filter</td><td>Read</td><td class="param-desc">Proxy the matching c3_* tool against the target project</td></tr>
1397
+ <tr><td class="param-name">subprojects, sub_tree, sub_inspect</td><td>Read</td><td class="param-desc">Direct children; the whole hierarchy; a read-only report on any path (what is there, who already claims it, what it claims, which nested projects under it are unlinked)</td></tr>
1398
+ <tr><td class="param-name">sub_add, sub_link, sub_remove, sub_cascade</td><td>Write</td><td class="param-desc">Designate / link by path / unlink / cascade across the subtree — require <code>allow_write=true</code> (<code>sub_cascade</code> with <code>mode='health'</code> is a read)</td></tr>
1397
1399
  <tr><td class="param-name">edit, shell, memory(add/update/delete)</td><td>Write</td><td class="param-desc">Mutate the target project — require <code>allow_write=true</code></td></tr>
1398
1400
  </tbody>
1399
1401
  </table>
1400
1402
 
1403
+ <p class="tool-desc"><strong>Sub-project hierarchy:</strong> <code>project</code> is the PARENT. A child need not live inside it — <code>sub_link</code> takes an absolute path anywhere on disk, including another drive — and hierarchies nest up to 8 levels. Nested children are excluded from the parent's index; externally linked ones were never in it. Linking is refused if it would make a project its own ancestor.</p>
1404
+
1401
1405
  <h4>Examples</h4>
1402
1406
  <pre><code><span class="com"># Which projects have C3 installed?</span>
1403
1407
  c3_project(action=<span class="str">'list'</span>)
@@ -1414,7 +1418,18 @@ c3_project(action=<span class="str">'memory'</span>, project=<span class="str">'
1414
1418
  c3_project(action=<span class="str">'edit'</span>, project=<span class="str">'Sentinel'</span>,
1415
1419
  file_path=<span class="str">'README.md'</span>,
1416
1420
  old_string=<span class="str">'v1'</span>, new_string=<span class="str">'v2'</span>,
1417
- allow_write=<span class="num">true</span>)</code></pre>
1421
+ allow_write=<span class="num">true</span>)
1422
+
1423
+ <span class="com"># What is at this path, and who already claims it? (read-only)</span>
1424
+ c3_project(action=<span class="str">'sub_inspect'</span>, project=<span class="str">'Code Context Control'</span>,
1425
+ target=<span class="str">r'U:\1. Projects\Claude Code Companion (C3)\c3-mobile'</span>)
1426
+
1427
+ <span class="com"># Link it — a sibling on disk, not nested, so containment cannot express it</span>
1428
+ c3_project(action=<span class="str">'sub_link'</span>, project=<span class="str">'Code Context Control'</span>,
1429
+ target=<span class="str">r'U:\1. Projects\Claude Code Companion (C3)\c3-mobile'</span>,
1430
+ allow_write=<span class="num">true</span>)
1431
+
1432
+ c3_project(action=<span class="str">'sub_tree'</span>, project=<span class="str">'Code Context Control'</span>) <span class="com"># every level</span></code></pre>
1418
1433
 
1419
1434
  <p class="tool-desc"><strong>Sub-action params:</strong> <code>search_action</code> (code/files/semantic/…), <code>mem_action</code> (recall/index/fetch/add/…), and <code>edits_action</code> (history/versions/stats) pick the operation for those verbs; <code>scan_roots</code> (comma-separated) overrides where <code>scan</code> looks.</p>
1420
1435
  </div>
@@ -912,12 +912,29 @@ c3_session(action=<span class="str">'restore'</span>)</code></pre>
912
912
  <section class="section" id="subprojects">
913
913
  <div class="section-title"><span class="icon">🌳</span> Sub-projects</div>
914
914
 
915
- <p>Since v2.44.0, a c3 project can link folders that physically live inside it as <strong>sub-projects</strong>. Each child gets its own full <code>.c3</code> (index, memory, config), the parent's index excludes the child's subtree, and the link is recorded in three places at once: the parent config's <code>subprojects[]</code> array, a back-link in the child's config, and the hub registry. Links are depth-1 — a child cannot have children of its own.</p>
915
+ <p>A c3 project can claim another one as a <strong>sub-project</strong>. Each child keeps its own full <code>.c3</code> (index, memory, config), and the link is recorded in three places at once: the parent config's <code>subprojects[]</code> array, a back-link in the child's config, and the hub registry.</p>
916
+
917
+ <p>Since v2.96.0 the child does <strong>not</strong> have to live inside the parent's folder, and hierarchies nest to <strong>8 levels</strong> — a sub-project can have sub-projects of its own. There are two link kinds, and C3 picks from the path you give it:</p>
918
+ <table class="table">
919
+ <thead><tr><th>Kind</th><th>Where the child lives</th><th>Effect on the parent's index</th></tr></thead>
920
+ <tbody>
921
+ <tr><td><code>nested</code></td><td>Inside the parent's folder</td><td>The child's subtree is excluded, so nothing is indexed twice</td></tr>
922
+ <tr><td><code>external</code></td><td>Anywhere — a sibling folder, another drive</td><td>None: the child was never inside the tree the parent scans</td></tr>
923
+ </tbody>
924
+ </table>
916
925
 
917
926
  <div class="callout callout-tip">
918
927
  <span class="callout-icon">💡</span>
919
928
  <div class="callout-body">
920
- Use sub-projects for monorepos and nested apps: each child stays independently searchable with its own memory, while the parent can fan queries out across the whole family.
929
+ Use sub-projects for monorepos and nested apps, and for the case that has no folder to nest: two checkouts of one product sitting side by side. Each child stays independently searchable with its own memory, while the parent can fan queries out across the whole family.
930
+ </div>
931
+ </div>
932
+
933
+ <div class="callout callout-warning">
934
+ <span class="callout-icon">⚠️</span>
935
+ <div class="callout-body">
936
+ <strong>No cycles</strong>
937
+ Hierarchy is a strict tree: one parent per project. While a child had to live inside its parent, a loop was impossible by construction; now that it can live anywhere, every link is checked against the ancestor chain and refused if it would make a project its own ancestor.
921
938
  </div>
922
939
  </div>
923
940
 
@@ -925,21 +942,33 @@ c3_session(action=<span class="str">'restore'</span>)</code></pre>
925
942
  <table class="table">
926
943
  <thead><tr><th>Command</th><th>Effect</th></tr></thead>
927
944
  <tbody>
945
+ <tr>
946
+ <td><code>c3 sub link &lt;path&gt; [--name --init]</code></td>
947
+ <td>Link an existing C3 project as a child — anywhere on disk, including another drive. Refuses a folder that is not already a project unless you pass <code>--init</code>.</td>
948
+ </tr>
949
+ <tr>
950
+ <td><code>c3 sub inspect &lt;path&gt;</code></td>
951
+ <td>Read-only report on any path: is there a project there, what is in it, who already claims it, what it claims, and which nested projects underneath are not linked yet. Mutates nothing.</td>
952
+ </tr>
928
953
  <tr>
929
954
  <td><code>c3 sub add &lt;folder&gt; [--name --ide]</code></td>
930
- <td>Link a folder inside the current project. Adopts an existing <code>.c3</code>, otherwise runs a full init. <code>--no-init</code> links without initializing; <code>--parent &lt;path&gt;</code> targets a different parent.</td>
955
+ <td>Designate a folder as a child, initializing it. Adopts an existing <code>.c3</code>, otherwise runs a full init. <code>--no-init</code> links without initializing; <code>--parent &lt;path&gt;</code> targets a different parent.</td>
931
956
  </tr>
932
957
  <tr>
933
958
  <td><code>c3 sub list</code></td>
934
- <td>Tree of linked children with status (the default when no sub-command is given).</td>
959
+ <td>Direct children with status and link kind (the default when no sub-command is given).</td>
960
+ </tr>
961
+ <tr>
962
+ <td><code>c3 sub tree [--depth N]</code></td>
963
+ <td>The whole hierarchy, indented, with a subtree rollup. <code>--depth</code> stops the walk early.</td>
935
964
  </tr>
936
965
  <tr>
937
966
  <td><code>c3 sub remove &lt;ref&gt; [--clear]</code></td>
938
967
  <td>Unlink a child and promote it to a standalone top-level project (its <code>.c3</code> is kept). <code>--clear</code> also wipes <code>.c3</code>, uninstalls the MCP config and instruction docs, and deregisters it.</td>
939
968
  </tr>
940
969
  <tr>
941
- <td><code>c3 sub run &lt;update|reindex|health&gt; [--include-parent]</code></td>
942
- <td>Cascade an operation across every child. <code>--mcp</code> also reinstalls the MCP config during <code>update</code>.</td>
970
+ <td><code>c3 sub run &lt;update|reindex|health&gt; [--include-parent --depth N]</code></td>
971
+ <td>Cascade an operation across the whole subtree. <code>--mcp</code> also reinstalls the MCP config during <code>update</code>.</td>
943
972
  </tr>
944
973
  <tr>
945
974
  <td><code>c3 sub check [--fix --prune]</code></td>
@@ -954,8 +983,8 @@ c3_session(action=<span class="str">'restore'</span>)</code></pre>
954
983
  <thead><tr><th>Value</th><th>Meaning</th></tr></thead>
955
984
  <tbody>
956
985
  <tr><td><code>''</code></td><td>Current project only (default)</td></tr>
957
- <tr><td><code>'all'</code></td><td>Fan out across the parent and all linked children</td></tr>
958
- <tr><td><code>'&lt;child&gt;'</code></td><td>One specific sub-project by name</td></tr>
986
+ <tr><td><code>'all'</code></td><td>Fan out across the parent and every descendant, at any depth</td></tr>
987
+ <tr><td><code>'&lt;child&gt;'</code></td><td>One specific descendant by name</td></tr>
959
988
  </tbody>
960
989
  </table>
961
990
 
@@ -967,11 +996,12 @@ c3_memory(action=<span class="str">'recall'</span>, query=<span class="str">'pay
967
996
  <h4>Hub UI management (v2.57.0)</h4>
968
997
  <p>Everything the CLI does — plus the operations that need multi-step safety — is available in the Hub:</p>
969
998
  <ul>
999
+ <li><strong>"Link project by path…"</strong> (v2.96.0) — kebab menu on any card, at any depth. Browse anywhere or paste a path; C3 inspects it before anything happens and shows you what you are about to claim — the project's name and version, whether the hub already knows it, how much is in it, who already claims it, and what sub-projects it would bring with it. One confirm registers and links an unregistered project. Unlinked nested projects it finds are offered as suggestions, never applied.</li>
970
1000
  <li><strong>Designate</strong> — kebab menu → "Designate sub-project…" on any project card, or from the drill-in <strong>Sub-projects</strong> tab. A folder picker fenced to the parent's subtree validates the choice, shows adopt-vs-initialize expectations up front, and offers an instruction-docs/IDE picker (<code>auto | claude | vscode | cursor | codex | antigravity</code>).</li>
971
- <li><strong>"Make sub-project of…"</strong> — the reverse move: a top-level card whose folder physically sits inside a registered parent can link itself under it (containment is mandatory).</li>
1001
+ <li><strong>"Make sub-project of…"</strong> — the reverse move: pick any registered project to sit under. The only projects excluded are this one and its own descendants.</li>
972
1002
  <li><strong>Sub-projects drill tab</strong> — per-child rows with inline <em>Validate</em> and <em>Promote</em>; inline <em>Reconcile</em> with per-issue detail and planned repair actions; a <em>Cascade</em> launcher with operation picker, include-parent toggle, affected-children preview, live progress, and Cancel.</li>
973
1003
  <li><strong>Link health, passively</strong> — parents show a red "N link issues" badge and children a status badge (<code>ok · backlink_broken · unregistered · missing_folder · missing_c3 · orphan</code>), computed from config + filesystem on every listing — no manual reconcile needed to spot trouble.</li>
974
- <li><strong>"Change parent…"</strong> — a staged wizard, because folders must physically move: ready-now targets relink immediately; otherwise unlink move the folder yourself validate → relink, with a per-step checklist and Undo before the move.</li>
1004
+ <li><strong>"Change parent…"</strong> — unlink validate relink, with a per-step checklist. Since a child can be addressed by absolute path, re-parenting is a configuration change and no files move.</li>
975
1005
  <li><strong>"De-initialize…"</strong> — clear-mode removal behind a typed-name confirmation: deletes <code>.c3</code>, uninstalls the MCP config, removes instruction docs, deregisters. Your code is untouched.</li>
976
1006
  <li><strong>Federation toggles</strong> — the config editor's Sub-projects section on parent projects: memory roll-up, search fan-out for <code>scope='all'</code>, and max children per query, persisted under <code>hybrid.subprojects</code>.</li>
977
1007
  <li><strong>Ctrl-K scope chips</strong> — global search can target All, Top-level only, or one parent + its children; child results carry a "parent › child" breadcrumb.</li>
@@ -537,7 +537,7 @@ def _compute_subproject_links(projects):
537
537
  config_paths = set()
538
538
  for entry in entries:
539
539
  try:
540
- key = _norm_realpath(mgr._abs_child(entry.get("rel_path", "")))
540
+ key = _norm_realpath(mgr._abs_child(entry))
541
541
  status = mgr._entry_status(entry, registry)
542
542
  except Exception:
543
543
  continue
@@ -609,10 +609,26 @@ def api_projects_list():
609
609
  from services.task_store import open_task_count
610
610
  projects = _pm().list_projects()
611
611
  parent_paths = {os.path.normcase(p["parent_path"]) for p in projects if p.get("parent_path")}
612
+ # Depth from the registry alone — no disk reads. Bounded by the number
613
+ # of projects and a visited-set, so a corrupt cycle cannot hang the poll.
614
+ by_path = {os.path.normcase(p.get("path", "")): p for p in projects}
615
+
616
+ def _depth(row):
617
+ n, seen, cursor = 0, set(), row
618
+ while cursor and cursor.get("parent_path"):
619
+ key = os.path.normcase(cursor["parent_path"])
620
+ if key in seen or key not in by_path:
621
+ break
622
+ seen.add(key)
623
+ cursor = by_path[key]
624
+ n += 1
625
+ return n
626
+
612
627
  for p in projects:
613
628
  p["notification_count"] = _notification_count(p.get("path", ""))
614
629
  p["is_parent"] = os.path.normcase(p.get("path", "")) in parent_paths
615
630
  p["open_task_count"] = open_task_count(p.get("path", ""))
631
+ p["depth"] = _depth(p)
616
632
  _annotate_subproject_links(projects)
617
633
  return jsonify(projects)
618
634
  except Exception as e:
@@ -1438,18 +1454,87 @@ def _parse_json_tail(output: str):
1438
1454
 
1439
1455
  @app.route("/api/projects/subprojects", methods=["GET"])
1440
1456
  def api_subprojects_tree():
1441
- """Parent + children + rollup. Query: ?parent=<path>"""
1457
+ """Parent + descendants + rollup. Query: ?parent=<path>&depth=<n>
1458
+
1459
+ ``depth`` defaults to the full hierarchy; pass 1 for the direct-children
1460
+ shape the drill-in panel used before 2.96.
1461
+ """
1442
1462
  parent = (request.args.get("parent") or "").strip()
1443
1463
  if not parent:
1444
1464
  return jsonify({"error": "parent is required"}), 400
1445
1465
  try:
1446
- return jsonify(_sub_manager(parent).tree())
1466
+ from services.subprojects import MAX_DEPTH
1467
+ depth = int(request.args.get("depth") or MAX_DEPTH)
1468
+ except (TypeError, ValueError):
1469
+ return jsonify({"error": "depth must be an integer"}), 400
1470
+ try:
1471
+ return jsonify(_sub_manager(parent).tree(depth=depth))
1447
1472
  except ValueError as e:
1448
1473
  return jsonify({"error": str(e)}), 404
1449
1474
  except Exception as e:
1450
1475
  return jsonify({"error": str(e)}), 500
1451
1476
 
1452
1477
 
1478
+ @app.route("/api/projects/subprojects/inspect", methods=["POST"])
1479
+ def api_subprojects_inspect():
1480
+ """Read-only report on any path. Body: {path, detect?}
1481
+
1482
+ Answers "is there a C3 project here, what is in it, who already claims it,
1483
+ what does it claim, and what nested projects under it are unlinked?" —
1484
+ without registering, linking, or initializing anything. This is what the
1485
+ Hub calls before offering to link, so it must stay side-effect free.
1486
+ """
1487
+ data = request.get_json(force=True) or {}
1488
+ path = (data.get("path") or "").strip()
1489
+ if not path:
1490
+ return jsonify({"error": "path is required"}), 400
1491
+ try:
1492
+ from services.subprojects import inspect_path
1493
+ return jsonify(inspect_path(path, detect=bool(data.get("detect", True))))
1494
+ except Exception as e:
1495
+ return jsonify({"error": str(e)}), 500
1496
+
1497
+
1498
+ @app.route("/api/projects/hierarchy", methods=["GET"])
1499
+ def api_projects_hierarchy():
1500
+ """The whole forest: every root project with its descendants. Query: ?depth=<n>
1501
+
1502
+ Roots are registered projects with no parent back-link. A project whose
1503
+ parent is not itself registered still surfaces as a root, so nothing can
1504
+ fall out of the listing because of a broken link.
1505
+ """
1506
+ try:
1507
+ from services.subprojects import MAX_DEPTH, SubprojectManager, parent_link
1508
+ depth = int(request.args.get("depth") or MAX_DEPTH)
1509
+ except (TypeError, ValueError):
1510
+ return jsonify({"error": "depth must be an integer"}), 400
1511
+ try:
1512
+ projects = ProjectManager()._read_projects()
1513
+ known = {os.path.normcase(p.get("path", "")) for p in projects}
1514
+ roots, claimed = [], set()
1515
+ for p in projects:
1516
+ path = p.get("path", "")
1517
+ if not path:
1518
+ continue
1519
+ link = parent_link(path)
1520
+ parent = link.get("path") or p.get("parent_path")
1521
+ if parent and os.path.normcase(str(Path(parent).resolve())) in known:
1522
+ claimed.add(os.path.normcase(path))
1523
+ continue
1524
+ roots.append(p)
1525
+ out = []
1526
+ for p in roots:
1527
+ try:
1528
+ out.append(SubprojectManager(p["path"]).tree(depth=depth))
1529
+ except Exception as e: # one bad root must not blank the forest
1530
+ out.append({"parent": {"name": p.get("name"), "path": p["path"]},
1531
+ "children": [], "rollup": {}, "error": str(e)})
1532
+ return jsonify({"roots": out, "root_count": len(out),
1533
+ "linked_count": len(claimed), "depth": depth})
1534
+ except Exception as e:
1535
+ return jsonify({"error": str(e)}), 500
1536
+
1537
+
1453
1538
  @app.route("/api/projects/subprojects/validate", methods=["POST"])
1454
1539
  def api_subprojects_validate():
1455
1540
  """Pre-flight a designation. Body: {parent, folder}"""
@@ -1493,6 +1578,41 @@ def api_subprojects_add():
1493
1578
  return jsonify(body), (201 if ok else 500)
1494
1579
 
1495
1580
 
1581
+ @app.route("/api/projects/subprojects/link", methods=["POST"])
1582
+ def api_subprojects_link():
1583
+ """Link an existing project by path. Body: {parent, folder, name?, ide?, init?}
1584
+
1585
+ The register-and-link one-shot behind the Hub's "Register + link" button.
1586
+ ``folder`` may be anywhere on disk — inside the parent, a sibling, another
1587
+ drive. Unlike ``/add`` this refuses a folder that is not already a C3
1588
+ project unless ``init`` is set, so "link" never silently creates one.
1589
+
1590
+ Shells out like /add and /remove, so errors land at result.error and the
1591
+ frontend's apiErr() keeps reading them.
1592
+ """
1593
+ data = request.get_json(force=True) or {}
1594
+ parent = (data.get("parent") or "").strip()
1595
+ folder = (data.get("folder") or "").strip()
1596
+ if not parent or not folder:
1597
+ return jsonify({"error": "parent and folder are required"}), 400
1598
+ args = ["sub", "link", folder, "--parent", parent, "--json"]
1599
+ name = (data.get("name") or "").strip()
1600
+ if name:
1601
+ args += ["--name", name]
1602
+ ide = (data.get("ide") or "").strip()
1603
+ if ide and ide != "unknown":
1604
+ args += ["--ide", ide]
1605
+ if data.get("init"):
1606
+ args.append("--init")
1607
+ res = _run_c3(args, timeout=300)
1608
+ payload = _parse_json_tail(res.get("output", ""))
1609
+ ok = res.get("success") and bool((payload or {}).get("added"))
1610
+ if ok:
1611
+ _invalidate_subproject_links()
1612
+ body = {"success": ok, "result": payload, "output": res.get("output", "")}
1613
+ return jsonify(body), (201 if ok else 500)
1614
+
1615
+
1496
1616
  @app.route("/api/projects/subprojects/remove", methods=["POST"])
1497
1617
  def api_subprojects_remove():
1498
1618
  """Unlink (Promote) or clear a sub-project. Body: {parent, ref, mode: unlink|clear}"""
@@ -111,9 +111,11 @@ function DrillNeedsInit({ project, onReady }) {
111
111
  }
112
112
 
113
113
  function DrillPanel({ project, tab, setTab, onClose, onChanged, onOpenModal, projects }) {
114
- // Children can't have children (depth-1) hide the Sub-projects tab for them.
114
+ // A sub-project may have sub-projects of its own, so the Sub-projects tab
115
+ // stays available at every level. isChild now only drives the "under
116
+ // <parent>" chip below.
115
117
  const isChild = !!project.parent_path;
116
- const tabs = DRILL_PANEL_TABS.filter(([id]) => id !== 'subprojects' || !isChild);
118
+ const tabs = DRILL_PANEL_TABS;
117
119
  // "under <parent>" chip: friendly name from the loaded project list when
118
120
  // available (optional prop), else the last path segment.
119
121
  const parentName = (() => {