devcouncil 0.3.1 → 0.4.0
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 +46 -30
- 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 +49 -30
- 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 +960 -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 +99 -0
- package/src/devcouncil/integrations/mcp/handlers/graph.py +36 -0
- package/src/devcouncil/integrations/mcp/handlers/handoff.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/knowledge.py +30 -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 +101 -0
- package/src/devcouncil/integrations/mcp/handlers/read.py +74 -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 +84 -0
- package/src/devcouncil/integrations/mcp/handlers/scope.py +56 -0
- package/src/devcouncil/integrations/mcp/handlers/status.py +114 -0
- package/src/devcouncil/integrations/mcp/handlers/task.py +100 -0
- package/src/devcouncil/integrations/mcp/handlers/tool_specs.py +904 -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 +60 -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 +303 -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/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
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
from devcouncil.utils.json_persist import dump_json
|
|
2
|
+
import logging
|
|
2
3
|
import typer
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from rich.console import Console
|
|
@@ -8,9 +9,23 @@ from devcouncil.storage.db import get_db
|
|
|
8
9
|
from devcouncil.storage.repositories import TaskRepository
|
|
9
10
|
from devcouncil.verification.test_resolver import TestResolver
|
|
10
11
|
from devcouncil.verification.verifier import Verifier
|
|
12
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
11
13
|
|
|
12
14
|
app = typer.Typer(help="Evidence suggestion utilities.")
|
|
13
15
|
console = Console()
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_repo_map(root: Path) -> dict | None:
|
|
20
|
+
"""Load `.devcouncil/repo_map.json` so test suggestions can use the map's
|
|
21
|
+
dependents/role_files index; returns None (name-based fallback) when absent."""
|
|
22
|
+
import json
|
|
23
|
+
from typing import Any, cast
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
return cast(dict[Any, Any], json.loads((root / ".devcouncil" / "repo_map.json").read_text(encoding="utf-8")))
|
|
27
|
+
except (OSError, ValueError):
|
|
28
|
+
return None
|
|
14
29
|
|
|
15
30
|
|
|
16
31
|
@app.command("suggest")
|
|
@@ -21,28 +36,34 @@ def suggest(
|
|
|
21
36
|
project_root: Path = typer.Option(Path("."), "--project-root"),
|
|
22
37
|
):
|
|
23
38
|
root = project_root.expanduser().resolve()
|
|
39
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
40
|
+
set_log_dir(root)
|
|
41
|
+
logger.info("dev evidence suggest: task=%s apply=%s", task_id, apply)
|
|
24
42
|
initialize_project(root, quiet=True)
|
|
25
43
|
db = get_db(root)
|
|
26
44
|
if not db:
|
|
27
45
|
console.print("[red]DevCouncil not initialized.[/red]")
|
|
28
46
|
raise typer.Exit(code=1)
|
|
29
47
|
|
|
30
|
-
with
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
suggestions =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
with log_stage("evidence", project_root=root, task_id=task_id):
|
|
49
|
+
log_step("evidence/1: resolving test suggestions", project_root=root, task_id=task_id, trace=True)
|
|
50
|
+
with db.get_session() as session:
|
|
51
|
+
task = TaskRepository(session).get_by_id(task_id)
|
|
52
|
+
if not task:
|
|
53
|
+
console.print(f"[red]Task {task_id} not found.[/red]")
|
|
54
|
+
raise typer.Exit(code=1)
|
|
55
|
+
changed = Verifier(root).get_task_changed_files(task_id)
|
|
56
|
+
suggestions = TestResolver(root, _load_repo_map(root)).suggest_for_task(task, changed)
|
|
57
|
+
if not include_low_confidence:
|
|
58
|
+
suggestions = [s for s in suggestions if s.confidence != "low"]
|
|
59
|
+
if apply:
|
|
60
|
+
for item in suggestions:
|
|
61
|
+
if item.confidence == "high" and item.command not in task.expected_tests:
|
|
62
|
+
task.expected_tests.append(item.command)
|
|
63
|
+
TaskRepository(session).save(task)
|
|
64
|
+
typer.echo(dump_json({
|
|
65
|
+
"task_id": task_id,
|
|
66
|
+
"suggestions": [s.model_dump() for s in suggestions],
|
|
67
|
+
"expected_tests": task.expected_tests,
|
|
68
|
+
}, indent=2))
|
|
69
|
+
log_step("evidence/complete", project_root=root, task_id=task_id, trace=True)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Export DevCouncil project state to a portable JSON snapshot (no secrets)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from devcouncil.utils.json_persist import dump_json
|
|
6
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from devcouncil.utils.json_persist import write_json
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
from devcouncil.cli.commands.gaps import _gaps_payload
|
|
15
|
+
from devcouncil.cli.commands.init import initialize_project
|
|
16
|
+
from devcouncil.storage.db import get_db
|
|
17
|
+
from devcouncil.storage.repositories import RequirementRepository, TaskRepository
|
|
18
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
EXPORT_RELATIVE = Path(".devcouncil") / "export" / "state.json"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _export_payload(project_root: Path) -> dict:
|
|
27
|
+
initialize_project(project_root, quiet=True)
|
|
28
|
+
db = get_db(project_root)
|
|
29
|
+
if not db:
|
|
30
|
+
return {"initialized": False, "requirements": [], "tasks": [], "gaps": {}}
|
|
31
|
+
|
|
32
|
+
with db.get_session() as session:
|
|
33
|
+
requirements = [req.model_dump() for req in RequirementRepository(session).get_all()]
|
|
34
|
+
tasks = [task.model_dump() for task in TaskRepository(session).get_all()]
|
|
35
|
+
gaps = _gaps_payload(project_root)
|
|
36
|
+
return {
|
|
37
|
+
"initialized": True,
|
|
38
|
+
"requirements": requirements,
|
|
39
|
+
"tasks": tasks,
|
|
40
|
+
"gaps": {
|
|
41
|
+
"blocking_count": gaps.get("blocking_count", 0),
|
|
42
|
+
"advisory_count": gaps.get("advisory_count", 0),
|
|
43
|
+
"total_count": gaps.get("total_count", 0),
|
|
44
|
+
"items": gaps.get("gaps", []),
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def export_state(
|
|
50
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
51
|
+
output: Path | None = typer.Option(None, "--output", "-o", help="Output path (default: .devcouncil/export/state.json)."),
|
|
52
|
+
json_format: bool = typer.Option(False, "--json", help="Print payload to stdout instead of writing a file."),
|
|
53
|
+
):
|
|
54
|
+
"""Write requirements, tasks, and gaps summary to .devcouncil/export/state.json."""
|
|
55
|
+
root = project_root.expanduser().resolve()
|
|
56
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
57
|
+
|
|
58
|
+
set_log_dir(root)
|
|
59
|
+
logger.info("dev export: json=%s", json_format)
|
|
60
|
+
|
|
61
|
+
with log_stage("export", project_root=root):
|
|
62
|
+
log_step("export/1: building snapshot", project_root=root, trace=True)
|
|
63
|
+
payload = _export_payload(root)
|
|
64
|
+
if json_format:
|
|
65
|
+
typer.echo(dump_json(payload, indent=2))
|
|
66
|
+
log_step("export/complete", project_root=root, trace=True)
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
dest = (output or (root / EXPORT_RELATIVE)).expanduser().resolve()
|
|
70
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
write_json(dest, payload)
|
|
72
|
+
console.print(f"[green]Exported DevCouncil state[/green] to {dest}")
|
|
73
|
+
log_step("export/complete", project_root=root, path=str(dest), trace=True)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
from devcouncil.cli.commands.init import initialize_project
|
|
8
|
+
from devcouncil.storage.db import get_db
|
|
9
|
+
from devcouncil.storage.repositories import ArtifactGraphRepository
|
|
10
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
11
|
+
from devcouncil.utils.json_persist import dump_json
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _gaps_payload(
|
|
18
|
+
project_root: Path,
|
|
19
|
+
*,
|
|
20
|
+
blocking_only: bool = False,
|
|
21
|
+
task_id: str | None = None,
|
|
22
|
+
) -> dict:
|
|
23
|
+
initialize_project(project_root, quiet=True)
|
|
24
|
+
db = get_db(project_root)
|
|
25
|
+
if not db:
|
|
26
|
+
return {"initialized": False, "gaps": [], "blocking_count": 0, "advisory_count": 0}
|
|
27
|
+
|
|
28
|
+
with db.get_session() as session:
|
|
29
|
+
if task_id:
|
|
30
|
+
from devcouncil.storage.repositories import GapRepository
|
|
31
|
+
|
|
32
|
+
gaps = GapRepository(session).get_for_task(task_id)
|
|
33
|
+
if blocking_only:
|
|
34
|
+
gaps = [g for g in gaps if g.blocking]
|
|
35
|
+
return {
|
|
36
|
+
"initialized": True,
|
|
37
|
+
"ok": True,
|
|
38
|
+
"task_id": task_id,
|
|
39
|
+
"gaps": [gap.model_dump() for gap in gaps],
|
|
40
|
+
"blocking_count": sum(1 for g in gaps if g.blocking),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
graph = ArtifactGraphRepository(session).load_graph()
|
|
44
|
+
if blocking_only:
|
|
45
|
+
gaps = graph.blocking_gaps()
|
|
46
|
+
else:
|
|
47
|
+
gaps = sorted(graph.gaps.values(), key=lambda g: (not g.blocking, g.id))
|
|
48
|
+
blocking = [g for g in gaps if g.blocking]
|
|
49
|
+
advisory = [g for g in gaps if not g.blocking]
|
|
50
|
+
return {
|
|
51
|
+
"initialized": True,
|
|
52
|
+
"gaps": [gap.model_dump() for gap in gaps],
|
|
53
|
+
"blocking_count": len(blocking),
|
|
54
|
+
"advisory_count": len(advisory),
|
|
55
|
+
"total_count": len(gaps),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _next_actions_payload(project_root: Path, task_id: str) -> dict:
|
|
60
|
+
from devcouncil.integrations.mcp.util import allowed_next_tools
|
|
61
|
+
from devcouncil.storage.repositories import GapRepository, TaskRepository
|
|
62
|
+
from devcouncil.verification.next_actions import split_next_actions
|
|
63
|
+
|
|
64
|
+
initialize_project(project_root, quiet=True)
|
|
65
|
+
db = get_db(project_root)
|
|
66
|
+
if not db:
|
|
67
|
+
return {"ok": False, "error": "DevCouncil state is unavailable in this directory."}
|
|
68
|
+
|
|
69
|
+
with db.get_session() as session:
|
|
70
|
+
gaps = GapRepository(session).get_for_task(task_id)
|
|
71
|
+
task = TaskRepository(session).get_by_id(task_id)
|
|
72
|
+
blocking_actions, advisory_actions = split_next_actions(gaps)
|
|
73
|
+
has_blocking = any(g.blocking for g in gaps)
|
|
74
|
+
return {
|
|
75
|
+
"ok": True,
|
|
76
|
+
"task_id": task_id,
|
|
77
|
+
"next_actions": [a.model_dump() for a in blocking_actions],
|
|
78
|
+
"advisory_actions": [a.model_dump() for a in advisory_actions],
|
|
79
|
+
"allowed_next_tools": allowed_next_tools(task.status if task else "planned", has_blocking),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def gaps(
|
|
84
|
+
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
85
|
+
blocking_only: bool = typer.Option(
|
|
86
|
+
False,
|
|
87
|
+
"--blocking-only",
|
|
88
|
+
help="Show only blocking gaps.",
|
|
89
|
+
),
|
|
90
|
+
task_id: str | None = typer.Option(
|
|
91
|
+
None,
|
|
92
|
+
"--task-id",
|
|
93
|
+
help="Scope gaps to a single task (MCP-compatible payload).",
|
|
94
|
+
),
|
|
95
|
+
next_actions: bool = typer.Option(
|
|
96
|
+
False,
|
|
97
|
+
"--next-actions",
|
|
98
|
+
help="Emit repair next-actions for --task-id instead of raw gaps.",
|
|
99
|
+
),
|
|
100
|
+
fail_on_blocking: bool = typer.Option(
|
|
101
|
+
False,
|
|
102
|
+
"--fail-on-blocking",
|
|
103
|
+
help="Exit non-zero when blocking gaps remain.",
|
|
104
|
+
),
|
|
105
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
106
|
+
):
|
|
107
|
+
"""List all verification gaps (blocking and advisory)."""
|
|
108
|
+
root = project_root.expanduser().resolve()
|
|
109
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
110
|
+
set_log_dir(root)
|
|
111
|
+
logger.info(
|
|
112
|
+
"dev gaps: json=%s blocking_only=%s fail_on_blocking=%s",
|
|
113
|
+
json_format,
|
|
114
|
+
blocking_only,
|
|
115
|
+
fail_on_blocking,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
with log_stage("gaps", project_root=root):
|
|
119
|
+
log_step("gaps/1: loading gaps", project_root=root, trace=True)
|
|
120
|
+
if next_actions:
|
|
121
|
+
if not task_id:
|
|
122
|
+
message = "--next-actions requires --task-id"
|
|
123
|
+
if json_format:
|
|
124
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
125
|
+
else:
|
|
126
|
+
console.print(f"[red]{message}[/red]")
|
|
127
|
+
raise typer.Exit(code=1)
|
|
128
|
+
payload = _next_actions_payload(root, task_id)
|
|
129
|
+
else:
|
|
130
|
+
payload = _gaps_payload(root, blocking_only=blocking_only, task_id=task_id)
|
|
131
|
+
|
|
132
|
+
def _maybe_fail() -> None:
|
|
133
|
+
if fail_on_blocking and payload.get("blocking_count", 0) > 0:
|
|
134
|
+
raise typer.Exit(code=1)
|
|
135
|
+
|
|
136
|
+
if json_format:
|
|
137
|
+
typer.echo(dump_json(payload, indent=2))
|
|
138
|
+
_maybe_fail()
|
|
139
|
+
log_step("gaps/complete", project_root=root, trace=True)
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
if not payload["initialized"]:
|
|
143
|
+
console.print("[yellow]DevCouncil state is not available in this directory.[/yellow]")
|
|
144
|
+
log_step("gaps/complete", project_root=root, trace=True)
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
table = Table(title="DevCouncil Gaps")
|
|
148
|
+
table.add_column("ID", style="cyan", no_wrap=True)
|
|
149
|
+
table.add_column("Blocking", justify="center")
|
|
150
|
+
table.add_column("Severity")
|
|
151
|
+
table.add_column("Type")
|
|
152
|
+
table.add_column("Task")
|
|
153
|
+
table.add_column("Description")
|
|
154
|
+
|
|
155
|
+
for gap in payload["gaps"]:
|
|
156
|
+
blocking_style = "[red]yes[/red]" if gap.get("blocking") else "[dim]no[/dim]"
|
|
157
|
+
desc = (gap.get("description") or "")[:80]
|
|
158
|
+
table.add_row(
|
|
159
|
+
gap.get("id", ""),
|
|
160
|
+
blocking_style,
|
|
161
|
+
gap.get("severity", ""),
|
|
162
|
+
gap.get("gap_type", ""),
|
|
163
|
+
gap.get("task_id") or "",
|
|
164
|
+
desc,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
console.print(table)
|
|
168
|
+
console.print(
|
|
169
|
+
f"\n[bold]{payload['total_count']}[/bold] gap(s): "
|
|
170
|
+
f"[red]{payload['blocking_count']} blocking[/red], "
|
|
171
|
+
f"[dim]{payload['advisory_count']} advisory[/dim]"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
_maybe_fail()
|
|
175
|
+
log_step("gaps/complete", project_root=root, trace=True)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""`dev write` and `dev apply-patch` — policy-gated file writes through the CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from devcouncil.execution.gated_write import apply_patch_payload, write_file_payload
|
|
12
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
13
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
14
|
+
from devcouncil.utils.json_persist import dump_json
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def write(
|
|
20
|
+
task_id: str = typer.Argument(..., help="Task ID."),
|
|
21
|
+
lease_token: str = typer.Option(..., "--lease-token", help="Lease token from checkout."),
|
|
22
|
+
path: str = typer.Option(..., "--path", help="Repository-relative file path."),
|
|
23
|
+
content: str | None = typer.Option(None, "--content", help="File content (or read from stdin when omitted)."),
|
|
24
|
+
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
25
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Write a file for a leased task through DevCouncil's policy gate."""
|
|
28
|
+
root = project_root.expanduser().resolve()
|
|
29
|
+
set_log_dir(root)
|
|
30
|
+
body = content if content is not None else sys.stdin.read()
|
|
31
|
+
logger.info("dev write: task=%s path=%s", task_id, path)
|
|
32
|
+
|
|
33
|
+
with log_stage("write", project_root=root, task_id=task_id):
|
|
34
|
+
payload = write_file_payload(root, task_id=task_id, lease_token=lease_token, rel_path=path, content=body)
|
|
35
|
+
if json_format:
|
|
36
|
+
typer.echo(dump_json(payload, indent=2))
|
|
37
|
+
log_step("write/complete", project_root=root, task_id=task_id, trace=True)
|
|
38
|
+
if not payload.get("ok"):
|
|
39
|
+
raise typer.Exit(code=1)
|
|
40
|
+
return
|
|
41
|
+
if not payload.get("ok"):
|
|
42
|
+
typer.echo(payload.get("error") or payload.get("rejected_files"))
|
|
43
|
+
raise typer.Exit(code=1)
|
|
44
|
+
typer.echo(f"Wrote {path}")
|
|
45
|
+
log_step("write/complete", project_root=root, task_id=task_id, trace=True)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def apply_patch(
|
|
49
|
+
task_id: str = typer.Argument(..., help="Task ID."),
|
|
50
|
+
lease_token: str = typer.Option(..., "--lease-token", help="Lease token from checkout."),
|
|
51
|
+
unified_diff: str | None = typer.Option(None, "--unified-diff", help="Unified diff (or read from stdin)."),
|
|
52
|
+
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
53
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Apply a unified diff for a leased task through DevCouncil's policy gate."""
|
|
56
|
+
root = project_root.expanduser().resolve()
|
|
57
|
+
set_log_dir(root)
|
|
58
|
+
diff_body = unified_diff if unified_diff is not None else sys.stdin.read()
|
|
59
|
+
if not diff_body.strip():
|
|
60
|
+
typer.echo("unified_diff must be a non-empty string")
|
|
61
|
+
raise typer.Exit(code=1)
|
|
62
|
+
logger.info("dev apply-patch: task=%s", task_id)
|
|
63
|
+
|
|
64
|
+
with log_stage("apply-patch", project_root=root, task_id=task_id):
|
|
65
|
+
payload = apply_patch_payload(root, task_id=task_id, lease_token=lease_token, unified_diff=diff_body)
|
|
66
|
+
if json_format:
|
|
67
|
+
typer.echo(dump_json(payload, indent=2))
|
|
68
|
+
log_step("apply-patch/complete", project_root=root, task_id=task_id, trace=True)
|
|
69
|
+
if not payload.get("ok"):
|
|
70
|
+
raise typer.Exit(code=1)
|
|
71
|
+
return
|
|
72
|
+
if not payload.get("ok"):
|
|
73
|
+
typer.echo(payload.get("error") or payload.get("rejected_files"))
|
|
74
|
+
raise typer.Exit(code=1)
|
|
75
|
+
typer.echo(f"Applied patch to {payload.get('applied_files')}")
|
|
76
|
+
log_step("apply-patch/complete", project_root=root, task_id=task_id, trace=True)
|