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
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
10
|
from devcouncil.integrations.check import build_integration_check_report
|
|
11
|
+
from devcouncil.executors.agent_registry import GEMINI_DEPRECATION_MESSAGE
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
13
14
|
|
|
@@ -18,6 +19,7 @@ VALID_INTEGRATION_TARGETS = {
|
|
|
18
19
|
"gemini",
|
|
19
20
|
"claude",
|
|
20
21
|
"cursor",
|
|
22
|
+
"grok",
|
|
21
23
|
"opencode",
|
|
22
24
|
"antigravity",
|
|
23
25
|
"agy",
|
|
@@ -104,6 +106,11 @@ def apply_integration_target(
|
|
|
104
106
|
add_result(name, code == 0, None, "MCP registration command exited " + str(code))
|
|
105
107
|
|
|
106
108
|
def apply_project_file(name: str) -> None:
|
|
109
|
+
if name == "grok":
|
|
110
|
+
ok = integrate._configure_grok(root, apply=True)
|
|
111
|
+
path = integrate._grok_config_path(root)
|
|
112
|
+
add_result(name, ok, path if path.exists() else None, "Grok MCP integration configured." if ok else "Grok MCP integration failed.")
|
|
113
|
+
return
|
|
107
114
|
writers = {
|
|
108
115
|
"cursor": integrate._write_cursor_config,
|
|
109
116
|
"opencode": integrate._write_opencode_config,
|
|
@@ -126,7 +133,8 @@ def apply_integration_target(
|
|
|
126
133
|
|
|
127
134
|
def apply_hooks() -> None:
|
|
128
135
|
integrate._configure_native_hooks(root, "all", apply=True, claude_write_gate=claude_write_gate)
|
|
129
|
-
|
|
136
|
+
integrate._install_git_map_hooks(root, apply=True)
|
|
137
|
+
add_result("hooks", True, None, "Native hook files configured (incl. git map --if-stale).")
|
|
130
138
|
|
|
131
139
|
def apply_claude_assets() -> None:
|
|
132
140
|
try:
|
|
@@ -134,16 +142,21 @@ def apply_integration_target(
|
|
|
134
142
|
except (ValueError, FileNotFoundError, OSError) as exc:
|
|
135
143
|
add_result("claude-assets", False, None, f"Claude asset setup failed: {exc}")
|
|
136
144
|
return
|
|
145
|
+
integrate._record_claude_config(
|
|
146
|
+
root,
|
|
147
|
+
scope=claude_scope,
|
|
148
|
+
write_gate=claude_write_gate,
|
|
149
|
+
)
|
|
137
150
|
add_result("claude-assets", True, None, f"Claude assets installed ({len(written)} file(s)).")
|
|
138
151
|
|
|
139
152
|
if normalized == "all":
|
|
140
|
-
for name in ("
|
|
153
|
+
for name in ("claude", "codex"):
|
|
141
154
|
apply_first_party(name)
|
|
142
155
|
# Batch the per-tool config.yaml record updates (project files, aider,
|
|
143
156
|
# and native hooks) into one load/save cycle. _batched_raw_config is
|
|
144
157
|
# re-entrant, so apply_hooks()'s own batching participates in this one.
|
|
145
158
|
with integrate._batched_raw_config(root):
|
|
146
|
-
for name in ("cursor", "opencode", "antigravity", "warp"):
|
|
159
|
+
for name in ("cursor", "grok", "opencode", "antigravity", "warp"):
|
|
147
160
|
apply_project_file(name)
|
|
148
161
|
apply_aider()
|
|
149
162
|
if include_hooks:
|
|
@@ -153,8 +166,18 @@ def apply_integration_target(
|
|
|
153
166
|
# the claude CLI is on PATH, since these are plain files.
|
|
154
167
|
apply_claude_assets()
|
|
155
168
|
elif normalized in {"codex", "gemini", "claude"}:
|
|
169
|
+
if normalized == "gemini":
|
|
170
|
+
warnings.append(GEMINI_DEPRECATION_MESSAGE)
|
|
171
|
+
integrate.console.print(f"[yellow]{GEMINI_DEPRECATION_MESSAGE}[/yellow]")
|
|
156
172
|
apply_first_party(normalized)
|
|
157
|
-
|
|
173
|
+
if include_hooks and normalized == "codex":
|
|
174
|
+
integrate._configure_native_hooks(root, "codex", apply=True)
|
|
175
|
+
add_result("codex-hooks", True, root / ".codex" / "hooks.json", "Codex native hooks configured; review trust with /hooks.")
|
|
176
|
+
elif include_hooks and normalized == "claude":
|
|
177
|
+
integrate._install_claude_hooks(root, write_gate=claude_write_gate)
|
|
178
|
+
apply_claude_assets()
|
|
179
|
+
add_result("claude-hooks", True, root / ".claude" / "settings.local.json", "Claude native hooks configured.")
|
|
180
|
+
elif normalized in {"cursor", "grok", "opencode", "antigravity", "warp"}:
|
|
158
181
|
apply_project_file(normalized)
|
|
159
182
|
elif normalized == "aider":
|
|
160
183
|
apply_aider()
|
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import shutil
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
+
import tomllib
|
|
9
10
|
from dataclasses import dataclass
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any
|
|
@@ -13,12 +14,15 @@ from typing import Any
|
|
|
13
14
|
from devcouncil.executors.agent_registry import (
|
|
14
15
|
CODING_CLI_INTEGRATION_INFO,
|
|
15
16
|
CODING_CLI_VERSION_COMMANDS,
|
|
17
|
+
DEPRECATED_CODING_CLIS,
|
|
18
|
+
GEMINI_DEPRECATION_MESSAGE,
|
|
16
19
|
detect_available_coding_cli,
|
|
17
20
|
resolve_automated_executor,
|
|
18
21
|
resolve_coding_cli_executable,
|
|
19
22
|
resolve_coding_cli_probe_order,
|
|
20
23
|
)
|
|
21
24
|
from devcouncil.utils.subprocess_env import clean_subprocess_env
|
|
25
|
+
from devcouncil.utils.json_persist import read_json
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
@dataclass(frozen=True)
|
|
@@ -58,6 +62,7 @@ CODING_CLI_CHECK_LABELS: dict[str, str] = {
|
|
|
58
62
|
"gemini": "Gemini CLI",
|
|
59
63
|
"claude": "Claude Code",
|
|
60
64
|
"cursor": "Cursor",
|
|
65
|
+
"grok": "Grok Build",
|
|
61
66
|
"opencode": "OpenCode",
|
|
62
67
|
"antigravity": "Google Antigravity CLI",
|
|
63
68
|
"warp": "Warp / Oz",
|
|
@@ -127,19 +132,123 @@ def _load_json_file(path: Path) -> dict[str, Any]:
|
|
|
127
132
|
if not path.exists():
|
|
128
133
|
return {}
|
|
129
134
|
try:
|
|
130
|
-
loaded =
|
|
135
|
+
loaded = read_json(path) or {}
|
|
131
136
|
except json.JSONDecodeError:
|
|
132
137
|
return {}
|
|
133
138
|
return loaded if isinstance(loaded, dict) else {}
|
|
134
139
|
|
|
135
140
|
|
|
141
|
+
def _claude_config_status(project_root: Path) -> tuple[str, bool, list[str]]:
|
|
142
|
+
settings_path = project_root / ".claude" / "settings.local.json"
|
|
143
|
+
assets_dir = project_root / ".claude" / "commands" / "devcouncil"
|
|
144
|
+
data = _load_json_file(settings_path)
|
|
145
|
+
status_line = data.get("statusLine") if data else {}
|
|
146
|
+
status_line_ok = (
|
|
147
|
+
isinstance(status_line, dict)
|
|
148
|
+
and "hook claude-statusline" in str(status_line.get("command", ""))
|
|
149
|
+
)
|
|
150
|
+
mcp_ok = "devcouncil" in (data.get("enabledMcpjsonServers") or [])
|
|
151
|
+
assets_ok = assets_dir.is_dir() and (assets_dir / "status.md").exists()
|
|
152
|
+
ok = (status_line_ok or mcp_ok) and assets_ok
|
|
153
|
+
paths = [str(settings_path)]
|
|
154
|
+
if assets_ok:
|
|
155
|
+
paths.append(str(assets_dir))
|
|
156
|
+
if ok:
|
|
157
|
+
return "ok", False, paths
|
|
158
|
+
if not settings_path.exists() and not assets_ok:
|
|
159
|
+
return "missing", True, paths
|
|
160
|
+
return "drifted", True, paths
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _codex_config_status(project_root: Path) -> tuple[str, bool, list[str]]:
|
|
164
|
+
"""Check the client-persisted MCP registration, not just repo hook files."""
|
|
165
|
+
executable = resolve_coding_cli_executable(project_root, "codex")
|
|
166
|
+
logical_path = "codex mcp:devcouncil"
|
|
167
|
+
if not executable:
|
|
168
|
+
return "missing", True, [logical_path]
|
|
169
|
+
try:
|
|
170
|
+
result = subprocess.run(
|
|
171
|
+
[executable, "mcp", "get", "devcouncil", "--json"],
|
|
172
|
+
cwd=project_root,
|
|
173
|
+
capture_output=True,
|
|
174
|
+
text=True,
|
|
175
|
+
encoding="utf-8",
|
|
176
|
+
errors="replace",
|
|
177
|
+
timeout=10,
|
|
178
|
+
env=clean_subprocess_env(),
|
|
179
|
+
)
|
|
180
|
+
data = json.loads(result.stdout) if result.returncode == 0 else {}
|
|
181
|
+
except (OSError, subprocess.TimeoutExpired, json.JSONDecodeError):
|
|
182
|
+
data = {}
|
|
183
|
+
|
|
184
|
+
from devcouncil.integrations.clients.common import _server_args
|
|
185
|
+
|
|
186
|
+
expected = _server_args(project_root)
|
|
187
|
+
transport = data.get("transport") if isinstance(data, dict) else {}
|
|
188
|
+
transport = transport if isinstance(transport, dict) else {}
|
|
189
|
+
env = transport.get("env") or {}
|
|
190
|
+
ok = (
|
|
191
|
+
data.get("enabled") is True
|
|
192
|
+
and transport.get("type") == "stdio"
|
|
193
|
+
and transport.get("command") == expected[0]
|
|
194
|
+
and transport.get("args") == expected[1:]
|
|
195
|
+
and isinstance(env, dict)
|
|
196
|
+
and env.get("DEVCOUNCIL_PROJECT_ROOT") == str(project_root)
|
|
197
|
+
)
|
|
198
|
+
return ("ok" if ok else "drifted"), not ok, [logical_path, str(project_root / ".codex" / "hooks.json")]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _codex_hook_schema_status(project_root: Path) -> tuple[bool, str]:
|
|
202
|
+
"""Validate the repository Codex hook schema and canonical feature flag."""
|
|
203
|
+
hooks_path = project_root / ".codex" / "hooks.json"
|
|
204
|
+
config_path = project_root / ".codex" / "config.toml"
|
|
205
|
+
try:
|
|
206
|
+
data = read_json(hooks_path)
|
|
207
|
+
config = tomllib.loads(config_path.read_text(encoding="utf-8"))
|
|
208
|
+
except (OSError, json.JSONDecodeError, tomllib.TOMLDecodeError) as exc:
|
|
209
|
+
return False, f"Invalid Codex hook configuration: {exc}"
|
|
210
|
+
hooks = data.get("hooks") if isinstance(data, dict) else None
|
|
211
|
+
required = {"PreToolUse", "PostToolUse", "SessionStart", "Stop", "SubagentStop"}
|
|
212
|
+
if not isinstance(hooks, dict) or not required.issubset(hooks):
|
|
213
|
+
missing = sorted(required - set(hooks or {}))
|
|
214
|
+
return False, f"Missing Codex hook events: {', '.join(missing)}"
|
|
215
|
+
for event, groups in hooks.items():
|
|
216
|
+
if not isinstance(groups, list):
|
|
217
|
+
return False, f"Codex hook event {event} must contain a list"
|
|
218
|
+
for group in groups:
|
|
219
|
+
for hook in group.get("hooks", []) if isinstance(group, dict) else []:
|
|
220
|
+
if not isinstance(hook, dict) or not str(hook.get("name", "")).startswith("devcouncil-"):
|
|
221
|
+
continue
|
|
222
|
+
timeout = hook.get("timeout")
|
|
223
|
+
if not isinstance(timeout, int) or not 1 <= timeout <= 600:
|
|
224
|
+
return False, f"Codex hook {hook.get('name')} timeout must be 1..600 seconds"
|
|
225
|
+
if "--client codex" not in str(hook.get("command", "")):
|
|
226
|
+
return False, f"Codex hook {hook.get('name')} does not select the Codex schema"
|
|
227
|
+
features = config.get("features") if isinstance(config, dict) else {}
|
|
228
|
+
if not isinstance(features, dict) or features.get("hooks") is not True:
|
|
229
|
+
return False, "[features].hooks must be true"
|
|
230
|
+
if "codex_hooks" in features:
|
|
231
|
+
return False, "Deprecated [features].codex_hooks is still present"
|
|
232
|
+
return True, f"{hooks_path}; hook trust must be reviewed in Codex with /hooks"
|
|
233
|
+
|
|
234
|
+
|
|
136
235
|
def _cursor_config_status(project_root: Path) -> tuple[str, bool, list[str]]:
|
|
137
236
|
path = project_root / ".cursor" / "mcp.json"
|
|
138
237
|
data = _load_json_file(path)
|
|
139
238
|
server = ((data.get("mcpServers") or {}).get("devcouncil") or {}) if data else {}
|
|
239
|
+
command = server.get("command")
|
|
240
|
+
# Accept bare ``devcouncil`` or an absolute/relative path whose basename is
|
|
241
|
+
# ``devcouncil`` (project venv). Both are valid; absolute paths are preferred
|
|
242
|
+
# when Cursor's shell PATH does not include the project venv.
|
|
243
|
+
from pathlib import Path as _Path
|
|
244
|
+
|
|
245
|
+
name_ok = False
|
|
246
|
+
if isinstance(command, str) and command.strip():
|
|
247
|
+
name = _Path(command.replace("\\", "/")).name.lower()
|
|
248
|
+
name_ok = name in {"devcouncil", "devcouncil.exe"}
|
|
140
249
|
ok = (
|
|
141
250
|
server.get("type") == "stdio"
|
|
142
|
-
and
|
|
251
|
+
and name_ok
|
|
143
252
|
and server.get("args") == ["mcp-server"]
|
|
144
253
|
and (server.get("env") or {}).get("DEVCOUNCIL_PROJECT_ROOT") == str(project_root)
|
|
145
254
|
)
|
|
@@ -148,6 +257,22 @@ def _cursor_config_status(project_root: Path) -> tuple[str, bool, list[str]]:
|
|
|
148
257
|
return ("missing" if not path.exists() else "drifted"), True, [str(path)]
|
|
149
258
|
|
|
150
259
|
|
|
260
|
+
def _grok_config_status(project_root: Path) -> tuple[str, bool, list[str]]:
|
|
261
|
+
path = project_root / ".grok" / "config.toml"
|
|
262
|
+
if not path.exists():
|
|
263
|
+
return "missing", True, [str(path)]
|
|
264
|
+
text = path.read_text(encoding="utf-8")
|
|
265
|
+
ok = (
|
|
266
|
+
"[mcp_servers.devcouncil]" in text
|
|
267
|
+
and "devcouncil" in text
|
|
268
|
+
and "mcp-server" in text
|
|
269
|
+
and str(project_root) in text
|
|
270
|
+
)
|
|
271
|
+
if ok:
|
|
272
|
+
return "ok", False, [str(path)]
|
|
273
|
+
return "drifted", True, [str(path)]
|
|
274
|
+
|
|
275
|
+
|
|
151
276
|
def _opencode_config_status(project_root: Path) -> tuple[str, bool, list[str]]:
|
|
152
277
|
path = project_root / "opencode.json"
|
|
153
278
|
data = _load_json_file(path)
|
|
@@ -203,8 +328,14 @@ def integration_capability_rows(project_root: Path) -> list[dict[str, object]]:
|
|
|
203
328
|
config_status = "not_applicable"
|
|
204
329
|
fixable = bool(info.mcp or info.hooks or info.launcher_shim)
|
|
205
330
|
paths: list[str] = []
|
|
206
|
-
if client == "
|
|
331
|
+
if client == "codex":
|
|
332
|
+
config_status, fixable, paths = _codex_config_status(project_root)
|
|
333
|
+
elif client == "cursor":
|
|
207
334
|
config_status, fixable, paths = _cursor_config_status(project_root)
|
|
335
|
+
elif client == "grok":
|
|
336
|
+
config_status, fixable, paths = _grok_config_status(project_root)
|
|
337
|
+
elif client == "claude":
|
|
338
|
+
config_status, fixable, paths = _claude_config_status(project_root)
|
|
208
339
|
elif client == "opencode":
|
|
209
340
|
config_status, fixable, paths = _opencode_config_status(project_root)
|
|
210
341
|
elif client == "antigravity":
|
|
@@ -235,20 +366,28 @@ def integration_capability_rows(project_root: Path) -> list[dict[str, object]]:
|
|
|
235
366
|
def _hook_config_references_devcouncil(path: Path) -> bool | None:
|
|
236
367
|
"""Return whether a client hook config still wires DevCouncil's gate.
|
|
237
368
|
|
|
238
|
-
``True`` -> the file exists and
|
|
369
|
+
``True`` -> the file exists and still invokes the DevCouncil pre/post-tool gate.
|
|
239
370
|
``False`` -> the file exists but no longer references it (tampered/disarmed).
|
|
240
371
|
``None`` -> the file does not exist (client was never integrated here).
|
|
241
372
|
|
|
242
373
|
Reads the raw text rather than parsing each client's bespoke schema so it works
|
|
243
374
|
uniformly across JSON hook files and is resilient to format drift; the goal is a
|
|
244
|
-
tamper tripwire, not full schema validation.
|
|
375
|
+
tamper tripwire, not full schema validation.
|
|
376
|
+
|
|
377
|
+
Accepts either an explicit ``devcouncil`` marker (named hooks / ``devcouncil`` CLI)
|
|
378
|
+
or the project-venv ``dev hook …`` command Cursor installs (no named hook id).
|
|
379
|
+
"""
|
|
245
380
|
if not path.exists():
|
|
246
381
|
return None
|
|
247
382
|
try:
|
|
248
383
|
text = path.read_text(encoding="utf-8")
|
|
249
384
|
except OSError:
|
|
250
385
|
return False
|
|
251
|
-
|
|
386
|
+
lower = text.lower()
|
|
387
|
+
if "devcouncil" in lower:
|
|
388
|
+
return True
|
|
389
|
+
# Cursor flat hooks: ``…/bin/dev hook pre-tool-use --client cursor …``
|
|
390
|
+
return "hook pre-tool-use" in lower or "hook post-tool-use" in lower
|
|
252
391
|
|
|
253
392
|
|
|
254
393
|
def _hook_config_tamper_targets(project_root: Path) -> list[tuple[str, Path]]:
|
|
@@ -257,6 +396,7 @@ def _hook_config_tamper_targets(project_root: Path) -> list[tuple[str, Path]]:
|
|
|
257
396
|
("Codex", project_root / ".codex" / "hooks.json"),
|
|
258
397
|
("Gemini", project_root / ".gemini" / "settings.json"),
|
|
259
398
|
("Cursor", project_root / ".cursor" / "hooks.json"),
|
|
399
|
+
("Grok", project_root / ".grok" / "hooks" / "devcouncil.json"),
|
|
260
400
|
]
|
|
261
401
|
|
|
262
402
|
|
|
@@ -273,9 +413,9 @@ def build_integration_check_report(project_root: Path, *, strict: bool = False)
|
|
|
273
413
|
failures += 1
|
|
274
414
|
|
|
275
415
|
def add_optional(ok: bool, name: str, details: str) -> None:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
416
|
+
# Coding CLIs (and similar optional probes) stay informational even under
|
|
417
|
+
# --strict. Absence is never a failure; --strict still elevates real
|
|
418
|
+
# project/integration defects via add() / add_soft().
|
|
279
419
|
rows.append(IntegrationCheckRow(name=name, status="ok" if ok else "missing", details=details))
|
|
280
420
|
|
|
281
421
|
def add_skip(name: str, details: str) -> None:
|
|
@@ -284,20 +424,29 @@ def build_integration_check_report(project_root: Path, *, strict: bool = False)
|
|
|
284
424
|
root = project_root.expanduser().resolve()
|
|
285
425
|
detected = detect_available_coding_cli(root)
|
|
286
426
|
add((root / ".devcouncil").exists(), "Project state", str(root / ".devcouncil"))
|
|
287
|
-
|
|
427
|
+
from devcouncil.integrations.clients.common import resolve_dev_executable
|
|
428
|
+
|
|
429
|
+
devcouncil_path = resolve_dev_executable(root)
|
|
430
|
+
resolved_dev = Path(devcouncil_path).exists() or shutil.which(devcouncil_path) is not None
|
|
431
|
+
devcouncil_launch = [devcouncil_path] if resolved_dev else [sys.executable, "-m", "devcouncil"]
|
|
288
432
|
add(
|
|
289
|
-
|
|
433
|
+
resolved_dev or Path(sys.executable).exists(),
|
|
290
434
|
"devcouncil CLI",
|
|
291
|
-
|
|
435
|
+
" ".join(devcouncil_launch),
|
|
292
436
|
)
|
|
293
437
|
|
|
294
|
-
devcouncil_launch = [devcouncil_path] if devcouncil_path else [sys.executable, "-m", "devcouncil"]
|
|
295
438
|
code, output = integrate._run_capture([*devcouncil_launch, "--help"])
|
|
296
439
|
add(code == 0, "devcouncil command", output.splitlines()[0] if output else "No output")
|
|
297
440
|
|
|
298
|
-
|
|
441
|
+
probe_order = resolve_coding_cli_probe_order(root)
|
|
442
|
+
for client in probe_order:
|
|
299
443
|
cli_ok, cli_details = probe_coding_cli_version(client)
|
|
300
444
|
add_optional(cli_ok, CODING_CLI_CHECK_LABELS.get(client, client), cli_details)
|
|
445
|
+
for client in sorted(DEPRECATED_CODING_CLIS):
|
|
446
|
+
if client in probe_order:
|
|
447
|
+
continue
|
|
448
|
+
label = CODING_CLI_CHECK_LABELS.get(client, client)
|
|
449
|
+
add_skip(label, GEMINI_DEPRECATION_MESSAGE)
|
|
301
450
|
|
|
302
451
|
rec_ok, rec_details = recommended_executor_status(root, detected)
|
|
303
452
|
add_optional(rec_ok, "Recommended coding CLI", rec_details)
|
|
@@ -320,7 +469,7 @@ def build_integration_check_report(project_root: Path, *, strict: bool = False)
|
|
|
320
469
|
hooks_ok = False
|
|
321
470
|
if cursor_hooks.exists():
|
|
322
471
|
try:
|
|
323
|
-
hooks_data =
|
|
472
|
+
hooks_data = read_json(cursor_hooks) or {}
|
|
324
473
|
hooks_ok = "preToolUse" in hooks_data.get("hooks", {})
|
|
325
474
|
except json.JSONDecodeError:
|
|
326
475
|
hooks_ok = False
|
|
@@ -329,6 +478,29 @@ def build_integration_check_report(project_root: Path, *, strict: bool = False)
|
|
|
329
478
|
"Cursor hooks",
|
|
330
479
|
str(cursor_hooks) if hooks_ok else "Run dev integrate hooks --apply --tool cursor.",
|
|
331
480
|
)
|
|
481
|
+
from devcouncil.integrations.clients.cursor import probe_cursor_auth
|
|
482
|
+
|
|
483
|
+
auth_ok, auth_details = probe_cursor_auth()
|
|
484
|
+
# Authentication for an optional, non-selected client is machine/user
|
|
485
|
+
# state, not a broken repository integration. Keep it informational even
|
|
486
|
+
# under --strict, matching missing optional CLI handling.
|
|
487
|
+
add_optional(auth_ok, "Cursor auth", auth_details)
|
|
488
|
+
|
|
489
|
+
grok_hooks = root / ".grok" / "hooks" / "devcouncil.json"
|
|
490
|
+
grok_enabled = bool(raw_config.get("integrations", {}).get("grok", {}).get("enabled"))
|
|
491
|
+
if grok_enabled or grok_hooks.exists():
|
|
492
|
+
hooks_ok = False
|
|
493
|
+
if grok_hooks.exists():
|
|
494
|
+
try:
|
|
495
|
+
hooks_data = read_json(grok_hooks) or {}
|
|
496
|
+
hooks_ok = "PreToolUse" in hooks_data.get("hooks", {})
|
|
497
|
+
except json.JSONDecodeError:
|
|
498
|
+
hooks_ok = False
|
|
499
|
+
add(
|
|
500
|
+
hooks_ok,
|
|
501
|
+
"Grok hooks",
|
|
502
|
+
str(grok_hooks) if hooks_ok else "Run dev integrate hooks --apply --tool grok (then /hooks-trust in Grok).",
|
|
503
|
+
)
|
|
332
504
|
|
|
333
505
|
opencode_config = integrate._opencode_config_path(root)
|
|
334
506
|
opencode_enabled = bool(raw_config.get("integrations", {}).get("opencode", {}).get("enabled"))
|
|
@@ -338,7 +510,7 @@ def build_integration_check_report(project_root: Path, *, strict: bool = False)
|
|
|
338
510
|
plugin_registered = False
|
|
339
511
|
if plugin_ok and opencode_config.exists():
|
|
340
512
|
try:
|
|
341
|
-
opencode_data =
|
|
513
|
+
opencode_data = read_json(opencode_config) or {}
|
|
342
514
|
plugin_registered = (
|
|
343
515
|
f"./.devcouncil/integrations/{integrate.OPENCODE_HOOK_PLUGIN_NAME}"
|
|
344
516
|
in (opencode_data.get("plugin") or [])
|
|
@@ -387,10 +559,30 @@ def build_integration_check_report(project_root: Path, *, strict: bool = False)
|
|
|
387
559
|
str(hook_path) if references else f"{hook_path} no longer references devcouncil (tampered/disarmed).",
|
|
388
560
|
)
|
|
389
561
|
|
|
562
|
+
codex_hooks_path = root / ".codex" / "hooks.json"
|
|
563
|
+
if codex_hooks_path.exists():
|
|
564
|
+
codex_schema_ok, codex_schema_details = _codex_hook_schema_status(root)
|
|
565
|
+
add(codex_schema_ok, "Codex hook schema", codex_schema_details)
|
|
566
|
+
add_skip("Codex hook trust", "Open Codex in this project and run /hooks after any hook change.")
|
|
567
|
+
|
|
568
|
+
from devcouncil.integrations.clients.common import check_hook_dev_executable
|
|
569
|
+
|
|
570
|
+
hook_exe_ok, hook_exe_details = check_hook_dev_executable(root)
|
|
571
|
+
if common_recorded_hook_exe(root) is not None:
|
|
572
|
+
add(hook_exe_ok, "Hook `dev` executable", hook_exe_details)
|
|
573
|
+
else:
|
|
574
|
+
add_skip("Hook `dev` executable", hook_exe_details)
|
|
575
|
+
|
|
390
576
|
recommended = resolve_automated_executor(root, None) if detected else None
|
|
391
577
|
return IntegrationCheckReport(tuple(rows), recommended, failures)
|
|
392
578
|
|
|
393
579
|
|
|
580
|
+
def common_recorded_hook_exe(project_root: Path) -> str | None:
|
|
581
|
+
from devcouncil.integrations.clients.common import recorded_hook_dev_executable
|
|
582
|
+
|
|
583
|
+
return recorded_hook_dev_executable(project_root)
|
|
584
|
+
|
|
585
|
+
|
|
394
586
|
def integration_status_summary(project_root: Path) -> dict[str, Any]:
|
|
395
587
|
from devcouncil.app.config import load_config
|
|
396
588
|
|
|
@@ -402,11 +594,13 @@ def integration_status_summary(project_root: Path) -> dict[str, Any]:
|
|
|
402
594
|
default_executor = execution.default_executor
|
|
403
595
|
stream_cli_output = execution.stream_cli_output
|
|
404
596
|
cursor_resume_mode = execution.cursor_resume_mode
|
|
597
|
+
grok_resume_mode = execution.grok_resume_mode
|
|
405
598
|
custom_probe_order = list(execution.coding_cli_probe_order)
|
|
406
599
|
except Exception:
|
|
407
600
|
default_executor = "manual"
|
|
408
601
|
stream_cli_output = False
|
|
409
602
|
cursor_resume_mode = "off"
|
|
603
|
+
grok_resume_mode = "off"
|
|
410
604
|
custom_probe_order = []
|
|
411
605
|
|
|
412
606
|
resolved = resolve_automated_executor(project_root, None) if detected or default_executor != "manual" else "manual"
|
|
@@ -422,5 +616,6 @@ def integration_status_summary(project_root: Path) -> dict[str, Any]:
|
|
|
422
616
|
"custom_probe_order": custom_probe_order,
|
|
423
617
|
"stream_cli_output": stream_cli_output,
|
|
424
618
|
"cursor_resume_mode": cursor_resume_mode,
|
|
619
|
+
"grok_resume_mode": grok_resume_mode,
|
|
425
620
|
"capabilities": integration_capability_rows(project_root),
|
|
426
621
|
}
|