devcouncil 0.3.1 → 0.4.1
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.
- package/README.md +92 -33
- package/package.json +6 -2
- package/packages/codeintel-grammars/hatch_build.py +43 -0
- package/packages/codeintel-grammars/pyproject.toml +16 -0
- package/packages/codeintel-grammars/src/devcouncil_codeintel_grammars/__init__.py +93 -0
- package/pyproject.toml +99 -4
- package/src/devcouncil/app/config.py +512 -20
- package/src/devcouncil/app/events.py +4 -23
- package/src/devcouncil/app/orchestrator.py +5 -0
- package/src/devcouncil/app/run_context.py +3 -3
- package/src/devcouncil/assets/__init__.py +4 -1
- package/src/devcouncil/assets/vendor/force-graph.min.js +5 -0
- package/src/devcouncil/campaign/__init__.py +71 -0
- package/src/devcouncil/campaign/bloom.py +137 -0
- package/src/devcouncil/campaign/dashboard.py +123 -0
- package/src/devcouncil/campaign/mailbox.py +305 -0
- package/src/devcouncil/campaign/notify.py +91 -0
- package/src/devcouncil/campaign/orchestrator.py +592 -0
- package/src/devcouncil/campaign/prompts/coordinator.md +29 -0
- package/src/devcouncil/campaign/prompts/director.md +21 -0
- package/src/devcouncil/campaign/prompts/protocol.md +46 -0
- package/src/devcouncil/campaign/prompts/reviewer.md +24 -0
- package/src/devcouncil/campaign/prompts/worker.md +24 -0
- package/src/devcouncil/campaign/roles.py +202 -0
- package/src/devcouncil/campaign/watcher.py +153 -0
- package/src/devcouncil/cli/commands/agents.py +24 -17
- package/src/devcouncil/cli/commands/artifacts.py +36 -27
- package/src/devcouncil/cli/commands/ast.py +12 -3
- package/src/devcouncil/cli/commands/baseline.py +21 -12
- package/src/devcouncil/cli/commands/boot.py +218 -0
- package/src/devcouncil/cli/commands/campaign.py +302 -0
- package/src/devcouncil/cli/commands/check.py +225 -12
- package/src/devcouncil/cli/commands/config.py +221 -74
- package/src/devcouncil/cli/commands/cost.py +137 -28
- package/src/devcouncil/cli/commands/dashboard.py +12 -4
- package/src/devcouncil/cli/commands/debug_cmd.py +249 -0
- package/src/devcouncil/cli/commands/design.py +27 -17
- package/src/devcouncil/cli/commands/doctor.py +790 -8
- package/src/devcouncil/cli/commands/evidence.py +41 -20
- package/src/devcouncil/cli/commands/export.py +73 -0
- package/src/devcouncil/cli/commands/gaps.py +175 -0
- package/src/devcouncil/cli/commands/gated_write.py +76 -0
- package/src/devcouncil/cli/commands/go.py +220 -68
- package/src/devcouncil/cli/commands/graph_cmd.py +1192 -0
- package/src/devcouncil/cli/commands/handoff.py +45 -34
- package/src/devcouncil/cli/commands/hook.py +630 -85
- package/src/devcouncil/cli/commands/init.py +89 -30
- package/src/devcouncil/cli/commands/integrate.py +296 -1385
- package/src/devcouncil/cli/commands/lease.py +120 -0
- package/src/devcouncil/cli/commands/logs.py +12 -5
- package/src/devcouncil/cli/commands/lsp.py +40 -5
- package/src/devcouncil/cli/commands/map.py +317 -74
- package/src/devcouncil/cli/commands/mcp_server.py +12 -2
- package/src/devcouncil/cli/commands/okf.py +44 -6
- package/src/devcouncil/cli/commands/plan.py +184 -69
- package/src/devcouncil/cli/commands/prompt.py +26 -17
- package/src/devcouncil/cli/commands/provenance.py +79 -0
- package/src/devcouncil/cli/commands/repair.py +60 -49
- package/src/devcouncil/cli/commands/report.py +148 -40
- package/src/devcouncil/cli/commands/requirements.py +104 -0
- package/src/devcouncil/cli/commands/reset_demo_state.py +13 -4
- package/src/devcouncil/cli/commands/rollback.py +46 -35
- package/src/devcouncil/cli/commands/run.py +173 -8
- package/src/devcouncil/cli/commands/runs.py +298 -68
- package/src/devcouncil/cli/commands/scaffold.py +33 -12
- package/src/devcouncil/cli/commands/semantic.py +29 -14
- package/src/devcouncil/cli/commands/setup.py +103 -93
- package/src/devcouncil/cli/commands/shell.py +51 -42
- package/src/devcouncil/cli/commands/show.py +56 -42
- package/src/devcouncil/cli/commands/skills.py +29 -20
- package/src/devcouncil/cli/commands/status.py +80 -67
- package/src/devcouncil/cli/commands/task_gate.py +295 -0
- package/src/devcouncil/cli/commands/tasks.py +248 -19
- package/src/devcouncil/cli/commands/trace.py +14 -8
- package/src/devcouncil/cli/commands/verify.py +33 -8
- package/src/devcouncil/cli/commands/version.py +14 -6
- package/src/devcouncil/cli/commands/watch.py +56 -40
- package/src/devcouncil/cli/commands/watch_fs.py +30 -19
- package/src/devcouncil/cli/commands/wiki.py +278 -0
- package/src/devcouncil/cli/main.py +58 -1
- package/src/devcouncil/codeintel/__init__.py +16 -0
- package/src/devcouncil/codeintel/build_control.py +429 -0
- package/src/devcouncil/codeintel/build_worker.py +78 -0
- package/src/devcouncil/codeintel/debug/__init__.py +17 -0
- package/src/devcouncil/codeintel/debug/broker.py +114 -0
- package/src/devcouncil/codeintel/debug/broker_client.py +61 -0
- package/src/devcouncil/codeintel/debug/consent.py +36 -0
- package/src/devcouncil/codeintel/debug/discovery.py +132 -0
- package/src/devcouncil/codeintel/debug/fingerprint.py +85 -0
- package/src/devcouncil/codeintel/debug/protocol.py +259 -0
- package/src/devcouncil/codeintel/debug/python_trace_runner.py +81 -0
- package/src/devcouncil/codeintel/debug/session.py +238 -0
- package/src/devcouncil/codeintel/debug/tracing.py +201 -0
- package/src/devcouncil/codeintel/languages/__init__.py +17 -0
- package/src/devcouncil/codeintel/languages/generic_extractor.py +236 -0
- package/src/devcouncil/codeintel/languages/registry.py +149 -0
- package/src/devcouncil/codeintel/languages/workers.py +245 -0
- package/src/devcouncil/codeintel/query/__init__.py +5 -0
- package/src/devcouncil/codeintel/query/engine.py +289 -0
- package/src/devcouncil/codeintel/resolution/__init__.py +6 -0
- package/src/devcouncil/codeintel/resolution/abstract_state.py +301 -0
- package/src/devcouncil/codeintel/resolution/frameworks/__init__.py +33 -0
- package/src/devcouncil/codeintel/resolution/frameworks/base.py +46 -0
- package/src/devcouncil/codeintel/resolution/frameworks/di.py +56 -0
- package/src/devcouncil/codeintel/resolution/frameworks/events.py +45 -0
- package/src/devcouncil/codeintel/resolution/frameworks/routes.py +88 -0
- package/src/devcouncil/codeintel/resolution/semantic.py +887 -0
- package/src/devcouncil/codeintel/service.py +104 -0
- package/src/devcouncil/codeintel/store/__init__.py +15 -0
- package/src/devcouncil/codeintel/store/sqlite.py +1565 -0
- package/src/devcouncil/codeintel/sync/__init__.py +19 -0
- package/src/devcouncil/codeintel/sync/coordinator.py +430 -0
- package/src/devcouncil/codeintel/sync/incremental.py +484 -0
- package/src/devcouncil/codeintel/sync/lease.py +96 -0
- package/src/devcouncil/codeintel/sync/scope.py +98 -0
- package/src/devcouncil/council/__init__.py +4 -0
- package/src/devcouncil/council/prompts/__init__.py +4 -0
- package/src/devcouncil/domain/checkpoint_refs.py +17 -0
- package/src/devcouncil/domain/evidence.py +1 -0
- package/src/devcouncil/domain/gap.py +10 -0
- package/src/devcouncil/domain/requirement.py +5 -1
- package/src/devcouncil/domain/task.py +43 -2
- package/src/devcouncil/execution/checkpoints.py +25 -31
- package/src/devcouncil/execution/context_builder.py +15 -44
- package/src/devcouncil/execution/fs_watcher.py +64 -0
- package/src/devcouncil/execution/gated_write.py +203 -0
- package/src/devcouncil/execution/handoff.py +2 -1
- package/src/devcouncil/execution/hook_policy.py +19 -5
- package/src/devcouncil/execution/lease_ops.py +177 -0
- package/src/devcouncil/execution/lease_validation.py +71 -0
- package/src/devcouncil/execution/patch.py +3 -0
- package/src/devcouncil/execution/permissions.py +1 -0
- package/src/devcouncil/execution/policy_engine.py +205 -10
- package/src/devcouncil/execution/prompt_builder.py +278 -33
- package/src/devcouncil/execution/run_trace.py +356 -0
- package/src/devcouncil/execution/shell_session.py +46 -5
- package/src/devcouncil/execution/stop_gate.py +746 -0
- package/src/devcouncil/execution/stop_gate_history.py +113 -0
- package/src/devcouncil/execution/stop_gate_state.py +54 -0
- package/src/devcouncil/execution/stop_gate_verify_cache.py +69 -0
- package/src/devcouncil/execution/task_gate_ops.py +590 -0
- package/src/devcouncil/execution/task_runner.py +19 -0
- package/src/devcouncil/executors/advisor_tool.py +315 -0
- package/src/devcouncil/executors/agent_registry.py +125 -17
- package/src/devcouncil/executors/claude_sdk.py +376 -0
- package/src/devcouncil/executors/coding_cli.py +724 -25
- package/src/devcouncil/executors/mini_swe.py +50 -8
- package/src/devcouncil/executors/native/agent.py +224 -19
- package/src/devcouncil/executors/openhands.py +50 -8
- package/src/devcouncil/executors/transient_retry.py +99 -0
- package/src/devcouncil/gating/checks/clean_git.py +5 -2
- package/src/devcouncil/gating/checks/planned_files_check.py +38 -11
- package/src/devcouncil/gating/checks/secret_scan_check.py +2 -2
- package/src/devcouncil/gating/policy.py +46 -2
- package/src/devcouncil/indexing/ast_matcher.py +41 -4
- package/src/devcouncil/indexing/graph/__init__.py +78 -0
- package/src/devcouncil/indexing/graph/api_routes.py +522 -0
- package/src/devcouncil/indexing/graph/build.py +862 -0
- package/src/devcouncil/indexing/graph/cache.py +329 -0
- package/src/devcouncil/indexing/graph/communities.py +28 -0
- package/src/devcouncil/indexing/graph/cypher.py +107 -0
- package/src/devcouncil/indexing/graph/embeddings.py +194 -0
- package/src/devcouncil/indexing/graph/export.py +381 -0
- package/src/devcouncil/indexing/graph/export_links.py +81 -0
- package/src/devcouncil/indexing/graph/extract_python.py +307 -0
- package/src/devcouncil/indexing/graph/extract_ts.py +1205 -0
- package/src/devcouncil/indexing/graph/intel.py +668 -0
- package/src/devcouncil/indexing/graph/liveness.py +992 -0
- package/src/devcouncil/indexing/graph/okf_export.py +65 -0
- package/src/devcouncil/indexing/graph/pdg/__init__.py +67 -0
- package/src/devcouncil/indexing/graph/pdg/build.py +11 -0
- package/src/devcouncil/indexing/graph/pdg/cdg.py +41 -0
- package/src/devcouncil/indexing/graph/pdg/cfg.py +199 -0
- package/src/devcouncil/indexing/graph/pdg/query.py +21 -0
- package/src/devcouncil/indexing/graph/pdg/reaching_def.py +126 -0
- package/src/devcouncil/indexing/graph/pdg/schema.py +253 -0
- package/src/devcouncil/indexing/graph/pdg/taint.py +154 -0
- package/src/devcouncil/indexing/graph/query.py +302 -0
- package/src/devcouncil/indexing/graph/resolve.py +1020 -0
- package/src/devcouncil/indexing/graph/schema.py +103 -0
- package/src/devcouncil/indexing/graph_index.py +20 -29
- package/src/devcouncil/indexing/lsp.py +57 -25
- package/src/devcouncil/indexing/lsp_client.py +577 -0
- package/src/devcouncil/indexing/map_artifacts.py +355 -0
- package/src/devcouncil/indexing/map_refresh.py +141 -0
- package/src/devcouncil/indexing/repo_mapper.py +1509 -138
- package/src/devcouncil/indexing/semantic_index.py +12 -6
- package/src/devcouncil/indexing/subsystem_map.py +163 -0
- package/src/devcouncil/indexing/ts_imports.py +343 -0
- package/src/devcouncil/indexing/viz.py +964 -0
- package/src/devcouncil/indexing/walk.py +52 -0
- package/src/devcouncil/indexing/wiring.py +1776 -0
- package/src/devcouncil/integrations/actions.py +27 -4
- package/src/devcouncil/integrations/check.py +211 -16
- package/src/devcouncil/integrations/claude_assets.py +209 -12
- package/src/devcouncil/integrations/clients/__init__.py +1 -0
- package/src/devcouncil/integrations/clients/aider.py +52 -0
- package/src/devcouncil/integrations/clients/antigravity.py +87 -0
- package/src/devcouncil/integrations/clients/claude.py +339 -0
- package/src/devcouncil/integrations/clients/codex.py +39 -0
- package/src/devcouncil/integrations/clients/common.py +332 -0
- package/src/devcouncil/integrations/clients/cursor.py +164 -0
- package/src/devcouncil/integrations/clients/gemini.py +49 -0
- package/src/devcouncil/integrations/clients/grok.py +105 -0
- package/src/devcouncil/integrations/clients/hooks.py +500 -0
- package/src/devcouncil/integrations/clients/opencode.py +96 -0
- package/src/devcouncil/integrations/clients/warp.py +75 -0
- package/src/devcouncil/integrations/code_review_graph.py +2 -2
- package/src/devcouncil/integrations/github.py +73 -7
- package/src/devcouncil/integrations/integration_cli.py +197 -0
- package/src/devcouncil/integrations/mcp/handlers/__init__.py +1 -0
- package/src/devcouncil/integrations/mcp/handlers/ast_lsp.py +77 -0
- package/src/devcouncil/integrations/mcp/handlers/checkout.py +50 -0
- package/src/devcouncil/integrations/mcp/handlers/cli_gate.py +43 -0
- package/src/devcouncil/integrations/mcp/handlers/codeintel.py +182 -0
- package/src/devcouncil/integrations/mcp/handlers/debug.py +236 -0
- package/src/devcouncil/integrations/mcp/handlers/evidence.py +70 -0
- package/src/devcouncil/integrations/mcp/handlers/git.py +281 -0
- package/src/devcouncil/integrations/mcp/handlers/graph.py +34 -0
- package/src/devcouncil/integrations/mcp/handlers/handoff.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/knowledge.py +28 -0
- package/src/devcouncil/integrations/mcp/handlers/lease.py +70 -0
- package/src/devcouncil/integrations/mcp/handlers/live.py +108 -0
- package/src/devcouncil/integrations/mcp/handlers/map.py +676 -0
- package/src/devcouncil/integrations/mcp/handlers/next_task.py +35 -0
- package/src/devcouncil/integrations/mcp/handlers/policy.py +80 -0
- package/src/devcouncil/integrations/mcp/handlers/prompts.py +168 -0
- package/src/devcouncil/integrations/mcp/handlers/provenance.py +87 -0
- package/src/devcouncil/integrations/mcp/handlers/read.py +103 -0
- package/src/devcouncil/integrations/mcp/handlers/router_cache.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/run.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/runs.py +69 -0
- package/src/devcouncil/integrations/mcp/handlers/scope.py +56 -0
- package/src/devcouncil/integrations/mcp/handlers/status.py +199 -0
- package/src/devcouncil/integrations/mcp/handlers/task.py +88 -0
- package/src/devcouncil/integrations/mcp/handlers/tool_specs.py +922 -0
- package/src/devcouncil/integrations/mcp/handlers/trace.py +65 -0
- package/src/devcouncil/integrations/mcp/handlers/verify.py +45 -0
- package/src/devcouncil/integrations/mcp/handlers/wiki.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/write.py +69 -0
- package/src/devcouncil/integrations/mcp/server.py +265 -2391
- package/src/devcouncil/integrations/mcp/util.py +325 -0
- package/src/devcouncil/integrations/setup.py +152 -0
- package/src/devcouncil/knowledge/fetch.py +4 -0
- package/src/devcouncil/knowledge/knowledge_select.py +38 -0
- package/src/devcouncil/knowledge/okf.py +2 -1
- package/src/devcouncil/knowledge/resource_discovery.py +40 -0
- package/src/devcouncil/knowledge/wiki.py +643 -0
- package/src/devcouncil/knowledge/wiki_read.py +87 -0
- package/src/devcouncil/live/cards.py +7 -7
- package/src/devcouncil/live/models.py +4 -1
- package/src/devcouncil/live/reviewer.py +90 -11
- package/src/devcouncil/live/signals.py +4 -2
- package/src/devcouncil/live/summary.py +21 -3
- package/src/devcouncil/live/tasks.py +12 -3
- package/src/devcouncil/live/transcripts.py +69 -2
- package/src/devcouncil/llm/cache.py +5 -6
- package/src/devcouncil/llm/model_defaults.yaml +10 -10
- package/src/devcouncil/llm/provider.py +647 -73
- package/src/devcouncil/llm/router.py +271 -46
- package/src/devcouncil/llm/semantic_bridge.py +614 -0
- package/src/devcouncil/optimization/gepa_agent.py +6 -4
- package/src/devcouncil/optimization/skillopt.py +9 -5
- package/src/devcouncil/planning/arbiter_service.py +12 -3
- package/src/devcouncil/planning/correction_manifest.py +107 -10
- package/src/devcouncil/planning/plan_difficulty.py +69 -0
- package/src/devcouncil/planning/plan_service.py +5 -2
- package/src/devcouncil/planning/planned_files_reconcile.py +191 -0
- package/src/devcouncil/planning/prompt_enhancer_service.py +6 -5
- package/src/devcouncil/planning/question_conversion.py +56 -0
- package/src/devcouncil/planning/spec_service.py +9 -3
- package/src/devcouncil/repo/ci_scaffold.py +197 -1
- package/src/devcouncil/repo/gitignore.py +1 -2
- package/src/devcouncil/reporting/evidence_export.py +124 -0
- package/src/devcouncil/reporting/evidence_html.py +210 -0
- package/src/devcouncil/reporting/json_report.py +16 -12
- package/src/devcouncil/reporting/markdown_report.py +38 -9
- package/src/devcouncil/reporting/mcp_resources.py +142 -0
- package/src/devcouncil/reporting/report_builder.py +40 -4
- package/src/devcouncil/reporting/task_provenance.py +42 -0
- package/src/devcouncil/reporting/verdict.py +75 -0
- package/src/devcouncil/skills/library/README.md +1 -0
- package/src/devcouncil/skills/library/devcouncil-hero-loop.md +109 -0
- package/src/devcouncil/skills/library/devcouncil-verification.md +109 -0
- package/src/devcouncil/skills/library/devcouncil.md +93 -0
- package/src/devcouncil/skills/registry.py +43 -12
- package/src/devcouncil/storage/db.py +57 -11
- package/src/devcouncil/storage/models.py +6 -0
- package/src/devcouncil/storage/native.py +5 -3
- package/src/devcouncil/storage/repositories.py +50 -18
- package/src/devcouncil/telemetry/context.py +28 -0
- package/src/devcouncil/telemetry/cost.py +4 -5
- package/src/devcouncil/telemetry/logging_setup.py +78 -11
- package/src/devcouncil/telemetry/model_pricing.yaml +7 -0
- package/src/devcouncil/telemetry/stages.py +27 -2
- package/src/devcouncil/telemetry/tracker.py +50 -13
- package/src/devcouncil/ui/dashboard.py +120 -8
- package/src/devcouncil/utils/fsio.py +58 -0
- package/src/devcouncil/utils/git_snapshot.py +112 -0
- package/src/devcouncil/utils/json_persist.py +53 -0
- package/src/devcouncil/utils/proc.py +89 -0
- package/src/devcouncil/verification/acceptance_compiler.py +36 -13
- package/src/devcouncil/verification/ad_hoc_check.py +95 -3
- package/src/devcouncil/verification/checks/__init__.py +41 -0
- package/src/devcouncil/verification/checks/acceptance.py +39 -0
- package/src/devcouncil/verification/checks/acceptance_corpus.py +194 -0
- package/src/devcouncil/verification/checks/acceptance_evidence.py +239 -0
- package/src/devcouncil/verification/checks/command_evidence.py +148 -0
- package/src/devcouncil/verification/checks/compiled_acceptance.py +179 -0
- package/src/devcouncil/verification/checks/corpus_stale.py +124 -0
- package/src/devcouncil/verification/checks/corpus_verification.py +9 -0
- package/src/devcouncil/verification/checks/dead_symbols.py +360 -0
- package/src/devcouncil/verification/checks/diff_coverage_gate.py +101 -0
- package/src/devcouncil/verification/checks/doc_code_ref.py +79 -0
- package/src/devcouncil/verification/checks/liveness_ratchet.py +336 -0
- package/src/devcouncil/verification/checks/orphan_diff.py +104 -0
- package/src/devcouncil/verification/checks/planned_files.py +98 -0
- package/src/devcouncil/verification/checks/semantic_diff.py +241 -0
- package/src/devcouncil/verification/checks/stale_map.py +80 -0
- package/src/devcouncil/verification/checks/stub_scan.py +71 -0
- package/src/devcouncil/verification/checks/subsystem_boundary.py +103 -0
- package/src/devcouncil/verification/checks/wiring.py +216 -0
- package/src/devcouncil/verification/claims/__init__.py +23 -0
- package/src/devcouncil/verification/claims/checks.py +395 -0
- package/src/devcouncil/verification/claims/mapper.py +168 -0
- package/src/devcouncil/verification/claims/models.py +39 -0
- package/src/devcouncil/verification/claims/transcript.py +92 -0
- package/src/devcouncil/verification/claims/verdict.py +88 -0
- package/src/devcouncil/verification/command_evidence.py +170 -0
- package/src/devcouncil/verification/command_malformation.py +147 -0
- package/src/devcouncil/verification/command_runner.py +164 -0
- package/src/devcouncil/verification/coverage_measurement.py +292 -0
- package/src/devcouncil/verification/diff_coverage.py +151 -0
- package/src/devcouncil/verification/difficulty.py +296 -0
- package/src/devcouncil/verification/effort_heuristics.py +178 -0
- package/src/devcouncil/verification/gap_ids.py +63 -0
- package/src/devcouncil/verification/gate_cache.py +194 -0
- package/src/devcouncil/verification/gate_selector.py +344 -0
- package/src/devcouncil/verification/git_diff_fallback.py +272 -0
- package/src/devcouncil/verification/implementation_reviewer.py +13 -0
- package/src/devcouncil/verification/incremental_check.py +241 -0
- package/src/devcouncil/verification/next_actions.py +60 -1
- package/src/devcouncil/verification/rigor_analytics.py +130 -0
- package/src/devcouncil/verification/sandbox.py +38 -11
- package/src/devcouncil/verification/stub_detector.py +369 -0
- package/src/devcouncil/verification/test_resolver.py +67 -1
- package/src/devcouncil/verification/verifier.py +137 -1666
- package/src/devcouncil/verification/verify_orchestration.py +610 -0
- package/src/devcouncil/verification/verify_setup.py +176 -0
- package/src/devcouncil/verification/wiki_refresh.py +208 -0
- package/src/semantic_layer/__init__.py +58 -0
- package/src/semantic_layer/benchmark.py +75 -0
- package/src/semantic_layer/cache.py +290 -0
- package/src/semantic_layer/compressor.py +137 -0
- package/src/semantic_layer/config.py +75 -0
- package/src/semantic_layer/embeddings.py +69 -0
- package/src/semantic_layer/llm_backends.py +99 -0
- package/src/semantic_layer/pipeline.py +111 -0
- package/src/semantic_layer/router.py +128 -0
- package/src/semantic_layer/tuner.py +72 -0
- package/uv.lock +973 -9
- package/src/devcouncil/artifacts/migrations.py +0 -20
- package/src/devcouncil/artifacts/schemas.py +0 -23
- package/src/devcouncil/artifacts/serializer.py +0 -21
- package/src/devcouncil/integrations/gitnexus.py +0 -70
- package/src/devcouncil/integrations/graphify.py +0 -34
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"""Repo-map artifact writers (JSON + agent guides) — indexing leaf, no CLI import.
|
|
2
|
+
|
|
3
|
+
CLI ``dev map``, init, wiki remap, and map_refresh all call into this module so
|
|
4
|
+
indexing/verification do not import ``cli.commands.map``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import sqlite3
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
|
|
16
|
+
from devcouncil.indexing.repo_mapper import RepoMap, RepoMapper
|
|
17
|
+
from devcouncil.indexing.graph.schema import CodeGraph
|
|
18
|
+
from devcouncil.integrations.code_review_graph import CodeReviewGraphAdapter
|
|
19
|
+
from devcouncil.utils.json_persist import write_model_json
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
status_console = Console(stderr=True)
|
|
23
|
+
|
|
24
|
+
AGENT_GUIDE_MARKER = "<!-- Managed by dev map: keep this file in sync with .devcouncil/repo_map.json. -->"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _stamp_existing_map_degraded(output: Path, *, reason: str) -> None:
|
|
28
|
+
"""Mark an on-disk repo map degraded without rewriting fingerprints as fresh."""
|
|
29
|
+
if not output.is_file():
|
|
30
|
+
return
|
|
31
|
+
try:
|
|
32
|
+
from devcouncil.utils.json_persist import read_json
|
|
33
|
+
|
|
34
|
+
repo_map = RepoMap.model_validate(read_json(output))
|
|
35
|
+
repo_map.graph_degraded = True
|
|
36
|
+
repo_map.graph_degraded_reason = reason[:500]
|
|
37
|
+
write_model_json(output, repo_map)
|
|
38
|
+
except Exception:
|
|
39
|
+
logger.debug("failed to stamp graph_degraded on partial map", exc_info=True)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class GraphRefreshResult:
|
|
44
|
+
repo_map: RepoMap
|
|
45
|
+
graph: CodeGraph | None
|
|
46
|
+
generation: int | None
|
|
47
|
+
mode: str
|
|
48
|
+
degraded: bool = False
|
|
49
|
+
reason: str = ""
|
|
50
|
+
# True when SQLite committed but code_graph.json export was skipped (size cap).
|
|
51
|
+
# Not fail-closed for sync/watch — doctor/export health owns this signal.
|
|
52
|
+
compatibility_export_degraded: bool = False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _important_surfaces(repo_map: RepoMap) -> list[str]:
|
|
56
|
+
"""Derive the 'important surfaces' list from the computed map."""
|
|
57
|
+
lines: list[str] = []
|
|
58
|
+
for index, subsystem in enumerate(repo_map.subsystems[:6], start=1):
|
|
59
|
+
lines.append(f"{index}. `{subsystem.area}/` — {subsystem.summary}")
|
|
60
|
+
if not lines:
|
|
61
|
+
for index, path in enumerate(repo_map.important_files[:6], start=1):
|
|
62
|
+
lines.append(f"{index}. `{path}`")
|
|
63
|
+
return lines or ["1. See `.devcouncil/repo_map.json` for the file index."]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _wiki_index_rel(repo_root: Path) -> str | None:
|
|
67
|
+
"""Relative path to the codebase wiki index, when a generated wiki exists."""
|
|
68
|
+
from devcouncil.knowledge.wiki import wiki_dir_for
|
|
69
|
+
|
|
70
|
+
index = wiki_dir_for(repo_root) / "index.md"
|
|
71
|
+
if not index.is_file():
|
|
72
|
+
return None
|
|
73
|
+
try:
|
|
74
|
+
return index.relative_to(repo_root).as_posix()
|
|
75
|
+
except ValueError:
|
|
76
|
+
return str(index)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def agent_guide_text(repo_map_path: Path, repo_root: Path, repo_map: RepoMap) -> str:
|
|
80
|
+
wiki_index = _wiki_index_rel(repo_root)
|
|
81
|
+
wiki_lines = (
|
|
82
|
+
[
|
|
83
|
+
"",
|
|
84
|
+
f"Codebase wiki: `{wiki_index}` — agent-facing subsystem docs (OKF bundle). "
|
|
85
|
+
"Read the relevant subsystem page before working in it; refresh with `dev wiki update`.",
|
|
86
|
+
]
|
|
87
|
+
if wiki_index
|
|
88
|
+
else []
|
|
89
|
+
)
|
|
90
|
+
return "\n".join(
|
|
91
|
+
[
|
|
92
|
+
AGENT_GUIDE_MARKER,
|
|
93
|
+
"",
|
|
94
|
+
"# Agent Workspace Guide",
|
|
95
|
+
"",
|
|
96
|
+
"Use `.devcouncil/repo_map.json` as the primary file index for this workspace.",
|
|
97
|
+
f"Repo map: `{repo_map_path.relative_to(repo_root).as_posix() if repo_map_path.is_relative_to(repo_root) else repo_map_path}`",
|
|
98
|
+
"Code graph: `.devcouncil/graph/code_graph.json` (symbol-level; query with `dev graph`).",
|
|
99
|
+
*wiki_lines,
|
|
100
|
+
"",
|
|
101
|
+
"Workflow for agents:",
|
|
102
|
+
"1. Open `.devcouncil/repo_map.json` before guessing at file locations.",
|
|
103
|
+
"2. Use the `files` list to resolve module ownership and nearby siblings.",
|
|
104
|
+
"3. Use `subsystems` for subsystem-level navigation.",
|
|
105
|
+
"4. In `subsystems`, use `entry_points` + `critical_files` for entry points and starting context.",
|
|
106
|
+
"5. Use `role_files` in `subsystems` for subsystem role buckets (entry, runtime, policy, adapters, etc.).",
|
|
107
|
+
"6. Use `neighbors` and `handoff_paths` in `subsystems` to follow cross-subsystem flow.",
|
|
108
|
+
"7. Prefer `dev graph dead --confidence extracted` + file greps for dead code. "
|
|
109
|
+
"Treat `inferred` as unconfirmed. Prefer `unwired_candidates` / "
|
|
110
|
+
"`dead_symbol_candidates` over `unreachable_files` (static BFS is often "
|
|
111
|
+
"noisy for routers / dynamic imports / JSX). If `entry_roots` are empty / "
|
|
112
|
+
"`liveness_unreachable_unreliable`, ignore `unreachable_files` and mass inferred dead. "
|
|
113
|
+
"Check `unwired_candidates` / `dead_symbol_candidates` before creating new modules — "
|
|
114
|
+
"wire what you create into a real caller.",
|
|
115
|
+
"8. Use `dev graph query <name>` / `dev graph trace <a> <b>` / `dev graph dead` "
|
|
116
|
+
"for symbol callers, paths, and dead-code tiers; `dev graph html` for the visualizer. "
|
|
117
|
+
"SQLite (`.devcouncil/codeintel/index.sqlite`) is canonical — prefer "
|
|
118
|
+
"`dev graph` commands when `code_graph.json` is missing or a size-capped stub.",
|
|
119
|
+
"9. Run `dev map` (or `dev map --watch` / `dev graph watch`) after large refactors.",
|
|
120
|
+
"",
|
|
121
|
+
"Important surfaces:",
|
|
122
|
+
*_important_surfaces(repo_map),
|
|
123
|
+
"",
|
|
124
|
+
"If the map and source disagree, trust the source and regenerate the map.",
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def write_agent_guides(repo_root: Path, repo_map_path: Path, repo_map: RepoMap) -> None:
|
|
130
|
+
for filename in ("AGENTS.md", "CLAUDE.md"):
|
|
131
|
+
path = repo_root / filename
|
|
132
|
+
existing: str | None = None
|
|
133
|
+
if path.exists():
|
|
134
|
+
try:
|
|
135
|
+
existing = path.read_text(encoding="utf-8", errors="replace")
|
|
136
|
+
except OSError:
|
|
137
|
+
continue
|
|
138
|
+
if AGENT_GUIDE_MARKER not in existing:
|
|
139
|
+
continue
|
|
140
|
+
text = agent_guide_text(repo_map_path, repo_root, repo_map) + "\n"
|
|
141
|
+
# Skip rewrite when unchanged so content_fingerprint (size+mtime) stays stable
|
|
142
|
+
# across consecutive identical `dev map` runs.
|
|
143
|
+
if existing == text:
|
|
144
|
+
continue
|
|
145
|
+
path.write_text(text, encoding="utf-8")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def generate_map_artifacts(
|
|
149
|
+
root: Path,
|
|
150
|
+
output: Path,
|
|
151
|
+
goal: str = "",
|
|
152
|
+
*,
|
|
153
|
+
scan_dependencies: bool = False,
|
|
154
|
+
liveness: bool = True,
|
|
155
|
+
lsp_refs: bool = False,
|
|
156
|
+
quiet: bool = False,
|
|
157
|
+
graph=None, # noqa: ANN001
|
|
158
|
+
paths: list[str] | None = None,
|
|
159
|
+
) -> RepoMap:
|
|
160
|
+
"""Build the repo map and write repo_map.json + agent guides (no LLM, no re-init).
|
|
161
|
+
|
|
162
|
+
Assumes ``.devcouncil/`` already exists. Shared by the ``dev map`` command and
|
|
163
|
+
by project initialization so a freshly set-up repo is immediately navigable.
|
|
164
|
+
``scan_dependencies`` is opt-in (off for init and default mapping) because it can
|
|
165
|
+
shell out to dependency auditors.
|
|
166
|
+
``lsp_refs`` opts into live LSP confirmation of dead-symbol candidates.
|
|
167
|
+
``quiet`` suppresses stderr status lines (for ``dev status --json`` auto-init).
|
|
168
|
+
"""
|
|
169
|
+
return refresh_map_artifacts(
|
|
170
|
+
root,
|
|
171
|
+
output,
|
|
172
|
+
goal,
|
|
173
|
+
scan_dependencies=scan_dependencies,
|
|
174
|
+
liveness=liveness,
|
|
175
|
+
lsp_refs=lsp_refs,
|
|
176
|
+
quiet=quiet,
|
|
177
|
+
graph=graph,
|
|
178
|
+
paths=paths,
|
|
179
|
+
).repo_map
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def refresh_map_artifacts(
|
|
183
|
+
root: Path,
|
|
184
|
+
output: Path,
|
|
185
|
+
goal: str = "",
|
|
186
|
+
*,
|
|
187
|
+
scan_dependencies: bool = False,
|
|
188
|
+
liveness: bool = True,
|
|
189
|
+
lsp_refs: bool = False,
|
|
190
|
+
quiet: bool = False,
|
|
191
|
+
graph=None, # noqa: ANN001
|
|
192
|
+
paths: list[str] | None = None,
|
|
193
|
+
) -> GraphRefreshResult:
|
|
194
|
+
"""Refresh graph and map once, falling back to a lean map on graph failure."""
|
|
195
|
+
import time
|
|
196
|
+
|
|
197
|
+
from devcouncil.codeintel import get_codeintel_service
|
|
198
|
+
from devcouncil.codeintel.build_control import (
|
|
199
|
+
GraphBuildBusy,
|
|
200
|
+
graph_build_session,
|
|
201
|
+
run_isolated_full_build,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
t0 = time.perf_counter()
|
|
205
|
+
root = root.expanduser().resolve()
|
|
206
|
+
mode = "incremental" if paths is not None else "full"
|
|
207
|
+
degraded = False
|
|
208
|
+
reason = ""
|
|
209
|
+
compatibility_export_degraded = False
|
|
210
|
+
with graph_build_session(root):
|
|
211
|
+
# A damaged index.sqlite fails scattered reads all over the build; move
|
|
212
|
+
# it aside now (we hold the writer lease) so this run rebuilds cleanly
|
|
213
|
+
# instead of stamping a lean/degraded map forever.
|
|
214
|
+
service = get_codeintel_service(root)
|
|
215
|
+
try:
|
|
216
|
+
store_corrupt = service.store.exists() and service.status()["state"] == "corrupt"
|
|
217
|
+
except Exception:
|
|
218
|
+
store_corrupt = False
|
|
219
|
+
if store_corrupt and service.store.quarantine():
|
|
220
|
+
logger.warning(
|
|
221
|
+
"codeintel store was corrupt; quarantined to %s — running a full rebuild",
|
|
222
|
+
service.store.path.name + ".corrupt",
|
|
223
|
+
)
|
|
224
|
+
paths = None
|
|
225
|
+
mode = "full"
|
|
226
|
+
if graph is None:
|
|
227
|
+
try:
|
|
228
|
+
if paths is not None and get_codeintel_service(root).load() is not None:
|
|
229
|
+
from devcouncil.codeintel.sync.incremental import sync_affected_paths
|
|
230
|
+
|
|
231
|
+
graph = sync_affected_paths(
|
|
232
|
+
get_codeintel_service(root),
|
|
233
|
+
paths,
|
|
234
|
+
liveness=liveness,
|
|
235
|
+
)
|
|
236
|
+
# Export-only skips are recorded on build status; SQLite is healthy.
|
|
237
|
+
try:
|
|
238
|
+
from devcouncil.codeintel.build_control import read_build_status
|
|
239
|
+
|
|
240
|
+
status = read_build_status(root)
|
|
241
|
+
if status.compatibility_export == "degraded":
|
|
242
|
+
compatibility_export_degraded = True
|
|
243
|
+
reason = status.degraded_reason or reason
|
|
244
|
+
except Exception:
|
|
245
|
+
logger.debug("build status read after incremental failed", exc_info=True)
|
|
246
|
+
else:
|
|
247
|
+
isolated = run_isolated_full_build(
|
|
248
|
+
root,
|
|
249
|
+
changed_paths=None if paths is None else set(paths),
|
|
250
|
+
liveness=liveness,
|
|
251
|
+
)
|
|
252
|
+
graph = isolated.graph
|
|
253
|
+
mode = "full"
|
|
254
|
+
# Compatibility JSON size limits must not stamp graph_degraded
|
|
255
|
+
# (that would force perpetual --if-stale rebuilds while SQLite is fine).
|
|
256
|
+
if isolated.status.compatibility_export == "degraded":
|
|
257
|
+
compatibility_export_degraded = True
|
|
258
|
+
reason = isolated.status.degraded_reason or reason
|
|
259
|
+
elif isolated.status.state == "degraded" and graph is None:
|
|
260
|
+
degraded = True
|
|
261
|
+
reason = isolated.status.degraded_reason or reason
|
|
262
|
+
except GraphBuildBusy:
|
|
263
|
+
# Lease contention after/during a concurrent writer must not stamp a
|
|
264
|
+
# lean/degraded map over a healthy SQLite generation (retry storm).
|
|
265
|
+
raise
|
|
266
|
+
except Exception as exc: # noqa: BLE001
|
|
267
|
+
logger.warning("graph refresh failed; writing lean repo map", exc_info=True)
|
|
268
|
+
degraded = True
|
|
269
|
+
reason = f"{type(exc).__name__}: {exc}"
|
|
270
|
+
mode = "lean"
|
|
271
|
+
|
|
272
|
+
# True lean/unavailable only — never export-only.
|
|
273
|
+
if graph is None:
|
|
274
|
+
degraded = True
|
|
275
|
+
if mode != "lean":
|
|
276
|
+
mode = "lean"
|
|
277
|
+
|
|
278
|
+
mapper = RepoMapper(root)
|
|
279
|
+
if graph is not None:
|
|
280
|
+
mapper._prebuilt_code_graph = graph # type: ignore[attr-defined]
|
|
281
|
+
else:
|
|
282
|
+
mapper._skip_code_graph_build = True # type: ignore[attr-defined]
|
|
283
|
+
try:
|
|
284
|
+
repo_map = mapper.map_repo(
|
|
285
|
+
goal,
|
|
286
|
+
scan_dependencies=scan_dependencies,
|
|
287
|
+
liveness=liveness,
|
|
288
|
+
lsp_refs=lsp_refs,
|
|
289
|
+
)
|
|
290
|
+
except Exception as exc:
|
|
291
|
+
# Graph may already be committed (incremental/full) while the map
|
|
292
|
+
# rebuild failed — fail closed so --if-stale / verify keep retrying.
|
|
293
|
+
logger.warning("repo map rebuild failed after graph commit", exc_info=True)
|
|
294
|
+
_stamp_existing_map_degraded(
|
|
295
|
+
output if output.is_absolute() else root / output,
|
|
296
|
+
reason=f"{type(exc).__name__}: {exc}",
|
|
297
|
+
)
|
|
298
|
+
raise
|
|
299
|
+
# Stamp degraded handshake before first write so agents never see a
|
|
300
|
+
# fingerprint-fresh lean map without graph_degraded=True.
|
|
301
|
+
repo_map.graph_degraded = bool(degraded)
|
|
302
|
+
repo_map.graph_degraded_reason = reason if degraded else ""
|
|
303
|
+
elapsed = time.perf_counter() - t0
|
|
304
|
+
if not quiet:
|
|
305
|
+
status_console.print(f"[dim]map completed in {elapsed:.2f}s[/dim]")
|
|
306
|
+
if degraded:
|
|
307
|
+
status_console.print(
|
|
308
|
+
f"[yellow]Graph degraded; wrote {mode} map: {reason}[/yellow]"
|
|
309
|
+
)
|
|
310
|
+
elif compatibility_export_degraded:
|
|
311
|
+
status_console.print(
|
|
312
|
+
f"[yellow]Compatibility export degraded (SQLite ok; "
|
|
313
|
+
f"stub/compact JSON may still exist): {reason}[/yellow]"
|
|
314
|
+
)
|
|
315
|
+
graph_context = CodeReviewGraphAdapter(root).get_context()
|
|
316
|
+
output = output if output.is_absolute() else root / output
|
|
317
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
318
|
+
write_model_json(output, repo_map)
|
|
319
|
+
write_agent_guides(root, output, repo_map)
|
|
320
|
+
# Agent guides can add/change tracked files after the fingerprint was taken;
|
|
321
|
+
# re-stamp so the on-disk map is not immediately stale for checkout/verify.
|
|
322
|
+
mapper = RepoMapper(root)
|
|
323
|
+
try:
|
|
324
|
+
files = mapper.get_git_files()
|
|
325
|
+
repo_map.generated_head = mapper._git_head()
|
|
326
|
+
repo_map.indexed_hash = mapper._files_fingerprint(files)
|
|
327
|
+
repo_map.content_fingerprint = mapper._content_fingerprint(files)
|
|
328
|
+
repo_map.graph_degraded = bool(degraded)
|
|
329
|
+
repo_map.graph_degraded_reason = reason if degraded else ""
|
|
330
|
+
write_model_json(output, repo_map)
|
|
331
|
+
except Exception:
|
|
332
|
+
logger.debug("Failed to re-stamp map fingerprints after agent guides", exc_info=True)
|
|
333
|
+
if graph_context.available:
|
|
334
|
+
graph_output = output.with_name("code_review_graph_context.json")
|
|
335
|
+
write_model_json(graph_output, graph_context)
|
|
336
|
+
if not quiet:
|
|
337
|
+
status_console.print(
|
|
338
|
+
f"[green]Wrote code-review-graph context to {graph_output}[/green]"
|
|
339
|
+
)
|
|
340
|
+
try:
|
|
341
|
+
generation = get_codeintel_service(root).store.current_generation()
|
|
342
|
+
except sqlite3.DatabaseError:
|
|
343
|
+
# A store that turned unreadable mid-run must not crash the refresh
|
|
344
|
+
# result — the map artifacts above were already written.
|
|
345
|
+
logger.warning("could not read store generation after refresh", exc_info=True)
|
|
346
|
+
generation = None
|
|
347
|
+
return GraphRefreshResult(
|
|
348
|
+
repo_map=repo_map,
|
|
349
|
+
graph=graph,
|
|
350
|
+
generation=generation,
|
|
351
|
+
mode=mode,
|
|
352
|
+
degraded=degraded,
|
|
353
|
+
reason=reason,
|
|
354
|
+
compatibility_export_degraded=compatibility_export_degraded,
|
|
355
|
+
)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Best-effort refresh of ``.devcouncil/repo_map.json`` when fingerprints drift."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from devcouncil.utils.json_persist import read_json, write_model_json
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def refresh_stale_map_if_needed(
|
|
14
|
+
project_root: Path,
|
|
15
|
+
*,
|
|
16
|
+
on_checkout: bool = True,
|
|
17
|
+
on_verify: bool = True,
|
|
18
|
+
) -> bool:
|
|
19
|
+
"""Regenerate ``repo_map.json`` when its tracked-content fingerprint is stale.
|
|
20
|
+
|
|
21
|
+
Honors ``execution.refresh_stale_map_on_checkout`` and
|
|
22
|
+
``execution.refresh_stale_map_on_verify`` (both default on). Pass
|
|
23
|
+
``on_checkout=False`` or ``on_verify=False`` to skip a context. Never raises;
|
|
24
|
+
returns True when a remap ran successfully.
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
from devcouncil.app.config import load_config
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
cfg = load_config(project_root)
|
|
31
|
+
checkout_enabled = bool(
|
|
32
|
+
getattr(cfg.execution, "refresh_stale_map_on_checkout", True)
|
|
33
|
+
)
|
|
34
|
+
verify_enabled = bool(
|
|
35
|
+
getattr(cfg.execution, "refresh_stale_map_on_verify", True)
|
|
36
|
+
)
|
|
37
|
+
except Exception:
|
|
38
|
+
checkout_enabled = True
|
|
39
|
+
verify_enabled = True
|
|
40
|
+
|
|
41
|
+
if on_checkout and not on_verify:
|
|
42
|
+
enabled = checkout_enabled
|
|
43
|
+
elif on_verify and not on_checkout:
|
|
44
|
+
enabled = verify_enabled
|
|
45
|
+
else:
|
|
46
|
+
enabled = checkout_enabled or verify_enabled
|
|
47
|
+
if not enabled:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
map_path = project_root / ".devcouncil" / "repo_map.json"
|
|
51
|
+
if not map_path.is_file():
|
|
52
|
+
data: dict = {}
|
|
53
|
+
else:
|
|
54
|
+
loaded = read_json(map_path)
|
|
55
|
+
data = loaded if isinstance(loaded, dict) else {}
|
|
56
|
+
|
|
57
|
+
from devcouncil.indexing.repo_mapper import RepoMapper
|
|
58
|
+
|
|
59
|
+
mapper = RepoMapper(project_root)
|
|
60
|
+
if map_path.is_file() and not mapper.map_is_stale(data):
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
from devcouncil.indexing.map_artifacts import generate_map_artifacts
|
|
64
|
+
|
|
65
|
+
generate_map_artifacts(project_root, map_path, quiet=True)
|
|
66
|
+
return True
|
|
67
|
+
except Exception as exc:
|
|
68
|
+
# Concurrent MCP/watcher builds commonly hold the writer lease during
|
|
69
|
+
# checkout/verify. Defer quietly — map stays stale until the next
|
|
70
|
+
# successful refresh rather than failing the lease acquisition path.
|
|
71
|
+
try:
|
|
72
|
+
from devcouncil.codeintel.build_control import GraphBuildBusy
|
|
73
|
+
except Exception:
|
|
74
|
+
GraphBuildBusy = () # type: ignore[misc, assignment]
|
|
75
|
+
if isinstance(exc, GraphBuildBusy):
|
|
76
|
+
logger.info("map refresh deferred (writer lease busy): %s", exc)
|
|
77
|
+
return False
|
|
78
|
+
logger.warning("map refresh failed", exc_info=True)
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def refresh_map_for_paths(root, paths, *, liveness: bool = True):
|
|
83
|
+
"""Re-export of graph.build.refresh_map_for_paths."""
|
|
84
|
+
from devcouncil.indexing.graph.build import refresh_map_for_paths as _refresh
|
|
85
|
+
|
|
86
|
+
return _refresh(root, paths, liveness=liveness)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def refresh_repo_map_from_graph(
|
|
90
|
+
root,
|
|
91
|
+
graph,
|
|
92
|
+
affected: set[str],
|
|
93
|
+
files: list[str],
|
|
94
|
+
*,
|
|
95
|
+
mapper=None,
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Update repo-map compatibility fields from an already committed graph.
|
|
98
|
+
|
|
99
|
+
Incremental code-intelligence sync must not trigger a second repository-wide
|
|
100
|
+
graph build merely to refresh ``repo_map.json``. This function keeps the
|
|
101
|
+
compatibility artifact aligned using the graph and affected-file set that the
|
|
102
|
+
caller has already computed.
|
|
103
|
+
"""
|
|
104
|
+
path = root / ".devcouncil" / "repo_map.json"
|
|
105
|
+
if not path.is_file():
|
|
106
|
+
return
|
|
107
|
+
try:
|
|
108
|
+
from devcouncil.indexing.repo_mapper import RepoMap, RepoMapper
|
|
109
|
+
|
|
110
|
+
repo_mapper = mapper or RepoMapper(root)
|
|
111
|
+
repo_map = RepoMap.model_validate(read_json(path))
|
|
112
|
+
by_path = {
|
|
113
|
+
entry.path: entry
|
|
114
|
+
for entry in repo_map.files
|
|
115
|
+
if entry.path not in affected
|
|
116
|
+
}
|
|
117
|
+
file_set = set(files)
|
|
118
|
+
for rel in affected:
|
|
119
|
+
if rel in file_set and (root / rel).is_file():
|
|
120
|
+
by_path[rel] = repo_mapper.describe_file(rel)
|
|
121
|
+
repo_map.files = [by_path[rel] for rel in sorted(by_path)]
|
|
122
|
+
repo_map.entry_roots = list(graph.entry_roots)
|
|
123
|
+
cap = repo_mapper._LIVENESS_CAP
|
|
124
|
+
repo_map.unwired_candidates = list(graph.unwired_candidates)[:cap]
|
|
125
|
+
repo_map.unreachable_files = list(graph.unreachable_files)[:cap]
|
|
126
|
+
repo_map.liveness_unreachable_unreliable = bool(
|
|
127
|
+
graph.meta.get("liveness_unreachable_unreliable")
|
|
128
|
+
)
|
|
129
|
+
repo_map.dead_symbol_candidates = list(
|
|
130
|
+
graph.meta.get("legacy_dead_symbol_candidates") or []
|
|
131
|
+
)[:cap]
|
|
132
|
+
# Do NOT stamp fingerprints or clear graph_degraded here. A later
|
|
133
|
+
# ``mapper.map_repo`` failure in ``refresh_map_artifacts`` would otherwise
|
|
134
|
+
# leave a fingerprint-fresh, non-degraded map while subsystems are stale.
|
|
135
|
+
# Full map writes own the freshness handshake.
|
|
136
|
+
write_model_json(path, repo_map)
|
|
137
|
+
except Exception:
|
|
138
|
+
logger.warning("incremental repo-map compatibility refresh failed", exc_info=True)
|
|
139
|
+
|
|
140
|
+
# Back-compat alias for plan expected tests.
|
|
141
|
+
refresh_repository_map = refresh_stale_map_if_needed
|