devcouncil 0.1.1 → 0.2.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.
Files changed (129) hide show
  1. package/README.md +190 -6
  2. package/package.json +9 -2
  3. package/pyproject.toml +34 -2
  4. package/src/devcouncil/app/config.py +167 -5
  5. package/src/devcouncil/artifacts/graph.py +23 -3
  6. package/src/devcouncil/assets/__init__.py +1 -0
  7. package/src/devcouncil/assets/devcouncil-logo.svg +60 -0
  8. package/src/devcouncil/assets/devcouncil_logo_premium.png +0 -0
  9. package/src/devcouncil/cli/commands/agents.py +292 -0
  10. package/src/devcouncil/cli/commands/artifacts.py +6 -3
  11. package/src/devcouncil/cli/commands/check.py +209 -0
  12. package/src/devcouncil/cli/commands/config.py +43 -4
  13. package/src/devcouncil/cli/commands/cost.py +57 -0
  14. package/src/devcouncil/cli/commands/dashboard.py +6 -1
  15. package/src/devcouncil/cli/commands/doctor.py +221 -21
  16. package/src/devcouncil/cli/commands/evidence.py +48 -0
  17. package/src/devcouncil/cli/commands/go.py +452 -33
  18. package/src/devcouncil/cli/commands/handoff.py +69 -0
  19. package/src/devcouncil/cli/commands/hook.py +124 -15
  20. package/src/devcouncil/cli/commands/init.py +154 -18
  21. package/src/devcouncil/cli/commands/integrate.py +894 -105
  22. package/src/devcouncil/cli/commands/map.py +80 -10
  23. package/src/devcouncil/cli/commands/plan.py +212 -51
  24. package/src/devcouncil/cli/commands/prompt.py +18 -7
  25. package/src/devcouncil/cli/commands/repair.py +40 -23
  26. package/src/devcouncil/cli/commands/report.py +8 -0
  27. package/src/devcouncil/cli/commands/reset_demo_state.py +4 -2
  28. package/src/devcouncil/cli/commands/rollback.py +27 -28
  29. package/src/devcouncil/cli/commands/run.py +69 -49
  30. package/src/devcouncil/cli/commands/runs.py +223 -0
  31. package/src/devcouncil/cli/commands/scaffold.py +32 -0
  32. package/src/devcouncil/cli/commands/semantic.py +47 -0
  33. package/src/devcouncil/cli/commands/setup.py +145 -6
  34. package/src/devcouncil/cli/commands/shell.py +73 -0
  35. package/src/devcouncil/cli/commands/skills.py +88 -0
  36. package/src/devcouncil/cli/commands/status.py +25 -1
  37. package/src/devcouncil/cli/commands/trace.py +47 -3
  38. package/src/devcouncil/cli/commands/verify.py +138 -3
  39. package/src/devcouncil/cli/commands/watch.py +9 -9
  40. package/src/devcouncil/cli/commands/watch_fs.py +40 -0
  41. package/src/devcouncil/cli/main.py +56 -7
  42. package/src/devcouncil/domain/evidence.py +22 -2
  43. package/src/devcouncil/domain/gap.py +27 -1
  44. package/src/devcouncil/domain/task.py +31 -2
  45. package/src/devcouncil/execution/checkpoints.py +246 -0
  46. package/src/devcouncil/execution/context_builder.py +1 -1
  47. package/src/devcouncil/execution/fs_watcher.py +180 -0
  48. package/src/devcouncil/execution/handoff.py +102 -0
  49. package/src/devcouncil/execution/hook_policy.py +162 -74
  50. package/src/devcouncil/execution/patch.py +59 -10
  51. package/src/devcouncil/execution/permissions.py +17 -24
  52. package/src/devcouncil/execution/policy_engine.py +343 -0
  53. package/src/devcouncil/execution/prompt_builder.py +633 -21
  54. package/src/devcouncil/execution/shell_session.py +225 -0
  55. package/src/devcouncil/execution/task_runner.py +6 -2
  56. package/src/devcouncil/executors/agent_registry.py +575 -0
  57. package/src/devcouncil/executors/coding_cli.py +663 -39
  58. package/src/devcouncil/executors/native/agent.py +121 -20
  59. package/src/devcouncil/gating/checks/clean_git.py +3 -1
  60. package/src/devcouncil/gating/checks/secret_scan_check.py +40 -21
  61. package/src/devcouncil/gating/policy.py +158 -10
  62. package/src/devcouncil/hardware.py +184 -0
  63. package/src/devcouncil/indexing/ast_matcher.py +1 -1
  64. package/src/devcouncil/indexing/lsp.py +45 -4
  65. package/src/devcouncil/indexing/repo_mapper.py +1256 -9
  66. package/src/devcouncil/indexing/semantic_index.py +205 -0
  67. package/src/devcouncil/integrations/actions.py +146 -0
  68. package/src/devcouncil/integrations/check.py +423 -0
  69. package/src/devcouncil/integrations/github_intent.py +142 -0
  70. package/src/devcouncil/integrations/gitnexus.py +35 -0
  71. package/src/devcouncil/integrations/mcp/server.py +1552 -29
  72. package/src/devcouncil/integrations/opencode_devcouncil_plugin.mjs +24 -0
  73. package/src/devcouncil/live/cards.py +161 -19
  74. package/src/devcouncil/live/signals.py +2 -2
  75. package/src/devcouncil/live/transcripts.py +9 -6
  76. package/src/devcouncil/llm/cache.py +10 -6
  77. package/src/devcouncil/llm/model_defaults.yaml +44 -0
  78. package/src/devcouncil/llm/provider.py +515 -34
  79. package/src/devcouncil/llm/router.py +231 -46
  80. package/src/devcouncil/optimization/__init__.py +1 -0
  81. package/src/devcouncil/optimization/gepa_agent.py +318 -0
  82. package/src/devcouncil/planning/correction_manifest.py +303 -0
  83. package/src/devcouncil/planning/critique_service.py +7 -2
  84. package/src/devcouncil/planning/plan_service.py +17 -3
  85. package/src/devcouncil/planning/prompt_enhancer_service.py +82 -1
  86. package/src/devcouncil/planning/spec_service.py +27 -1
  87. package/src/devcouncil/repo/ci_scaffold.py +157 -0
  88. package/src/devcouncil/repo/gitignore.py +123 -0
  89. package/src/devcouncil/repo/sca.py +374 -0
  90. package/src/devcouncil/reporting/json_report.py +11 -1
  91. package/src/devcouncil/reporting/markdown_report.py +15 -0
  92. package/src/devcouncil/skills/__init__.py +19 -0
  93. package/src/devcouncil/skills/library/README.md +46 -0
  94. package/src/devcouncil/skills/library/ai-training.md +50 -0
  95. package/src/devcouncil/skills/library/android.md +50 -0
  96. package/src/devcouncil/skills/library/backend.md +52 -0
  97. package/src/devcouncil/skills/library/core-engineering.md +95 -0
  98. package/src/devcouncil/skills/library/data-engineering.md +47 -0
  99. package/src/devcouncil/skills/library/desktop.md +46 -0
  100. package/src/devcouncil/skills/library/devops.md +48 -0
  101. package/src/devcouncil/skills/library/game-dev.md +46 -0
  102. package/src/devcouncil/skills/library/ios.md +48 -0
  103. package/src/devcouncil/skills/library/mobile-cross-platform.md +46 -0
  104. package/src/devcouncil/skills/library/security.md +48 -0
  105. package/src/devcouncil/skills/library/systems.md +48 -0
  106. package/src/devcouncil/skills/library/web.md +47 -0
  107. package/src/devcouncil/skills/library/windows.md +47 -0
  108. package/src/devcouncil/skills/registry.py +330 -0
  109. package/src/devcouncil/storage/db.py +83 -2
  110. package/src/devcouncil/storage/models.py +121 -0
  111. package/src/devcouncil/storage/native.py +557 -0
  112. package/src/devcouncil/storage/repositories.py +137 -75
  113. package/src/devcouncil/telemetry/cost.py +123 -17
  114. package/src/devcouncil/telemetry/model_pricing.yaml +48 -0
  115. package/src/devcouncil/telemetry/pricing.py +28 -0
  116. package/src/devcouncil/telemetry/traces.py +62 -7
  117. package/src/devcouncil/telemetry/tracker.py +12 -9
  118. package/src/devcouncil/ui/dashboard.py +324 -23
  119. package/src/devcouncil/utils/redaction.py +9 -3
  120. package/src/devcouncil/utils/subprocess_env.py +69 -0
  121. package/src/devcouncil/verification/acceptance_compiler.py +125 -0
  122. package/src/devcouncil/verification/ad_hoc_check.py +129 -0
  123. package/src/devcouncil/verification/diff_coverage.py +353 -0
  124. package/src/devcouncil/verification/next_actions.py +189 -0
  125. package/src/devcouncil/verification/sandbox.py +178 -0
  126. package/src/devcouncil/verification/test_resolver.py +91 -0
  127. package/src/devcouncil/verification/verifier.py +1065 -47
  128. package/uv.lock +205 -64
  129. package/src/devcouncil/indexing/symbol_index.py +0 -0
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # DevCouncil: The Gated AI Orchestrator
2
2
 
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/bharathvbcr/DevCouncil/main/src/devcouncil/assets/devcouncil_logo_premium.png" alt="DevCouncil Logo" width="300">
5
+ </p>
6
+
3
7
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
4
8
  [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
5
9
  [![uv](https://img.shields.io/badge/managed%20by-uv-purple.svg)](https://github.com/astral-sh/uv)
@@ -8,16 +12,19 @@
8
12
 
9
13
  DevCouncil is a high-integrity command-line orchestration platform for AI-assisted software development. It turns AI implementation from a black-box generation task into a gated engineering workflow where every change is authorized, verified, and traceable back to a requirement.
10
14
 
11
- DevCouncil does not replace coding agents. It sits beside tools like Codex CLI, Gemini CLI, Claude Code, Cursor, and Aider, then owns the plan, task scope, verification loop, repair prompts, and evidence trail.
15
+ DevCouncil does not replace coding agents. It sits beside tools like Codex CLI, Gemini CLI, Claude Code, OpenCode, Google Antigravity CLI, Warp/Oz, Cursor, Aider, and bring-your-own prompt-taking CLIs, then owns the plan, task scope, verification loop, repair prompts, and evidence trail.
12
16
 
13
17
  ## Documentation
14
18
 
15
19
  - [Quickstart](docs/quickstart.md): shortest install-to-first-task path.
16
20
  - [Daily workflow](docs/workflow.md): manual sidecar loop, verification, repair, and rollback.
17
- - [Coding CLI integration](docs/coding-cli-integration.md): Codex, Gemini, Claude Code, Cursor, Aider, MCP, hooks, and automated executors.
21
+ - [Coding CLI integration](docs/coding-cli-integration.md): Codex, Gemini, Claude Code, OpenCode, Antigravity, Cursor, Aider, MCP, hooks, and automated executors.
22
+ - [Integration tiers](docs/integration-tiers.md): headless executor vs MCP-only vs sidecar definitions.
18
23
  - [CLI command reference](docs/cli-reference.md): available `dev` commands.
19
24
  - [Architecture](docs/architecture.md): components, artifact graph, state machine, and gated execution.
25
+ - [Executor adapters](docs/executor-adapters.md): manual, coding CLI, native-preview, Mini-SWE, and OpenHands execution paths.
20
26
  - [Live review](docs/live-review.md): `dev watch` session review, cards, signals, and blocking behavior.
27
+ - [Model routing](docs/model-routing.md): provider selection, role models, OpenRouter, Vertex AI, Doubleword, and Ollama (local) setup.
21
28
  - [Security model](docs/security.md): redaction, permissions, allowlists, and local state.
22
29
  - [Project status](docs/project-status.md): current maturity by subsystem.
23
30
  - [Roadmap](docs/roadmap.md): planned work.
@@ -73,12 +80,27 @@ dev verify TASK-001
73
80
 
74
81
  On a fresh interactive setup, DevCouncil can configure supported coding CLI integrations immediately; pass `--skip-integrations` if you want to defer that step.
75
82
 
76
- Paste only the output from `dev prompt TASK-001` into Codex, Gemini, Claude Code, Cursor, Aider, or another coding tool. Keep `dev setup`, `dev plan`, `dev run`, and `dev verify` in the terminal at the repository root.
83
+ ### Run locally on macOS (Apple Silicon + Ollama)
84
+
85
+ DevCouncil runs fully offline against [Ollama](https://ollama.com) — no API key, no per-token cost. It is Apple-Silicon-aware: `dev setup --provider ollama` sizes the default local model to your Mac's unified memory, and `dev doctor` reports the chip/RAM, pings the Ollama server, and flags a too-small context window.
86
+
87
+ ```bash
88
+ brew install ollama && ollama serve
89
+ ollama pull qwen2.5-coder:32b # use the size `dev doctor` recommends for your RAM
90
+ export OLLAMA_NUM_CTX=16384 # large planning prompts need a raised context window
91
+ dev setup --provider ollama # auto-selects the model for your RAM
92
+ ```
93
+
94
+ See [Model routing → macOS / Apple Silicon](docs/model-routing.md) for the RAM-to-model table.
95
+
96
+ Paste only the output from `dev prompt TASK-001` into Codex, Gemini, Claude Code, OpenCode, Antigravity, Warp, Cursor, Aider, or another coding tool. Keep `dev setup`, `dev plan`, `dev run`, and `dev verify` in the terminal at the repository root.
77
97
 
78
98
  For an automated end-to-end run with a supported coding CLI installed:
79
99
 
80
100
  ```bash
81
101
  dev e2e "Describe the implementation goal" --executor codex
102
+ dev e2e "Describe the implementation goal" --executor antigravity
103
+ dev e2e "Describe the implementation goal" --executor warp
82
104
  dev go "Describe the implementation goal" --executor codex
83
105
  ```
84
106
 
@@ -87,14 +109,132 @@ dev go "Describe the implementation goal" --executor codex
87
109
  For machine-readable agent handoff, write the final report to a stable file:
88
110
 
89
111
  ```bash
90
- dev e2e "Describe the implementation goal" --agent
91
- dev e2e "Describe the implementation goal" --json --report-file .devcouncil/reports/latest.json
112
+ dev e2e "Describe the implementation goal" --executor codex --agent
113
+ dev e2e "Describe the implementation goal" --executor codex --json --report-file .devcouncil/reports/latest.json
92
114
  ```
93
115
 
94
- `--agent` is the lowest-friction integration preset. It enables JSON output and writes `.devcouncil/reports/latest.json`.
116
+ `--agent` enables JSON output and writes `.devcouncil/reports/latest.json`. Fresh projects default to manual sidecar mode, so pass an automated executor or set `execution.default_executor` before using `dev e2e` without `--executor`.
95
117
 
96
118
  See the full [quickstart](docs/quickstart.md) for installation variants, API-key setup, and first-run guidance.
97
119
 
120
+ OpenCode and Google Antigravity CLI are built-in executors and MCP integrations:
121
+
122
+ ```bash
123
+ dev integrate opencode --apply
124
+ dev run TASK-001 --executor opencode
125
+ dev agents run TASK-001 --agent opencode --profile default
126
+ dev integrate antigravity --apply
127
+ dev run TASK-001 --executor antigravity
128
+ dev agents run TASK-001 --agent agy --profile default
129
+ ```
130
+
131
+ Register any other local CLI that accepts prompts. `dev agents` is the first-class agent hub; `dev integrate cli-agent` remains available for older scripts:
132
+
133
+ ```bash
134
+ dev agents add myagent --command myagent --arg run --input-mode prompt-file --prompt-arg=--prompt-file --supports-mcp
135
+ dev agents
136
+ dev agents doctor
137
+ dev agents run TASK-001 --agent myagent --profile default
138
+ ```
139
+
140
+ GEPA prompt-profile optimization is available for the agent hub:
141
+
142
+ ```bash
143
+ dev agents optimize --agent codex --profile yolo --evals .devcouncil/evals/agent-profile.jsonl --dry-run
144
+ dev agents optimize --agent codex --profile yolo --evals .devcouncil/evals/agent-profile.jsonl --apply
145
+ ```
146
+
147
+ ## Feature Set
148
+
149
+ DevCouncil is an application layer around coding agents. It does not just emit prompts; it owns the workflow state, validates task scope, records evidence, and produces release-style reports.
150
+
151
+ ### Workflow Features
152
+
153
+ - **Repository onboarding:** `dev setup` initializes `.devcouncil/`, generates the repo map + `AGENTS.md`/`CLAUDE.md` guides, scaffolds applicable engineering skills, runs environment checks, offers integration setup, and prints the next useful commands. Use `--skip-map` / `--skip-skills` to opt out, or `--scaffold-ci` to also write a starter GitHub Actions workflow.
154
+ - **Repository mapping:** `dev map` writes `.devcouncil/repo_map.json`, identifies important files and subsystems, filters generated/temp files, and keeps managed `AGENTS.md` / `CLAUDE.md` workspace guides synchronized. Subsystems, entry points, neighbors, and important surfaces are now inferred generically for **any** repository — grouped from the directory tree and ranked by an import-graph in-degree — so the map (and the structural context it feeds into prompts) is meaningful outside DevCouncil's own tree, not just within it. The map records the git HEAD and tracked-file fingerprint it was built from; when prompts reuse a map that has fallen behind the current code, they flag it as stale (run `dev map` to refresh) rather than silently feeding wrong structure. The map is also generated automatically on first init.
155
+ - **Engineering skills:** `dev skills` lists the bundled skills and shows which apply to the repository; `dev skills scaffold` writes them into `.claude/skills/<name>/SKILL.md`. A merged always-on `core-engineering` skill (think-before-coding, simplicity, surgical changes, goal-driven execution, evidence-grounded communication) plus domain skills (Android, iOS, Windows, web, AI training) that brief the agent on current SDKs, deprecations, and tooling before coding. Applicable skills are also embedded into `dev prompt` output.
156
+ - **CI scaffolding:** `dev scaffold-ci` writes a starter `.github/workflows/devcouncil.yml` derived from the configured test/lint/typecheck commands, filtered to the detected language stack; it never overwrites existing CI unless `--force`.
157
+ - **Planning council:** `dev plan` turns a goal into requirements, acceptance criteria, assumptions, critique findings, and executable tasks.
158
+ - **Task graph:** `dev tasks` and `dev show TASK-001` expose requirement links, acceptance-criterion links, planned files, expected tests, allowed commands, forbidden changes, dependencies, and status. Tasks can declare `depends_on`; the plan gate rejects unknown dependencies and cycles, and `dev go`/`dev e2e` run tasks in topological order and skip a task whose prerequisites didn't complete (rather than letting it fail spuriously and burn its repair budget).
159
+ - **Scoped task prompts:** `dev prompt TASK-001` creates a constrained implementation prompt for sidecar agents, including file scope, verification expectations, and forbidden changes. The prompt now embeds the current (secret-redacted) contents of each planned file with a top-level symbol outline, structural orientation (from the code-review graph when available, otherwise the generated `repo_map.json`), and a **dependents (blast-radius) list** — the files that import each file being changed, from the map's precomputed reverse-import index — so the agent edits in place and keeps call sites working instead of starting blind. A central prompt budget keeps the core (goal/scope/instructions) always present and fits the optional context sections in priority order (file contents > structural > dependents > skills), dropping the lowest-priority ones with an explicit marker rather than overflowing silently.
160
+ - **Execution:** `dev run TASK-001` supports manual sidecar mode, built-in coding CLI executors, external executors, and registered custom CLI agents.
161
+ - **One-command flow:** `dev e2e "goal"` and `dev go "goal"` can initialize state, plan, run approved tasks, verify the diff, and generate a report. With an automated executor the run is now a **closed loop**: a task that fails verification is re-driven through a bounded self-repair loop (a correction manifest is written and the executor re-run) until it verifies or the `execution.max_repair_attempts` budget is spent, with no-progress detection that stops early when the same blocking gaps reappear.
162
+ - **Verification:** `dev verify TASK-001` captures the diff, runs expected evidence commands, checks planned-file compliance, detects orphan changes, flags unplanned dependency edits, scans for secrets, and links evidence to acceptance criteria. An **empty diff can no longer pass** a task that declares files to create or modify (work that committed earlier is still recognized via the task checkpoint), and the result reports the rigor it ran at (`verification_mode` compiled vs coarse, `diff_empty`, `coverage_measured`/`coverage_skipped_reason`) plus a distinct `advisory_actions` list so an agent never mistakes "passed" for "proven." `dev verify` exits non-zero when a task is blocked so shell-driven agents can gate on `$?`.
163
+ - **Repair:** `dev repair` converts blocking gaps into focused follow-up work instead of leaving failures as vague test output.
164
+ - **Rollback:** `dev rollback TASK-001` uses task checkpoints to revert scoped work when a task needs to be backed out.
165
+ - **Reporting:** `dev report` emits a requirements coverage table, evidence summary, blocking gaps, and live-review blockers; JSON and PR-comment paths are available for automation.
166
+
167
+ ### App Surfaces
168
+
169
+ - **CLI:** `dev` and `devcouncil` expose the same Typer command surface for local terminal workflows.
170
+ - **Agent hub:** `dev agents` lists built-in and custom agents, `dev agents add` registers prompt-taking CLIs, `dev agents doctor` checks wiring, `dev agents run` executes a task through a named agent/profile, and `dev agents optimize` uses GEPA to tune profile preambles from offline eval examples.
171
+ - **Integration hub:** `dev integrate all --apply` configures supported coding CLI and MCP integrations; targeted setup exists for Codex, Gemini, Claude Code, OpenCode, Antigravity, Cursor, Warp/Oz, hooks, and custom CLI agents. `dev integrate check` now reports each client's **enforcement posture** — `pre-action` (a native hook blocks unauthorized writes before they happen) vs `verify-only` (forbidden changes are caught only after the fact by verification) — so the containment guarantee isn't overstated for clients without a pre-action gate.
172
+ - **MCP server:** `dev mcp-server` exposes DevCouncil context and workflow tools over stdio for MCP-capable clients. `devcouncil_verify_task` now runs DevCouncil's strong compiled per-criterion checks when a provider key is configured (falling back to a clearly-labeled `coarse` mode otherwise), refuses to pass on an empty diff, and returns `verification_mode`, `diff_empty`, `coverage_measured`/`coverage_skipped_reason`, and an `advisory_actions` array alongside the blocking `next_actions`. Cheap, re-verify-free read tools — `devcouncil_get_gaps` and `devcouncil_get_next_actions` — let a reconnecting agent resume outstanding work from persisted gaps (which now carry `file`/`line`/`suggested_command`/`acceptance_criterion_id`). Task leases expire on a config-driven TTL so a crashed agent's task frees itself, with `devcouncil_renew_lease` and `devcouncil_list_leases` for long runs and fleet supervision; a partial-unique DB index enforces a single active lease per task, so concurrent checkouts can't both win the writer slot. A pure-MCP agent can now make the change itself through lease-gated write tools — `devcouncil_write_file` and `devcouncil_apply_patch` — which policy-check every target path *before* it lands (out-of-scope, protected, or escaping paths are rejected; a patch with any out-of-scope target is rejected whole, never partially applied), write atomically, and record a `FileChangeEvent` for provenance. The corpus is also browsable as MCP **resources** (`devcouncil://report`, `devcouncil://tasks`, `devcouncil://gaps`, `devcouncil://cards`, `devcouncil://task/{id}`) so a host can read project state without a tool call. `devcouncil_get_task_provenance` then exposes that audit trail — gated file changes, verification runs, diff-coverage evidence, and the latest correction manifest — so what happened on disk is inspectable. The diff↔coverage proof is now also retained across graph reloads (it was previously dropped), so reports and `dev status` reflect whether the changed lines were actually exercised.
173
+ - **Live review:** `dev watch` tracks review cards, signals, blocking feedback, and repair guidance while a session is active.
174
+ - **Trace viewer:** `dev trace tail --follow` streams local DevCouncil trace events for execution, verification, and agent handoff.
175
+ - **Dashboard:** `dev dashboard --open` serves a local status dashboard and opens it in the default browser for project state and live workflow visibility.
176
+ - **Agent-consumable CLI:** machine output for shell-driven agents — `dev prompt --json` (`{ok, task_id, prompt}`), `dev handoff --json` (`{ok, manifest_path, run_id, next_command}` to chain `dev run`), `dev verify` exits non-zero when blocked, and `dev status`/`dev report` accept `--fail-on-blocking` to exit non-zero on outstanding blocking gaps so a loop can gate on `$?`.
177
+ - **Config editor:** `dev config` and `dev config models` inspect/update provider, model, executor, and command configuration.
178
+ - **Artifact tools:** `dev artifacts validate` checks stored graph integrity.
179
+ - **Code intelligence:** `dev lsp inspect` checks optional language-server readiness, and `dev ast match` searches code structurally.
180
+ - **Doctor:** `dev doctor` validates local dependencies, commands, and environment prerequisites before a workflow fails deeper in execution.
181
+
182
+ ### Agent And Executor Support
183
+
184
+ DevCouncil works with human-in-the-loop sidecar sessions and automated prompt handoff:
185
+
186
+ - **Manual sidecar:** paste `dev prompt TASK-001` into any agent, then run `dev verify TASK-001`.
187
+ - **Built-in coding CLI adapters:** `codex`, `gemini`, `claude`, `opencode`, `antigravity`, `warp`, `cursor`, `aider`, and aliases such as `codex-cli`, `gemini-cli`, `claude-code`, `opencode-cli`, `antigravity-cli`, `agy`, `agy-cli`, `warp-cli`, `oz`, `cursor-agent`, and `cursor-cli`.
188
+ - **Custom CLI agents:** register any prompt-taking command with stdin, argument, or prompt-file handoff.
189
+ - **Execution profiles:** custom agents can use profiles such as `default`, `yolo`, and `prod` to adjust prompt constraints while DevCouncil still verifies the final diff.
190
+ - **External automated adapters:** `mini`, `openhands`, `native-preview`, and `native` are available when the corresponding local executor is configured.
191
+ - **Hook-aware clients:** `dev integrate hooks --apply` installs write/shell hooks for Codex, Gemini, Claude, Cursor, and OpenCode so DevCouncil policy can block unauthorized actions before verification. The post-task hook can run deterministic verification of the active task and record gaps (enable `execution.verify_on_post_task`; off by default to keep hooks fast). File-write policy uses one shared path normalizer across the hook and task-policy paths that resolves every target and **denies anything outside the project root** (so the path that's checked is the path that's enforced), and the pre-tool-use hook is fail-closed: an unparseable or error payload is surfaced (and blocked under `--strict`/`DEVCOUNCIL_HOOK_STRICT`) rather than silently allowed.
192
+
193
+ ### Gates And Evidence
194
+
195
+ DevCouncil blocks completion on concrete gaps rather than model confidence:
196
+
197
+ - **Plan approval gates:** requirements must have acceptance criteria, acceptance criteria need verification methods, tasks must map to known requirements and acceptance criteria, high-impact assumptions must be resolved, and high/critical critique findings must be closed.
198
+ - **Task readiness gates:** the working tree must be clean for the task, planned files must be declared, and each task needs allowed commands plus expected verification evidence.
199
+ - **Diff gates:** verification detects files changed outside the planned task scope, dependency-file edits made without authorization, deleted/added files, and untracked file diffs.
200
+ - **Evidence gates:** passing evidence commands are linked back to acceptance criteria; missing passing evidence becomes a blocking gap.
201
+ - **Security gates:** secret scanning runs over captured diffs, and command output is redacted before it is written to logs.
202
+ - **Live-review gates:** unresolved critical review cards can block task verification and appear in reports.
203
+
204
+ ### Providers, Models, And Cost Tracking
205
+
206
+ - **Providers:** OpenRouter, Vertex AI, Doubleword, and Ollama (local, no key) are supported through local configuration and secrets.
207
+ - **Role models:** planner, critic, arbiter, reviewer, and repair roles can share one model or use per-role overrides.
208
+ - **Structured repair:** model routing includes JSON repair paths for structured planning and review outputs.
209
+ - **Model defaults:** packaged YAML defaults ship with the tool so installed CLI environments do not depend on source-tree-only files.
210
+ - **Telemetry:** local trace and cost data feed `dev status`, reports, and dashboard surfaces.
211
+
212
+ ### Reports And Automation Outputs
213
+
214
+ - **Markdown reports:** include verdict, coverage summary, requirement/task mapping, blocking gaps, and live-review status.
215
+ - **JSON reports:** `--json` and `--report-file` support machine-readable handoff to other automation.
216
+ - **Agent preset:** `--agent` writes `.devcouncil/reports/latest.json` for stable downstream consumption.
217
+ - **PR comments:** `dev report --github-pr-comment` and `dev report --gitlab-pr-comment` can publish verification summaries to pull/merge requests.
218
+ - **GitHub checks:** preview GitHub report/check surfaces are available for repository automation.
219
+
220
+ ### Local State And Files
221
+
222
+ DevCouncil stores local workflow state in the target repository:
223
+
224
+ - `.devcouncil/config.yaml`: provider, executor, command, integration, and workflow settings.
225
+ - `.devcouncil/secrets.env`: local provider secrets such as API keys or Vertex AI project/location values. Git-ignored; copy `.devcouncil/secrets.env.example` and fill in real values. Environment variables take precedence over this file.
226
+ - `.devcouncil/repo_map.json`: generated repository map and subsystem navigation index.
227
+ - `.devcouncil/state.sqlite`: SQLite state for requirements, assumptions, tasks, evidence, gaps, critique findings, and project phase history.
228
+ - `.devcouncil/checkpoints/`: task snapshots used by verification and rollback.
229
+ - `.devcouncil/logs/`: redacted stdout/stderr from verification commands.
230
+ - `.devcouncil/runs/<run-id>/agent-run.json`: prompt, executor, profile, exit status, and run metadata for automated agent executions.
231
+ - `.devcouncil/reports/latest.json`: optional machine-readable report generated by `dev e2e --agent`.
232
+ - `.devcouncil/integrations/` and `.agents/`: generated integration files such as Warp/Oz MCP JSON and Antigravity MCP config.
233
+
234
+ ### Maturity
235
+
236
+ The stable daily workflow is planning, manual sidecar execution, verification, repair, rollback, and reporting. Coding CLI executors, MCP, live review, dashboard, PR comments, LSP/AST tools, and GitHub check surfaces are preview features. Native autonomous execution is experimental and still requires DevCouncil verification before work is considered complete.
237
+
98
238
  ## Core Flow
99
239
 
100
240
  DevCouncil's recommended default is **Manual Sidecar Mode**:
@@ -108,6 +248,50 @@ DevCouncil's recommended default is **Manual Sidecar Mode**:
108
248
 
109
249
  The detailed task-by-task workflow lives in [docs/workflow.md](docs/workflow.md).
110
250
 
251
+ ## How The Repo Runs
252
+
253
+ ```mermaid
254
+ flowchart TD
255
+ user["User runs dev/devcouncil"] --> cli["Typer CLI\nsrc/devcouncil/cli/main.py"]
256
+ cli --> config["Config + secrets\n.devcouncil/config.yaml\n.devcouncil/secrets.env"]
257
+ cli --> map["Repo map\nsrc/devcouncil/indexing/repo_mapper.py"]
258
+ cli --> planning["Planning commands\ndev plan / dev prompt / dev tasks"]
259
+
260
+ config --> providers["Model providers\nOpenRouter, Vertex AI, Doubleword, or Ollama"]
261
+ providers --> router["ModelRouter\nrole models, cache, telemetry, structured JSON repair"]
262
+ router --> planning
263
+
264
+ planning --> storage["SQLite + repositories\nrequirements, tasks, gaps, evidence, state"]
265
+ storage --> artifactGraph["Artifact graph\nRequirement -> Task -> Diff -> Evidence"]
266
+ artifactGraph --> gates["Gate policy\nplanned files, commands, secret checks"]
267
+
268
+ gates --> manual["Manual sidecar\ndev prompt + user agent edits"]
269
+ gates --> coding["Coding CLI executor\nCodex, Gemini, Claude, OpenCode, Antigravity, Warp, custom CLIs"]
270
+ gates --> native["Native preview executor\nLLM router + TaskRunner"]
271
+ gates --> external["Mini-SWE / OpenHands adapters"]
272
+
273
+ coding --> runlog["Run artifacts\nprompt file, redacted logs, manifest, trace events"]
274
+ native --> runlog
275
+ external --> runlog
276
+ manual --> diff["Repository diff"]
277
+ runlog --> diff
278
+
279
+ diff --> verify["Verifier\ndev verify / automatic post-run verification"]
280
+ verify --> evidence["Evidence + gaps"]
281
+ evidence --> storage
282
+ evidence --> repair["Repair loop\ndev repair / dev watch repair"]
283
+ evidence --> report["Reports\ndev report, JSON, GitHub/GitLab comments"]
284
+
285
+ cli --> mcp["MCP server\ndev mcp-server"]
286
+ mcp --> storage
287
+ mcp --> artifactGraph
288
+ mcp --> repair
289
+
290
+ cli --> live["Live review\ndev watch"]
291
+ live --> cards["Cards + signals\nblocking review feedback"]
292
+ cards --> report
293
+ ```
294
+
111
295
  ## Install From Source
112
296
 
113
297
  For local development inside this checkout:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devcouncil",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Gated orchestrator for AI-assisted software development",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/bharathvbcr/DevCouncil#readme",
@@ -27,6 +27,10 @@
27
27
  "bin/",
28
28
  "src/**/*.py",
29
29
  "src/**/*.md",
30
+ "src/**/*.png",
31
+ "src/**/*.svg",
32
+ "src/**/*.yaml",
33
+ "src/**/*.mjs",
30
34
  "pyproject.toml",
31
35
  "uv.lock",
32
36
  "README.md",
@@ -37,9 +41,12 @@
37
41
  "install:editable": "uv pip install -e .",
38
42
  "dev": "uv run devcouncil",
39
43
  "pack:check": "npm pack --dry-run",
44
+ "smoke:wheel": "uv run python scripts/check-wheel-assets.py",
45
+ "smoke:package": "node scripts/npm-runtime-smoke.mjs",
40
46
  "test": "uv run pytest",
41
47
  "lint": "uv run ruff check .",
42
- "check": "uv run ruff check . && uv run pytest"
48
+ "typecheck": "uv run mypy src",
49
+ "check": "uv run ruff check . && uv run mypy src && uv run pytest"
43
50
  },
44
51
  "engines": {
45
52
  "node": ">=18"
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devcouncil"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  description = "Gated orchestrator for AI-assisted software development"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -11,10 +11,17 @@ dependencies = [
11
11
  "pyyaml>=6.0.1",
12
12
  "sqlmodel>=0.0.19",
13
13
  "httpx>=0.27.0",
14
- "gitpython>=3.1.43",
14
+ "gitpython>=3.1.49",
15
15
  "mcp>=1.27.0",
16
+ "gepa>=0.1.1",
17
+ "watchdog>=4.0.0",
16
18
  ]
17
19
 
20
+ [project.urls]
21
+ Homepage = "https://github.com/bharathvbcr/DevCouncil"
22
+ Repository = "https://github.com/bharathvbcr/DevCouncil.git"
23
+ Issues = "https://github.com/bharathvbcr/DevCouncil/issues"
24
+
18
25
  [project.scripts]
19
26
  dev = "devcouncil.cli.main:app"
20
27
  devcouncil = "devcouncil.cli.main:app"
@@ -24,6 +31,24 @@ dev = [
24
31
  "pytest>=8.2.0",
25
32
  "ruff>=0.4.4",
26
33
  "mypy>=1.10.0",
34
+ "types-pyyaml>=6.0.12.20260408",
35
+ # Used only to exercise the diff↔coverage path end-to-end in tests. DevCouncil
36
+ # never requires coverage at runtime — it uses whatever the target repo provides.
37
+ "coverage>=7.4",
38
+ ]
39
+
40
+ [tool.uv]
41
+ # Security floors for transitive dependencies (Dependabot advisories, 2026-06).
42
+ # These are not direct deps; constraint-dependencies pins their minimum patched
43
+ # version in the resolver without adding them to the dependency tree, so they can
44
+ # never resolve back below the fix.
45
+ constraint-dependencies = [
46
+ "cryptography>=48.0.1",
47
+ "starlette>=1.3.1",
48
+ "pyjwt>=2.13.0",
49
+ "python-multipart>=0.0.31",
50
+ "pydantic-settings>=2.14.2",
51
+ "idna>=3.15",
27
52
  ]
28
53
 
29
54
  [tool.pytest.ini_options]
@@ -32,3 +57,10 @@ testpaths = ["tests"]
32
57
  [build-system]
33
58
  requires = ["hatchling"]
34
59
  build-backend = "hatchling.build"
60
+
61
+ [tool.hatch.build.targets.wheel.force-include]
62
+ "src/devcouncil/llm/model_defaults.yaml" = "devcouncil/llm/model_defaults.yaml"
63
+ "src/devcouncil/telemetry/model_pricing.yaml" = "devcouncil/telemetry/model_pricing.yaml"
64
+ "src/devcouncil/assets/devcouncil-logo.svg" = "devcouncil/assets/devcouncil-logo.svg"
65
+ "src/devcouncil/assets/devcouncil_logo_premium.png" = "devcouncil/assets/devcouncil_logo_premium.png"
66
+ "src/devcouncil/integrations/opencode_devcouncil_plugin.mjs" = "devcouncil/integrations/opencode_devcouncil_plugin.mjs"
@@ -6,6 +6,8 @@ Replaces scattered yaml.safe_load() calls with a single validated config service
6
6
  from __future__ import annotations
7
7
 
8
8
  import os
9
+ import shutil
10
+ import subprocess
9
11
  from pathlib import Path
10
12
  from typing import Dict, List
11
13
 
@@ -34,6 +36,34 @@ class CommandsConfig(BaseModel):
34
36
  typecheck: List[str] = Field(default_factory=list)
35
37
 
36
38
 
39
+ class VerificationSandboxConfig(BaseModel):
40
+ docker_image: str = "python:3.12-slim"
41
+ docker_setup_commands: List[str] = Field(default_factory=list)
42
+ nix_flake_attr: str | None = None
43
+
44
+
45
+ class DiffCoverageConfig(BaseModel):
46
+ """Diff↔coverage gating: prove the *changed* lines were exercised by tests.
47
+
48
+ ``measure`` runs the diff-coverage analysis and records it as evidence (and a
49
+ non-blocking signal) whenever the target repo's coverage tooling is present.
50
+ ``enforce`` promotes an unexercised diff to a *blocking* gap. Enforcement is
51
+ off by default so the signal is visible before it ever gates — a passing test
52
+ that does not touch the new code is surfaced first, then teams opt in to
53
+ blocking. ``min_ratio`` of 0.0 means "require at least one changed executable
54
+ line to be exercised"; a higher value demands that fraction of changed lines.
55
+ """
56
+
57
+ measure: bool = True
58
+ enforce: bool = False
59
+ min_ratio: float = 0.0
60
+
61
+
62
+ class VerificationConfig(BaseModel):
63
+ sandbox: VerificationSandboxConfig = Field(default_factory=VerificationSandboxConfig)
64
+ diff_coverage: DiffCoverageConfig = Field(default_factory=DiffCoverageConfig)
65
+
66
+
37
67
  class GatesConfig(BaseModel):
38
68
  require_clean_git_before_task: bool = True
39
69
  block_orphan_diffs: bool = True
@@ -48,6 +78,16 @@ class ExecutionConfig(BaseModel):
48
78
  max_repair_attempts: int = 3
49
79
  checkpoint_before_each_task: bool = True
50
80
  command_timeout: int = 300
81
+ stream_cli_output: bool = False
82
+ # Default lifetime of an MCP task lease. A crashed/disconnected agent's lease
83
+ # auto-expires after this, so the task frees up without a human running force.
84
+ lease_ttl_seconds: int = 1800
85
+ # When true, the post-task coding-CLI hook runs deterministic verification of the
86
+ # active task (and records gaps) instead of only printing a reminder. Off by default
87
+ # so hooks stay fast/cheap unless a team opts in.
88
+ verify_on_post_task: bool = False
89
+ cursor_resume_mode: str = "off"
90
+ coding_cli_probe_order: List[str] = Field(default_factory=list)
51
91
 
52
92
 
53
93
  class PrivacyConfig(BaseModel):
@@ -75,10 +115,89 @@ class LiveReviewIntegrationConfig(BaseModel):
75
115
  default_client: str = "claude"
76
116
 
77
117
 
118
+ class WarpIntegrationConfig(BaseModel):
119
+ enabled: bool = False
120
+ command: str = "oz"
121
+ run_mode: str = "local"
122
+ mcp_config_path: str = ".devcouncil/integrations/warp-mcp.json"
123
+ profile: str | None = None
124
+ model: str | None = None
125
+ environment: str | None = None
126
+ share: List[str] = Field(default_factory=list)
127
+
128
+
129
+ class OpenCodeIntegrationConfig(BaseModel):
130
+ enabled: bool = False
131
+ config_path: str = "opencode.json"
132
+
133
+
134
+ class AntigravityIntegrationConfig(BaseModel):
135
+ enabled: bool = False
136
+ mcp_config_path: str = ".agents/mcp_config.json"
137
+
138
+
139
+ class CursorIntegrationConfig(BaseModel):
140
+ enabled: bool = False
141
+ config_path: str = ".cursor/mcp.json"
142
+ hooks_path: str = ".cursor/hooks.json"
143
+
144
+
145
+ class AiderIntegrationConfig(BaseModel):
146
+ enabled: bool = False
147
+
148
+
149
+ class CliAgentProfileConfig(BaseModel):
150
+ description: str = ""
151
+ timeout_seconds: int | None = None
152
+ prompt_preamble: str = ""
153
+ require_explicit_confirmation: bool = False
154
+ # Per-profile CLI containment overrides. Empty/None reproduce today's behavior
155
+ # exactly so a profile that only sets a prompt preamble is a no-op on the
156
+ # subprocess invocation. ``extra_args`` are appended verbatim to the resolved
157
+ # command, ``permission_mode`` is translated into the right per-CLI flag where
158
+ # known (and overly-permissive flags are dropped for stricter modes), and
159
+ # ``model`` overrides the model flag for CLIs that accept one.
160
+ extra_args: List[str] = Field(default_factory=list)
161
+ permission_mode: str | None = None
162
+ model: str | None = None
163
+
164
+
165
+ class CustomCliAgentConfig(BaseModel):
166
+ command: str
167
+ args: List[str] = Field(default_factory=list)
168
+ input_mode: str = "stdin"
169
+ prompt_arg: str | None = None
170
+ timeout_seconds: int | None = None
171
+ env: Dict[str, str] = Field(default_factory=dict)
172
+ display_name: str | None = None
173
+ kind: str = "custom"
174
+ supports_mcp: bool = False
175
+ supports_diff_review: bool = False
176
+ default_profile: str = "default"
177
+ help_command: List[str] = Field(default_factory=list)
178
+
179
+
180
+ class CliAgentsIntegrationConfig(BaseModel):
181
+ enabled: bool = True
182
+ profiles: Dict[str, CliAgentProfileConfig] = Field(default_factory=dict)
183
+ agents: Dict[str, CustomCliAgentConfig] = Field(default_factory=dict)
184
+
185
+
186
+ class McpIntegrationConfig(BaseModel):
187
+ write_task_scope_to_config: bool = False
188
+
189
+
78
190
  class IntegrationsConfig(BaseModel):
191
+ mcp: McpIntegrationConfig = Field(default_factory=McpIntegrationConfig)
79
192
  agent_flow: AgentFlowIntegrationConfig = Field(default_factory=AgentFlowIntegrationConfig)
80
193
  code_review_graph: CodeReviewGraphIntegrationConfig = Field(default_factory=CodeReviewGraphIntegrationConfig)
81
194
  live_review: LiveReviewIntegrationConfig = Field(default_factory=LiveReviewIntegrationConfig)
195
+ cursor: CursorIntegrationConfig = Field(default_factory=CursorIntegrationConfig)
196
+ aider: AiderIntegrationConfig = Field(default_factory=AiderIntegrationConfig)
197
+ antigravity: AntigravityIntegrationConfig = Field(default_factory=AntigravityIntegrationConfig)
198
+ warp: WarpIntegrationConfig = Field(default_factory=WarpIntegrationConfig)
199
+ opencode: OpenCodeIntegrationConfig = Field(default_factory=OpenCodeIntegrationConfig)
200
+ cli_agents: CliAgentsIntegrationConfig = Field(default_factory=CliAgentsIntegrationConfig)
82
201
 
83
202
 
84
203
  class ProviderConfig(BaseModel):
@@ -97,6 +216,7 @@ class DevCouncilConfig(BaseModel):
97
216
  commands: CommandsConfig = Field(default_factory=CommandsConfig)
98
217
  gates: GatesConfig = Field(default_factory=GatesConfig)
99
218
  execution: ExecutionConfig = Field(default_factory=ExecutionConfig)
219
+ verification: VerificationConfig = Field(default_factory=VerificationConfig)
100
220
  privacy: PrivacyConfig = Field(default_factory=PrivacyConfig)
101
221
  integrations: IntegrationsConfig = Field(default_factory=IntegrationsConfig)
102
222
 
@@ -107,25 +227,38 @@ def load_config(project_root: Path = Path(".")) -> DevCouncilConfig:
107
227
  Returns DevCouncilConfig with defaults for any missing fields.
108
228
  Raises FileNotFoundError if config doesn't exist.
109
229
  """
110
- import yaml
230
+ import yaml # type: ignore[import-untyped]
111
231
 
112
232
  config_path = project_root / ".devcouncil" / "config.yaml"
113
233
  if not config_path.exists():
114
234
  raise FileNotFoundError(f"Config not found at {config_path}. Run 'dev init' first.")
115
235
 
116
- with open(config_path) as f:
117
- raw = yaml.safe_load(f) or {}
236
+ with open(config_path, encoding="utf-8") as f:
237
+ try:
238
+ raw = yaml.safe_load(f) or {}
239
+ except yaml.YAMLError as exc:
240
+ raise ValueError(
241
+ f"Invalid YAML in {config_path}: {exc}. Fix the syntax or re-run 'dev init'."
242
+ ) from exc
118
243
 
119
244
  return DevCouncilConfig.model_validate(raw)
120
245
 
121
246
 
122
247
  def provider_api_key_env_var(provider: str = "openrouter") -> str:
248
+ normalized = provider.strip().lower().replace("-", "").replace("_", "")
123
249
  env_map = {
124
250
  "openrouter": "OPENROUTER_API_KEY",
251
+ "vertexai": "VERTEXAI_ACCESS_TOKEN",
252
+ "doubleword": "DOUBLEWORD_API_KEY",
253
+ "ollama": "OLLAMA_API_KEY",
125
254
  "openai": "OPENAI_API_KEY",
126
255
  "anthropic": "ANTHROPIC_API_KEY",
127
256
  }
128
- return env_map.get(provider, f"{provider.upper()}_API_KEY")
257
+ return env_map.get(normalized, f"{normalized.upper()}_API_KEY")
258
+
259
+
260
+ def _normalized_provider_name(provider: str) -> str:
261
+ return provider.strip().lower().replace("-", "").replace("_", "")
129
262
 
130
263
 
131
264
  def load_local_secrets(project_root: Path = Path(".")) -> Dict[str, str]:
@@ -143,6 +276,24 @@ def load_local_secrets(project_root: Path = Path(".")) -> Dict[str, str]:
143
276
  return secrets
144
277
 
145
278
 
279
+ def get_gcloud_access_token() -> str | None:
280
+ executable = shutil.which("gcloud")
281
+ if not executable:
282
+ return None
283
+ try:
284
+ token = subprocess.check_output(
285
+ [executable, "auth", "print-access-token"],
286
+ stderr=subprocess.STDOUT,
287
+ text=True,
288
+ encoding="utf-8",
289
+ errors="replace",
290
+ timeout=10,
291
+ ).strip()
292
+ except Exception:
293
+ return None
294
+ return token or None
295
+
296
+
146
297
  def get_api_key(provider: str = "openrouter", project_root: Path = Path(".")) -> str:
147
298
  """Retrieve the API key for the configured provider from environment.
148
299
 
@@ -150,9 +301,20 @@ def get_api_key(provider: str = "openrouter", project_root: Path = Path(".")) ->
150
301
  """
151
302
  env_var = provider_api_key_env_var(provider)
152
303
  key = os.environ.get(env_var) or load_local_secrets(project_root).get(env_var)
304
+ if not key and _normalized_provider_name(provider) == "vertexai":
305
+ key = get_gcloud_access_token()
306
+ if not key and _normalized_provider_name(provider) == "ollama":
307
+ # Ollama is a local server and needs no API key; an explicitly-set
308
+ # OLLAMA_API_KEY still flows through above if present.
309
+ return ""
153
310
  if not key:
311
+ extra = (
312
+ " You can also authenticate with 'gcloud auth login' for vertexai."
313
+ if _normalized_provider_name(provider) == "vertexai"
314
+ else ""
315
+ )
154
316
  raise ValueError(
155
317
  f"API key not found. Set {env_var} in your environment or run 'dev setup'. "
156
- f"Provider: {provider}"
318
+ f"Provider: {provider}.{extra}"
157
319
  )
158
320
  return key
@@ -18,7 +18,7 @@ from typing import Any, Dict, List, Set, Tuple
18
18
  from devcouncil.domain.requirement import Requirement, AcceptanceCriterion
19
19
  from devcouncil.domain.task import Task
20
20
  from devcouncil.domain.assumption import Assumption
21
- from devcouncil.domain.evidence import CommandResult, DiffEvidence, TestEvidence
21
+ from devcouncil.domain.evidence import CommandResult, DiffCoverageEvidence, DiffEvidence, TestEvidence
22
22
  from devcouncil.domain.gap import Gap
23
23
  from devcouncil.domain.critique import CritiqueFinding
24
24
 
@@ -35,6 +35,7 @@ class ArtifactGraph:
35
35
  test_evidence: List[TestEvidence] = field(default_factory=list)
36
36
  diff_evidence: List[DiffEvidence] = field(default_factory=list)
37
37
  command_results: List[CommandResult] = field(default_factory=list)
38
+ diff_coverage_evidence: List[DiffCoverageEvidence] = field(default_factory=list)
38
39
 
39
40
  # --- Mutation ---
40
41
 
@@ -62,6 +63,17 @@ class ArtifactGraph:
62
63
  def add_command_result(self, cr: CommandResult) -> None:
63
64
  self.command_results.append(cr)
64
65
 
66
+ def add_diff_coverage_evidence(self, ev: DiffCoverageEvidence) -> None:
67
+ self.diff_coverage_evidence.append(ev)
68
+
69
+ def diff_coverage_findings(self) -> List[DiffCoverageEvidence]:
70
+ """Measured diff-coverage runs where the changed lines were NOT fully exercised
71
+ — i.e. a green suite that did not actually run the new code."""
72
+ return [
73
+ ev for ev in self.diff_coverage_evidence
74
+ if ev.measured and ev.changed_lines and ev.covered_lines < ev.changed_lines
75
+ ]
76
+
65
77
  # --- Coverage Queries ---
66
78
 
67
79
  def requirements_without_tasks(self) -> List[Requirement]:
@@ -80,10 +92,16 @@ class ArtifactGraph:
80
92
  return [r for r in self.requirements.values() if not r.acceptance_criteria]
81
93
 
82
94
  def acceptance_criteria_without_evidence(self) -> List[Tuple[str, AcceptanceCriterion]]:
83
- """AC IDs that have no test evidence mapped to them."""
95
+ """AC IDs that have no *passing* test evidence mapped to them.
96
+
97
+ Only ``passed`` evidence counts: a failed or not-run check is not proof, so
98
+ it must not remove a criterion from the unproven list (which would feed a
99
+ falsely-green coverage summary to ``dev status`` and the MCP surface).
100
+ """
84
101
  evidenced_ac_ids: Set[str] = set()
85
102
  for ev in self.test_evidence:
86
- evidenced_ac_ids.add(ev.acceptance_criterion_id)
103
+ if getattr(ev, "status", "passed") == "passed":
104
+ evidenced_ac_ids.add(ev.acceptance_criterion_id)
87
105
 
88
106
  results: List[Tuple[str, AcceptanceCriterion]] = []
89
107
  for req in self.requirements.values():
@@ -137,6 +155,8 @@ class ArtifactGraph:
137
155
  "ac_without_evidence": len(self.acceptance_criteria_without_evidence()),
138
156
  "total_gaps": len(self.gaps),
139
157
  "blocking_gaps": len(self.blocking_gaps()),
158
+ "diff_coverage_runs": len(self.diff_coverage_evidence),
159
+ "unexercised_diff_findings": len(self.diff_coverage_findings()),
140
160
  "open_findings": len(self.open_findings()),
141
161
  "high_critical_open_findings": len(self.open_findings("high")),
142
162
  "unconfirmed_high_assumptions": len(self.unconfirmed_high_impact_assumptions()),