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
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""Claude integration adapter."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from devcouncil.integrations.clients import common as _common
|
|
11
|
+
_project_root = _common._project_root
|
|
12
|
+
_warn_if_verify_only = _common._warn_if_verify_only
|
|
13
|
+
_server_args = _common._server_args
|
|
14
|
+
_format_command = _common._format_command
|
|
15
|
+
_quote_powershell_arg = _common._quote_powershell_arg
|
|
16
|
+
_run = _common._run
|
|
17
|
+
_run_capture = _common._run_capture
|
|
18
|
+
_config_path = _common._config_path
|
|
19
|
+
_load_raw_config = _common._load_raw_config
|
|
20
|
+
_save_raw_config = _common._save_raw_config
|
|
21
|
+
_load_json = _common._load_json
|
|
22
|
+
_save_json = _common._save_json
|
|
23
|
+
_load_json_strict = _common._load_json_strict
|
|
24
|
+
_mutate_raw_config = _common._mutate_raw_config
|
|
25
|
+
_batched_raw_config = _common._batched_raw_config
|
|
26
|
+
_probe_mcp_tools = _common._probe_mcp_tools
|
|
27
|
+
_print_command = _common._print_command
|
|
28
|
+
_configure = _common._configure
|
|
29
|
+
|
|
30
|
+
console = _common.console
|
|
31
|
+
|
|
32
|
+
from devcouncil.integrations.clients import hooks as _hooks # noqa: E402
|
|
33
|
+
_install_claude_hooks = _hooks._install_claude_hooks
|
|
34
|
+
def _claude_command(project_root: Path, scope: str) -> list[str]:
|
|
35
|
+
# The server name must come BEFORE --env: the current Claude CLI treats --env
|
|
36
|
+
# as variadic, so `--env KEY=VALUE devcouncil` swallows the name `devcouncil`
|
|
37
|
+
# as a second (invalid) env var. Putting the name first — matching the working
|
|
38
|
+
# codex form — and terminating options with `--` avoids that.
|
|
39
|
+
return [
|
|
40
|
+
"claude",
|
|
41
|
+
"mcp",
|
|
42
|
+
"add",
|
|
43
|
+
"--scope",
|
|
44
|
+
scope,
|
|
45
|
+
"devcouncil",
|
|
46
|
+
"--env",
|
|
47
|
+
f"DEVCOUNCIL_PROJECT_ROOT={project_root}",
|
|
48
|
+
"--",
|
|
49
|
+
*_server_args(project_root),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
def _devcouncil_version() -> str:
|
|
53
|
+
"""Package version for plugin manifests, or a stable placeholder when uninstalled."""
|
|
54
|
+
import importlib.metadata
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
return importlib.metadata.version("devcouncil")
|
|
58
|
+
except importlib.metadata.PackageNotFoundError:
|
|
59
|
+
return "0.0.0"
|
|
60
|
+
|
|
61
|
+
_CLAUDE_OUTPUT_STYLE = "DevCouncil"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _record_claude_config(
|
|
65
|
+
project_root: Path, *, scope: str = "project", write_gate: bool = False
|
|
66
|
+
) -> None:
|
|
67
|
+
def mutate(config: dict) -> None:
|
|
68
|
+
claude = config.setdefault("integrations", {}).setdefault("claude", {})
|
|
69
|
+
claude.update({
|
|
70
|
+
"enabled": True,
|
|
71
|
+
"scope": scope,
|
|
72
|
+
"write_gate": write_gate,
|
|
73
|
+
"settings_path": ".claude/settings.local.json",
|
|
74
|
+
})
|
|
75
|
+
_common.seed_stop_gate_assist_if_unset(config)
|
|
76
|
+
|
|
77
|
+
_mutate_raw_config(project_root, mutate)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _configured_advisor_model(project_root: Path) -> str | None:
|
|
81
|
+
"""Return pairing-safe advisor_model from the **default** CLI profile, if any.
|
|
82
|
+
|
|
83
|
+
Interactive Claude settings use the default profile only (not an arbitrary
|
|
84
|
+
first-matching profile) so ``advisorModel`` matches the usual integrate path.
|
|
85
|
+
Soft-skips clear bad pairs the CLI would also skip.
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
from devcouncil.executors.advisor_tool import advisor_pairing_ok
|
|
89
|
+
from devcouncil.executors.agent_registry import load_agent_profiles
|
|
90
|
+
|
|
91
|
+
profiles = load_agent_profiles(project_root)
|
|
92
|
+
except Exception:
|
|
93
|
+
return None
|
|
94
|
+
profile = profiles.get("default")
|
|
95
|
+
if profile is None:
|
|
96
|
+
return None
|
|
97
|
+
advisor = (getattr(profile, "advisor_model", None) or "").strip()
|
|
98
|
+
if not advisor:
|
|
99
|
+
return None
|
|
100
|
+
ok, _reason = advisor_pairing_ok(getattr(profile, "model", None), advisor)
|
|
101
|
+
if not ok:
|
|
102
|
+
return None
|
|
103
|
+
return advisor
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _default_profile_advisor_raw(project_root: Path) -> str | None:
|
|
107
|
+
"""Raw default-profile advisor_model (no pairing filter) for uninstall matching."""
|
|
108
|
+
try:
|
|
109
|
+
from devcouncil.executors.agent_registry import load_agent_profiles
|
|
110
|
+
|
|
111
|
+
profiles = load_agent_profiles(project_root)
|
|
112
|
+
except Exception:
|
|
113
|
+
return None
|
|
114
|
+
profile = profiles.get("default")
|
|
115
|
+
if profile is None:
|
|
116
|
+
return None
|
|
117
|
+
advisor = (getattr(profile, "advisor_model", None) or "").strip()
|
|
118
|
+
return advisor or None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _install_claude_settings(project_root: Path) -> tuple[Path, bool]:
|
|
122
|
+
"""Write the statusLine, MCP enablement, and permission allow-list into Claude settings.
|
|
123
|
+
|
|
124
|
+
Merges into .claude/settings.local.json without clobbering existing user entries.
|
|
125
|
+
When the default CLI profile configures a pairing-safe ``advisor_model``, merges
|
|
126
|
+
``advisorModel``; when unset or soft-skipped, leaves any existing ``advisorModel``
|
|
127
|
+
alone. Returns (path, changed); only rewrites the file when the merge changes
|
|
128
|
+
something so re-running integration is a true no-op."""
|
|
129
|
+
from devcouncil.integrations.claude_assets import claude_bash_permission_allow
|
|
130
|
+
|
|
131
|
+
path = project_root / ".claude" / "settings.local.json"
|
|
132
|
+
settings = _load_json(path)
|
|
133
|
+
before = json.dumps(settings, sort_keys=True)
|
|
134
|
+
|
|
135
|
+
settings["statusLine"] = {
|
|
136
|
+
"type": "command",
|
|
137
|
+
"command": _format_command([
|
|
138
|
+
_common.resolve_dev_executable(project_root),
|
|
139
|
+
"hook",
|
|
140
|
+
"claude-statusline",
|
|
141
|
+
"--project-root",
|
|
142
|
+
str(project_root),
|
|
143
|
+
]),
|
|
144
|
+
}
|
|
145
|
+
if settings.get("outputStyle") != _CLAUDE_OUTPUT_STYLE:
|
|
146
|
+
settings["outputStyle"] = _CLAUDE_OUTPUT_STYLE
|
|
147
|
+
# Auto-enable the project-scoped DevCouncil MCP server so a teammate cloning the repo
|
|
148
|
+
# doesn't have to approve it interactively.
|
|
149
|
+
enabled = settings.setdefault("enabledMcpjsonServers", [])
|
|
150
|
+
if isinstance(enabled, list) and "devcouncil" not in enabled:
|
|
151
|
+
enabled.append("devcouncil")
|
|
152
|
+
|
|
153
|
+
permissions = settings.setdefault("permissions", {})
|
|
154
|
+
if isinstance(permissions, dict):
|
|
155
|
+
allow = permissions.setdefault("allow", [])
|
|
156
|
+
if isinstance(allow, list):
|
|
157
|
+
for rule in claude_bash_permission_allow():
|
|
158
|
+
if rule not in allow:
|
|
159
|
+
allow.append(rule)
|
|
160
|
+
|
|
161
|
+
advisor = _configured_advisor_model(project_root)
|
|
162
|
+
if advisor:
|
|
163
|
+
settings["advisorModel"] = advisor
|
|
164
|
+
|
|
165
|
+
changed = json.dumps(settings, sort_keys=True) != before
|
|
166
|
+
if changed:
|
|
167
|
+
_save_json(path, settings)
|
|
168
|
+
return path, changed
|
|
169
|
+
|
|
170
|
+
def _selected_skill_assets(project_root: Path):
|
|
171
|
+
"""Scaffold the applicable skills and return them as GeneratedAsset-like records.
|
|
172
|
+
|
|
173
|
+
Returns (written_paths, skill_assets) where skill_assets carry (path, content) for the
|
|
174
|
+
plugin bundler so the plugin ships the same skill bodies that land in .claude/skills/."""
|
|
175
|
+
from devcouncil.integrations.claude_assets import GeneratedAsset
|
|
176
|
+
from devcouncil.skills.registry import scaffold_skills, select_skills
|
|
177
|
+
|
|
178
|
+
skills = select_skills("", project_root)
|
|
179
|
+
written = scaffold_skills(project_root, skills)
|
|
180
|
+
assets: list[GeneratedAsset] = []
|
|
181
|
+
skills_root = project_root / ".claude" / "skills"
|
|
182
|
+
for skill in skills:
|
|
183
|
+
target = skills_root / skill.name / "SKILL.md"
|
|
184
|
+
if target.exists():
|
|
185
|
+
assets.append(GeneratedAsset(target, target.read_text(encoding="utf-8")))
|
|
186
|
+
return written, assets
|
|
187
|
+
|
|
188
|
+
def _install_claude_assets(project_root: Path) -> list[Path]:
|
|
189
|
+
"""Generate the static Claude Code asset surface (commands, agents, output style,
|
|
190
|
+
statusline, permissions) and scaffold the applicable skills. Idempotent."""
|
|
191
|
+
from devcouncil.integrations import claude_assets
|
|
192
|
+
|
|
193
|
+
written: list[Path] = []
|
|
194
|
+
assets: list[claude_assets.GeneratedAsset] = []
|
|
195
|
+
assets += claude_assets.build_slash_commands(project_root)
|
|
196
|
+
assets += claude_assets.build_subagents(project_root)
|
|
197
|
+
assets += claude_assets.build_output_style(project_root)
|
|
198
|
+
for asset in assets:
|
|
199
|
+
if asset.write_if_changed():
|
|
200
|
+
written.append(asset.path)
|
|
201
|
+
|
|
202
|
+
skills_written, _ = _selected_skill_assets(project_root)
|
|
203
|
+
written.extend(skills_written)
|
|
204
|
+
settings_path, settings_changed = _install_claude_settings(project_root)
|
|
205
|
+
if settings_changed:
|
|
206
|
+
written.append(settings_path)
|
|
207
|
+
return written
|
|
208
|
+
|
|
209
|
+
def _install_claude_plugin(project_root: Path, *, write_gate: bool = False) -> list[Path]:
|
|
210
|
+
"""Build the self-contained Claude Code plugin + single-repo marketplace bundle.
|
|
211
|
+
|
|
212
|
+
Bundles the commands, agents, applicable skills, hooks, and MCP config so the entire
|
|
213
|
+
DevCouncil integration installs with one `/plugin install`. Assist-mode hooks by
|
|
214
|
+
default; pass write_gate=True to bundle the blocking containment gate."""
|
|
215
|
+
from devcouncil.integrations import claude_assets
|
|
216
|
+
|
|
217
|
+
_, skill_assets = _selected_skill_assets(project_root)
|
|
218
|
+
bundle = claude_assets.build_plugin_bundle(
|
|
219
|
+
project_root, version=_devcouncil_version(), skill_assets=skill_assets, write_gate=write_gate
|
|
220
|
+
)
|
|
221
|
+
return [asset.path for asset in bundle if asset.write_if_changed()]
|
|
222
|
+
|
|
223
|
+
def _uninstall_claude(project_root: Path) -> list[str]:
|
|
224
|
+
"""Remove everything DevCouncil installed into a Claude Code project. Idempotent.
|
|
225
|
+
|
|
226
|
+
Strips DevCouncil's hooks (every event), the DevCouncil statusLine, the MCP enablement
|
|
227
|
+
and permission rules from .claude/settings.local.json, pops DevCouncil-written
|
|
228
|
+
``advisorModel`` when it matches the default profile value, deletes the generated
|
|
229
|
+
commands/subagents/output-style files, and best-effort de-registers the MCP server
|
|
230
|
+
via `claude mcp remove`. Returns a list of the changes made. The recoverable,
|
|
231
|
+
in-band counterpart to a fail-closed write-gate."""
|
|
232
|
+
removed: list[str] = []
|
|
233
|
+
path = project_root / ".claude" / "settings.local.json"
|
|
234
|
+
settings = _load_json(path)
|
|
235
|
+
before = json.dumps(settings, sort_keys=True)
|
|
236
|
+
|
|
237
|
+
# Hooks: drop any entry whose command invokes `devcouncil hook`, then prune empties.
|
|
238
|
+
hooks = settings.get("hooks")
|
|
239
|
+
if isinstance(hooks, dict):
|
|
240
|
+
for event in list(hooks):
|
|
241
|
+
groups = hooks.get(event)
|
|
242
|
+
if not isinstance(groups, list):
|
|
243
|
+
continue
|
|
244
|
+
kept_groups = []
|
|
245
|
+
for group in groups:
|
|
246
|
+
inner = group.get("hooks", []) if isinstance(group, dict) else []
|
|
247
|
+
inner_kept = [
|
|
248
|
+
h for h in inner
|
|
249
|
+
if "devcouncil hook" not in str(h.get("command", ""))
|
|
250
|
+
]
|
|
251
|
+
if inner_kept:
|
|
252
|
+
group["hooks"] = inner_kept
|
|
253
|
+
kept_groups.append(group)
|
|
254
|
+
if kept_groups:
|
|
255
|
+
hooks[event] = kept_groups
|
|
256
|
+
else:
|
|
257
|
+
hooks.pop(event)
|
|
258
|
+
if not hooks:
|
|
259
|
+
settings.pop("hooks")
|
|
260
|
+
removed.append(f"hooks in {path.name}")
|
|
261
|
+
|
|
262
|
+
# statusLine: only remove ours.
|
|
263
|
+
status = settings.get("statusLine")
|
|
264
|
+
if isinstance(status, dict) and "devcouncil" in str(status.get("command", "")):
|
|
265
|
+
settings.pop("statusLine")
|
|
266
|
+
removed.append("statusLine")
|
|
267
|
+
|
|
268
|
+
if settings.get("outputStyle") == _CLAUDE_OUTPUT_STYLE:
|
|
269
|
+
settings.pop("outputStyle", None)
|
|
270
|
+
removed.append("outputStyle")
|
|
271
|
+
|
|
272
|
+
enabled = settings.get("enabledMcpjsonServers")
|
|
273
|
+
if isinstance(enabled, list) and "devcouncil" in enabled:
|
|
274
|
+
enabled.remove("devcouncil")
|
|
275
|
+
if not enabled:
|
|
276
|
+
settings.pop("enabledMcpjsonServers")
|
|
277
|
+
removed.append("enabledMcpjsonServers entry")
|
|
278
|
+
|
|
279
|
+
permissions = settings.get("permissions")
|
|
280
|
+
if isinstance(permissions, dict) and isinstance(permissions.get("allow"), list):
|
|
281
|
+
from devcouncil.integrations.claude_assets import claude_bash_permission_allow
|
|
282
|
+
|
|
283
|
+
ours = set(claude_bash_permission_allow())
|
|
284
|
+
kept = [r for r in permissions["allow"] if r not in ours]
|
|
285
|
+
if len(kept) != len(permissions["allow"]):
|
|
286
|
+
permissions["allow"] = kept
|
|
287
|
+
removed.append("permission allow-rules")
|
|
288
|
+
if not permissions.get("allow"):
|
|
289
|
+
permissions.pop("allow", None)
|
|
290
|
+
if not permissions:
|
|
291
|
+
settings.pop("permissions")
|
|
292
|
+
|
|
293
|
+
# Pop DevCouncil-written advisorModel when it matches the default profile value
|
|
294
|
+
# (pairing-safe or raw) that integrate would have written.
|
|
295
|
+
current_advisor = settings.get("advisorModel")
|
|
296
|
+
if isinstance(current_advisor, str) and current_advisor.strip():
|
|
297
|
+
candidates = {
|
|
298
|
+
value
|
|
299
|
+
for value in (
|
|
300
|
+
_configured_advisor_model(project_root),
|
|
301
|
+
_default_profile_advisor_raw(project_root),
|
|
302
|
+
)
|
|
303
|
+
if value
|
|
304
|
+
}
|
|
305
|
+
if current_advisor.strip() in candidates:
|
|
306
|
+
settings.pop("advisorModel", None)
|
|
307
|
+
removed.append("advisorModel")
|
|
308
|
+
|
|
309
|
+
if json.dumps(settings, sort_keys=True) != before:
|
|
310
|
+
if settings:
|
|
311
|
+
_save_json(path, settings)
|
|
312
|
+
elif path.exists():
|
|
313
|
+
path.unlink()
|
|
314
|
+
removed.append(f"deleted empty {path.name}")
|
|
315
|
+
|
|
316
|
+
# Generated asset files.
|
|
317
|
+
targets = [
|
|
318
|
+
project_root / ".claude" / "commands" / "devcouncil",
|
|
319
|
+
project_root / ".claude" / "output-styles" / "devcouncil.md",
|
|
320
|
+
]
|
|
321
|
+
targets += [
|
|
322
|
+
project_root / ".claude" / "agents" / f"{name}.md"
|
|
323
|
+
for name in ("devcouncil-implementer", "devcouncil-verifier", "devcouncil-reviewer")
|
|
324
|
+
]
|
|
325
|
+
for target in targets:
|
|
326
|
+
if target.is_dir():
|
|
327
|
+
shutil.rmtree(target)
|
|
328
|
+
removed.append(str(target.relative_to(project_root)))
|
|
329
|
+
elif target.exists():
|
|
330
|
+
target.unlink()
|
|
331
|
+
removed.append(str(target.relative_to(project_root)))
|
|
332
|
+
|
|
333
|
+
# De-register the MCP server (best-effort; only if the claude CLI is present).
|
|
334
|
+
if shutil.which("claude"):
|
|
335
|
+
code = _run(["claude", "mcp", "remove", "devcouncil"])
|
|
336
|
+
if code == 0:
|
|
337
|
+
removed.append("claude mcp server registration")
|
|
338
|
+
|
|
339
|
+
return removed
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Codex integration adapter."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from devcouncil.integrations.clients import common as _common
|
|
9
|
+
_project_root = _common._project_root
|
|
10
|
+
_warn_if_verify_only = _common._warn_if_verify_only
|
|
11
|
+
_server_args = _common._server_args
|
|
12
|
+
_format_command = _common._format_command
|
|
13
|
+
_quote_powershell_arg = _common._quote_powershell_arg
|
|
14
|
+
_run = _common._run
|
|
15
|
+
_run_capture = _common._run_capture
|
|
16
|
+
_config_path = _common._config_path
|
|
17
|
+
_load_raw_config = _common._load_raw_config
|
|
18
|
+
_save_raw_config = _common._save_raw_config
|
|
19
|
+
_load_json = _common._load_json
|
|
20
|
+
_save_json = _common._save_json
|
|
21
|
+
_load_json_strict = _common._load_json_strict
|
|
22
|
+
_mutate_raw_config = _common._mutate_raw_config
|
|
23
|
+
_batched_raw_config = _common._batched_raw_config
|
|
24
|
+
_probe_mcp_tools = _common._probe_mcp_tools
|
|
25
|
+
_print_command = _common._print_command
|
|
26
|
+
_configure = _common._configure
|
|
27
|
+
|
|
28
|
+
console = _common.console
|
|
29
|
+
def _codex_command(project_root: Path) -> list[str]:
|
|
30
|
+
return [
|
|
31
|
+
"codex",
|
|
32
|
+
"mcp",
|
|
33
|
+
"add",
|
|
34
|
+
"devcouncil",
|
|
35
|
+
"--env",
|
|
36
|
+
f"DEVCOUNCIL_PROJECT_ROOT={project_root}",
|
|
37
|
+
"--",
|
|
38
|
+
*_server_args(project_root),
|
|
39
|
+
]
|