code-context-control 2.52.0__tar.gz → 2.54.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.
- {code_context_control-2.52.0 → code_context_control-2.54.0}/PKG-INFO +2 -2
- {code_context_control-2.52.0 → code_context_control-2.54.0}/README.md +1 -1
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/c3.py +54 -17
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/commands/parser.py +1 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_server.py +151 -26
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/task_board.js +94 -16
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/mcp_server.py +27 -4
- code_context_control-2.54.0/cli/progress.py +49 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/server.py +104 -16
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/tasks.py +182 -9
- code_context_control-2.54.0/cli/ui/components/tasks.js +821 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/pm_shared.js +72 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/code_context_control.egg-info/PKG-INFO +2 -2
- {code_context_control-2.52.0 → code_context_control-2.54.0}/code_context_control.egg-info/SOURCES.txt +5 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/pyproject.toml +1 -1
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/compressor.py +5 -13
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/doc_index.py +21 -17
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/embedding_index.py +49 -5
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/indexer.py +58 -21
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/protocol.py +37 -26
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/runtime.py +6 -0
- code_context_control-2.54.0/services/scanner.py +120 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/subprojects.py +3 -1
- code_context_control-2.54.0/services/task_store.py +1108 -0
- code_context_control-2.54.0/services/time_tracker.py +285 -0
- code_context_control-2.54.0/tests/test_init_scan.py +195 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_pm_api.py +91 -0
- code_context_control-2.54.0/tests/test_task_store.py +509 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_task_tool.py +3 -1
- code_context_control-2.54.0/tests/test_time_tracker.py +115 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_windows_reliability.py +2 -1
- code_context_control-2.52.0/cli/ui/components/tasks.js +0 -287
- code_context_control-2.52.0/services/task_store.py +0 -578
- code_context_control-2.52.0/tests/test_task_store.py +0 -232
- {code_context_control-2.52.0 → code_context_control-2.54.0}/LICENSE +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/commands/common.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/docs.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/edits.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/index.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_read.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/delegate.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/project.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/read.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/search.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/session.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/status.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/api.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/app.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/core/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/core/config.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/core/ide.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/core/web_security.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/config.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/oracle_server.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/app.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/settings.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/activity_log.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/agent_base.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/agents.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/artifact_store.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/auto_memory.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/claude_md.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/conversation_store.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/error_reporting.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/file_memory.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/git_context.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/hub_service.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory_graph.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory_queue.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/metrics.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/notifications.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/ollama_client.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/output_filter.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/parser.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/project_manager.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/project_runtime.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/proxy_state.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/retention.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/router.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/session_manager.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/session_preloader.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/telemetry.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/text_index.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/transcript_index.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/validation_cache.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/vector_store.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/watcher.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/setup.cfg +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_retention.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_validate.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/backend.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/main.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.52.0 → code_context_control-2.54.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.
|
|
3
|
+
Version: 2.54.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,
|
|
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,
|
|
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.
|
|
95
|
+
__version__ = "2.54.0"
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def _command_deps() -> CommandDeps:
|
|
@@ -165,6 +165,7 @@ def _build_init_config(project_path: str) -> dict:
|
|
|
165
165
|
"project_path": project_path,
|
|
166
166
|
"version": __version__,
|
|
167
167
|
"index_auto_update": True,
|
|
168
|
+
"index_max_files": 2000,
|
|
168
169
|
"compression_mode": "smart",
|
|
169
170
|
"mcp": {"mode": "direct"},
|
|
170
171
|
"hybrid": deepcopy(HYBRID_DEFAULTS),
|
|
@@ -890,7 +891,7 @@ def _parse_cli_ide_arg(value: str) -> str:
|
|
|
890
891
|
return normalized
|
|
891
892
|
|
|
892
893
|
|
|
893
|
-
def _do_init(project_path: str, ide_name: str = None):
|
|
894
|
+
def _do_init(project_path: str, ide_name: str = None, no_embed: bool = False):
|
|
894
895
|
"""Run the core init steps (shared by new install and re-init after clear/reset)."""
|
|
895
896
|
config = _build_init_config(project_path)
|
|
896
897
|
save_config(config, project_path)
|
|
@@ -898,10 +899,27 @@ def _do_init(project_path: str, ide_name: str = None):
|
|
|
898
899
|
for subdir in _C3_INIT_SUBDIRS:
|
|
899
900
|
(Path(project_path) / CONFIG_DIR / subdir).mkdir(parents=True, exist_ok=True)
|
|
900
901
|
|
|
902
|
+
import time as _t
|
|
903
|
+
|
|
904
|
+
from cli.progress import ProgressLine
|
|
905
|
+
|
|
901
906
|
print("Building code index...")
|
|
907
|
+
_prog = ProgressLine()
|
|
908
|
+
_t0 = _t.perf_counter()
|
|
902
909
|
indexer = CodeIndex(project_path)
|
|
903
|
-
result = indexer.build_index(
|
|
904
|
-
|
|
910
|
+
result = indexer.build_index(
|
|
911
|
+
on_progress=lambda entries, files, chunks: _prog.update(
|
|
912
|
+
f" scanning: {entries:,} entries | {files:,} files | {chunks:,} chunks"))
|
|
913
|
+
_prog.done()
|
|
914
|
+
entries = result.get("entries_scanned", 0)
|
|
915
|
+
print(f" Indexed {result['files_indexed']} files, {result['chunks_created']} chunks "
|
|
916
|
+
f"in {_t.perf_counter() - _t0:.1f}s ({entries:,} entries scanned)")
|
|
917
|
+
capped = result.get("files_capped", 0)
|
|
918
|
+
if capped:
|
|
919
|
+
total = result["files_indexed"] + capped
|
|
920
|
+
print(f" [!] Indexed {result['files_indexed']} of {total} candidate files "
|
|
921
|
+
f"(cap: index_max_files={result.get('max_files')}).")
|
|
922
|
+
print(" Raise index_max_files in .c3/config.json for full coverage.")
|
|
905
923
|
|
|
906
924
|
# Build embedding index if Ollama is available (non-blocking on failure)
|
|
907
925
|
try:
|
|
@@ -912,13 +930,24 @@ def _do_init(project_path: str, ide_name: str = None):
|
|
|
912
930
|
ollama = OllamaClient(ollama_url)
|
|
913
931
|
embed_model = config.get("embed_model", "nomic-embed-text")
|
|
914
932
|
ei = EmbeddingIndex(project_path, ollama, embed_model=embed_model)
|
|
915
|
-
if
|
|
933
|
+
if no_embed:
|
|
934
|
+
print(" Embedding index skipped (--no-embed)")
|
|
935
|
+
elif ei.probe()["ready"]:
|
|
936
|
+
# probe() initializes the lazy backends; checking .ready on a
|
|
937
|
+
# fresh instance is always False and silently skipped the build.
|
|
916
938
|
print("Building embedding index...")
|
|
917
|
-
|
|
939
|
+
_t0 = _t.perf_counter()
|
|
940
|
+
_eprog = ProgressLine()
|
|
941
|
+
ei_result = ei.build(
|
|
942
|
+
indexer,
|
|
943
|
+
on_progress=lambda done, total, chunks: _eprog.update(
|
|
944
|
+
f" embedding: file {done}/{total} | {chunks:,} chunks"))
|
|
945
|
+
_eprog.done()
|
|
918
946
|
print(f" Embedded {ei_result.get('chunks_embedded', 0)} chunks "
|
|
919
|
-
f"({ei_result.get('files_processed', 0)} files)"
|
|
947
|
+
f"({ei_result.get('files_processed', 0)} files) "
|
|
948
|
+
f"in {_t.perf_counter() - _t0:.1f}s")
|
|
920
949
|
else:
|
|
921
|
-
print(" Embedding index skipped (
|
|
950
|
+
print(f" Embedding index skipped ({ei.unavailable_reason()})")
|
|
922
951
|
except Exception:
|
|
923
952
|
_log.debug("Embedding index build failed", exc_info=True)
|
|
924
953
|
|
|
@@ -926,15 +955,22 @@ def _do_init(project_path: str, ide_name: str = None):
|
|
|
926
955
|
try:
|
|
927
956
|
from services.doc_index import DocIndex
|
|
928
957
|
print("Building doc index...")
|
|
958
|
+
_t0 = _t.perf_counter()
|
|
959
|
+
_dprog = ProgressLine()
|
|
929
960
|
di = DocIndex(project_path)
|
|
930
|
-
di_result = di.build(
|
|
931
|
-
|
|
961
|
+
di_result = di.build(
|
|
962
|
+
on_progress=lambda done, total: _dprog.update(
|
|
963
|
+
f" docs: {done}/{total}"))
|
|
964
|
+
_dprog.done()
|
|
965
|
+
print(f" Indexed {di_result['docs_indexed']} docs, "
|
|
966
|
+
f"{di_result['chunks_created']} chunks in {_t.perf_counter() - _t0:.1f}s")
|
|
932
967
|
except Exception:
|
|
933
968
|
_log.debug("Doc index build failed", exc_info=True)
|
|
934
969
|
|
|
935
970
|
print("Building compression dictionary...")
|
|
936
971
|
protocol = CompressionProtocol(project_path)
|
|
937
|
-
|
|
972
|
+
# Mine the in-memory index instead of re-reading the whole tree.
|
|
973
|
+
new_terms = protocol.build_project_dictionary(code_index=indexer)
|
|
938
974
|
print(f" Added {len(new_terms)} project-specific terms")
|
|
939
975
|
|
|
940
976
|
from core.ide import detect_ide, load_ide_config
|
|
@@ -966,11 +1002,12 @@ def cmd_init(args):
|
|
|
966
1002
|
if requested_ide != "auto":
|
|
967
1003
|
requested_ide = normalize_ide_name(requested_ide)
|
|
968
1004
|
git_requested = getattr(args, "git", False)
|
|
1005
|
+
no_embed = bool(getattr(args, "no_embed", False))
|
|
969
1006
|
|
|
970
1007
|
# ── Brand-new install ──────────────────────────────────────
|
|
971
1008
|
if not c3_dir.exists() or not (c3_dir / "config.json").exists():
|
|
972
1009
|
print_header(f"Initializing C3 for: {project_path}")
|
|
973
|
-
_do_init(project_path, ide_name=requested_ide)
|
|
1010
|
+
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
974
1011
|
try:
|
|
975
1012
|
from services.project_manager import ProjectManager
|
|
976
1013
|
ProjectManager().add_project(project_path)
|
|
@@ -1123,7 +1160,7 @@ def cmd_init(args):
|
|
|
1123
1160
|
ti_manifest = c3_dir / "transcript_index" / "manifest.json"
|
|
1124
1161
|
if ti_manifest.exists():
|
|
1125
1162
|
ti_manifest.write_text("{}", encoding="utf-8")
|
|
1126
|
-
_do_init(project_path, ide_name=requested_ide)
|
|
1163
|
+
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
1127
1164
|
if git_requested:
|
|
1128
1165
|
_init_local_git_repo(project_path)
|
|
1129
1166
|
_run_install_mcp(project_path, requested_ide, mcp_mode=getattr(args, "mcp_mode", "direct"), banner="Updating MCP tools...")
|
|
@@ -1152,7 +1189,7 @@ def cmd_init(args):
|
|
|
1152
1189
|
# ── Non-interactive (--force) ──────────────────────────────
|
|
1153
1190
|
if getattr(args, "force", False):
|
|
1154
1191
|
print("\n[--force] Applying update...")
|
|
1155
|
-
_do_init(project_path, ide_name=requested_ide)
|
|
1192
|
+
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
1156
1193
|
if git_requested:
|
|
1157
1194
|
_init_local_git_repo(project_path)
|
|
1158
1195
|
_run_install_mcp(project_path, requested_ide, mcp_mode=getattr(args, "mcp_mode", "direct"), banner="Updating MCP tools...")
|
|
@@ -1176,7 +1213,7 @@ def cmd_init(args):
|
|
|
1176
1213
|
|
|
1177
1214
|
if selected.startswith("Update"):
|
|
1178
1215
|
print()
|
|
1179
|
-
_do_init(project_path, ide_name=requested_ide)
|
|
1216
|
+
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
1180
1217
|
if git_requested:
|
|
1181
1218
|
_init_local_git_repo(project_path)
|
|
1182
1219
|
_prompt_install_mcp(project_path, requested_ide, default_mode=getattr(args, "mcp_mode", "direct"), banner="Updating MCP tools...")
|
|
@@ -1192,7 +1229,7 @@ def cmd_init(args):
|
|
|
1192
1229
|
shutil.rmtree(target)
|
|
1193
1230
|
print(f" Removed .c3/{subdir}/")
|
|
1194
1231
|
print()
|
|
1195
|
-
_do_init(project_path, ide_name=requested_ide)
|
|
1232
|
+
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
1196
1233
|
if git_requested:
|
|
1197
1234
|
_init_local_git_repo(project_path)
|
|
1198
1235
|
_prompt_install_mcp(project_path, requested_ide, default_mode=getattr(args, "mcp_mode", "direct"), banner="Updating MCP tools...")
|
|
@@ -1209,7 +1246,7 @@ def cmd_init(args):
|
|
|
1209
1246
|
shutil.rmtree(c3_dir)
|
|
1210
1247
|
print(" Deleted .c3/")
|
|
1211
1248
|
print()
|
|
1212
|
-
_do_init(project_path, ide_name=requested_ide)
|
|
1249
|
+
_do_init(project_path, ide_name=requested_ide, no_embed=no_embed)
|
|
1213
1250
|
if git_requested:
|
|
1214
1251
|
_init_local_git_repo(project_path)
|
|
1215
1252
|
_prompt_install_mcp(project_path, requested_ide, default_mode=getattr(args, "mcp_mode", "direct"), banner="Re-installing MCP tools...")
|
|
@@ -24,6 +24,7 @@ def build_parser(version: str, parse_cli_ide_arg):
|
|
|
24
24
|
p_init.add_argument("--ide", default="auto", type=parse_cli_ide_arg, metavar="{auto,claude,vscode,cursor,codex,antigravity}", help="Target IDE for MCP config (default: auto-detect)")
|
|
25
25
|
p_init.add_argument("--mcp-mode", choices=["direct", "proxy"], default="direct", help="Default MCP mode if install is selected during init (default: direct)")
|
|
26
26
|
p_init.add_argument("--git", action="store_true", help="Initialize a local Git repository during init/update")
|
|
27
|
+
p_init.add_argument("--no-embed", action="store_true", help="Skip building the semantic embedding index during init")
|
|
27
28
|
p_init.add_argument("--permissions", choices=["read-only", "c3-strict", "standard", "permissive"], default=None, help="Apply Claude Code permission tier (Claude Code only, used with --force)")
|
|
28
29
|
p_init.add_argument("--include-mcp-wildcard", action="store_true", help="Add mcp__* wildcard so non-C3 MCP servers don't prompt per-call")
|
|
29
30
|
|
|
@@ -1027,8 +1027,10 @@ def api_run_component():
|
|
|
1027
1027
|
ollama = OllamaClient(ollama_url)
|
|
1028
1028
|
embed_model = cfg.get("embed_model", "nomic-embed-text")
|
|
1029
1029
|
ei = EmbeddingIndex(resolved, ollama, embed_model=embed_model)
|
|
1030
|
-
|
|
1031
|
-
|
|
1030
|
+
# probe() initializes lazy backends; a fresh instance's .ready
|
|
1031
|
+
# is always False and would skip the build unconditionally.
|
|
1032
|
+
if not ei.probe()["ready"]:
|
|
1033
|
+
return jsonify({"success": True, "output": f"Embedding index skipped ({ei.unavailable_reason()})."})
|
|
1032
1034
|
indexer = CodeIndex(resolved)
|
|
1033
1035
|
if not indexer.chunks:
|
|
1034
1036
|
indexer._load_index()
|
|
@@ -1770,21 +1772,20 @@ def api_pm_task():
|
|
|
1770
1772
|
task_id = (data.get("id") or "").strip()
|
|
1771
1773
|
if not task_id:
|
|
1772
1774
|
return jsonify({"error": "id is required"}), 400
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
if fields:
|
|
1776
|
-
res = store.update_task(task_id, **fields)
|
|
1775
|
+
if data.get("restore"):
|
|
1776
|
+
res = store.restore_task(task_id, actor="hub")
|
|
1777
1777
|
if "error" in res:
|
|
1778
1778
|
return jsonify(res), 400
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
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:
|
|
1779
|
+
_pm_audit(resolved, "task", "restore", res["id"])
|
|
1780
|
+
return jsonify({"updated": True, "task": res})
|
|
1781
|
+
if not (data.get("fields") or data.get("move")):
|
|
1787
1782
|
return jsonify({"error": "fields or move required"}), 400
|
|
1783
|
+
res = store.mutate_task(task_id, fields=data.get("fields"),
|
|
1784
|
+
move=data.get("move"),
|
|
1785
|
+
expected_rev=data.get("expected_rev"),
|
|
1786
|
+
actor="hub")
|
|
1787
|
+
if "error" in res:
|
|
1788
|
+
return jsonify(res), 409 if res.get("code") == "rev_conflict" else 400
|
|
1788
1789
|
_pm_audit(resolved, "task", "update", res["id"])
|
|
1789
1790
|
return jsonify({"updated": True, "task": res})
|
|
1790
1791
|
|
|
@@ -1796,7 +1797,7 @@ def api_pm_task():
|
|
|
1796
1797
|
task_id = (data.get("id") or "").strip()
|
|
1797
1798
|
if not task_id:
|
|
1798
1799
|
return jsonify({"error": "id is required"}), 400
|
|
1799
|
-
res = store.archive_task(task_id)
|
|
1800
|
+
res = store.archive_task(task_id, actor="hub")
|
|
1800
1801
|
if "error" in res:
|
|
1801
1802
|
return jsonify(res), 400
|
|
1802
1803
|
_pm_audit(resolved, "task", "archive", res["id"])
|
|
@@ -1825,9 +1826,10 @@ def api_pm_milestone():
|
|
|
1825
1826
|
return jsonify({"error": "id is required"}), 400
|
|
1826
1827
|
|
|
1827
1828
|
if request.method == "PUT":
|
|
1828
|
-
res = store.update_milestone(ms_id,
|
|
1829
|
+
res = store.update_milestone(ms_id, expected_rev=data.get("expected_rev"),
|
|
1830
|
+
**(data.get("fields") or {}))
|
|
1829
1831
|
if "error" in res:
|
|
1830
|
-
return jsonify(res), 400
|
|
1832
|
+
return jsonify(res), 409 if res.get("code") == "rev_conflict" else 400
|
|
1831
1833
|
_pm_audit(resolved, "milestone", "update", res["id"])
|
|
1832
1834
|
return jsonify({"updated": True, "milestone": res})
|
|
1833
1835
|
|
|
@@ -1860,9 +1862,10 @@ def api_pm_note():
|
|
|
1860
1862
|
return jsonify({"error": "id is required"}), 400
|
|
1861
1863
|
|
|
1862
1864
|
if request.method == "PUT":
|
|
1863
|
-
res = store.update_note(note_id,
|
|
1865
|
+
res = store.update_note(note_id, expected_rev=data.get("expected_rev"),
|
|
1866
|
+
**(data.get("fields") or {}))
|
|
1864
1867
|
if "error" in res:
|
|
1865
|
-
return jsonify(res), 400
|
|
1868
|
+
return jsonify(res), 409 if res.get("code") == "rev_conflict" else 400
|
|
1866
1869
|
_pm_audit(resolved, "note", "update", res["id"])
|
|
1867
1870
|
return jsonify({"updated": True, "note": res})
|
|
1868
1871
|
|
|
@@ -1898,6 +1901,105 @@ def api_pm_link():
|
|
|
1898
1901
|
return jsonify({"task": res})
|
|
1899
1902
|
|
|
1900
1903
|
|
|
1904
|
+
@app.route("/api/projects/pm/events", methods=["GET"])
|
|
1905
|
+
def api_pm_events():
|
|
1906
|
+
"""PM event history for one project, newest first.
|
|
1907
|
+
Query: path, entity?, id?, op?, limit?"""
|
|
1908
|
+
resolved, err = _pm_resolve((request.args.get("path") or "").strip())
|
|
1909
|
+
if err:
|
|
1910
|
+
return err
|
|
1911
|
+
try:
|
|
1912
|
+
limit = max(1, min(int(request.args.get("limit") or 50), 500))
|
|
1913
|
+
except ValueError:
|
|
1914
|
+
limit = 50
|
|
1915
|
+
return jsonify({"path": str(resolved), "events": _pm_store(resolved).history(
|
|
1916
|
+
entity=(request.args.get("entity") or None),
|
|
1917
|
+
item_id=(request.args.get("id") or None),
|
|
1918
|
+
op=(request.args.get("op") or None),
|
|
1919
|
+
limit=limit)})
|
|
1920
|
+
|
|
1921
|
+
|
|
1922
|
+
@app.route("/api/projects/pm/deps", methods=["POST"])
|
|
1923
|
+
def api_pm_deps():
|
|
1924
|
+
"""Add/remove a blocked-by dependency: {path, id, blocker, op: add|remove}."""
|
|
1925
|
+
data = request.get_json(force=True) or {}
|
|
1926
|
+
resolved, err = _pm_resolve((data.get("path") or "").strip())
|
|
1927
|
+
if err:
|
|
1928
|
+
return err
|
|
1929
|
+
task_id = (data.get("id") or "").strip()
|
|
1930
|
+
blocker = (data.get("blocker") or "").strip()
|
|
1931
|
+
op = (data.get("op") or "add").strip()
|
|
1932
|
+
if not task_id or not blocker:
|
|
1933
|
+
return jsonify({"error": "id and blocker are required"}), 400
|
|
1934
|
+
if op not in ("add", "remove"):
|
|
1935
|
+
return jsonify({"error": "op must be add|remove"}), 400
|
|
1936
|
+
store = _pm_store(resolved)
|
|
1937
|
+
res = (store.add_dependency(task_id, blocker, actor="hub") if op == "add"
|
|
1938
|
+
else store.remove_dependency(task_id, blocker, actor="hub"))
|
|
1939
|
+
if "error" in res:
|
|
1940
|
+
return jsonify(res), 400
|
|
1941
|
+
_pm_audit(resolved, "deps", op, res["id"])
|
|
1942
|
+
return jsonify({"task": res})
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
@app.route("/api/projects/pm/report", methods=["GET"])
|
|
1946
|
+
def api_pm_report():
|
|
1947
|
+
"""PM health report for one project. Query: path"""
|
|
1948
|
+
resolved, err = _pm_resolve((request.args.get("path") or "").strip())
|
|
1949
|
+
if err:
|
|
1950
|
+
return err
|
|
1951
|
+
return jsonify({"path": str(resolved),
|
|
1952
|
+
"report": _pm_store(resolved).report()})
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
def _time_tracker(path):
|
|
1956
|
+
from services.time_tracker import TimeTracker
|
|
1957
|
+
return TimeTracker(str(path))
|
|
1958
|
+
|
|
1959
|
+
|
|
1960
|
+
@app.route("/api/projects/time", methods=["GET"])
|
|
1961
|
+
def api_projects_time():
|
|
1962
|
+
"""Time summary for one project. Query: path"""
|
|
1963
|
+
resolved, err = _pm_resolve((request.args.get("path") or "").strip())
|
|
1964
|
+
if err:
|
|
1965
|
+
return err
|
|
1966
|
+
tracker = _time_tracker(resolved)
|
|
1967
|
+
return jsonify({"path": str(resolved), "summary": tracker.summary(),
|
|
1968
|
+
"sessions": tracker.sessions()[:20],
|
|
1969
|
+
"entries": tracker.list_entries(limit=100)})
|
|
1970
|
+
|
|
1971
|
+
|
|
1972
|
+
@app.route("/api/projects/time/entry", methods=["POST", "PUT", "DELETE"])
|
|
1973
|
+
def api_projects_time_entry():
|
|
1974
|
+
data = request.get_json(force=True) or {}
|
|
1975
|
+
resolved, err = _pm_resolve((data.get("path") or "").strip())
|
|
1976
|
+
if err:
|
|
1977
|
+
return err
|
|
1978
|
+
tracker = _time_tracker(resolved)
|
|
1979
|
+
if request.method == "POST":
|
|
1980
|
+
res = tracker.add_entry(data.get("minutes"), note=data.get("note", ""),
|
|
1981
|
+
date=data.get("date") or None,
|
|
1982
|
+
task_id=data.get("task_id"), created_by="hub")
|
|
1983
|
+
if "error" in res:
|
|
1984
|
+
return jsonify(res), 400
|
|
1985
|
+
_pm_audit(resolved, "time", "create", res["id"])
|
|
1986
|
+
return jsonify({"created": True, "entry": res}), 201
|
|
1987
|
+
entry_id = (data.get("id") or "").strip()
|
|
1988
|
+
if not entry_id:
|
|
1989
|
+
return jsonify({"error": "id is required"}), 400
|
|
1990
|
+
if request.method == "PUT":
|
|
1991
|
+
res = tracker.update_entry(entry_id, **(data.get("fields") or {}))
|
|
1992
|
+
if "error" in res:
|
|
1993
|
+
return jsonify(res), 400
|
|
1994
|
+
_pm_audit(resolved, "time", "update", res["id"])
|
|
1995
|
+
return jsonify({"updated": True, "entry": res})
|
|
1996
|
+
res = tracker.delete_entry(entry_id)
|
|
1997
|
+
if "error" in res:
|
|
1998
|
+
return jsonify(res), 400
|
|
1999
|
+
_pm_audit(resolved, "time", "delete", entry_id)
|
|
2000
|
+
return jsonify(res)
|
|
2001
|
+
|
|
2002
|
+
|
|
1901
2003
|
# ── Agent artifacts: config tracking (v2.46.0) ────────────────────────────
|
|
1902
2004
|
# Direct ArtifactStore per request (load-per-op store — no runtime build
|
|
1903
2005
|
# needed); every mutation audited to the target project's activity log.
|
|
@@ -2012,24 +2114,47 @@ def api_pm_global():
|
|
|
2012
2114
|
skipped.append({"path": ppath, "reason": "not initialized"})
|
|
2013
2115
|
continue
|
|
2014
2116
|
try:
|
|
2015
|
-
|
|
2016
|
-
|
|
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"]
|
|
2117
|
+
everything = TaskStore(ppath).list_tasks(include_archived=True,
|
|
2118
|
+
limit=1000)
|
|
2021
2119
|
except Exception as e:
|
|
2022
2120
|
skipped.append({"path": ppath, "reason": str(e)})
|
|
2023
2121
|
continue
|
|
2122
|
+
active = [t for t in everything
|
|
2123
|
+
if t.get("lifecycle", "active") == "active"]
|
|
2124
|
+
if status_filter and status_filter != "all":
|
|
2125
|
+
rows = [t for t in active if t.get("status") == status_filter]
|
|
2126
|
+
elif status_filter == "all":
|
|
2127
|
+
rows = active
|
|
2128
|
+
else:
|
|
2129
|
+
rows = [t for t in active if t.get("status") != "done"]
|
|
2024
2130
|
if not rows:
|
|
2025
2131
|
continue
|
|
2132
|
+
# Resolve blockers server-side: the aggregate ships only open tasks,
|
|
2133
|
+
# so clients cannot tell a done/archived blocker from an open one.
|
|
2134
|
+
by_id = {t["id"]: t for t in everything}
|
|
2135
|
+
for t in rows:
|
|
2136
|
+
deps = t.get("blocked_by") or []
|
|
2137
|
+
if not deps:
|
|
2138
|
+
continue
|
|
2139
|
+
found = [(d, by_id.get(d)) for d in deps]
|
|
2140
|
+
t["blockers_open"] = sum(
|
|
2141
|
+
1 for _, b in found
|
|
2142
|
+
if b is not None and b.get("lifecycle", "active") == "active"
|
|
2143
|
+
and b.get("status") != "done")
|
|
2144
|
+
t["blocker_titles"] = [
|
|
2145
|
+
(b.get("title") or d[:8]) if b is not None else d[:8]
|
|
2146
|
+
for d, b in found]
|
|
2026
2147
|
proj_info = {"name": p.get("name"), "path": ppath}
|
|
2027
2148
|
if p.get("parent_path"):
|
|
2028
2149
|
proj_info["parent_path"] = p["parent_path"]
|
|
2029
2150
|
for t in rows:
|
|
2030
2151
|
t["project"] = proj_info
|
|
2031
2152
|
tasks.extend(rows)
|
|
2032
|
-
by_project[ppath] = {
|
|
2153
|
+
by_project[ppath] = {
|
|
2154
|
+
"name": p.get("name"),
|
|
2155
|
+
"open": sum(1 for t in rows if t.get("status") != "done"),
|
|
2156
|
+
"shown": len(rows),
|
|
2157
|
+
}
|
|
2033
2158
|
|
|
2034
2159
|
# priority asc, due asc, updated desc (stable two-stage sort)
|
|
2035
2160
|
tasks.sort(key=lambda t: t.get("updated_at") or "", reverse=True)
|
{code_context_control-2.52.0 → code_context_control-2.54.0}/cli/hub_ui/components/task_board.js
RENAMED
|
@@ -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
|
-
|
|
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"
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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) {
|
|
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
|
-
|
|
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) {
|
|
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
|
|
218
|
-
|
|
219
|
-
|
|
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' }}>
|