code-context-control 2.52.0__tar.gz → 2.53.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 (326) hide show
  1. {code_context_control-2.52.0 → code_context_control-2.53.0}/PKG-INFO +2 -2
  2. {code_context_control-2.52.0 → code_context_control-2.53.0}/README.md +1 -1
  3. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/c3.py +1 -1
  4. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_server.py +147 -24
  5. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/task_board.js +94 -16
  6. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/mcp_server.py +27 -4
  7. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/server.py +104 -16
  8. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/tasks.py +182 -9
  9. code_context_control-2.53.0/cli/ui/components/tasks.js +821 -0
  10. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/pm_shared.js +72 -0
  11. {code_context_control-2.52.0 → code_context_control-2.53.0}/code_context_control.egg-info/PKG-INFO +2 -2
  12. {code_context_control-2.52.0 → code_context_control-2.53.0}/code_context_control.egg-info/SOURCES.txt +2 -0
  13. {code_context_control-2.52.0 → code_context_control-2.53.0}/pyproject.toml +1 -1
  14. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/runtime.py +6 -0
  15. code_context_control-2.53.0/services/task_store.py +1108 -0
  16. code_context_control-2.53.0/services/time_tracker.py +285 -0
  17. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_pm_api.py +91 -0
  18. code_context_control-2.53.0/tests/test_task_store.py +509 -0
  19. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_task_tool.py +3 -1
  20. code_context_control-2.53.0/tests/test_time_tracker.py +115 -0
  21. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_windows_reliability.py +2 -1
  22. code_context_control-2.52.0/cli/ui/components/tasks.js +0 -287
  23. code_context_control-2.52.0/services/task_store.py +0 -578
  24. code_context_control-2.52.0/tests/test_task_store.py +0 -232
  25. {code_context_control-2.52.0 → code_context_control-2.53.0}/LICENSE +0 -0
  26. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/__init__.py +0 -0
  27. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/_hook_utils.py +0 -0
  28. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/commands/__init__.py +0 -0
  29. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/commands/common.py +0 -0
  30. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/commands/parser.py +0 -0
  31. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/docs.html +0 -0
  32. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/edits.html +0 -0
  33. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/bitbucket.html +0 -0
  34. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/getting-started.html +0 -0
  35. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/index.html +0 -0
  36. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/oracle.html +0 -0
  37. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/shared.css +0 -0
  38. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/tools.html +0 -0
  39. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/guide/workflow.html +0 -0
  40. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_artifact.py +0 -0
  41. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_auto_snapshot.py +0 -0
  42. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_c3_signal.py +0 -0
  43. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_c3read.py +0 -0
  44. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_dispatch.py +0 -0
  45. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_edit_ledger.py +0 -0
  46. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_edit_unlock.py +0 -0
  47. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_filter.py +0 -0
  48. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_ghost_files.py +0 -0
  49. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_pretool_enforce.py +0 -0
  50. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_prompt_recall.py +0 -0
  51. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_read.py +0 -0
  52. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_session_stats.py +0 -0
  53. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hook_terse_advisor.py +0 -0
  54. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub.html +0 -0
  55. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/app.js +0 -0
  56. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/add_project.js +0 -0
  57. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/config_editor.js +0 -0
  58. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
  59. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/drill_health.js +0 -0
  60. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/drill_panel.js +0 -0
  61. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/drill_tasks.js +0 -0
  62. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/drill_views.js +0 -0
  63. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/global_search.js +0 -0
  64. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/mcp_manager.js +0 -0
  65. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/modals.js +0 -0
  66. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/project_card.js +0 -0
  67. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/project_tree.js +0 -0
  68. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/session_drawer.js +0 -0
  69. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/settings_modal.js +0 -0
  70. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/sidebar.js +0 -0
  71. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/summary_bar.js +0 -0
  72. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/toasts.js +0 -0
  73. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/components/topbar.js +0 -0
  74. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui/state.js +0 -0
  75. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/hub_ui.html +0 -0
  76. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/mcp_proxy.py +0 -0
  77. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/__init__.py +0 -0
  78. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/_helpers.py +0 -0
  79. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/agent.py +0 -0
  80. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/artifacts.py +0 -0
  81. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/bitbucket.py +0 -0
  82. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/compress.py +0 -0
  83. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/delegate.py +0 -0
  84. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/edit.py +0 -0
  85. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/edits.py +0 -0
  86. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/federate.py +0 -0
  87. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/filter.py +0 -0
  88. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/impact.py +0 -0
  89. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/memory.py +0 -0
  90. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/project.py +0 -0
  91. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/read.py +0 -0
  92. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/search.py +0 -0
  93. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/session.py +0 -0
  94. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/shell.py +0 -0
  95. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/status.py +0 -0
  96. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/tools/validate.py +0 -0
  97. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/api.js +0 -0
  98. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/app.js +0 -0
  99. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/bitbucket.js +0 -0
  100. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/chat.js +0 -0
  101. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/dashboard.js +0 -0
  102. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/edits.js +0 -0
  103. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/instructions.js +0 -0
  104. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/memory.js +0 -0
  105. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/sessions.js +0 -0
  106. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/settings.js +0 -0
  107. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/components/sidebar.js +0 -0
  108. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/icons.js +0 -0
  109. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/shared.js +0 -0
  110. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui/theme.js +0 -0
  111. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui.html +0 -0
  112. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui_legacy.html +0 -0
  113. {code_context_control-2.52.0 → code_context_control-2.53.0}/cli/ui_nano.html +0 -0
  114. {code_context_control-2.52.0 → code_context_control-2.53.0}/code_context_control.egg-info/dependency_links.txt +0 -0
  115. {code_context_control-2.52.0 → code_context_control-2.53.0}/code_context_control.egg-info/entry_points.txt +0 -0
  116. {code_context_control-2.52.0 → code_context_control-2.53.0}/code_context_control.egg-info/requires.txt +0 -0
  117. {code_context_control-2.52.0 → code_context_control-2.53.0}/code_context_control.egg-info/top_level.txt +0 -0
  118. {code_context_control-2.52.0 → code_context_control-2.53.0}/core/__init__.py +0 -0
  119. {code_context_control-2.52.0 → code_context_control-2.53.0}/core/config.py +0 -0
  120. {code_context_control-2.52.0 → code_context_control-2.53.0}/core/ide.py +0 -0
  121. {code_context_control-2.52.0 → code_context_control-2.53.0}/core/mcp_toml.py +0 -0
  122. {code_context_control-2.52.0 → code_context_control-2.53.0}/core/web_security.py +0 -0
  123. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/__init__.py +0 -0
  124. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/config.py +0 -0
  125. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/mcp_oracle.py +0 -0
  126. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/oracle_server.py +0 -0
  127. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/oracle_ui.html +0 -0
  128. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/__init__.py +0 -0
  129. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/activity_reporter.py +0 -0
  130. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/api_auth.py +0 -0
  131. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/c3_bridge.py +0 -0
  132. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/chat_engine.py +0 -0
  133. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/chat_store.py +0 -0
  134. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/cross_memory.py +0 -0
  135. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/federated_graph.py +0 -0
  136. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/health_checker.py +0 -0
  137. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/insight_engine.py +0 -0
  138. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/local_session.py +0 -0
  139. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/memory_reader.py +0 -0
  140. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/memory_writer.py +0 -0
  141. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/project_scanner.py +0 -0
  142. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/review_agent.py +0 -0
  143. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/tool_executor.py +0 -0
  144. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/services/tool_registry.py +0 -0
  145. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/activity.js +0 -0
  146. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/agents.js +0 -0
  147. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/app.js +0 -0
  148. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/busy.js +0 -0
  149. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/chat/conversations.js +0 -0
  150. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/chat/input.js +0 -0
  151. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/chat/markdown.js +0 -0
  152. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/chat/send.js +0 -0
  153. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/chat/stream_renderer.js +0 -0
  154. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/chat/toolbar.js +0 -0
  155. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/core.js +0 -0
  156. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/crossgraph.js +0 -0
  157. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/header.js +0 -0
  158. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/insights.js +0 -0
  159. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/projects.js +0 -0
  160. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/settings.js +0 -0
  161. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/suggestions.js +0 -0
  162. {code_context_control-2.52.0 → code_context_control-2.53.0}/oracle/ui/theme_tabs.js +0 -0
  163. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/__init__.py +0 -0
  164. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/activity_log.py +0 -0
  165. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/agent_base.py +0 -0
  166. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/agents.py +0 -0
  167. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/artifact_defs.py +0 -0
  168. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/artifact_store.py +0 -0
  169. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/auto_memory.py +0 -0
  170. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/bench/__init__.py +0 -0
  171. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/bench/external/__init__.py +0 -0
  172. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/bench/external/aider_polyglot.py +0 -0
  173. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/bench/external/swe_bench.py +0 -0
  174. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/benchmark_dashboard.py +0 -0
  175. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/bitbucket_client.py +0 -0
  176. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/bitbucket_credentials.py +0 -0
  177. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/circuit_breaker.py +0 -0
  178. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/claude_md.py +0 -0
  179. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/compressor.py +0 -0
  180. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/context_snapshot.py +0 -0
  181. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/conversation_store.py +0 -0
  182. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/doc_index.py +0 -0
  183. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/e2e_benchmark.py +0 -0
  184. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/e2e_evaluator.py +0 -0
  185. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/e2e_tasks.py +0 -0
  186. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/edit_ledger.py +0 -0
  187. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/embedding_index.py +0 -0
  188. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/error_reporting.py +0 -0
  189. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/file_memory.py +0 -0
  190. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/git_context.py +0 -0
  191. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/hub_service.py +0 -0
  192. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/indexer.py +0 -0
  193. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory.py +0 -0
  194. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory_consolidator.py +0 -0
  195. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory_distiller.py +0 -0
  196. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory_graph.py +0 -0
  197. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory_grounder.py +0 -0
  198. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory_queue.py +0 -0
  199. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/memory_scorer.py +0 -0
  200. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/metrics.py +0 -0
  201. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/notifications.py +0 -0
  202. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/ollama_bridge.py +0 -0
  203. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/ollama_client.py +0 -0
  204. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/ollama_credentials.py +0 -0
  205. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/output_filter.py +0 -0
  206. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/parser.py +0 -0
  207. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/project_manager.py +0 -0
  208. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/project_runtime.py +0 -0
  209. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/protocol.py +0 -0
  210. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/proxy_state.py +0 -0
  211. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/retention.py +0 -0
  212. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/retrieval_broker.py +0 -0
  213. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/router.py +0 -0
  214. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/session_benchmark.py +0 -0
  215. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/session_manager.py +0 -0
  216. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/session_preloader.py +0 -0
  217. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/subprojects.py +0 -0
  218. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/telemetry.py +0 -0
  219. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/text_index.py +0 -0
  220. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/tool_classifier.py +0 -0
  221. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/transcript_index.py +0 -0
  222. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/validation_cache.py +0 -0
  223. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/vector_store.py +0 -0
  224. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/watcher.py +0 -0
  225. {code_context_control-2.52.0 → code_context_control-2.53.0}/services/win_subprocess.py +0 -0
  226. {code_context_control-2.52.0 → code_context_control-2.53.0}/setup.cfg +0 -0
  227. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_activity_reporter.py +0 -0
  228. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_aider_polyglot.py +0 -0
  229. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_antigravity_transition.py +0 -0
  230. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_artifact_store.py +0 -0
  231. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_artifact_tool.py +0 -0
  232. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_bitbucket_cli_smoke.py +0 -0
  233. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_bitbucket_client.py +0 -0
  234. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_bitbucket_config_fallback.py +0 -0
  235. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_bitbucket_credentials.py +0 -0
  236. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_bitbucket_tool.py +0 -0
  237. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
  238. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_c3_shell.py +0 -0
  239. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_circuit_breaker.py +0 -0
  240. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_claude_md_merge.py +0 -0
  241. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_cli_smoke.py +0 -0
  242. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_compressor_large_file.py +0 -0
  243. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_delegate_cascade.py +0 -0
  244. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_e2e_benchmark.py +0 -0
  245. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_edit_ledger_hook.py +0 -0
  246. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_edit_normalization.py +0 -0
  247. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_enforcement_flip.py +0 -0
  248. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_federated_graph.py +0 -0
  249. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_filter_backoff.py +0 -0
  250. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_ghost_files.py +0 -0
  251. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_ghost_generation.py +0 -0
  252. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_git_branch_awareness.py +0 -0
  253. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hook_dispatch.py +0 -0
  254. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hook_pretool_enforce.py +0 -0
  255. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hook_prompt_recall.py +0 -0
  256. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hook_smoke.py +0 -0
  257. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hook_state.py +0 -0
  258. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hook_utf8_stdio.py +0 -0
  259. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hub_inspect_api.py +0 -0
  260. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_hub_server_smoke.py +0 -0
  261. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_install_mcp_entrypoint.py +0 -0
  262. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_lazy_store_init.py +0 -0
  263. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_mcp_host_guard.py +0 -0
  264. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_mcp_memory_timeout.py +0 -0
  265. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_mcp_server_smoke.py +0 -0
  266. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_mcp_toml.py +0 -0
  267. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_memory_distiller.py +0 -0
  268. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_memory_graph_api.py +0 -0
  269. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_memory_queue.py +0 -0
  270. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_memory_system.py +0 -0
  271. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_notification_dedup.py +0 -0
  272. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_notification_discipline.py +0 -0
  273. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_ollama_credentials.py +0 -0
  274. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_api_auth.py +0 -0
  275. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_apikey_api.py +0 -0
  276. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_c3_bridge.py +0 -0
  277. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_chat_engine.py +0 -0
  278. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_discovery_api.py +0 -0
  279. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_local_auth.py +0 -0
  280. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_ollama_bridge.py +0 -0
  281. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_scanner_cache.py +0 -0
  282. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_security_fixes.py +0 -0
  283. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_subproject_awareness.py +0 -0
  284. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_oracle_ui_bundle.py +0 -0
  285. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_output_filter.py +0 -0
  286. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_permissions.py +0 -0
  287. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_project_manager.py +0 -0
  288. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_project_manager_merge.py +0 -0
  289. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_project_tool.py +0 -0
  290. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_read_coercion.py +0 -0
  291. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_read_edit_parity.py +0 -0
  292. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_related_facts.py +0 -0
  293. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_response_boilerplate.py +0 -0
  294. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_retention.py +0 -0
  295. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_review_digest.py +0 -0
  296. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_service_durability.py +0 -0
  297. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_session_benchmark.py +0 -0
  298. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_session_budget.py +0 -0
  299. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_shell_robustness.py +0 -0
  300. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_subproject_exclusion.py +0 -0
  301. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_subproject_federation.py +0 -0
  302. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_subprojects.py +0 -0
  303. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_swe_bench.py +0 -0
  304. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_token_telemetry.py +0 -0
  305. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_tool_registry.py +0 -0
  306. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_upgrade_and_version.py +0 -0
  307. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_validate.py +0 -0
  308. {code_context_control-2.52.0 → code_context_control-2.53.0}/tests/test_web_security.py +0 -0
  309. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/__init__.py +0 -0
  310. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/backend.py +0 -0
  311. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/main.py +0 -0
  312. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/__init__.py +0 -0
  313. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/benchmark_view.py +0 -0
  314. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/claudemd_view.py +0 -0
  315. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/compress_view.py +0 -0
  316. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/index_view.py +0 -0
  317. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/init_view.py +0 -0
  318. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/mcp_view.py +0 -0
  319. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/optimize_view.py +0 -0
  320. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/pipe_view.py +0 -0
  321. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/projects_view.py +0 -0
  322. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/search_view.py +0 -0
  323. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/session_view.py +0 -0
  324. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/stats.py +0 -0
  325. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/screens/ui_view.py +0 -0
  326. {code_context_control-2.52.0 → code_context_control-2.53.0}/tui/theme.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.52.0
3
+ Version: 2.53.0
4
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).
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -275,7 +275,7 @@ C3 exposes 18 tools as a native MCP server. Your IDE calls them directly:
275
275
  | `c3_edits` | Edit-ledger queries + version diffs + restore points + per-branch filter |
276
276
  | `c3_bitbucket` | Bitbucket Data Center integration — PRs, branches, builds, repo admin (v2.30.0) |
277
277
  | `c3_project` | Cross-project — discover & operate on other c3-installed projects; guarded writes (v2.31.0) |
278
- | `c3_task` | Durable per-project tasks, milestones, and decision notes (v2.45.0) |
278
+ | `c3_task` | Durable per-project PM — tasks with dependencies & subtasks, milestones, decision notes, event history, health reports, and auto+manual time tracking (v2.53.0) |
279
279
  | `c3_artifacts` | Agent-config tracking — version history, diff & restore for CLAUDE.md, settings/hooks, MCP configs, skills (v2.46.0) |
280
280
 
281
281
  On Windows, `c3_shell` uses Git Bash when available. Git Bash does not bundle
@@ -212,7 +212,7 @@ C3 exposes 18 tools as a native MCP server. Your IDE calls them directly:
212
212
  | `c3_edits` | Edit-ledger queries + version diffs + restore points + per-branch filter |
213
213
  | `c3_bitbucket` | Bitbucket Data Center integration — PRs, branches, builds, repo admin (v2.30.0) |
214
214
  | `c3_project` | Cross-project — discover & operate on other c3-installed projects; guarded writes (v2.31.0) |
215
- | `c3_task` | Durable per-project tasks, milestones, and decision notes (v2.45.0) |
215
+ | `c3_task` | Durable per-project PM — tasks with dependencies & subtasks, milestones, decision notes, event history, health reports, and auto+manual time tracking (v2.53.0) |
216
216
  | `c3_artifacts` | Agent-config tracking — version history, diff & restore for CLAUDE.md, settings/hooks, MCP configs, skills (v2.46.0) |
217
217
 
218
218
  On Windows, `c3_shell` uses Git Bash when available. Git Bash does not bundle
@@ -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.52.0"
95
+ __version__ = "2.53.0"
96
96
 
97
97
 
98
98
  def _command_deps() -> CommandDeps:
@@ -1770,21 +1770,20 @@ def api_pm_task():
1770
1770
  task_id = (data.get("id") or "").strip()
1771
1771
  if not task_id:
1772
1772
  return jsonify({"error": "id is required"}), 400
1773
- res = None
1774
- fields = data.get("fields") or {}
1775
- if fields:
1776
- res = store.update_task(task_id, **fields)
1773
+ if data.get("restore"):
1774
+ res = store.restore_task(task_id, actor="hub")
1777
1775
  if "error" in res:
1778
1776
  return jsonify(res), 400
1779
- move = data.get("move") or {}
1780
- if move:
1781
- res = store.move_task(task_id, status=move.get("status"),
1782
- before_id=move.get("before_id"),
1783
- after_id=move.get("after_id"))
1784
- if "error" in res:
1785
- return jsonify(res), 400
1786
- if res is None:
1777
+ _pm_audit(resolved, "task", "restore", res["id"])
1778
+ return jsonify({"updated": True, "task": res})
1779
+ if not (data.get("fields") or data.get("move")):
1787
1780
  return jsonify({"error": "fields or move required"}), 400
1781
+ res = store.mutate_task(task_id, fields=data.get("fields"),
1782
+ move=data.get("move"),
1783
+ expected_rev=data.get("expected_rev"),
1784
+ actor="hub")
1785
+ if "error" in res:
1786
+ return jsonify(res), 409 if res.get("code") == "rev_conflict" else 400
1788
1787
  _pm_audit(resolved, "task", "update", res["id"])
1789
1788
  return jsonify({"updated": True, "task": res})
1790
1789
 
@@ -1796,7 +1795,7 @@ def api_pm_task():
1796
1795
  task_id = (data.get("id") or "").strip()
1797
1796
  if not task_id:
1798
1797
  return jsonify({"error": "id is required"}), 400
1799
- res = store.archive_task(task_id)
1798
+ res = store.archive_task(task_id, actor="hub")
1800
1799
  if "error" in res:
1801
1800
  return jsonify(res), 400
1802
1801
  _pm_audit(resolved, "task", "archive", res["id"])
@@ -1825,9 +1824,10 @@ def api_pm_milestone():
1825
1824
  return jsonify({"error": "id is required"}), 400
1826
1825
 
1827
1826
  if request.method == "PUT":
1828
- res = store.update_milestone(ms_id, **(data.get("fields") or {}))
1827
+ res = store.update_milestone(ms_id, expected_rev=data.get("expected_rev"),
1828
+ **(data.get("fields") or {}))
1829
1829
  if "error" in res:
1830
- return jsonify(res), 400
1830
+ return jsonify(res), 409 if res.get("code") == "rev_conflict" else 400
1831
1831
  _pm_audit(resolved, "milestone", "update", res["id"])
1832
1832
  return jsonify({"updated": True, "milestone": res})
1833
1833
 
@@ -1860,9 +1860,10 @@ def api_pm_note():
1860
1860
  return jsonify({"error": "id is required"}), 400
1861
1861
 
1862
1862
  if request.method == "PUT":
1863
- res = store.update_note(note_id, **(data.get("fields") or {}))
1863
+ res = store.update_note(note_id, expected_rev=data.get("expected_rev"),
1864
+ **(data.get("fields") or {}))
1864
1865
  if "error" in res:
1865
- return jsonify(res), 400
1866
+ return jsonify(res), 409 if res.get("code") == "rev_conflict" else 400
1866
1867
  _pm_audit(resolved, "note", "update", res["id"])
1867
1868
  return jsonify({"updated": True, "note": res})
1868
1869
 
@@ -1898,6 +1899,105 @@ def api_pm_link():
1898
1899
  return jsonify({"task": res})
1899
1900
 
1900
1901
 
1902
+ @app.route("/api/projects/pm/events", methods=["GET"])
1903
+ def api_pm_events():
1904
+ """PM event history for one project, newest first.
1905
+ Query: path, entity?, id?, op?, limit?"""
1906
+ resolved, err = _pm_resolve((request.args.get("path") or "").strip())
1907
+ if err:
1908
+ return err
1909
+ try:
1910
+ limit = max(1, min(int(request.args.get("limit") or 50), 500))
1911
+ except ValueError:
1912
+ limit = 50
1913
+ return jsonify({"path": str(resolved), "events": _pm_store(resolved).history(
1914
+ entity=(request.args.get("entity") or None),
1915
+ item_id=(request.args.get("id") or None),
1916
+ op=(request.args.get("op") or None),
1917
+ limit=limit)})
1918
+
1919
+
1920
+ @app.route("/api/projects/pm/deps", methods=["POST"])
1921
+ def api_pm_deps():
1922
+ """Add/remove a blocked-by dependency: {path, id, blocker, op: add|remove}."""
1923
+ data = request.get_json(force=True) or {}
1924
+ resolved, err = _pm_resolve((data.get("path") or "").strip())
1925
+ if err:
1926
+ return err
1927
+ task_id = (data.get("id") or "").strip()
1928
+ blocker = (data.get("blocker") or "").strip()
1929
+ op = (data.get("op") or "add").strip()
1930
+ if not task_id or not blocker:
1931
+ return jsonify({"error": "id and blocker are required"}), 400
1932
+ if op not in ("add", "remove"):
1933
+ return jsonify({"error": "op must be add|remove"}), 400
1934
+ store = _pm_store(resolved)
1935
+ res = (store.add_dependency(task_id, blocker, actor="hub") if op == "add"
1936
+ else store.remove_dependency(task_id, blocker, actor="hub"))
1937
+ if "error" in res:
1938
+ return jsonify(res), 400
1939
+ _pm_audit(resolved, "deps", op, res["id"])
1940
+ return jsonify({"task": res})
1941
+
1942
+
1943
+ @app.route("/api/projects/pm/report", methods=["GET"])
1944
+ def api_pm_report():
1945
+ """PM health report for one project. Query: path"""
1946
+ resolved, err = _pm_resolve((request.args.get("path") or "").strip())
1947
+ if err:
1948
+ return err
1949
+ return jsonify({"path": str(resolved),
1950
+ "report": _pm_store(resolved).report()})
1951
+
1952
+
1953
+ def _time_tracker(path):
1954
+ from services.time_tracker import TimeTracker
1955
+ return TimeTracker(str(path))
1956
+
1957
+
1958
+ @app.route("/api/projects/time", methods=["GET"])
1959
+ def api_projects_time():
1960
+ """Time summary for one project. Query: path"""
1961
+ resolved, err = _pm_resolve((request.args.get("path") or "").strip())
1962
+ if err:
1963
+ return err
1964
+ tracker = _time_tracker(resolved)
1965
+ return jsonify({"path": str(resolved), "summary": tracker.summary(),
1966
+ "sessions": tracker.sessions()[:20],
1967
+ "entries": tracker.list_entries(limit=100)})
1968
+
1969
+
1970
+ @app.route("/api/projects/time/entry", methods=["POST", "PUT", "DELETE"])
1971
+ def api_projects_time_entry():
1972
+ data = request.get_json(force=True) or {}
1973
+ resolved, err = _pm_resolve((data.get("path") or "").strip())
1974
+ if err:
1975
+ return err
1976
+ tracker = _time_tracker(resolved)
1977
+ if request.method == "POST":
1978
+ res = tracker.add_entry(data.get("minutes"), note=data.get("note", ""),
1979
+ date=data.get("date") or None,
1980
+ task_id=data.get("task_id"), created_by="hub")
1981
+ if "error" in res:
1982
+ return jsonify(res), 400
1983
+ _pm_audit(resolved, "time", "create", res["id"])
1984
+ return jsonify({"created": True, "entry": res}), 201
1985
+ entry_id = (data.get("id") or "").strip()
1986
+ if not entry_id:
1987
+ return jsonify({"error": "id is required"}), 400
1988
+ if request.method == "PUT":
1989
+ res = tracker.update_entry(entry_id, **(data.get("fields") or {}))
1990
+ if "error" in res:
1991
+ return jsonify(res), 400
1992
+ _pm_audit(resolved, "time", "update", res["id"])
1993
+ return jsonify({"updated": True, "entry": res})
1994
+ res = tracker.delete_entry(entry_id)
1995
+ if "error" in res:
1996
+ return jsonify(res), 400
1997
+ _pm_audit(resolved, "time", "delete", entry_id)
1998
+ return jsonify(res)
1999
+
2000
+
1901
2001
  # ── Agent artifacts: config tracking (v2.46.0) ────────────────────────────
1902
2002
  # Direct ArtifactStore per request (load-per-op store — no runtime build
1903
2003
  # needed); every mutation audited to the target project's activity log.
@@ -2012,24 +2112,47 @@ def api_pm_global():
2012
2112
  skipped.append({"path": ppath, "reason": "not initialized"})
2013
2113
  continue
2014
2114
  try:
2015
- if status_filter and status_filter != "all":
2016
- rows = TaskStore(ppath).list_tasks(status=status_filter, limit=1000)
2017
- else:
2018
- rows = TaskStore(ppath).list_tasks(limit=1000)
2019
- if status_filter != "all":
2020
- rows = [t for t in rows if t.get("status") != "done"]
2115
+ everything = TaskStore(ppath).list_tasks(include_archived=True,
2116
+ limit=1000)
2021
2117
  except Exception as e:
2022
2118
  skipped.append({"path": ppath, "reason": str(e)})
2023
2119
  continue
2120
+ active = [t for t in everything
2121
+ if t.get("lifecycle", "active") == "active"]
2122
+ if status_filter and status_filter != "all":
2123
+ rows = [t for t in active if t.get("status") == status_filter]
2124
+ elif status_filter == "all":
2125
+ rows = active
2126
+ else:
2127
+ rows = [t for t in active if t.get("status") != "done"]
2024
2128
  if not rows:
2025
2129
  continue
2130
+ # Resolve blockers server-side: the aggregate ships only open tasks,
2131
+ # so clients cannot tell a done/archived blocker from an open one.
2132
+ by_id = {t["id"]: t for t in everything}
2133
+ for t in rows:
2134
+ deps = t.get("blocked_by") or []
2135
+ if not deps:
2136
+ continue
2137
+ found = [(d, by_id.get(d)) for d in deps]
2138
+ t["blockers_open"] = sum(
2139
+ 1 for _, b in found
2140
+ if b is not None and b.get("lifecycle", "active") == "active"
2141
+ and b.get("status") != "done")
2142
+ t["blocker_titles"] = [
2143
+ (b.get("title") or d[:8]) if b is not None else d[:8]
2144
+ for d, b in found]
2026
2145
  proj_info = {"name": p.get("name"), "path": ppath}
2027
2146
  if p.get("parent_path"):
2028
2147
  proj_info["parent_path"] = p["parent_path"]
2029
2148
  for t in rows:
2030
2149
  t["project"] = proj_info
2031
2150
  tasks.extend(rows)
2032
- by_project[ppath] = {"name": p.get("name"), "open": len(rows)}
2151
+ by_project[ppath] = {
2152
+ "name": p.get("name"),
2153
+ "open": sum(1 for t in rows if t.get("status") != "done"),
2154
+ "shown": len(rows),
2155
+ }
2033
2156
 
2034
2157
  # priority asc, due asc, updated desc (stable two-stage sort)
2035
2158
  tasks.sort(key=lambda t: t.get("updated_at") or "", reverse=True)
@@ -6,7 +6,9 @@
6
6
  // Shared PM primitives (PM_COLUMNS, PriorityDot, DueBadge, statusColor,
7
7
  // ...) come from ui/pm_shared.js, loaded earlier in the bundle.
8
8
 
9
- function TaskBoardCard({ task, mode, colTasks, index, onMoveStatus, onMoveRank, onOpenProject }) {
9
+ let _pmDragTask = null; // active drag payload (module-level; no useRef in this bundle)
10
+
11
+ function TaskBoardCard({ task, mode, colTasks, index, onMoveStatus, onMoveRank, onOpenProject, onMoveTo, onDropOnCard, byId }) {
10
12
  const keys = PM_COLUMNS.map(c => c[0]);
11
13
  const ci = keys.indexOf(task.status);
12
14
  const navBtn = (label, title, onClick) => (
@@ -18,10 +20,15 @@ function TaskBoardCard({ task, mode, colTasks, index, onMoveStatus, onMoveRank,
18
20
  }}>{label}</button>
19
21
  );
20
22
  return (
21
- <div className="fade-up" style={{
22
- background: T.surface, border: `1px solid ${T.border}`, borderRadius: 8,
23
- padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 6,
24
- }}>
23
+ <div className="fade-up" draggable
24
+ onDragStart={() => { _pmDragTask = task; }}
25
+ onDragOver={e => e.preventDefault()}
26
+ onDrop={e => { e.preventDefault(); e.stopPropagation(); onDropOnCard(task); }}
27
+ style={{
28
+ background: T.surface, border: `1px solid ${T.border}`, borderRadius: 8,
29
+ padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 6,
30
+ cursor: 'grab',
31
+ }}>
25
32
  <div style={{ display: 'flex', alignItems: 'flex-start', gap: 6 }}>
26
33
  <span style={{ marginTop: 3, display: 'inline-flex', flexShrink: 0 }}>
27
34
  <PriorityDot priority={task.priority} />
@@ -37,10 +44,13 @@ function TaskBoardCard({ task, mode, colTasks, index, onMoveStatus, onMoveRank,
37
44
  <Badge color={T.purple}>{task.project.name || 'project'}</Badge>
38
45
  </span>
39
46
  )}
47
+ <DepsBadge task={task} byId={byId} />
40
48
  <DueBadge task={task} />
41
49
  {(task.tags || []).map(tag => <Badge key={tag} color={T.blue}>{tag}</Badge>)}
42
50
  </div>
43
51
  <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
52
+ {isTaskReady(task, byId) &&
53
+ navBtn('✓', 'Unblocked — move to Backlog', () => onMoveTo(task, 'backlog'))}
44
54
  {ci > 0 && navBtn('◀', `Move to ${PM_COLUMNS[ci - 1][1]}`, () => onMoveStatus(task, -1))}
45
55
  {ci >= 0 && ci < keys.length - 1 &&
46
56
  navBtn('▶', `Move to ${PM_COLUMNS[ci + 1][1]}`, () => onMoveStatus(task, +1))}
@@ -61,6 +71,7 @@ function TaskBoard({ projects, onOpenDrill }) {
61
71
  const [msFilter, setMsFilter] = useState(''); // milestone id ('' = all)
62
72
  const [err, setErr] = useState('');
63
73
  const [newTitle, setNewTitle] = useState('');
74
+ const [timeData, setTimeData] = useState(null);
64
75
 
65
76
  const mode = scope === 'global' ? 'global' : 'project';
66
77
 
@@ -72,6 +83,10 @@ function TaskBoard({ projects, onOpenDrill }) {
72
83
  const q = msFilter ? `&milestone=${encodeURIComponent(msFilter)}` : '';
73
84
  const b = await api.get(`/api/projects/pm?path=${encodeURIComponent(scope)}${q}`);
74
85
  setBoardData(b);
86
+ try {
87
+ setTimeData(await api.get(
88
+ `/api/projects/time?path=${encodeURIComponent(scope)}`));
89
+ } catch (e2) { setTimeData(null); }
75
90
  }
76
91
  setErr('');
77
92
  } catch (e) {
@@ -85,7 +100,7 @@ function TaskBoard({ projects, onOpenDrill }) {
85
100
  }, [scope, msFilter]);
86
101
 
87
102
  // Reset per-project state when the scope changes (avoid stale board flash).
88
- useEffect(() => { setBoardData(null); setMsFilter(''); setNewTitle(''); setErr(''); }, [scope]);
103
+ useEffect(() => { setBoardData(null); setMsFilter(''); setNewTitle(''); setErr(''); setTimeData(null); }, [scope]);
89
104
  useEffect(() => { load(); }, [load]);
90
105
  usePoll(load, 10000);
91
106
 
@@ -98,10 +113,19 @@ function TaskBoard({ projects, onOpenDrill }) {
98
113
  const path = mode === 'global' ? (task.project && task.project.path) : scope;
99
114
  if (!path) return;
100
115
  try {
101
- await api.put('/api/projects/pm/task', { path, id: task.id, move: { status: next } });
116
+ const body = { path, id: task.id, move: { status: next } };
117
+ if (mode === 'project' && boardData && boardData.board && boardData.board.rev != null) {
118
+ body.expected_rev = boardData.board.rev;
119
+ }
120
+ await api.put('/api/projects/pm/task', body);
102
121
  notify(`Moved to ${PM_COLUMNS[ci + dir][1]}`);
103
122
  load();
104
- } catch (e) { notify(e.message || 'Move failed', 'err'); }
123
+ } catch (e) {
124
+ if (e && e.status === 409) {
125
+ notify('Board changed elsewhere — refreshed', 'err');
126
+ load();
127
+ } else { notify(e.message || 'Move failed', 'err'); }
128
+ }
105
129
  };
106
130
 
107
131
  const moveRank = async (task, colTasks, index, dir) => {
@@ -109,10 +133,47 @@ function TaskBoard({ projects, onOpenDrill }) {
109
133
  ? { before_id: colTasks[index - 1].id }
110
134
  : { after_id: colTasks[index + 1].id };
111
135
  try {
112
- await api.put('/api/projects/pm/task', { path: scope, id: task.id, move });
136
+ const body = { path: scope, id: task.id, move };
137
+ if (boardData && boardData.board && boardData.board.rev != null) {
138
+ body.expected_rev = boardData.board.rev;
139
+ }
140
+ await api.put('/api/projects/pm/task', body);
113
141
  notify(dir < 0 ? 'Moved up' : 'Moved down');
114
142
  load();
115
- } catch (e) { notify(e.message || 'Reorder failed', 'err'); }
143
+ } catch (e) {
144
+ if (e && e.status === 409) {
145
+ notify('Board changed elsewhere — refreshed', 'err');
146
+ load();
147
+ } else { notify(e.message || 'Reorder failed', 'err'); }
148
+ }
149
+ };
150
+
151
+ const moveTo = async (task, status) => {
152
+ const path = mode === 'global' ? (task.project && task.project.path) : scope;
153
+ if (!path || task.status === status) return;
154
+ try {
155
+ await api.put('/api/projects/pm/task', { path, id: task.id, move: { status } });
156
+ notify(`Moved to ${(PM_COLUMNS.find(c => c[0] === status) || [status, status])[1]}`);
157
+ load();
158
+ } catch (e) { notify(e.message || 'Move failed', 'err'); }
159
+ };
160
+
161
+ const dropOnColumn = (key) => {
162
+ const t = _pmDragTask; _pmDragTask = null;
163
+ if (!t) return;
164
+ moveTo(t, key);
165
+ };
166
+
167
+ const dropOnCard = (target) => {
168
+ const t = _pmDragTask; _pmDragTask = null;
169
+ if (!t || t.id === target.id) return;
170
+ if (mode !== 'project') { moveTo(t, target.status); return; }
171
+ const move = t.status === target.status
172
+ ? { before_id: target.id }
173
+ : { status: target.status, before_id: target.id };
174
+ api.put('/api/projects/pm/task', { path: scope, id: t.id, move })
175
+ .then(() => { notify('Moved'); load(); })
176
+ .catch(e => notify(e.message || 'Move failed', 'err'));
116
177
  };
117
178
 
118
179
  const addTask = async () => {
@@ -145,7 +206,11 @@ function TaskBoard({ projects, onOpenDrill }) {
145
206
  const cols = (((boardData || {}).board) || {}).columns || {};
146
207
  PM_COLUMNS.forEach(([k]) => { columns[k] = cols[k] || []; });
147
208
  }
209
+ const boardById = {};
210
+ Object.values(columns).forEach(list =>
211
+ list.forEach(t => { boardById[t.id] = t; }));
148
212
  const milestones = (((boardData || {}).board) || {}).milestones || [];
213
+ const recovery = (((boardData || {}).board) || {}).recovery || null;
149
214
  const byProject = (globalData || {}).by_project || {};
150
215
  const projChips = Object.entries(byProject)
151
216
  .sort((a, b) => (b[1].open || 0) - (a[1].open || 0) ||
@@ -191,7 +256,16 @@ function TaskBoard({ projects, onOpenDrill }) {
191
256
  </div>
192
257
  )}
193
258
 
259
+ {mode === 'project' && timeData && timeData.summary && (
260
+ <div className="mono" style={{ fontSize: 11, color: T.textDim }}>
261
+ ⏱ {fmtMinutes(timeData.summary.today.total_min)} today
262
+ {' · '}{fmtMinutes(timeData.summary.last_7d.total_min)} last 7d
263
+ {' · '}{fmtMinutes(timeData.summary.last_30d.total_min)} last 30d
264
+ {' · '}{(timeData.entries || []).length} manual
265
+ </div>
266
+ )}
194
267
  {err && <div style={{ fontSize: 12, color: T.error }}>{err}</div>}
268
+ {mode === 'project' && <RecoveryBanner recovery={recovery} />}
195
269
 
196
270
  {/* Board */}
197
271
  {loadingBoard ? (
@@ -214,10 +288,13 @@ function TaskBoard({ projects, onOpenDrill }) {
214
288
  {hiddenDone ? '—' : colTasks.length}
215
289
  </span>
216
290
  </div>
217
- <div style={{
218
- display: 'flex', flexDirection: 'column', gap: 8,
219
- overflowY: 'auto', maxHeight: 'calc(100vh - 220px)', paddingRight: 2,
220
- }}>
291
+ <div
292
+ onDragOver={e => e.preventDefault()}
293
+ onDrop={e => { e.preventDefault(); dropOnColumn(key); }}
294
+ style={{
295
+ display: 'flex', flexDirection: 'column', gap: 8,
296
+ overflowY: 'auto', maxHeight: 'calc(100vh - 220px)', paddingRight: 2,
297
+ }}>
221
298
  {mode === 'project' && key === 'backlog' && (
222
299
  <div style={{ display: 'flex', gap: 6 }}>
223
300
  <input value={newTitle}
@@ -246,9 +323,10 @@ function TaskBoard({ projects, onOpenDrill }) {
246
323
  <React.Fragment>
247
324
  {colTasks.map((t, i) => (
248
325
  <TaskBoardCard key={t.id} task={t} mode={mode}
249
- colTasks={colTasks} index={i}
326
+ colTasks={colTasks} index={i} byId={boardById}
250
327
  onMoveStatus={moveStatus} onMoveRank={moveRank}
251
- onOpenProject={openProject} />
328
+ onOpenProject={openProject}
329
+ onMoveTo={moveTo} onDropOnCard={dropOnCard} />
252
330
  ))}
253
331
  {colTasks.length === 0 && !(mode === 'project' && key === 'backlog') && (
254
332
  <div style={{ fontSize: 11, color: T.textDim, fontStyle: 'italic', padding: '4px 2px' }}>
@@ -88,7 +88,7 @@ def _build_instructions(ide_name: str) -> str:
88
88
  " DISTILL terminal/log output >10 lines → c3_filter\n"
89
89
  " EXECUTE shell (tests/git/build) → c3_shell\n"
90
90
  " RECALL cross-session knowledge → c3_memory(action='recall') (index+fetch for large stores)\n"
91
- " TRACK durable tasks/milestones/decisions → c3_task\n"
91
+ " TRACK durable tasks/milestones/decisions + time → c3_task\n"
92
92
  " AGENT CONFIG inventory/history/diff/restore → c3_artifacts\n"
93
93
  " SNAPSHOT before /clear → c3_session(action='snapshot')\n"
94
94
  " HEALTH/budget checks → c3_status\n"
@@ -102,6 +102,13 @@ async def lifespan(server):
102
102
  """Initialize all services, auto-start session, start file watcher."""
103
103
  project = PROJECT_PATH
104
104
  services = build_runtime(project, ide_name=_IDE_NAME)
105
+ if getattr(services, "time_tracker", None) is not None:
106
+ try:
107
+ # The IDE loading the c3 MCP server marks the start of a work
108
+ # session for this project.
109
+ services.time_tracker.ping("startup")
110
+ except Exception:
111
+ pass
105
112
  transcript_index = TranscriptIndex(project)
106
113
  services.transcript_index = transcript_index
107
114
  snapshots = services.snapshots or ContextSnapshot(project)
@@ -367,6 +374,12 @@ def _finalize_response(ctx: Context, tool_name: str, args: dict,
367
374
 
368
375
  svc.session_mgr.log_tool_call(tool_name, args, summary)
369
376
  svc.activity_log.log("tool_call", {"tool": tool_name, "args": args, "result_summary": summary})
377
+ tracker = getattr(svc, "time_tracker", None)
378
+ if tracker is not None:
379
+ try:
380
+ tracker.ping("tool") # throttled heartbeat; idle gaps close sessions
381
+ except Exception:
382
+ pass
370
383
  svc.session_mgr.track_response(tool_name, response, response_tokens=response_tokens)
371
384
 
372
385
  hybrid_cfg = svc.hybrid_config or {}
@@ -846,15 +859,24 @@ async def c3_task(
846
859
  target_date: str = "",
847
860
  query: str = "",
848
861
  limit: int = 50,
862
+ parent: str = "",
863
+ minutes: int = 0,
849
864
  ctx: Context = None,
850
865
  ) -> str:
851
866
  """TRACK WORK — durable per-project tasks, milestones, and decision notes; use when asked to create/update/complete tasks (reads safe in plan mode).
852
- Tasks: add (title [+description/priority p0-p3/due_date YYYY-MM-DD/tags CSV/milestone]),
867
+ Tasks: add (title [+description/priority p0-p3/due_date YYYY-MM-DD/tags CSV/milestone/parent]),
853
868
  update (task_id + changed fields incl. status backlog|in_progress|blocked|done), done (task_id),
854
869
  list (filters: status/priority/tags/milestone/query), get, board (kanban columns + milestone progress), archive,
855
- link/unlink (task_id + link_type file|commit|edit + ref — ties tasks to code).
870
+ link/unlink (task_id + link_type file|commit|edit + ref — ties tasks to code),
871
+ block/unblock (task_id + ref=blocking task id; cycle-safe; completing the last open blocker auto-releases dependents to backlog),
872
+ report (overdue, blocked chains + aging, ready-to-unblock, milestone health/at-risk, throughput).
873
+ Subtasks: one level via parent (add/update; update parent='none' clears).
874
+ Time: time_add (minutes 1-1440 [+note/due_date=date/task_id]), time_update / time_delete (ref=entry id),
875
+ time_list (manual entries + recent auto sessions), time_summary (today/7d/30d, auto vs manual).
876
+ Auto-tracking: server startup + tool calls ping .c3/time; idle gaps >15min close a session.
856
877
  Milestones: milestone_add (name [+target_date]), milestone_update, milestone_list (with progress %), milestone_archive.
857
878
  Notes: note_add (note [+kind=decision] [+task_id]), note_list.
879
+ History: history ([+task_id] [+limit]) — append-only event log (who/what/when, before->after), newest first.
858
880
  task_id accepts any unique id prefix (>=4 chars); milestone accepts id or unique name.
859
881
  Ephemeral session plans stay in c3_session(action='plan')."""
860
882
  svc = _svc(ctx)
@@ -868,7 +890,8 @@ async def c3_task(
868
890
  title=title, task_id=task_id, status=status, priority=priority,
869
891
  due_date=due_date, tags=tags, description=description, milestone=milestone,
870
892
  note=note, kind=kind, link_type=link_type, ref=ref, label=label,
871
- name=name, target_date=target_date, query=query, limit=limit)
893
+ name=name, target_date=target_date, query=query, limit=limit,
894
+ parent=parent, minutes=minutes)
872
895
 
873
896
 
874
897
  @mcp.tool()