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
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
from devcouncil.utils.json_persist import dump_json
|
|
2
|
+
import logging
|
|
2
3
|
from pathlib import Path
|
|
4
|
+
from typing import Literal, cast, Any
|
|
3
5
|
|
|
4
6
|
import typer
|
|
5
7
|
from rich.console import Console
|
|
@@ -7,14 +9,45 @@ from rich.table import Table
|
|
|
7
9
|
from devcouncil.cli.commands.init import initialize_project
|
|
8
10
|
from devcouncil.storage.db import get_db
|
|
9
11
|
from devcouncil.storage.repositories import TaskRepository
|
|
12
|
+
from devcouncil.storage.native import TaskLeaseRepository
|
|
13
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
14
|
+
from devcouncil.telemetry.traces import TraceLogger
|
|
10
15
|
|
|
11
16
|
app = typer.Typer()
|
|
12
17
|
console = Console()
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _lease_public_view(lease, *, expired: bool) -> dict[str, Any]:
|
|
22
|
+
return {
|
|
23
|
+
"owner": lease.owner,
|
|
24
|
+
"agent": lease.agent,
|
|
25
|
+
"expires_at": lease.expires_at,
|
|
26
|
+
"expired": expired,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _active_leases_by_task(session) -> dict[str, tuple[Any, bool]]:
|
|
31
|
+
return {
|
|
32
|
+
lease.task_id: (lease, expired)
|
|
33
|
+
for lease, expired in TaskLeaseRepository(session).list_leases(active_only=True)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _task_row_payload(task, leases_by_task: dict[str, tuple[Any, bool]]) -> Any:
|
|
38
|
+
payload = task.model_dump()
|
|
39
|
+
lease_pair = leases_by_task.get(task.id)
|
|
40
|
+
payload["lease"] = _lease_public_view(lease_pair[0], expired=lease_pair[1]) if lease_pair else None
|
|
41
|
+
return payload
|
|
42
|
+
|
|
13
43
|
|
|
14
44
|
@app.callback(invoke_without_command=True)
|
|
15
45
|
def tasks(
|
|
16
46
|
ctx: typer.Context,
|
|
17
47
|
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
48
|
+
status: str | None = typer.Option(None, "--status", help="Filter tasks by status."),
|
|
49
|
+
limit: int = typer.Option(100, "--limit", min=1, max=500, help="Max tasks to return (JSON pagination)."),
|
|
50
|
+
offset: int = typer.Option(0, "--offset", min=0, help="Skip this many tasks before returning results."),
|
|
18
51
|
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
19
52
|
):
|
|
20
53
|
"""
|
|
@@ -24,32 +57,228 @@ def tasks(
|
|
|
24
57
|
return
|
|
25
58
|
|
|
26
59
|
root = project_root.expanduser().resolve()
|
|
60
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
61
|
+
set_log_dir(root)
|
|
62
|
+
logger.info("dev tasks: json=%s", json_format)
|
|
27
63
|
initialize_project(root, quiet=True)
|
|
28
64
|
db = get_db(root)
|
|
29
65
|
if not db:
|
|
30
66
|
console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
|
|
31
67
|
raise typer.Exit(code=1)
|
|
32
68
|
|
|
33
|
-
with
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
with log_stage("tasks", project_root=root):
|
|
70
|
+
log_step("tasks/1: loading task graph", project_root=root, trace=True)
|
|
71
|
+
with db.get_session() as session:
|
|
72
|
+
task_repo = TaskRepository(session)
|
|
73
|
+
tasks_list = task_repo.get_all()
|
|
74
|
+
if status:
|
|
75
|
+
tasks_list = [t for t in tasks_list if t.status == status]
|
|
76
|
+
total = len(tasks_list)
|
|
77
|
+
window = tasks_list[offset:offset + limit]
|
|
78
|
+
leases_by_task = _active_leases_by_task(session)
|
|
79
|
+
|
|
80
|
+
if json_format:
|
|
81
|
+
typer.echo(dump_json({
|
|
82
|
+
"tasks": [_task_row_payload(task, leases_by_task) for task in window],
|
|
83
|
+
"total": total,
|
|
84
|
+
"offset": offset,
|
|
85
|
+
"limit": limit,
|
|
86
|
+
"returned": len(window),
|
|
87
|
+
}, indent=2))
|
|
88
|
+
log_step("tasks/complete", project_root=root, count=len(window), trace=True)
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
tasks_list = window
|
|
92
|
+
|
|
93
|
+
if not tasks_list:
|
|
94
|
+
console.print("No tasks found. Run 'dev plan' to generate tasks.")
|
|
95
|
+
log_step("tasks/complete", project_root=root, count=0, trace=True)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
table = Table(title="DevCouncil Tasks")
|
|
99
|
+
table.add_column("Task ID", style="cyan", no_wrap=True)
|
|
100
|
+
table.add_column("Title", style="white")
|
|
101
|
+
table.add_column("Status", style="magenta")
|
|
102
|
+
table.add_column("Priority", style="yellow")
|
|
103
|
+
table.add_column("Lease", style="blue")
|
|
104
|
+
table.add_column("Linked Reqs", style="green")
|
|
105
|
+
|
|
106
|
+
for t in tasks_list:
|
|
107
|
+
reqs = ", ".join(t.requirement_ids)
|
|
108
|
+
priority = t.priority or "-"
|
|
109
|
+
lease_pair = leases_by_task.get(t.id)
|
|
110
|
+
lease_label = lease_pair[0].owner if lease_pair else "—"
|
|
111
|
+
table.add_row(t.id, t.title, t.status, priority, lease_label, reqs)
|
|
112
|
+
|
|
113
|
+
console.print(table)
|
|
114
|
+
log_step("tasks/complete", project_root=root, count=len(tasks_list), trace=True)
|
|
115
|
+
|
|
36
116
|
|
|
117
|
+
@app.command("cancel")
|
|
118
|
+
def cancel_task(
|
|
119
|
+
task_id: str = typer.Argument(..., help="Task ID to cancel, e.g. TASK-001."),
|
|
120
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
121
|
+
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
122
|
+
):
|
|
123
|
+
"""Cancel a task that is not already done or cancelled."""
|
|
124
|
+
root = project_root.expanduser().resolve()
|
|
125
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
126
|
+
set_log_dir(root)
|
|
127
|
+
initialize_project(root, quiet=True)
|
|
128
|
+
db = get_db(root)
|
|
129
|
+
if not db:
|
|
130
|
+
message = "DevCouncil state is unavailable in this directory."
|
|
37
131
|
if json_format:
|
|
38
|
-
typer.echo(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
132
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
133
|
+
else:
|
|
134
|
+
console.print(f"[red]{message}[/red]")
|
|
135
|
+
raise typer.Exit(code=1)
|
|
136
|
+
|
|
137
|
+
with log_stage("tasks", project_root=root, subcommand="cancel"):
|
|
138
|
+
with db.get_session() as session:
|
|
139
|
+
task_repo = TaskRepository(session)
|
|
140
|
+
task = task_repo.get_by_id(task_id)
|
|
141
|
+
if task is None:
|
|
142
|
+
message = f"Task {task_id} not found."
|
|
143
|
+
if json_format:
|
|
144
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
145
|
+
else:
|
|
146
|
+
console.print(f"[red]{message}[/red]")
|
|
147
|
+
raise typer.Exit(code=1)
|
|
148
|
+
if task.status in {"done", "cancelled"}:
|
|
149
|
+
message = f"Task {task_id} cannot be cancelled (status={task.status})."
|
|
150
|
+
if json_format:
|
|
151
|
+
typer.echo(dump_json({"ok": False, "error": message, "task_id": task_id, "status": task.status}, indent=2))
|
|
152
|
+
else:
|
|
153
|
+
console.print(f"[red]{message}[/red]")
|
|
154
|
+
raise typer.Exit(code=2)
|
|
155
|
+
previous_status = task.status
|
|
156
|
+
task.status = "cancelled"
|
|
157
|
+
task_repo.save(task)
|
|
158
|
+
TraceLogger(root).log_event(
|
|
159
|
+
"task_cancelled",
|
|
160
|
+
{"task_id": task_id, "previous_status": previous_status},
|
|
161
|
+
task_id=task_id,
|
|
162
|
+
summary=f"Cancelled {task_id} (was {previous_status})",
|
|
163
|
+
)
|
|
164
|
+
payload = {"ok": True, "task_id": task_id, "status": "cancelled", "previous_status": previous_status}
|
|
165
|
+
if json_format:
|
|
166
|
+
typer.echo(dump_json(payload, indent=2))
|
|
167
|
+
else:
|
|
168
|
+
console.print(f"[green]Cancelled {task_id}[/green] (was {previous_status})")
|
|
44
169
|
|
|
45
|
-
table = Table(title="DevCouncil Tasks")
|
|
46
|
-
table.add_column("Task ID", style="cyan", no_wrap=True)
|
|
47
|
-
table.add_column("Title", style="white")
|
|
48
|
-
table.add_column("Status", style="magenta")
|
|
49
|
-
table.add_column("Linked Reqs", style="green")
|
|
50
170
|
|
|
51
|
-
|
|
52
|
-
reqs = ", ".join(t.requirement_ids)
|
|
53
|
-
table.add_row(t.id, t.title, t.status, reqs)
|
|
171
|
+
VALID_PRIORITIES = frozenset({"high", "medium", "low"})
|
|
54
172
|
|
|
55
|
-
|
|
173
|
+
|
|
174
|
+
@app.command("reprioritize")
|
|
175
|
+
def reprioritize_task(
|
|
176
|
+
task_id: str = typer.Argument(..., help="Task ID to reprioritize, e.g. TASK-001."),
|
|
177
|
+
priority: str = typer.Option(..., "--priority", help="New priority: high, medium, or low."),
|
|
178
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
179
|
+
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
180
|
+
):
|
|
181
|
+
"""Set or update a task's optional priority hint."""
|
|
182
|
+
normalized = priority.strip().lower()
|
|
183
|
+
if normalized not in VALID_PRIORITIES:
|
|
184
|
+
message = f"--priority must be one of: {', '.join(sorted(VALID_PRIORITIES))}."
|
|
185
|
+
if json_format:
|
|
186
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
187
|
+
else:
|
|
188
|
+
console.print(f"[red]{message}[/red]")
|
|
189
|
+
raise typer.Exit(code=2)
|
|
190
|
+
|
|
191
|
+
root = project_root.expanduser().resolve()
|
|
192
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
193
|
+
set_log_dir(root)
|
|
194
|
+
initialize_project(root, quiet=True)
|
|
195
|
+
db = get_db(root)
|
|
196
|
+
if not db:
|
|
197
|
+
message = "DevCouncil state is unavailable in this directory."
|
|
198
|
+
if json_format:
|
|
199
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
200
|
+
else:
|
|
201
|
+
console.print(f"[red]{message}[/red]")
|
|
202
|
+
raise typer.Exit(code=1)
|
|
203
|
+
|
|
204
|
+
with log_stage("tasks", project_root=root, subcommand="reprioritize"):
|
|
205
|
+
with db.get_session() as session:
|
|
206
|
+
task_repo = TaskRepository(session)
|
|
207
|
+
task = task_repo.get_by_id(task_id)
|
|
208
|
+
if task is None:
|
|
209
|
+
message = f"Task {task_id} not found."
|
|
210
|
+
if json_format:
|
|
211
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
212
|
+
else:
|
|
213
|
+
console.print(f"[red]{message}[/red]")
|
|
214
|
+
raise typer.Exit(code=1)
|
|
215
|
+
previous = task.priority
|
|
216
|
+
task.priority = cast(Literal["high", "medium", "low"], normalized)
|
|
217
|
+
task_repo.save(task)
|
|
218
|
+
payload = {
|
|
219
|
+
"ok": True,
|
|
220
|
+
"task_id": task_id,
|
|
221
|
+
"priority": normalized,
|
|
222
|
+
"previous_priority": previous,
|
|
223
|
+
}
|
|
224
|
+
if json_format:
|
|
225
|
+
typer.echo(dump_json(payload, indent=2))
|
|
226
|
+
else:
|
|
227
|
+
prev_label = previous or "(none)"
|
|
228
|
+
console.print(f"[green]Set {task_id} priority[/green] to {normalized} (was {prev_label})")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@app.command("edit")
|
|
232
|
+
def edit_task(
|
|
233
|
+
task_id: str = typer.Argument(..., help="Task ID to edit, e.g. TASK-001."),
|
|
234
|
+
title: str | None = typer.Option(None, "--title", help="New task title."),
|
|
235
|
+
description: str | None = typer.Option(None, "--description", help="New task description."),
|
|
236
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
237
|
+
json_format: bool = typer.Option(False, "--json", help="Output machine-readable JSON."),
|
|
238
|
+
):
|
|
239
|
+
"""Edit a task's title and/or description."""
|
|
240
|
+
if title is None and description is None:
|
|
241
|
+
message = "Provide at least one of --title or --description."
|
|
242
|
+
if json_format:
|
|
243
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
244
|
+
else:
|
|
245
|
+
console.print(f"[red]{message}[/red]")
|
|
246
|
+
raise typer.Exit(code=2)
|
|
247
|
+
|
|
248
|
+
root = project_root.expanduser().resolve()
|
|
249
|
+
from devcouncil.telemetry.logging_setup import set_log_dir
|
|
250
|
+
set_log_dir(root)
|
|
251
|
+
initialize_project(root, quiet=True)
|
|
252
|
+
db = get_db(root)
|
|
253
|
+
if not db:
|
|
254
|
+
message = "DevCouncil state is unavailable in this directory."
|
|
255
|
+
if json_format:
|
|
256
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
257
|
+
else:
|
|
258
|
+
console.print(f"[red]{message}[/red]")
|
|
259
|
+
raise typer.Exit(code=1)
|
|
260
|
+
|
|
261
|
+
with log_stage("tasks", project_root=root, subcommand="edit"):
|
|
262
|
+
with db.get_session() as session:
|
|
263
|
+
task_repo = TaskRepository(session)
|
|
264
|
+
task = task_repo.get_by_id(task_id)
|
|
265
|
+
if task is None:
|
|
266
|
+
message = f"Task {task_id} not found."
|
|
267
|
+
if json_format:
|
|
268
|
+
typer.echo(dump_json({"ok": False, "error": message}, indent=2))
|
|
269
|
+
else:
|
|
270
|
+
console.print(f"[red]{message}[/red]")
|
|
271
|
+
raise typer.Exit(code=1)
|
|
272
|
+
changes: dict[str, tuple[str, str]] = {}
|
|
273
|
+
if title is not None:
|
|
274
|
+
changes["title"] = (task.title, title)
|
|
275
|
+
task.title = title
|
|
276
|
+
if description is not None:
|
|
277
|
+
changes["description"] = (task.description, description)
|
|
278
|
+
task.description = description
|
|
279
|
+
task_repo.save(task)
|
|
280
|
+
payload = {"ok": True, "task_id": task_id, "changes": {k: {"from": v[0], "to": v[1]} for k, v in changes.items()}}
|
|
281
|
+
if json_format:
|
|
282
|
+
typer.echo(dump_json(payload, indent=2))
|
|
283
|
+
else:
|
|
284
|
+
console.print(f"[green]Updated {task_id}[/green]: {', '.join(changes)}")
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"""DELIBERATELY UNINSTRUMENTED: this command's subject *is* the trace/log
|
|
2
|
+
stream. Emitting log records or trace events from here mutates what the user
|
|
3
|
+
asked to inspect (``log_step`` appends to the very traces.jsonl being tailed)
|
|
4
|
+
and pollutes the machine-readable JSONL/--json output consumers parse. An
|
|
5
|
+
introspection command must not modify what it introspects."""
|
|
6
|
+
|
|
2
7
|
import time
|
|
3
8
|
from pathlib import Path
|
|
4
9
|
from typing import Optional
|
|
@@ -7,6 +12,7 @@ import typer
|
|
|
7
12
|
from rich.console import Console
|
|
8
13
|
|
|
9
14
|
from devcouncil.telemetry.traces import read_trace_events, read_trace_events_since
|
|
15
|
+
from devcouncil.utils.json_persist import dump_json
|
|
10
16
|
|
|
11
17
|
app = typer.Typer(help="Inspect DevCouncil trace events.")
|
|
12
18
|
console = Console()
|
|
@@ -37,15 +43,15 @@ def tail(
|
|
|
37
43
|
agent gets only the events appended after the cursor plus a ``next_cursor`` to
|
|
38
44
|
pass back on the next poll, so each poll is O(new) rather than O(all).
|
|
39
45
|
"""
|
|
40
|
-
|
|
46
|
+
root = project_root.expanduser().resolve()
|
|
41
47
|
|
|
42
48
|
# Incremental cursor mode: any of --since / --json / explicit --no-follow.
|
|
43
49
|
incremental = since is not None or json_summary
|
|
44
50
|
if incremental:
|
|
45
|
-
events, next_cursor = read_trace_events_since(
|
|
51
|
+
events, next_cursor = read_trace_events_since(root, since)
|
|
46
52
|
if json_summary:
|
|
47
53
|
typer.echo(
|
|
48
|
-
|
|
54
|
+
dump_json(
|
|
49
55
|
{
|
|
50
56
|
"events": [event.model_dump(by_alias=True) for event in events],
|
|
51
57
|
"next_cursor": next_cursor,
|
|
@@ -59,7 +65,7 @@ def tail(
|
|
|
59
65
|
else:
|
|
60
66
|
console.print(
|
|
61
67
|
f"{event.timestamp} {event.type} "
|
|
62
|
-
f"{event.task_id or '-'} {event.summary or
|
|
68
|
+
f"{event.task_id or '-'} {event.summary or dump_json(event.details)}"
|
|
63
69
|
)
|
|
64
70
|
console.print(f"[dim]next_cursor: {next_cursor}[/dim]")
|
|
65
71
|
return
|
|
@@ -67,7 +73,7 @@ def tail(
|
|
|
67
73
|
printed = 0
|
|
68
74
|
|
|
69
75
|
def emit_new(start_index: int) -> int:
|
|
70
|
-
events = list(read_trace_events(
|
|
76
|
+
events = list(read_trace_events(root))
|
|
71
77
|
selected = events[start_index:]
|
|
72
78
|
for event in selected:
|
|
73
79
|
if jsonl:
|
|
@@ -75,11 +81,11 @@ def tail(
|
|
|
75
81
|
else:
|
|
76
82
|
console.print(
|
|
77
83
|
f"{event.timestamp} {event.type} "
|
|
78
|
-
f"{event.task_id or '-'} {event.summary or
|
|
84
|
+
f"{event.task_id or '-'} {event.summary or dump_json(event.details)}"
|
|
79
85
|
)
|
|
80
86
|
return len(events)
|
|
81
87
|
|
|
82
|
-
events = list(read_trace_events(
|
|
88
|
+
events = list(read_trace_events(root))
|
|
83
89
|
start = max(0, len(events) - limit)
|
|
84
90
|
printed = emit_new(start)
|
|
85
91
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
import asyncio
|
|
3
|
-
import
|
|
3
|
+
from devcouncil.utils.json_persist import dump_json
|
|
4
4
|
import logging
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
from rich.table import Table
|
|
@@ -19,6 +19,7 @@ from devcouncil.app.config import load_config, get_api_key
|
|
|
19
19
|
from devcouncil.app.state_machine import ProjectPhase
|
|
20
20
|
from devcouncil.integrations.code_review_graph import CodeReviewGraphAdapter
|
|
21
21
|
from devcouncil.telemetry.traces import TraceLogger
|
|
22
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
22
23
|
|
|
23
24
|
console = Console()
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
@@ -64,11 +65,24 @@ def verify(
|
|
|
64
65
|
db = get_db(root)
|
|
65
66
|
if not db:
|
|
66
67
|
if json_format:
|
|
67
|
-
typer.echo(
|
|
68
|
+
typer.echo(dump_json({"ok": False, "error": "DevCouncil state is unavailable in this directory."}, indent=2))
|
|
68
69
|
else:
|
|
69
70
|
console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
|
|
70
71
|
return
|
|
71
72
|
|
|
73
|
+
with log_stage("verify", project_root=root, task_id=task_id or "ALL", sandbox=sandbox):
|
|
74
|
+
_run_verify_body(
|
|
75
|
+
root, task_id, sandbox, json_format, db,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _run_verify_body(
|
|
80
|
+
root: Path,
|
|
81
|
+
task_id: Optional[str],
|
|
82
|
+
sandbox: str,
|
|
83
|
+
json_format: bool,
|
|
84
|
+
db,
|
|
85
|
+
) -> None:
|
|
72
86
|
with db.get_session() as session:
|
|
73
87
|
task_repo = TaskRepository(session)
|
|
74
88
|
req_repo = RequirementRepository(session)
|
|
@@ -80,7 +94,7 @@ def verify(
|
|
|
80
94
|
if not tasks:
|
|
81
95
|
missing = f"Task {task_id} not found." if task_id else "No tasks found to verify."
|
|
82
96
|
if json_format:
|
|
83
|
-
typer.echo(
|
|
97
|
+
typer.echo(dump_json({"ok": False, "error": missing}, indent=2))
|
|
84
98
|
else:
|
|
85
99
|
console.print(f"[red]{missing}[/red]")
|
|
86
100
|
return
|
|
@@ -96,8 +110,9 @@ def verify(
|
|
|
96
110
|
provider = create_provider(config.models.provider, api_key, project_root=root, provider_prefs=config.provider)
|
|
97
111
|
role_config = {name: role.model_dump() for name, role in config.models.roles.items()}
|
|
98
112
|
router = ModelRouter(provider, role_config, project_root=root)
|
|
99
|
-
except Exception:
|
|
100
|
-
|
|
113
|
+
except Exception as e:
|
|
114
|
+
# Verification still runs, but silently without the LLM review layer.
|
|
115
|
+
logger.warning("Failed to build model router, verifying without LLM review: %s", e)
|
|
101
116
|
|
|
102
117
|
from devcouncil.verification.sandbox import get_sandbox
|
|
103
118
|
|
|
@@ -111,13 +126,19 @@ def verify(
|
|
|
111
126
|
per_task_gaps: dict[str, list] = {}
|
|
112
127
|
|
|
113
128
|
for task in tasks:
|
|
129
|
+
log_step(
|
|
130
|
+
f"verify task {task.id}",
|
|
131
|
+
project_root=root,
|
|
132
|
+
task_id=task.id,
|
|
133
|
+
sandbox=sandbox,
|
|
134
|
+
)
|
|
114
135
|
if sandbox != "local":
|
|
115
136
|
commands = task.expected_tests or task.allowed_commands
|
|
116
137
|
sandbox_result = get_sandbox(sandbox, root).run(task, commands, reqs)
|
|
117
138
|
if sandbox_result.status == "unsupported":
|
|
118
139
|
message = f"Sandbox {sandbox} is unavailable."
|
|
119
140
|
if json_format:
|
|
120
|
-
typer.echo(
|
|
141
|
+
typer.echo(dump_json({"ok": False, "error": message, "sandbox": sandbox}, indent=2))
|
|
121
142
|
else:
|
|
122
143
|
console.print(f"[red]{message}[/red]")
|
|
123
144
|
return
|
|
@@ -134,7 +155,7 @@ def verify(
|
|
|
134
155
|
"gaps": [],
|
|
135
156
|
})
|
|
136
157
|
if json_format:
|
|
137
|
-
typer.echo(
|
|
158
|
+
typer.echo(dump_json({
|
|
138
159
|
"ok": False,
|
|
139
160
|
"task_id": task.id,
|
|
140
161
|
"sandbox": sandbox,
|
|
@@ -231,6 +252,10 @@ def verify(
|
|
|
231
252
|
"diff_empty": outcome.diff_empty if outcome else False,
|
|
232
253
|
"coverage_measured": outcome.coverage_measured if outcome else False,
|
|
233
254
|
"coverage_skipped_reason": outcome.coverage_skipped_reason if outcome else None,
|
|
255
|
+
# Anti-laziness rigor: the task's difficulty classification and which
|
|
256
|
+
# escalations (blocking stub/effort gates, enforced coverage) applied.
|
|
257
|
+
"difficulty": outcome.difficulty if outcome else None,
|
|
258
|
+
"rigor_applied": list(outcome.rigor_applied) if outcome else [],
|
|
234
259
|
})
|
|
235
260
|
|
|
236
261
|
# Cross-task acceptance reconciliation (only meaningful across the full set).
|
|
@@ -276,7 +301,7 @@ def verify(
|
|
|
276
301
|
)
|
|
277
302
|
|
|
278
303
|
if json_format:
|
|
279
|
-
typer.echo(
|
|
304
|
+
typer.echo(dump_json({
|
|
280
305
|
"ok": blocked_tasks == 0,
|
|
281
306
|
"verified_tasks": len(tasks),
|
|
282
307
|
"blocked_tasks": blocked_tasks,
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import typer
|
|
2
3
|
from rich.console import Console
|
|
3
4
|
import importlib.metadata
|
|
4
5
|
|
|
6
|
+
from devcouncil.telemetry.stages import log_stage, log_step
|
|
7
|
+
|
|
5
8
|
app = typer.Typer()
|
|
6
9
|
console = Console()
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
7
11
|
|
|
8
12
|
@app.callback(invoke_without_command=True)
|
|
9
13
|
def version(ctx: typer.Context):
|
|
@@ -12,9 +16,13 @@ def version(ctx: typer.Context):
|
|
|
12
16
|
"""
|
|
13
17
|
if ctx.invoked_subcommand is not None:
|
|
14
18
|
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
logger.info("dev version")
|
|
21
|
+
with log_stage("version"):
|
|
22
|
+
log_step("version/1: reading package metadata", trace=True)
|
|
23
|
+
try:
|
|
24
|
+
ver = importlib.metadata.version("devcouncil")
|
|
25
|
+
console.print(f"DevCouncil version: [bold cyan]{ver}[/bold cyan]")
|
|
26
|
+
except importlib.metadata.PackageNotFoundError:
|
|
27
|
+
console.print("DevCouncil version: [yellow]unknown (editable/uninstalled)[/yellow]")
|
|
28
|
+
log_step("version/complete", trace=True)
|