code-context-control 2.56.0__tar.gz → 2.57.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 (343) hide show
  1. {code_context_control-2.56.0/code_context_control.egg-info → code_context_control-2.57.0}/PKG-INFO +10 -2
  2. {code_context_control-2.56.0 → code_context_control-2.57.0}/README.md +8 -0
  3. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/c3.py +1 -1
  4. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/workflow.html +87 -0
  5. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_server.py +162 -5
  6. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/app.js +1 -1
  7. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/config_editor.js +126 -2
  8. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/drill_panel.js +35 -6
  9. code_context_control-2.57.0/cli/hub_ui/components/drill_subprojects.js +514 -0
  10. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/drill_views.js +32 -20
  11. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/global_search.js +62 -9
  12. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/modals.js +483 -6
  13. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/project_card.js +179 -15
  14. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/toasts.js +9 -2
  15. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/state.js +4 -0
  16. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/api.js +8 -0
  17. {code_context_control-2.56.0 → code_context_control-2.57.0/code_context_control.egg-info}/PKG-INFO +10 -2
  18. {code_context_control-2.56.0 → code_context_control-2.57.0}/code_context_control.egg-info/SOURCES.txt +3 -0
  19. {code_context_control-2.56.0 → code_context_control-2.57.0}/pyproject.toml +2 -2
  20. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hub_inspect_api.py +24 -0
  21. code_context_control-2.57.0/tests/test_hub_subproject_links.py +223 -0
  22. code_context_control-2.57.0/tests/test_version_sync.py +36 -0
  23. {code_context_control-2.56.0 → code_context_control-2.57.0}/LICENSE +0 -0
  24. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/__init__.py +0 -0
  25. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/_hook_utils.py +0 -0
  26. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/commands/__init__.py +0 -0
  27. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/commands/common.py +0 -0
  28. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/commands/parser.py +0 -0
  29. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/docs.html +0 -0
  30. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/edits.html +0 -0
  31. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/bitbucket.html +0 -0
  32. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/getting-started.html +0 -0
  33. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/index.html +0 -0
  34. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/oracle.html +0 -0
  35. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/shared.css +0 -0
  36. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/guide/tools.html +0 -0
  37. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_artifact.py +0 -0
  38. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_auto_snapshot.py +0 -0
  39. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_c3_signal.py +0 -0
  40. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_c3read.py +0 -0
  41. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_dispatch.py +0 -0
  42. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_edit_ledger.py +0 -0
  43. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_edit_unlock.py +0 -0
  44. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_filter.py +0 -0
  45. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_ghost_files.py +0 -0
  46. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_pretool_enforce.py +0 -0
  47. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_prompt_recall.py +0 -0
  48. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_read.py +0 -0
  49. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_session_stats.py +0 -0
  50. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hook_terse_advisor.py +0 -0
  51. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub.html +0 -0
  52. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/add_project.js +0 -0
  53. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  54. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/drill_health.js +0 -0
  55. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  56. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  57. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/project_tree.js +0 -0
  58. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/session_drawer.js +0 -0
  59. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/settings_modal.js +0 -0
  60. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/sidebar.js +0 -0
  61. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/summary_bar.js +0 -0
  62. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/task_board.js +0 -0
  63. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui/components/topbar.js +0 -0
  64. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/hub_ui.html +0 -0
  65. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/mcp_proxy.py +0 -0
  66. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/mcp_server.py +0 -0
  67. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/progress.py +0 -0
  68. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/server.py +0 -0
  69. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/__init__.py +0 -0
  70. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/_helpers.py +0 -0
  71. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/agent.py +0 -0
  72. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/artifacts.py +0 -0
  73. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/bitbucket.py +0 -0
  74. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/compress.py +0 -0
  75. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/delegate.py +0 -0
  76. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/edit.py +0 -0
  77. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/edits.py +0 -0
  78. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/federate.py +0 -0
  79. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/filter.py +0 -0
  80. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/impact.py +0 -0
  81. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/jira.py +0 -0
  82. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/memory.py +0 -0
  83. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/project.py +0 -0
  84. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/read.py +0 -0
  85. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/search.py +0 -0
  86. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/session.py +0 -0
  87. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/shell.py +0 -0
  88. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/status.py +0 -0
  89. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/tasks.py +0 -0
  90. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/tools/validate.py +0 -0
  91. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/app.js +0 -0
  92. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/bitbucket.js +0 -0
  93. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/chat.js +0 -0
  94. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/dashboard.js +0 -0
  95. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/edits.js +0 -0
  96. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/instructions.js +0 -0
  97. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/jira.js +0 -0
  98. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/memory.js +0 -0
  99. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/sessions.js +0 -0
  100. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/settings.js +0 -0
  101. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/sidebar.js +0 -0
  102. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/components/tasks.js +0 -0
  103. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/icons.js +0 -0
  104. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/pm_shared.js +0 -0
  105. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/shared.js +0 -0
  106. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui/theme.js +0 -0
  107. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui.html +0 -0
  108. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui_legacy.html +0 -0
  109. {code_context_control-2.56.0 → code_context_control-2.57.0}/cli/ui_nano.html +0 -0
  110. {code_context_control-2.56.0 → code_context_control-2.57.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  111. {code_context_control-2.56.0 → code_context_control-2.57.0}/code_context_control.egg-info/entry_points.txt +0 -0
  112. {code_context_control-2.56.0 → code_context_control-2.57.0}/code_context_control.egg-info/requires.txt +0 -0
  113. {code_context_control-2.56.0 → code_context_control-2.57.0}/code_context_control.egg-info/top_level.txt +0 -0
  114. {code_context_control-2.56.0 → code_context_control-2.57.0}/core/__init__.py +0 -0
  115. {code_context_control-2.56.0 → code_context_control-2.57.0}/core/config.py +0 -0
  116. {code_context_control-2.56.0 → code_context_control-2.57.0}/core/ide.py +0 -0
  117. {code_context_control-2.56.0 → code_context_control-2.57.0}/core/mcp_toml.py +0 -0
  118. {code_context_control-2.56.0 → code_context_control-2.57.0}/core/web_security.py +0 -0
  119. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/__init__.py +0 -0
  120. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/config.py +0 -0
  121. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/mcp_oracle.py +0 -0
  122. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/oracle_server.py +0 -0
  123. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/oracle_ui.html +0 -0
  124. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/__init__.py +0 -0
  125. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/activity_reporter.py +0 -0
  126. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/api_auth.py +0 -0
  127. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/c3_bridge.py +0 -0
  128. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/chat_engine.py +0 -0
  129. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/chat_store.py +0 -0
  130. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/cross_memory.py +0 -0
  131. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/federated_graph.py +0 -0
  132. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/health_checker.py +0 -0
  133. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/insight_engine.py +0 -0
  134. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/local_session.py +0 -0
  135. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/memory_reader.py +0 -0
  136. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/memory_writer.py +0 -0
  137. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/project_scanner.py +0 -0
  138. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/review_agent.py +0 -0
  139. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/tool_executor.py +0 -0
  140. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/services/tool_registry.py +0 -0
  141. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/activity.js +0 -0
  142. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/agents.js +0 -0
  143. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/app.js +0 -0
  144. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/busy.js +0 -0
  145. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/chat/conversations.js +0 -0
  146. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/chat/input.js +0 -0
  147. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/chat/markdown.js +0 -0
  148. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/chat/send.js +0 -0
  149. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/chat/stream_renderer.js +0 -0
  150. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/chat/toolbar.js +0 -0
  151. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/core.js +0 -0
  152. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/crossgraph.js +0 -0
  153. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/header.js +0 -0
  154. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/insights.js +0 -0
  155. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/projects.js +0 -0
  156. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/settings.js +0 -0
  157. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/suggestions.js +0 -0
  158. {code_context_control-2.56.0 → code_context_control-2.57.0}/oracle/ui/theme_tabs.js +0 -0
  159. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/__init__.py +0 -0
  160. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/activity_log.py +0 -0
  161. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/agent_base.py +0 -0
  162. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/agents.py +0 -0
  163. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/artifact_defs.py +0 -0
  164. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/artifact_store.py +0 -0
  165. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/auto_memory.py +0 -0
  166. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/bench/__init__.py +0 -0
  167. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/bench/external/__init__.py +0 -0
  168. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/bench/external/aider_polyglot.py +0 -0
  169. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/bench/external/swe_bench.py +0 -0
  170. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/benchmark_dashboard.py +0 -0
  171. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/bitbucket_client.py +0 -0
  172. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/bitbucket_credentials.py +0 -0
  173. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/circuit_breaker.py +0 -0
  174. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/claude_md.py +0 -0
  175. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/compressor.py +0 -0
  176. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/context_snapshot.py +0 -0
  177. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/conversation_store.py +0 -0
  178. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/doc_index.py +0 -0
  179. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/e2e_benchmark.py +0 -0
  180. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/e2e_evaluator.py +0 -0
  181. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/e2e_tasks.py +0 -0
  182. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/edit_ledger.py +0 -0
  183. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/embedding_index.py +0 -0
  184. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/error_reporting.py +0 -0
  185. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/file_memory.py +0 -0
  186. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/git_context.py +0 -0
  187. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/hub_service.py +0 -0
  188. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/indexer.py +0 -0
  189. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/jira_client.py +0 -0
  190. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/jira_cloud.py +0 -0
  191. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/jira_credentials.py +0 -0
  192. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/jira_data_center.py +0 -0
  193. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/jira_links.py +0 -0
  194. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory.py +0 -0
  195. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory_consolidator.py +0 -0
  196. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory_distiller.py +0 -0
  197. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory_graph.py +0 -0
  198. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory_grounder.py +0 -0
  199. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory_queue.py +0 -0
  200. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/memory_scorer.py +0 -0
  201. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/metrics.py +0 -0
  202. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/notifications.py +0 -0
  203. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/ollama_bridge.py +0 -0
  204. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/ollama_client.py +0 -0
  205. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/ollama_credentials.py +0 -0
  206. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/output_filter.py +0 -0
  207. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/parser.py +0 -0
  208. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/project_manager.py +0 -0
  209. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/project_runtime.py +0 -0
  210. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/protocol.py +0 -0
  211. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/proxy_state.py +0 -0
  212. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/retention.py +0 -0
  213. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/retrieval_broker.py +0 -0
  214. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/router.py +0 -0
  215. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/runtime.py +0 -0
  216. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/scanner.py +0 -0
  217. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/session_benchmark.py +0 -0
  218. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/session_manager.py +0 -0
  219. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/session_preloader.py +0 -0
  220. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/subprojects.py +0 -0
  221. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/task_store.py +0 -0
  222. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/telemetry.py +0 -0
  223. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/text_index.py +0 -0
  224. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/time_tracker.py +0 -0
  225. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/tool_classifier.py +0 -0
  226. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/transcript_index.py +0 -0
  227. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/validation_cache.py +0 -0
  228. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/vector_store.py +0 -0
  229. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/watcher.py +0 -0
  230. {code_context_control-2.56.0 → code_context_control-2.57.0}/services/win_subprocess.py +0 -0
  231. {code_context_control-2.56.0 → code_context_control-2.57.0}/setup.cfg +0 -0
  232. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_activity_reporter.py +0 -0
  233. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_aider_polyglot.py +0 -0
  234. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_antigravity_transition.py +0 -0
  235. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_artifact_store.py +0 -0
  236. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_artifact_tool.py +0 -0
  237. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  238. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_bitbucket_client.py +0 -0
  239. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_bitbucket_config_fallback.py +0 -0
  240. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_bitbucket_credentials.py +0 -0
  241. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_bitbucket_tool.py +0 -0
  242. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  243. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_c3_shell.py +0 -0
  244. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_circuit_breaker.py +0 -0
  245. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_claude_md_merge.py +0 -0
  246. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_cli_smoke.py +0 -0
  247. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_compressor_large_file.py +0 -0
  248. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_delegate_cascade.py +0 -0
  249. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_e2e_benchmark.py +0 -0
  250. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_edit_ledger_hook.py +0 -0
  251. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_edit_normalization.py +0 -0
  252. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_enforcement_flip.py +0 -0
  253. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_federated_graph.py +0 -0
  254. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_filter_backoff.py +0 -0
  255. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_ghost_files.py +0 -0
  256. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_ghost_generation.py +0 -0
  257. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_git_branch_awareness.py +0 -0
  258. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hook_dispatch.py +0 -0
  259. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hook_pretool_enforce.py +0 -0
  260. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hook_prompt_recall.py +0 -0
  261. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hook_smoke.py +0 -0
  262. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hook_state.py +0 -0
  263. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hook_utf8_stdio.py +0 -0
  264. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_hub_server_smoke.py +0 -0
  265. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_init_scan.py +0 -0
  266. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_install_mcp_entrypoint.py +0 -0
  267. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_cli_smoke.py +0 -0
  268. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_client.py +0 -0
  269. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_config_fallback.py +0 -0
  270. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_credentials.py +0 -0
  271. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_links.py +0 -0
  272. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_routes.py +0 -0
  273. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_jira_tool.py +0 -0
  274. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_lazy_store_init.py +0 -0
  275. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_mcp_host_guard.py +0 -0
  276. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_mcp_memory_timeout.py +0 -0
  277. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_mcp_server_smoke.py +0 -0
  278. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_mcp_toml.py +0 -0
  279. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_memory_distiller.py +0 -0
  280. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_memory_graph_api.py +0 -0
  281. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_memory_queue.py +0 -0
  282. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_memory_system.py +0 -0
  283. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_notification_dedup.py +0 -0
  284. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_notification_discipline.py +0 -0
  285. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_ollama_credentials.py +0 -0
  286. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_api_auth.py +0 -0
  287. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_apikey_api.py +0 -0
  288. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_c3_bridge.py +0 -0
  289. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_chat_engine.py +0 -0
  290. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_discovery_api.py +0 -0
  291. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_local_auth.py +0 -0
  292. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_ollama_bridge.py +0 -0
  293. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_scanner_cache.py +0 -0
  294. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_security_fixes.py +0 -0
  295. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_subproject_awareness.py +0 -0
  296. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_oracle_ui_bundle.py +0 -0
  297. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_output_filter.py +0 -0
  298. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_permissions.py +0 -0
  299. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_pm_api.py +0 -0
  300. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_project_manager.py +0 -0
  301. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_project_manager_merge.py +0 -0
  302. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_project_tool.py +0 -0
  303. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_read_coercion.py +0 -0
  304. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_read_edit_parity.py +0 -0
  305. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_related_facts.py +0 -0
  306. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_response_boilerplate.py +0 -0
  307. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_retention.py +0 -0
  308. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_review_digest.py +0 -0
  309. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_service_durability.py +0 -0
  310. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_session_benchmark.py +0 -0
  311. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_session_budget.py +0 -0
  312. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_shell_robustness.py +0 -0
  313. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_subproject_exclusion.py +0 -0
  314. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_subproject_federation.py +0 -0
  315. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_subprojects.py +0 -0
  316. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_swe_bench.py +0 -0
  317. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_task_store.py +0 -0
  318. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_task_tool.py +0 -0
  319. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_time_tracker.py +0 -0
  320. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_token_telemetry.py +0 -0
  321. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_tool_registry.py +0 -0
  322. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_upgrade_and_version.py +0 -0
  323. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_validate.py +0 -0
  324. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_web_security.py +0 -0
  325. {code_context_control-2.56.0 → code_context_control-2.57.0}/tests/test_windows_reliability.py +0 -0
  326. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/__init__.py +0 -0
  327. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/backend.py +0 -0
  328. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/main.py +0 -0
  329. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/__init__.py +0 -0
  330. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/benchmark_view.py +0 -0
  331. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/claudemd_view.py +0 -0
  332. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/compress_view.py +0 -0
  333. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/index_view.py +0 -0
  334. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/init_view.py +0 -0
  335. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/mcp_view.py +0 -0
  336. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/optimize_view.py +0 -0
  337. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/pipe_view.py +0 -0
  338. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/projects_view.py +0 -0
  339. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/search_view.py +0 -0
  340. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/session_view.py +0 -0
  341. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/stats.py +0 -0
  342. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/screens/ui_view.py +0 -0
  343. {code_context_control-2.56.0 → code_context_control-2.57.0}/tui/theme.tcss +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.56.0
4
- Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Copilot, Cursor, Antigravity). Retrieve less, read less, edit safer — and version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP).
3
+ Version: 2.57.0
4
+ Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Copilot, Cursor, Antigravity). Retrieve less, read less, edit safer — version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP), and manage multi-project + sub-project hierarchies from a local hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/drknowhow/code-context-control
@@ -177,6 +177,14 @@ Every C3-initialized project on your machine appears here automatically — `c3
177
177
 
178
178
  Per-card actions cover the full lifecycle: launch the IDE, open the per-project UI, edit name / tags / notes, transfer the registration to a new path, **merge** another project's memory + conversation history + edit ledger into this one (with optional source cleanup), or remove it from the registry.
179
179
 
180
+ **Sub-projects — nested repos as first-class children.** Designate any sub-folder (a service in a monorepo, a vendored tool) as a linked child project with its own `.c3`: the parent's index excludes the child's subtree, and `c3_search` / `c3_memory` fan out across children on demand (`scope='all'` or one child by name). The whole hierarchy is manageable from the hub:
181
+
182
+ - **Designate** from any project card or the drill-in **Sub-projects** tab — folder picker with upfront validation, adopt-vs-initialize preview, and IDE choice. An existing top-level project that physically sits inside a parent can be re-linked via **"Make sub-project of…"**.
183
+ - **Link health is passive** — parent cards show a red "N link issues" badge and children show their link status automatically; **Reconcile** shows exactly what's broken and repairs it on confirm.
184
+ - **Cascade** update / reindex / health across all children (cancellable, optionally including the parent), **promote** a child back to top-level, or **de-initialize** it entirely behind a typed-name confirm.
185
+ - **Change parent…** moves a child between parents honestly: folders must physically nest, so the wizard stages the move, validates every step, and never leaves a broken state silently.
186
+ - Federation behavior per parent — memory roll-up, search fan-out, children per query — is editable in the project's config editor.
187
+
180
188
  **Open in your IDE of choice** — C3 auto-detects which CLIs you have installed and gives you one-click launchers:
181
189
 
182
190
  <p align="center">
@@ -114,6 +114,14 @@ Every C3-initialized project on your machine appears here automatically — `c3
114
114
 
115
115
  Per-card actions cover the full lifecycle: launch the IDE, open the per-project UI, edit name / tags / notes, transfer the registration to a new path, **merge** another project's memory + conversation history + edit ledger into this one (with optional source cleanup), or remove it from the registry.
116
116
 
117
+ **Sub-projects — nested repos as first-class children.** Designate any sub-folder (a service in a monorepo, a vendored tool) as a linked child project with its own `.c3`: the parent's index excludes the child's subtree, and `c3_search` / `c3_memory` fan out across children on demand (`scope='all'` or one child by name). The whole hierarchy is manageable from the hub:
118
+
119
+ - **Designate** from any project card or the drill-in **Sub-projects** tab — folder picker with upfront validation, adopt-vs-initialize preview, and IDE choice. An existing top-level project that physically sits inside a parent can be re-linked via **"Make sub-project of…"**.
120
+ - **Link health is passive** — parent cards show a red "N link issues" badge and children show their link status automatically; **Reconcile** shows exactly what's broken and repairs it on confirm.
121
+ - **Cascade** update / reindex / health across all children (cancellable, optionally including the parent), **promote** a child back to top-level, or **de-initialize** it entirely behind a typed-name confirm.
122
+ - **Change parent…** moves a child between parents honestly: folders must physically nest, so the wizard stages the move, validates every step, and never leaves a broken state silently.
123
+ - Federation behavior per parent — memory roll-up, search fan-out, children per query — is editable in the project's config editor.
124
+
117
125
  **Open in your IDE of choice** — C3 auto-detects which CLIs you have installed and gives you one-click launchers:
118
126
 
119
127
  <p align="center">
@@ -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.55.0"
95
+ __version__ = "2.57.0"
96
96
 
97
97
 
98
98
  def _command_deps() -> CommandDeps:
@@ -249,6 +249,7 @@
249
249
  <a href="#plan-mode" class="sidebar-link"><span class="icon">📐</span> Plan Mode</a>
250
250
  <a href="#session-restart" class="sidebar-link"><span class="icon">🔄</span> Session Restart</a>
251
251
  <a href="#terse-skill" class="sidebar-link"><span class="icon">⚡</span> /terse Skill</a>
252
+ <a href="#subprojects" class="sidebar-link"><span class="icon">🌳</span> Sub-projects</a>
252
253
  <a href="#native-fallback" class="sidebar-link"><span class="icon">⚠️</span> Native Fallback</a>
253
254
  </div>
254
255
  </aside>
@@ -355,6 +356,12 @@
355
356
  <td><code>c3_project(action='list')</code></td>
356
357
  <td>v2.31.0 — discover and operate on OTHER c3-installed projects; reads run free, writes need <code>allow_write=true</code>. <a href="tools.html#c3_project">Tool card →</a></td>
357
358
  </tr>
359
+ <tr>
360
+ <td><span class="badge badge-purple">+</span></td>
361
+ <td><strong>Sub-projects</strong></td>
362
+ <td><code>c3 sub add &lt;folder&gt;</code></td>
363
+ <td>v2.44.0 — link child projects living inside a parent; federated search/memory + Hub management. <a href="#subprojects">Section →</a></td>
364
+ </tr>
358
365
  </tbody>
359
366
  </table>
360
367
  </section>
@@ -897,6 +904,86 @@ c3_session(action=<span class="str">'restore'</span>)</code></pre>
897
904
 
898
905
  <hr class="divider">
899
906
 
907
+ <!-- Sub-projects -->
908
+ <section class="section" id="subprojects">
909
+ <div class="section-title"><span class="icon">🌳</span> Sub-projects</div>
910
+
911
+ <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>
912
+
913
+ <div class="callout callout-tip">
914
+ <span class="callout-icon">💡</span>
915
+ <div class="callout-body">
916
+ 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.
917
+ </div>
918
+ </div>
919
+
920
+ <h4>CLI — <code>c3 sub</code></h4>
921
+ <table class="table">
922
+ <thead><tr><th>Command</th><th>Effect</th></tr></thead>
923
+ <tbody>
924
+ <tr>
925
+ <td><code>c3 sub add &lt;folder&gt; [--name --ide]</code></td>
926
+ <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>
927
+ </tr>
928
+ <tr>
929
+ <td><code>c3 sub list</code></td>
930
+ <td>Tree of linked children with status (the default when no sub-command is given).</td>
931
+ </tr>
932
+ <tr>
933
+ <td><code>c3 sub remove &lt;ref&gt; [--clear]</code></td>
934
+ <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>
935
+ </tr>
936
+ <tr>
937
+ <td><code>c3 sub run &lt;update|reindex|health&gt; [--include-parent]</code></td>
938
+ <td>Cascade an operation across every child. <code>--mcp</code> also reinstalls the MCP config during <code>update</code>.</td>
939
+ </tr>
940
+ <tr>
941
+ <td><code>c3 sub check [--fix --prune]</code></td>
942
+ <td>Reconcile the three-way links. <code>--fix</code> repairs from the parent config; adding <code>--prune</code> drops entries whose folder is gone.</td>
943
+ </tr>
944
+ </tbody>
945
+ </table>
946
+
947
+ <h4>Agent surface — the <code>scope</code> parameter</h4>
948
+ <p><code>c3_search</code> and <code>c3_memory</code> accept a <code>scope</code> parameter on parent projects:</p>
949
+ <table class="table">
950
+ <thead><tr><th>Value</th><th>Meaning</th></tr></thead>
951
+ <tbody>
952
+ <tr><td><code>''</code></td><td>Current project only (default)</td></tr>
953
+ <tr><td><code>'all'</code></td><td>Fan out across the parent and all linked children</td></tr>
954
+ <tr><td><code>'&lt;child&gt;'</code></td><td>One specific sub-project by name</td></tr>
955
+ </tbody>
956
+ </table>
957
+
958
+ <pre><code>c3_search(query=<span class="str">'auth middleware'</span>, scope=<span class="str">'all'</span>)
959
+ c3_memory(action=<span class="str">'recall'</span>, query=<span class="str">'payment gotchas'</span>, scope=<span class="str">'billing-service'</span>)</code></pre>
960
+
961
+ <p>With <code>hybrid.subprojects.memory_rollup</code> enabled, a parent-side recall also unions in the children's facts automatically.</p>
962
+
963
+ <h4>Hub UI management (v2.57.0)</h4>
964
+ <p>Everything the CLI does — plus the operations that need multi-step safety — is available in the Hub:</p>
965
+ <ul>
966
+ <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>
967
+ <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>
968
+ <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>
969
+ <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>
970
+ <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>
971
+ <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>
972
+ <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>
973
+ <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>
974
+ </ul>
975
+
976
+ <div class="callout callout-warning">
977
+ <span class="callout-icon">⚠️</span>
978
+ <div class="callout-body">
979
+ <strong>Managed keys</strong>
980
+ The raw <code>subprojects</code> / <code>parent</code> config keys render read-only in the Hub config editor by design — hierarchy changes only go through the dedicated actions above (CLI or Hub), never by hand-editing the link records.
981
+ </div>
982
+ </div>
983
+ </section>
984
+
985
+ <hr class="divider">
986
+
900
987
  <!-- Native fallback -->
901
988
  <section class="section" id="native-fallback">
902
989
  <div class="section-title"><span class="icon">⚠️</span> When Native Tools Are Permitted</div>
@@ -333,6 +333,7 @@ _HUB_JS_FILES = [
333
333
  "hub_ui/components/session_drawer.js",
334
334
  "hub_ui/components/drill_panel.js",
335
335
  "hub_ui/components/drill_views.js",
336
+ "hub_ui/components/drill_subprojects.js",
336
337
  "hub_ui/components/drill_health.js",
337
338
  "hub_ui/components/drill_tasks.js",
338
339
  "hub_ui/components/drill_artifacts.js",
@@ -477,6 +478,129 @@ def _notification_count(project_path: str) -> int:
477
478
  return count
478
479
 
479
480
 
481
+ _sub_links_cache = {"fp": None, "ts": 0.0, "data": None, "computing": False}
482
+ _sub_links_lock = threading.Lock()
483
+ _SUB_LINKS_TTL = 15.0
484
+
485
+
486
+ def _invalidate_subproject_links():
487
+ """Drop the cached link-health snapshot (call after any hierarchy mutation)."""
488
+ with _sub_links_lock:
489
+ _sub_links_cache.update({"fp": None, "ts": 0.0, "data": None})
490
+
491
+
492
+ def _sub_federation_errors(sub):
493
+ """Schema check for the hybrid.subprojects federation dict (config PUT)."""
494
+ allowed = {"memory_rollup", "search_fanout", "max_children_per_query"}
495
+ unknown = sorted(set(sub) - allowed)
496
+ if unknown:
497
+ return f"unknown keys {unknown}"
498
+ for k in ("memory_rollup", "search_fanout"):
499
+ if k in sub and not isinstance(sub[k], bool):
500
+ return f"'{k}' must be a boolean"
501
+ v = sub.get("max_children_per_query")
502
+ if v is not None and (isinstance(v, bool) or not isinstance(v, int) or not 1 <= v <= 256):
503
+ return "'max_children_per_query' must be an integer between 1 and 256"
504
+ return ""
505
+
506
+
507
+ def _norm_realpath(s):
508
+ """resolve()+normcase, matching services.subprojects._norm — plain
509
+ normcase alone would flag symlinked/8.3/trailing-slash registry rows
510
+ as false orphans that Reconcile then denies."""
511
+ try:
512
+ return os.path.normcase(str(Path(s).resolve()))
513
+ except Exception:
514
+ return os.path.normcase(str(s or ""))
515
+
516
+
517
+ def _compute_subproject_links(projects):
518
+ """Statuses only — deliberately NOT SubprojectManager.list(), which
519
+ also parses facts.json and notifications.jsonl per child; those counts
520
+ were computed and thrown away on every poll recompute."""
521
+ from services.subprojects import SubprojectManager, get_subprojects
522
+ parents_out, children_out = {}, {}
523
+ registry = None
524
+ for parent in projects:
525
+ if not (parent.get("is_parent") or parent.get("subproject_count")):
526
+ continue
527
+ try:
528
+ mgr = SubprojectManager(parent.get("path", ""))
529
+ entries = get_subprojects(mgr.parent_path)
530
+ if registry is None:
531
+ registry = mgr.pm._read_projects()
532
+ except Exception:
533
+ continue
534
+ issues = 0
535
+ config_paths = set()
536
+ for entry in entries:
537
+ try:
538
+ key = _norm_realpath(mgr._abs_child(entry.get("rel_path", "")))
539
+ status = mgr._entry_status(entry, registry)
540
+ except Exception:
541
+ continue
542
+ config_paths.add(key)
543
+ if status != "ok":
544
+ issues += 1
545
+ children_out[key] = status
546
+ parent_key = _norm_realpath(parent.get("path", ""))
547
+ for row in projects:
548
+ rp = row.get("parent_path")
549
+ if (rp and _norm_realpath(rp) == parent_key
550
+ and _norm_realpath(row.get("path", "")) not in config_paths):
551
+ issues += 1
552
+ children_out[_norm_realpath(row.get("path", ""))] = "orphan"
553
+ if issues:
554
+ parents_out[parent_key] = issues
555
+ return {"parents": parents_out, "children": children_out}
556
+
557
+
558
+ def _annotate_subproject_links(projects):
559
+ """Per-parent link-health rollup + per-child link_status for the cards.
560
+
561
+ SubprojectManager.list() is config + filesystem + raw-registry reads
562
+ only (no child runtimes, no port probing). Registry orphans (rows
563
+ claiming a parent that has no matching config entry) count as issues
564
+ too — same rule as reconcile. The snapshot is cached _SUB_LINKS_TTL
565
+ seconds and computed single-flight, so overlapping UI polls never
566
+ stack behind a slow disk (e.g. a disconnected network drive); the
567
+ fingerprint ties the cache to the registry rows, and mutating
568
+ sub-project endpoints call _invalidate_subproject_links().
569
+ """
570
+ fp = tuple(sorted((os.path.normcase(p.get("path", "")),
571
+ os.path.normcase(p.get("parent_path") or ""))
572
+ for p in projects))
573
+ now = time.time()
574
+ do_compute = False
575
+ with _sub_links_lock:
576
+ snap = _sub_links_cache["data"]
577
+ fresh = (snap is not None and _sub_links_cache["fp"] == fp
578
+ and now - _sub_links_cache["ts"] < _SUB_LINKS_TTL)
579
+ if not fresh:
580
+ if _sub_links_cache["computing"]:
581
+ if _sub_links_cache["fp"] != fp:
582
+ snap = None # stale snapshot of a different registry — skip
583
+ else:
584
+ _sub_links_cache["computing"] = True
585
+ do_compute = True
586
+ if do_compute:
587
+ try:
588
+ snap = _compute_subproject_links(projects)
589
+ with _sub_links_lock:
590
+ _sub_links_cache.update({"fp": fp, "ts": time.time(), "data": snap})
591
+ finally:
592
+ with _sub_links_lock:
593
+ _sub_links_cache["computing"] = False
594
+ if not snap:
595
+ return
596
+ for p in projects:
597
+ key = _norm_realpath(p.get("path", ""))
598
+ if key in snap["parents"]:
599
+ p["subproject_issues"] = snap["parents"][key]
600
+ if key in snap["children"]:
601
+ p["link_status"] = snap["children"][key]
602
+
603
+
480
604
  @app.route("/api/projects", methods=["GET"])
481
605
  def api_projects_list():
482
606
  try:
@@ -487,6 +611,7 @@ def api_projects_list():
487
611
  p["notification_count"] = _notification_count(p.get("path", ""))
488
612
  p["is_parent"] = os.path.normcase(p.get("path", "")) in parent_paths
489
613
  p["open_task_count"] = open_task_count(p.get("path", ""))
614
+ _annotate_subproject_links(projects)
490
615
  return jsonify(projects)
491
616
  except Exception as e:
492
617
  return jsonify({"error": str(e)}), 500
@@ -519,6 +644,8 @@ def api_projects_remove():
519
644
  orphans = [p.get("path") for p in _pm().list_projects()
520
645
  if p.get("parent_path") and os.path.normcase(p["parent_path"]) == resolved]
521
646
  result = {"removed": _pm().remove_project(path)}
647
+ if result["removed"]:
648
+ _invalidate_subproject_links()
522
649
  if result["removed"] and orphans:
523
650
  result["orphaned_children"] = orphans
524
651
  return jsonify(result)
@@ -1226,6 +1353,8 @@ def api_subprojects_add():
1226
1353
  res = _run_c3(args, timeout=300)
1227
1354
  payload = _parse_json_tail(res.get("output", ""))
1228
1355
  ok = res.get("success") and bool((payload or {}).get("added"))
1356
+ if ok:
1357
+ _invalidate_subproject_links()
1229
1358
  body = {"success": ok, "result": payload, "output": res.get("output", "")}
1230
1359
  return jsonify(body), (201 if ok else 500)
1231
1360
 
@@ -1247,6 +1376,8 @@ def api_subprojects_remove():
1247
1376
  res = _run_c3(args, timeout=300)
1248
1377
  payload = _parse_json_tail(res.get("output", ""))
1249
1378
  ok = res.get("success") and bool((payload or {}).get("removed"))
1379
+ if ok:
1380
+ _invalidate_subproject_links()
1250
1381
  body = {"success": ok, "result": payload, "output": res.get("output", "")}
1251
1382
  return jsonify(body), (200 if ok else 500)
1252
1383
 
@@ -1259,8 +1390,11 @@ def api_subprojects_reconcile():
1259
1390
  if not parent:
1260
1391
  return jsonify({"error": "parent is required"}), 400
1261
1392
  try:
1262
- return jsonify(_sub_manager(parent).reconcile(
1263
- fix=bool(data.get("fix")), prune=bool(data.get("prune"))))
1393
+ result = _sub_manager(parent).reconcile(
1394
+ fix=bool(data.get("fix")), prune=bool(data.get("prune")))
1395
+ if data.get("fix"):
1396
+ _invalidate_subproject_links()
1397
+ return jsonify(result)
1264
1398
  except ValueError as e:
1265
1399
  return jsonify({"error": str(e)}), 404
1266
1400
  except Exception as e:
@@ -1321,6 +1455,7 @@ def _sub_cascade_worker(op: str, targets: list):
1321
1455
  _sub_cascade_state["running"] = False
1322
1456
  _sub_cascade_state["done"] = True
1323
1457
  _sub_cascade_state["current"] = None
1458
+ _invalidate_subproject_links()
1324
1459
 
1325
1460
 
1326
1461
  @app.route("/api/projects/subprojects/cascade", methods=["POST"])
@@ -1339,11 +1474,20 @@ def api_subprojects_cascade():
1339
1474
  return jsonify({"error": "op must be update|reindex|health"}), 400
1340
1475
  try:
1341
1476
  sm = _sub_manager(parent)
1342
- targets = sm.list()
1477
+ listed = sm.list()
1478
+ # Skipped children (missing folder; missing .c3 unless the op can
1479
+ # create it) are excluded up front so total and the success
1480
+ # denominator reflect real work — previously they inflated total
1481
+ # and reported as failures.
1482
+ skipped = [t for t in listed
1483
+ if t.get("status") == "missing_folder"
1484
+ or (t.get("status") == "missing_c3" and op != "update")]
1485
+ targets = [t for t in listed if t not in skipped]
1343
1486
  if data.get("include_parent"):
1344
1487
  targets.append({"name": "(parent)", "path": sm.parent_path, "status": "ok"})
1345
1488
  if not targets:
1346
- return jsonify({"error": "No sub-projects designated"}), 400
1489
+ return jsonify({"error": "No cascade-eligible sub-projects"
1490
+ + (f" ({len(skipped)} skipped)" if skipped else "")}), 400
1347
1491
  with _sub_cascade_lock:
1348
1492
  _sub_cascade_state = {
1349
1493
  "running": True, "cancelled": False, "results": [],
@@ -1352,7 +1496,8 @@ def api_subprojects_cascade():
1352
1496
  }
1353
1497
  t = threading.Thread(target=_sub_cascade_worker, args=(op, targets), daemon=True)
1354
1498
  t.start()
1355
- return jsonify({"started": True, "total": len(targets), "op": op})
1499
+ return jsonify({"started": True, "total": len(targets), "op": op,
1500
+ "skipped": len(skipped)})
1356
1501
  except ValueError as e:
1357
1502
  return jsonify({"error": str(e)}), 404
1358
1503
  except Exception as e:
@@ -1643,6 +1788,18 @@ def api_projects_config_put():
1643
1788
  return jsonify({"error": "values must be an object"}), 400
1644
1789
  for refused in _CONFIG_REFUSED_KEYS:
1645
1790
  if refused in values:
1791
+ # 'subprojects' under the hybrid section is the federation
1792
+ # settings DICT (memory_rollup / search_fanout / …), not the
1793
+ # top-level child-links ARRAY — the array stays refused (and is
1794
+ # unreachable through the section whitelist anyway). Schema-check
1795
+ # the dict: exactly these keys, exactly these types — the generic
1796
+ # deep-merge below would happily persist anything.
1797
+ if (section == "hybrid" and refused == "subprojects"
1798
+ and isinstance(values["subprojects"], dict)):
1799
+ bad = _sub_federation_errors(values["subprojects"])
1800
+ if bad:
1801
+ return jsonify({"error": f"hybrid.subprojects: {bad}"}), 400
1802
+ continue
1646
1803
  return jsonify({"error": f"'{refused}' cannot be edited here"}), 400
1647
1804
  try:
1648
1805
  resolved = _resolve_project_path(path)
@@ -128,7 +128,7 @@ function App() {
128
128
  {drawerProject &&
129
129
  <SessionDrawer project={drawerProject} onClose={() => setDrawerProject(null)} />}
130
130
  {drill &&
131
- <DrillPanel project={drill} tab={drillTab} setTab={setDrillTab}
131
+ <DrillPanel project={drill} tab={drillTab} setTab={setDrillTab} projects={projects}
132
132
  onClose={() => setDrill(null)} onChanged={loadProjects} onOpenModal={openModal} />}
133
133
  <GlobalSearch open={searchOpen} onClose={() => setSearchOpen(false)} projects={projects}
134
134
  onOpenProject={(p, tab) => { setSearchOpen(false); openDrill(p, tab || 'overview'); }} />
@@ -6,6 +6,15 @@
6
6
  const CFG_ED_SECTIONS = ['hybrid', 'agents', 'delegate', 'proxy', 'mcp', 'meta', 'memory_llm', 'bitbucket'];
7
7
  const CFG_ED_READONLY = ['bitbucket'];
8
8
  const CFG_ED_REFUSED = ['version', 'project_path', 'permission_tier', 'subprojects', 'parent', 'api_key'];
9
+ // Refused keys worth surfacing read-only when present, rather than hiding —
10
+ // still never editable here (server refuses the write regardless).
11
+ const CFG_ED_MANAGED = ['subprojects', 'parent'];
12
+ const CFG_ED_MANAGED_HINT = 'Managed via sub-project actions — use the project card menu or drill-in panel.';
13
+ // hybrid.subprojects federation settings get a friendly "Sub-projects"
14
+ // section on parent projects. The hub API accepts dict-valued 'subprojects'
15
+ // under section=hybrid (schema-checked server-side); the read-only fallback
16
+ // below only engages if this flag is ever flipped off again.
17
+ const CFG_ED_SUBPROJECTS_WRITABLE = true;
9
18
 
10
19
  function ConfigEditor({ project }) {
11
20
  const [section, setSection] = useState('hybrid');
@@ -36,11 +45,43 @@ function ConfigEditor({ project }) {
36
45
  };
37
46
  useEffect(() => { setCfg(null); load(); }, [project.path, section]);
38
47
 
48
+ // ── Sub-projects (hybrid.subprojects): friendly federation controls ─────
49
+ // Shown for parent projects on the hybrid section. Edits nest under one
50
+ // 'subprojects' key holding only the changed sub-keys, so the server's
51
+ // deep-merge preserves untouched siblings (e.g. parent_memory_visible).
52
+ const isParent = !!project.is_parent || (project.subproject_count || 0) > 0;
53
+ const showSubprojects = section === 'hybrid' && !!cfg
54
+ && (isParent || cfg.subprojects !== undefined);
55
+ const subCfg = (cfg && cfg.subprojects && typeof cfg.subprojects === 'object') ? cfg.subprojects : {};
56
+ const subDefs = (defs.subprojects && typeof defs.subprojects === 'object') ? defs.subprojects : {};
57
+ const subEdits = (edits.subprojects && typeof edits.subprojects === 'object') ? edits.subprojects : {};
58
+ const subVal = (k) => Object.prototype.hasOwnProperty.call(subEdits, k) ? subEdits[k]
59
+ : (subCfg[k] !== undefined ? subCfg[k] : subDefs[k]);
60
+ const subEdited = (k) => Object.prototype.hasOwnProperty.call(subEdits, k);
61
+ const subDot = (k) => subEdited(k)
62
+ ? <span style={{ width: 6, height: 6, borderRadius: '50%', background: T.accent, display: 'inline-block', flexShrink: 0 }} />
63
+ : null;
64
+ const setSubEdit = (k, v) => {
65
+ if (!CFG_ED_SUBPROJECTS_WRITABLE) return;
66
+ setEdits(e => {
67
+ const cur = Object.assign({}, (e.subprojects && typeof e.subprojects === 'object') ? e.subprojects : {});
68
+ cur[k] = v;
69
+ return Object.assign({}, e, { subprojects: cur });
70
+ });
71
+ };
72
+
39
73
  const keys = [];
74
+ const managedKeys = [];
40
75
  if (cfg) {
41
- Object.keys(defs).forEach(k => { if (CFG_ED_REFUSED.indexOf(k) < 0) keys.push(k); });
76
+ Object.keys(defs).forEach(k => {
77
+ if (CFG_ED_REFUSED.indexOf(k) < 0) keys.push(k);
78
+ else if (CFG_ED_MANAGED.indexOf(k) >= 0 && managedKeys.indexOf(k) < 0
79
+ && !(k === 'subprojects' && showSubprojects)) managedKeys.push(k);
80
+ });
42
81
  Object.keys(cfg).forEach(k => {
43
- if (CFG_ED_REFUSED.indexOf(k) < 0 && keys.indexOf(k) < 0) keys.push(k);
82
+ if (CFG_ED_REFUSED.indexOf(k) < 0) { if (keys.indexOf(k) < 0) keys.push(k); }
83
+ else if (CFG_ED_MANAGED.indexOf(k) >= 0 && managedKeys.indexOf(k) < 0
84
+ && !(k === 'subprojects' && showSubprojects)) managedKeys.push(k);
44
85
  });
45
86
  }
46
87
  const currentVal = (k) => Object.prototype.hasOwnProperty.call(edits, k) ? edits[k]
@@ -66,6 +107,14 @@ function ConfigEditor({ project }) {
66
107
  v = Number(v);
67
108
  if (!isFinite(v)) throw new Error(`'${k}' must be a number`);
68
109
  }
110
+ if (k === 'subprojects' && v && typeof v === 'object'
111
+ && Object.prototype.hasOwnProperty.call(v, 'max_children_per_query')) {
112
+ const n = Number(v.max_children_per_query);
113
+ if (!isFinite(n) || n < 1 || Math.floor(n) !== n) {
114
+ throw new Error("'max_children_per_query' must be a positive integer");
115
+ }
116
+ v = Object.assign({}, v, { max_children_per_query: n });
117
+ }
69
118
  values[k] = v;
70
119
  });
71
120
  const d = await api.put('/api/projects/config', { path: project.path, section, values });
@@ -83,6 +132,13 @@ function ConfigEditor({ project }) {
83
132
  Object.keys(defs).forEach(k => {
84
133
  if (CFG_ED_REFUSED.indexOf(k) < 0) next[k] = JSON.parse(JSON.stringify(defs[k] === undefined ? null : defs[k]));
85
134
  });
135
+ if (showSubprojects && CFG_ED_SUBPROJECTS_WRITABLE) {
136
+ next.subprojects = {
137
+ memory_rollup: subDefs.memory_rollup !== false,
138
+ search_fanout: subDefs.search_fanout !== false,
139
+ max_children_per_query: subDefs.max_children_per_query !== undefined ? subDefs.max_children_per_query : 8,
140
+ };
141
+ }
86
142
  setEdits(next);
87
143
  setJsonText({}); setJsonBad({});
88
144
  notify(`Prefilled ${section} defaults — review, then Save section`, 'warn');
@@ -91,6 +147,18 @@ function ConfigEditor({ project }) {
91
147
  const dirty = Object.keys(edits).length > 0;
92
148
  const invalid = Object.keys(jsonBad).some(k => jsonBad[k]);
93
149
 
150
+ const renderManagedField = (k) => {
151
+ const val = cfg && cfg[k] !== undefined ? cfg[k] : defs[k];
152
+ const text = (val !== null && typeof val === 'object')
153
+ ? JSON.stringify(val) : String(val === undefined ? '' : val);
154
+ return (
155
+ <div key={k} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: '6px 0', opacity: 0.55 }}>
156
+ <span className="mono" style={{ fontSize: 11, color: T.textDim, width: 200, flexShrink: 0, overflowWrap: 'anywhere' }}>{k}</span>
157
+ <span className="mono" style={{ fontSize: 11, color: T.textDim, flex: 1, overflowWrap: 'anywhere' }}>{text}</span>
158
+ </div>
159
+ );
160
+ };
161
+
94
162
  const renderField = (k) => {
95
163
  const kind = kindOf(k);
96
164
  const val = currentVal(k);
@@ -215,6 +283,62 @@ function ConfigEditor({ project }) {
215
283
  <div style={{ display: 'flex', flexDirection: 'column' }}>
216
284
  {keys.map(renderField)}
217
285
  </div>
286
+ {showSubprojects && (
287
+ <div style={{ marginTop: 14, paddingTop: 14, borderTop: `1px solid ${T.border}` }}>
288
+ <div style={{ fontSize: 12, fontWeight: 700, color: T.text }}>Sub-projects</div>
289
+ <div style={{ fontSize: 11, color: T.textDim, margin: '2px 0 4px' }}>
290
+ How this parent federates with its linked children (<span className="mono">hybrid.subprojects</span>).
291
+ </div>
292
+ {!CFG_ED_SUBPROJECTS_WRITABLE && (
293
+ <div style={{ fontSize: 11, color: T.textMuted, marginBottom: 4 }}>
294
+ Read-only for now — the hub API refuses <span className="mono">subprojects</span> config writes.
295
+ Edit <span className="mono">.c3/config.json</span> directly to change these.
296
+ </div>
297
+ )}
298
+ <div style={CFG_ED_SUBPROJECTS_WRITABLE ? undefined : { opacity: 0.55, pointerEvents: 'none' }}>
299
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
300
+ <div style={{ flex: 1 }}>
301
+ {renderBoolToggle('Parent memory recall includes child facts', !!subVal('memory_rollup'),
302
+ () => setSubEdit('memory_rollup', !subVal('memory_rollup')),
303
+ `memory_rollup — default: ${subDefs.memory_rollup === false ? 'off' : 'on'}`)}
304
+ </div>
305
+ {subDot('memory_rollup')}
306
+ </div>
307
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
308
+ <div style={{ flex: 1 }}>
309
+ {renderBoolToggle("Search scope 'all' fans out to children", !!subVal('search_fanout'),
310
+ () => setSubEdit('search_fanout', !subVal('search_fanout')),
311
+ `search_fanout — default: ${subDefs.search_fanout === false ? 'off' : 'on'}`)}
312
+ </div>
313
+ {subDot('search_fanout')}
314
+ </div>
315
+ <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '8px 0' }}>
316
+ <span style={{ display: 'flex', flexDirection: 'column', gap: 3, minWidth: 0 }}>
317
+ <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
318
+ <span style={{ color: T.textMuted, fontSize: 12 }}>Max children per query</span>
319
+ {subDot('max_children_per_query')}
320
+ </span>
321
+ <span style={{ color: T.textDim, fontSize: 11 }}>
322
+ max_children_per_query — cap per federated call, default: {subDefs.max_children_per_query !== undefined ? subDefs.max_children_per_query : 8}
323
+ </span>
324
+ </span>
325
+ <input type="number" min={1} step={1} disabled={!CFG_ED_SUBPROJECTS_WRITABLE}
326
+ value={subVal('max_children_per_query') == null ? '' : String(subVal('max_children_per_query'))}
327
+ onChange={ev => setSubEdit('max_children_per_query', ev.target.value)}
328
+ className="mono"
329
+ style={drillFieldStyle({ width: 110, textAlign: 'right', fontSize: 11 })} />
330
+ </label>
331
+ </div>
332
+ </div>
333
+ )}
334
+ {managedKeys.length > 0 && (
335
+ <div style={{ marginTop: 14, paddingTop: 14, borderTop: `1px solid ${T.border}` }}>
336
+ <div style={{ fontSize: 11, color: T.textDim, marginBottom: 8 }}>{CFG_ED_MANAGED_HINT}</div>
337
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
338
+ {managedKeys.map(renderManagedField)}
339
+ </div>
340
+ </div>
341
+ )}
218
342
  {!readOnly && (
219
343
  <div style={{
220
344
  display: 'flex', alignItems: 'center', gap: 10, marginTop: 20,