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.
Files changed (366) hide show
  1. package/README.md +92 -33
  2. package/package.json +6 -2
  3. package/packages/codeintel-grammars/hatch_build.py +43 -0
  4. package/packages/codeintel-grammars/pyproject.toml +16 -0
  5. package/packages/codeintel-grammars/src/devcouncil_codeintel_grammars/__init__.py +93 -0
  6. package/pyproject.toml +99 -4
  7. package/src/devcouncil/app/config.py +512 -20
  8. package/src/devcouncil/app/events.py +4 -23
  9. package/src/devcouncil/app/orchestrator.py +5 -0
  10. package/src/devcouncil/app/run_context.py +3 -3
  11. package/src/devcouncil/assets/__init__.py +4 -1
  12. package/src/devcouncil/assets/vendor/force-graph.min.js +5 -0
  13. package/src/devcouncil/campaign/__init__.py +71 -0
  14. package/src/devcouncil/campaign/bloom.py +137 -0
  15. package/src/devcouncil/campaign/dashboard.py +123 -0
  16. package/src/devcouncil/campaign/mailbox.py +305 -0
  17. package/src/devcouncil/campaign/notify.py +91 -0
  18. package/src/devcouncil/campaign/orchestrator.py +592 -0
  19. package/src/devcouncil/campaign/prompts/coordinator.md +29 -0
  20. package/src/devcouncil/campaign/prompts/director.md +21 -0
  21. package/src/devcouncil/campaign/prompts/protocol.md +46 -0
  22. package/src/devcouncil/campaign/prompts/reviewer.md +24 -0
  23. package/src/devcouncil/campaign/prompts/worker.md +24 -0
  24. package/src/devcouncil/campaign/roles.py +202 -0
  25. package/src/devcouncil/campaign/watcher.py +153 -0
  26. package/src/devcouncil/cli/commands/agents.py +24 -17
  27. package/src/devcouncil/cli/commands/artifacts.py +36 -27
  28. package/src/devcouncil/cli/commands/ast.py +12 -3
  29. package/src/devcouncil/cli/commands/baseline.py +21 -12
  30. package/src/devcouncil/cli/commands/boot.py +218 -0
  31. package/src/devcouncil/cli/commands/campaign.py +302 -0
  32. package/src/devcouncil/cli/commands/check.py +225 -12
  33. package/src/devcouncil/cli/commands/config.py +221 -74
  34. package/src/devcouncil/cli/commands/cost.py +137 -28
  35. package/src/devcouncil/cli/commands/dashboard.py +12 -4
  36. package/src/devcouncil/cli/commands/debug_cmd.py +249 -0
  37. package/src/devcouncil/cli/commands/design.py +27 -17
  38. package/src/devcouncil/cli/commands/doctor.py +790 -8
  39. package/src/devcouncil/cli/commands/evidence.py +41 -20
  40. package/src/devcouncil/cli/commands/export.py +73 -0
  41. package/src/devcouncil/cli/commands/gaps.py +175 -0
  42. package/src/devcouncil/cli/commands/gated_write.py +76 -0
  43. package/src/devcouncil/cli/commands/go.py +220 -68
  44. package/src/devcouncil/cli/commands/graph_cmd.py +1192 -0
  45. package/src/devcouncil/cli/commands/handoff.py +45 -34
  46. package/src/devcouncil/cli/commands/hook.py +630 -85
  47. package/src/devcouncil/cli/commands/init.py +89 -30
  48. package/src/devcouncil/cli/commands/integrate.py +296 -1385
  49. package/src/devcouncil/cli/commands/lease.py +120 -0
  50. package/src/devcouncil/cli/commands/logs.py +12 -5
  51. package/src/devcouncil/cli/commands/lsp.py +40 -5
  52. package/src/devcouncil/cli/commands/map.py +317 -74
  53. package/src/devcouncil/cli/commands/mcp_server.py +12 -2
  54. package/src/devcouncil/cli/commands/okf.py +44 -6
  55. package/src/devcouncil/cli/commands/plan.py +184 -69
  56. package/src/devcouncil/cli/commands/prompt.py +26 -17
  57. package/src/devcouncil/cli/commands/provenance.py +79 -0
  58. package/src/devcouncil/cli/commands/repair.py +60 -49
  59. package/src/devcouncil/cli/commands/report.py +148 -40
  60. package/src/devcouncil/cli/commands/requirements.py +104 -0
  61. package/src/devcouncil/cli/commands/reset_demo_state.py +13 -4
  62. package/src/devcouncil/cli/commands/rollback.py +46 -35
  63. package/src/devcouncil/cli/commands/run.py +173 -8
  64. package/src/devcouncil/cli/commands/runs.py +298 -68
  65. package/src/devcouncil/cli/commands/scaffold.py +33 -12
  66. package/src/devcouncil/cli/commands/semantic.py +29 -14
  67. package/src/devcouncil/cli/commands/setup.py +103 -93
  68. package/src/devcouncil/cli/commands/shell.py +51 -42
  69. package/src/devcouncil/cli/commands/show.py +56 -42
  70. package/src/devcouncil/cli/commands/skills.py +29 -20
  71. package/src/devcouncil/cli/commands/status.py +80 -67
  72. package/src/devcouncil/cli/commands/task_gate.py +295 -0
  73. package/src/devcouncil/cli/commands/tasks.py +248 -19
  74. package/src/devcouncil/cli/commands/trace.py +14 -8
  75. package/src/devcouncil/cli/commands/verify.py +33 -8
  76. package/src/devcouncil/cli/commands/version.py +14 -6
  77. package/src/devcouncil/cli/commands/watch.py +56 -40
  78. package/src/devcouncil/cli/commands/watch_fs.py +30 -19
  79. package/src/devcouncil/cli/commands/wiki.py +278 -0
  80. package/src/devcouncil/cli/main.py +58 -1
  81. package/src/devcouncil/codeintel/__init__.py +16 -0
  82. package/src/devcouncil/codeintel/build_control.py +429 -0
  83. package/src/devcouncil/codeintel/build_worker.py +78 -0
  84. package/src/devcouncil/codeintel/debug/__init__.py +17 -0
  85. package/src/devcouncil/codeintel/debug/broker.py +114 -0
  86. package/src/devcouncil/codeintel/debug/broker_client.py +61 -0
  87. package/src/devcouncil/codeintel/debug/consent.py +36 -0
  88. package/src/devcouncil/codeintel/debug/discovery.py +132 -0
  89. package/src/devcouncil/codeintel/debug/fingerprint.py +85 -0
  90. package/src/devcouncil/codeintel/debug/protocol.py +259 -0
  91. package/src/devcouncil/codeintel/debug/python_trace_runner.py +81 -0
  92. package/src/devcouncil/codeintel/debug/session.py +238 -0
  93. package/src/devcouncil/codeintel/debug/tracing.py +201 -0
  94. package/src/devcouncil/codeintel/languages/__init__.py +17 -0
  95. package/src/devcouncil/codeintel/languages/generic_extractor.py +236 -0
  96. package/src/devcouncil/codeintel/languages/registry.py +149 -0
  97. package/src/devcouncil/codeintel/languages/workers.py +245 -0
  98. package/src/devcouncil/codeintel/query/__init__.py +5 -0
  99. package/src/devcouncil/codeintel/query/engine.py +289 -0
  100. package/src/devcouncil/codeintel/resolution/__init__.py +6 -0
  101. package/src/devcouncil/codeintel/resolution/abstract_state.py +301 -0
  102. package/src/devcouncil/codeintel/resolution/frameworks/__init__.py +33 -0
  103. package/src/devcouncil/codeintel/resolution/frameworks/base.py +46 -0
  104. package/src/devcouncil/codeintel/resolution/frameworks/di.py +56 -0
  105. package/src/devcouncil/codeintel/resolution/frameworks/events.py +45 -0
  106. package/src/devcouncil/codeintel/resolution/frameworks/routes.py +88 -0
  107. package/src/devcouncil/codeintel/resolution/semantic.py +887 -0
  108. package/src/devcouncil/codeintel/service.py +104 -0
  109. package/src/devcouncil/codeintel/store/__init__.py +15 -0
  110. package/src/devcouncil/codeintel/store/sqlite.py +1565 -0
  111. package/src/devcouncil/codeintel/sync/__init__.py +19 -0
  112. package/src/devcouncil/codeintel/sync/coordinator.py +430 -0
  113. package/src/devcouncil/codeintel/sync/incremental.py +484 -0
  114. package/src/devcouncil/codeintel/sync/lease.py +96 -0
  115. package/src/devcouncil/codeintel/sync/scope.py +98 -0
  116. package/src/devcouncil/council/__init__.py +4 -0
  117. package/src/devcouncil/council/prompts/__init__.py +4 -0
  118. package/src/devcouncil/domain/checkpoint_refs.py +17 -0
  119. package/src/devcouncil/domain/evidence.py +1 -0
  120. package/src/devcouncil/domain/gap.py +10 -0
  121. package/src/devcouncil/domain/requirement.py +5 -1
  122. package/src/devcouncil/domain/task.py +43 -2
  123. package/src/devcouncil/execution/checkpoints.py +25 -31
  124. package/src/devcouncil/execution/context_builder.py +15 -44
  125. package/src/devcouncil/execution/fs_watcher.py +64 -0
  126. package/src/devcouncil/execution/gated_write.py +203 -0
  127. package/src/devcouncil/execution/handoff.py +2 -1
  128. package/src/devcouncil/execution/hook_policy.py +19 -5
  129. package/src/devcouncil/execution/lease_ops.py +177 -0
  130. package/src/devcouncil/execution/lease_validation.py +71 -0
  131. package/src/devcouncil/execution/patch.py +3 -0
  132. package/src/devcouncil/execution/permissions.py +1 -0
  133. package/src/devcouncil/execution/policy_engine.py +205 -10
  134. package/src/devcouncil/execution/prompt_builder.py +278 -33
  135. package/src/devcouncil/execution/run_trace.py +356 -0
  136. package/src/devcouncil/execution/shell_session.py +46 -5
  137. package/src/devcouncil/execution/stop_gate.py +746 -0
  138. package/src/devcouncil/execution/stop_gate_history.py +113 -0
  139. package/src/devcouncil/execution/stop_gate_state.py +54 -0
  140. package/src/devcouncil/execution/stop_gate_verify_cache.py +69 -0
  141. package/src/devcouncil/execution/task_gate_ops.py +590 -0
  142. package/src/devcouncil/execution/task_runner.py +19 -0
  143. package/src/devcouncil/executors/advisor_tool.py +315 -0
  144. package/src/devcouncil/executors/agent_registry.py +125 -17
  145. package/src/devcouncil/executors/claude_sdk.py +376 -0
  146. package/src/devcouncil/executors/coding_cli.py +724 -25
  147. package/src/devcouncil/executors/mini_swe.py +50 -8
  148. package/src/devcouncil/executors/native/agent.py +224 -19
  149. package/src/devcouncil/executors/openhands.py +50 -8
  150. package/src/devcouncil/executors/transient_retry.py +99 -0
  151. package/src/devcouncil/gating/checks/clean_git.py +5 -2
  152. package/src/devcouncil/gating/checks/planned_files_check.py +38 -11
  153. package/src/devcouncil/gating/checks/secret_scan_check.py +2 -2
  154. package/src/devcouncil/gating/policy.py +46 -2
  155. package/src/devcouncil/indexing/ast_matcher.py +41 -4
  156. package/src/devcouncil/indexing/graph/__init__.py +78 -0
  157. package/src/devcouncil/indexing/graph/api_routes.py +522 -0
  158. package/src/devcouncil/indexing/graph/build.py +862 -0
  159. package/src/devcouncil/indexing/graph/cache.py +329 -0
  160. package/src/devcouncil/indexing/graph/communities.py +28 -0
  161. package/src/devcouncil/indexing/graph/cypher.py +107 -0
  162. package/src/devcouncil/indexing/graph/embeddings.py +194 -0
  163. package/src/devcouncil/indexing/graph/export.py +381 -0
  164. package/src/devcouncil/indexing/graph/export_links.py +81 -0
  165. package/src/devcouncil/indexing/graph/extract_python.py +307 -0
  166. package/src/devcouncil/indexing/graph/extract_ts.py +1205 -0
  167. package/src/devcouncil/indexing/graph/intel.py +668 -0
  168. package/src/devcouncil/indexing/graph/liveness.py +992 -0
  169. package/src/devcouncil/indexing/graph/okf_export.py +65 -0
  170. package/src/devcouncil/indexing/graph/pdg/__init__.py +67 -0
  171. package/src/devcouncil/indexing/graph/pdg/build.py +11 -0
  172. package/src/devcouncil/indexing/graph/pdg/cdg.py +41 -0
  173. package/src/devcouncil/indexing/graph/pdg/cfg.py +199 -0
  174. package/src/devcouncil/indexing/graph/pdg/query.py +21 -0
  175. package/src/devcouncil/indexing/graph/pdg/reaching_def.py +126 -0
  176. package/src/devcouncil/indexing/graph/pdg/schema.py +253 -0
  177. package/src/devcouncil/indexing/graph/pdg/taint.py +154 -0
  178. package/src/devcouncil/indexing/graph/query.py +302 -0
  179. package/src/devcouncil/indexing/graph/resolve.py +1020 -0
  180. package/src/devcouncil/indexing/graph/schema.py +103 -0
  181. package/src/devcouncil/indexing/graph_index.py +20 -29
  182. package/src/devcouncil/indexing/lsp.py +57 -25
  183. package/src/devcouncil/indexing/lsp_client.py +577 -0
  184. package/src/devcouncil/indexing/map_artifacts.py +355 -0
  185. package/src/devcouncil/indexing/map_refresh.py +141 -0
  186. package/src/devcouncil/indexing/repo_mapper.py +1509 -138
  187. package/src/devcouncil/indexing/semantic_index.py +12 -6
  188. package/src/devcouncil/indexing/subsystem_map.py +163 -0
  189. package/src/devcouncil/indexing/ts_imports.py +343 -0
  190. package/src/devcouncil/indexing/viz.py +964 -0
  191. package/src/devcouncil/indexing/walk.py +52 -0
  192. package/src/devcouncil/indexing/wiring.py +1776 -0
  193. package/src/devcouncil/integrations/actions.py +27 -4
  194. package/src/devcouncil/integrations/check.py +211 -16
  195. package/src/devcouncil/integrations/claude_assets.py +209 -12
  196. package/src/devcouncil/integrations/clients/__init__.py +1 -0
  197. package/src/devcouncil/integrations/clients/aider.py +52 -0
  198. package/src/devcouncil/integrations/clients/antigravity.py +87 -0
  199. package/src/devcouncil/integrations/clients/claude.py +339 -0
  200. package/src/devcouncil/integrations/clients/codex.py +39 -0
  201. package/src/devcouncil/integrations/clients/common.py +332 -0
  202. package/src/devcouncil/integrations/clients/cursor.py +164 -0
  203. package/src/devcouncil/integrations/clients/gemini.py +49 -0
  204. package/src/devcouncil/integrations/clients/grok.py +105 -0
  205. package/src/devcouncil/integrations/clients/hooks.py +500 -0
  206. package/src/devcouncil/integrations/clients/opencode.py +96 -0
  207. package/src/devcouncil/integrations/clients/warp.py +75 -0
  208. package/src/devcouncil/integrations/code_review_graph.py +2 -2
  209. package/src/devcouncil/integrations/github.py +73 -7
  210. package/src/devcouncil/integrations/integration_cli.py +197 -0
  211. package/src/devcouncil/integrations/mcp/handlers/__init__.py +1 -0
  212. package/src/devcouncil/integrations/mcp/handlers/ast_lsp.py +77 -0
  213. package/src/devcouncil/integrations/mcp/handlers/checkout.py +50 -0
  214. package/src/devcouncil/integrations/mcp/handlers/cli_gate.py +43 -0
  215. package/src/devcouncil/integrations/mcp/handlers/codeintel.py +182 -0
  216. package/src/devcouncil/integrations/mcp/handlers/debug.py +236 -0
  217. package/src/devcouncil/integrations/mcp/handlers/evidence.py +70 -0
  218. package/src/devcouncil/integrations/mcp/handlers/git.py +281 -0
  219. package/src/devcouncil/integrations/mcp/handlers/graph.py +34 -0
  220. package/src/devcouncil/integrations/mcp/handlers/handoff.py +53 -0
  221. package/src/devcouncil/integrations/mcp/handlers/knowledge.py +28 -0
  222. package/src/devcouncil/integrations/mcp/handlers/lease.py +70 -0
  223. package/src/devcouncil/integrations/mcp/handlers/live.py +108 -0
  224. package/src/devcouncil/integrations/mcp/handlers/map.py +676 -0
  225. package/src/devcouncil/integrations/mcp/handlers/next_task.py +35 -0
  226. package/src/devcouncil/integrations/mcp/handlers/policy.py +80 -0
  227. package/src/devcouncil/integrations/mcp/handlers/prompts.py +168 -0
  228. package/src/devcouncil/integrations/mcp/handlers/provenance.py +87 -0
  229. package/src/devcouncil/integrations/mcp/handlers/read.py +103 -0
  230. package/src/devcouncil/integrations/mcp/handlers/router_cache.py +53 -0
  231. package/src/devcouncil/integrations/mcp/handlers/run.py +53 -0
  232. package/src/devcouncil/integrations/mcp/handlers/runs.py +69 -0
  233. package/src/devcouncil/integrations/mcp/handlers/scope.py +56 -0
  234. package/src/devcouncil/integrations/mcp/handlers/status.py +199 -0
  235. package/src/devcouncil/integrations/mcp/handlers/task.py +88 -0
  236. package/src/devcouncil/integrations/mcp/handlers/tool_specs.py +922 -0
  237. package/src/devcouncil/integrations/mcp/handlers/trace.py +65 -0
  238. package/src/devcouncil/integrations/mcp/handlers/verify.py +45 -0
  239. package/src/devcouncil/integrations/mcp/handlers/wiki.py +53 -0
  240. package/src/devcouncil/integrations/mcp/handlers/write.py +69 -0
  241. package/src/devcouncil/integrations/mcp/server.py +265 -2391
  242. package/src/devcouncil/integrations/mcp/util.py +325 -0
  243. package/src/devcouncil/integrations/setup.py +152 -0
  244. package/src/devcouncil/knowledge/fetch.py +4 -0
  245. package/src/devcouncil/knowledge/knowledge_select.py +38 -0
  246. package/src/devcouncil/knowledge/okf.py +2 -1
  247. package/src/devcouncil/knowledge/resource_discovery.py +40 -0
  248. package/src/devcouncil/knowledge/wiki.py +643 -0
  249. package/src/devcouncil/knowledge/wiki_read.py +87 -0
  250. package/src/devcouncil/live/cards.py +7 -7
  251. package/src/devcouncil/live/models.py +4 -1
  252. package/src/devcouncil/live/reviewer.py +90 -11
  253. package/src/devcouncil/live/signals.py +4 -2
  254. package/src/devcouncil/live/summary.py +21 -3
  255. package/src/devcouncil/live/tasks.py +12 -3
  256. package/src/devcouncil/live/transcripts.py +69 -2
  257. package/src/devcouncil/llm/cache.py +5 -6
  258. package/src/devcouncil/llm/model_defaults.yaml +10 -10
  259. package/src/devcouncil/llm/provider.py +647 -73
  260. package/src/devcouncil/llm/router.py +271 -46
  261. package/src/devcouncil/llm/semantic_bridge.py +614 -0
  262. package/src/devcouncil/optimization/gepa_agent.py +6 -4
  263. package/src/devcouncil/optimization/skillopt.py +9 -5
  264. package/src/devcouncil/planning/arbiter_service.py +12 -3
  265. package/src/devcouncil/planning/correction_manifest.py +107 -10
  266. package/src/devcouncil/planning/plan_difficulty.py +69 -0
  267. package/src/devcouncil/planning/plan_service.py +5 -2
  268. package/src/devcouncil/planning/planned_files_reconcile.py +191 -0
  269. package/src/devcouncil/planning/prompt_enhancer_service.py +6 -5
  270. package/src/devcouncil/planning/question_conversion.py +56 -0
  271. package/src/devcouncil/planning/spec_service.py +9 -3
  272. package/src/devcouncil/repo/ci_scaffold.py +197 -1
  273. package/src/devcouncil/repo/gitignore.py +1 -2
  274. package/src/devcouncil/reporting/evidence_export.py +124 -0
  275. package/src/devcouncil/reporting/evidence_html.py +210 -0
  276. package/src/devcouncil/reporting/json_report.py +16 -12
  277. package/src/devcouncil/reporting/markdown_report.py +38 -9
  278. package/src/devcouncil/reporting/mcp_resources.py +142 -0
  279. package/src/devcouncil/reporting/report_builder.py +40 -4
  280. package/src/devcouncil/reporting/task_provenance.py +42 -0
  281. package/src/devcouncil/reporting/verdict.py +75 -0
  282. package/src/devcouncil/skills/library/README.md +1 -0
  283. package/src/devcouncil/skills/library/devcouncil-hero-loop.md +109 -0
  284. package/src/devcouncil/skills/library/devcouncil-verification.md +109 -0
  285. package/src/devcouncil/skills/library/devcouncil.md +93 -0
  286. package/src/devcouncil/skills/registry.py +43 -12
  287. package/src/devcouncil/storage/db.py +57 -11
  288. package/src/devcouncil/storage/models.py +6 -0
  289. package/src/devcouncil/storage/native.py +5 -3
  290. package/src/devcouncil/storage/repositories.py +50 -18
  291. package/src/devcouncil/telemetry/context.py +28 -0
  292. package/src/devcouncil/telemetry/cost.py +4 -5
  293. package/src/devcouncil/telemetry/logging_setup.py +78 -11
  294. package/src/devcouncil/telemetry/model_pricing.yaml +7 -0
  295. package/src/devcouncil/telemetry/stages.py +27 -2
  296. package/src/devcouncil/telemetry/tracker.py +50 -13
  297. package/src/devcouncil/ui/dashboard.py +120 -8
  298. package/src/devcouncil/utils/fsio.py +58 -0
  299. package/src/devcouncil/utils/git_snapshot.py +112 -0
  300. package/src/devcouncil/utils/json_persist.py +53 -0
  301. package/src/devcouncil/utils/proc.py +89 -0
  302. package/src/devcouncil/verification/acceptance_compiler.py +36 -13
  303. package/src/devcouncil/verification/ad_hoc_check.py +95 -3
  304. package/src/devcouncil/verification/checks/__init__.py +41 -0
  305. package/src/devcouncil/verification/checks/acceptance.py +39 -0
  306. package/src/devcouncil/verification/checks/acceptance_corpus.py +194 -0
  307. package/src/devcouncil/verification/checks/acceptance_evidence.py +239 -0
  308. package/src/devcouncil/verification/checks/command_evidence.py +148 -0
  309. package/src/devcouncil/verification/checks/compiled_acceptance.py +179 -0
  310. package/src/devcouncil/verification/checks/corpus_stale.py +124 -0
  311. package/src/devcouncil/verification/checks/corpus_verification.py +9 -0
  312. package/src/devcouncil/verification/checks/dead_symbols.py +360 -0
  313. package/src/devcouncil/verification/checks/diff_coverage_gate.py +101 -0
  314. package/src/devcouncil/verification/checks/doc_code_ref.py +79 -0
  315. package/src/devcouncil/verification/checks/liveness_ratchet.py +336 -0
  316. package/src/devcouncil/verification/checks/orphan_diff.py +104 -0
  317. package/src/devcouncil/verification/checks/planned_files.py +98 -0
  318. package/src/devcouncil/verification/checks/semantic_diff.py +241 -0
  319. package/src/devcouncil/verification/checks/stale_map.py +80 -0
  320. package/src/devcouncil/verification/checks/stub_scan.py +71 -0
  321. package/src/devcouncil/verification/checks/subsystem_boundary.py +103 -0
  322. package/src/devcouncil/verification/checks/wiring.py +216 -0
  323. package/src/devcouncil/verification/claims/__init__.py +23 -0
  324. package/src/devcouncil/verification/claims/checks.py +395 -0
  325. package/src/devcouncil/verification/claims/mapper.py +168 -0
  326. package/src/devcouncil/verification/claims/models.py +39 -0
  327. package/src/devcouncil/verification/claims/transcript.py +92 -0
  328. package/src/devcouncil/verification/claims/verdict.py +88 -0
  329. package/src/devcouncil/verification/command_evidence.py +170 -0
  330. package/src/devcouncil/verification/command_malformation.py +147 -0
  331. package/src/devcouncil/verification/command_runner.py +164 -0
  332. package/src/devcouncil/verification/coverage_measurement.py +292 -0
  333. package/src/devcouncil/verification/diff_coverage.py +151 -0
  334. package/src/devcouncil/verification/difficulty.py +296 -0
  335. package/src/devcouncil/verification/effort_heuristics.py +178 -0
  336. package/src/devcouncil/verification/gap_ids.py +63 -0
  337. package/src/devcouncil/verification/gate_cache.py +194 -0
  338. package/src/devcouncil/verification/gate_selector.py +344 -0
  339. package/src/devcouncil/verification/git_diff_fallback.py +272 -0
  340. package/src/devcouncil/verification/implementation_reviewer.py +13 -0
  341. package/src/devcouncil/verification/incremental_check.py +241 -0
  342. package/src/devcouncil/verification/next_actions.py +60 -1
  343. package/src/devcouncil/verification/rigor_analytics.py +130 -0
  344. package/src/devcouncil/verification/sandbox.py +38 -11
  345. package/src/devcouncil/verification/stub_detector.py +369 -0
  346. package/src/devcouncil/verification/test_resolver.py +67 -1
  347. package/src/devcouncil/verification/verifier.py +137 -1666
  348. package/src/devcouncil/verification/verify_orchestration.py +610 -0
  349. package/src/devcouncil/verification/verify_setup.py +176 -0
  350. package/src/devcouncil/verification/wiki_refresh.py +208 -0
  351. package/src/semantic_layer/__init__.py +58 -0
  352. package/src/semantic_layer/benchmark.py +75 -0
  353. package/src/semantic_layer/cache.py +290 -0
  354. package/src/semantic_layer/compressor.py +137 -0
  355. package/src/semantic_layer/config.py +75 -0
  356. package/src/semantic_layer/embeddings.py +69 -0
  357. package/src/semantic_layer/llm_backends.py +99 -0
  358. package/src/semantic_layer/pipeline.py +111 -0
  359. package/src/semantic_layer/router.py +128 -0
  360. package/src/semantic_layer/tuner.py +72 -0
  361. package/uv.lock +973 -9
  362. package/src/devcouncil/artifacts/migrations.py +0 -20
  363. package/src/devcouncil/artifacts/schemas.py +0 -23
  364. package/src/devcouncil/artifacts/serializer.py +0 -21
  365. package/src/devcouncil/integrations/gitnexus.py +0 -70
  366. package/src/devcouncil/integrations/graphify.py +0 -34
@@ -0,0 +1,218 @@
1
+ """One-command onboarding: setup, integrate, and run the full DevCouncil loop."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import typer
10
+ from rich.console import Console
11
+
12
+ from devcouncil.app.config import load_config
13
+ from devcouncil.cli.commands.doctor import render_doctor_check
14
+ from devcouncil.cli.commands.go import go as go_command
15
+ from devcouncil.cli.commands.init import initialize_project, parse_role_model_overrides
16
+ from devcouncil.cli.commands.setup import (
17
+ _configure_api_key,
18
+ _configure_coding_cli_integrations,
19
+ _configure_vertexai_settings,
20
+ _set_model_provider,
21
+ _set_model_roles,
22
+ )
23
+ from devcouncil.llm.provider import validate_model_provider
24
+ from devcouncil.repo.ci_scaffold import EVIDENCE_WORKFLOW_RELPATH, WORKFLOW_RELPATH, scaffold_ci, scaffold_evidence_ci
25
+ from devcouncil.telemetry.logging_setup import set_log_dir
26
+ from devcouncil.telemetry.stages import log_stage, log_step
27
+
28
+ console = Console()
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ def _run_setup_path(
33
+ root: Path,
34
+ *,
35
+ name: str | None,
36
+ provider: str | None,
37
+ model: str | None,
38
+ role_models: dict[str, str],
39
+ api_key: str | None,
40
+ skip_api_key: bool,
41
+ skip_integrations: bool,
42
+ skip_map: bool,
43
+ skip_skills: bool,
44
+ scaffold_ci_flag: bool,
45
+ scaffold_ci_evidence: bool,
46
+ gemini_scope: str,
47
+ ) -> None:
48
+ created = initialize_project(
49
+ root,
50
+ project_name=name,
51
+ model_provider=validate_model_provider(provider) if provider else "openrouter",
52
+ model=model,
53
+ role_models=role_models,
54
+ with_map=not skip_map,
55
+ with_skills=not skip_skills,
56
+ )
57
+ if not created:
58
+ console.print(f"[yellow]DevCouncil is already initialized at {root / '.devcouncil'}.[/yellow]")
59
+
60
+ if provider:
61
+ _set_model_provider(root, provider)
62
+
63
+ _set_model_roles(root, model=model, role_models=role_models)
64
+
65
+ configured_provider = load_config(root).models.provider
66
+ _configure_vertexai_settings(root, configured_provider, None, None)
67
+ _configure_api_key(root, api_key, skip_api_key)
68
+
69
+ console.print()
70
+ render_doctor_check(root)
71
+
72
+ if scaffold_ci_flag:
73
+ written = scaffold_ci(root)
74
+ if written is None:
75
+ console.print(f"[yellow]{WORKFLOW_RELPATH.as_posix()} already exists; left unchanged.[/yellow]")
76
+ else:
77
+ console.print(f"[green]Wrote starter CI workflow {written.relative_to(root).as_posix()}.[/green]")
78
+
79
+ if scaffold_ci_evidence:
80
+ evidence_written = scaffold_evidence_ci(root)
81
+ if evidence_written is None:
82
+ console.print(
83
+ f"[yellow]{EVIDENCE_WORKFLOW_RELPATH.as_posix()} already exists; left unchanged.[/yellow]"
84
+ )
85
+ else:
86
+ console.print(
87
+ f"[green]Wrote evidence CI workflow {evidence_written.relative_to(root).as_posix()}.[/green]"
88
+ )
89
+
90
+ if not skip_integrations:
91
+ _configure_coding_cli_integrations(root, apply=True, gemini_scope=gemini_scope)
92
+
93
+
94
+ def boot(
95
+ ctx: typer.Context,
96
+ goal: str = typer.Argument(..., help="Implementation goal passed through to dev go."),
97
+ executor: str | None = typer.Option(
98
+ None,
99
+ "--executor",
100
+ "-e",
101
+ help="Automated executor to use. Defaults to execution.default_executor in .devcouncil/config.yaml.",
102
+ ),
103
+ dry_run: bool = typer.Option(False, "--dry-run", help="Use mock planning responses for local smoke testing."),
104
+ quick: bool = typer.Option(
105
+ False,
106
+ "--quick",
107
+ help="Skip the planning council for a single spec + plan.",
108
+ ),
109
+ force: bool = typer.Option(
110
+ False,
111
+ "--force",
112
+ "--yes",
113
+ "-y",
114
+ help="Proceed past unresolved planning gaps without manual approval.",
115
+ ),
116
+ continue_on_blocked: bool = typer.Option(
117
+ False,
118
+ "--continue-on-blocked",
119
+ help="Continue later tasks even if an earlier task is blocked by verification.",
120
+ ),
121
+ json_report: bool = typer.Option(False, "--json-report", "--json", help="Print the final report as JSON."),
122
+ report_file: Path | None = typer.Option(
123
+ None,
124
+ "--report-file",
125
+ help="Write the final report to a file. Relative paths resolve from --project-root.",
126
+ ),
127
+ agent: bool = typer.Option(
128
+ False,
129
+ "--agent",
130
+ help="Use coding-agent defaults: JSON report plus .devcouncil/reports/latest.json.",
131
+ ),
132
+ profile: str | None = typer.Option(None, "--profile", help="CLI-agent execution profile to pass to dev run."),
133
+ stream: bool = typer.Option(
134
+ False,
135
+ "--stream",
136
+ help="Stream coding CLI stdout/stderr live during execution.",
137
+ ),
138
+ project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
139
+ name: str | None = typer.Option(None, "--name", "-n", help="Project name for .devcouncil/config.yaml."),
140
+ provider: str | None = typer.Option(None, "--provider", help="Set models.provider before configuring the API key."),
141
+ model: str | None = typer.Option(None, "--model", "-m", help="Model id to use for every default role."),
142
+ role_model: list[str] | None = typer.Option(
143
+ None,
144
+ "--role-model",
145
+ help="Per-role model override in ROLE=MODEL form. Can be repeated.",
146
+ ),
147
+ api_key: str | None = typer.Option(None, "--api-key", help="Store the configured provider API key in local secrets."),
148
+ skip_api_key: bool = typer.Option(False, "--skip-api-key", help="Skip the first-run model API key prompt."),
149
+ skip_integrations: bool = typer.Option(
150
+ False,
151
+ "--skip-integrations",
152
+ help="Skip applying coding CLI integrations (default applies dev integrate --apply).",
153
+ ),
154
+ skip_map: bool = typer.Option(False, "--skip-map", help="Skip generating repo_map.json and agent guides on init."),
155
+ skip_skills: bool = typer.Option(False, "--skip-skills", help="Skip scaffolding engineering skills on init."),
156
+ scaffold_ci_flag: bool = typer.Option(False, "--scaffold-ci", help="Write a starter GitHub Actions workflow."),
157
+ scaffold_ci_evidence: bool = typer.Option(
158
+ False,
159
+ "--scaffold-ci-evidence",
160
+ help="Write .github/workflows/devcouncil-evidence.yml.",
161
+ ),
162
+ gemini_scope: str = typer.Option("project", "--gemini-scope", help="Gemini MCP config scope: project or user."),
163
+ ):
164
+ """
165
+ Initialize the repo, apply integrations, and run the full DevCouncil loop.
166
+ """
167
+ if gemini_scope not in {"project", "user"}:
168
+ console.print("[red]--gemini-scope must be 'project' or 'user'.[/red]")
169
+ raise typer.Exit(code=2)
170
+
171
+ root = project_root.expanduser().resolve()
172
+ set_log_dir(root)
173
+ logger.info("dev boot: goal=%r skip_integrations=%s", goal, skip_integrations)
174
+
175
+ try:
176
+ role_models = parse_role_model_overrides(role_model)
177
+ if provider:
178
+ validate_model_provider(provider)
179
+ except ValueError as exc:
180
+ console.print(f"[red]{exc}[/red]")
181
+ raise typer.Exit(code=2) from exc
182
+
183
+ effective_skip_api_key = skip_api_key or not sys.stdin.isatty()
184
+
185
+ with log_stage("boot", project_root=root, goal=goal):
186
+ log_step("boot/1: setup", project_root=root, trace=True)
187
+ _run_setup_path(
188
+ root,
189
+ name=name,
190
+ provider=provider,
191
+ model=model,
192
+ role_models=role_models,
193
+ api_key=api_key,
194
+ skip_api_key=effective_skip_api_key,
195
+ skip_integrations=skip_integrations,
196
+ skip_map=skip_map,
197
+ skip_skills=skip_skills,
198
+ scaffold_ci_flag=scaffold_ci_flag,
199
+ scaffold_ci_evidence=scaffold_ci_evidence,
200
+ gemini_scope=gemini_scope,
201
+ )
202
+
203
+ log_step("boot/2: go", project_root=root, trace=True)
204
+ go_command(
205
+ ctx,
206
+ goal=goal,
207
+ executor=executor,
208
+ dry_run=dry_run,
209
+ quick=quick,
210
+ force=force,
211
+ continue_on_blocked=continue_on_blocked,
212
+ json_report=json_report,
213
+ report_file=report_file,
214
+ agent=agent,
215
+ profile=profile,
216
+ stream=stream,
217
+ project_root=root,
218
+ )
@@ -0,0 +1,302 @@
1
+ """``dev campaign`` — run the planned task graph as a parallel multi-agent campaign.
2
+
3
+ The Director relays your goal to the Coordinator, who dispatches the plan's tasks to a
4
+ pool of Worker in parallel (dependency-aware) and routes each finished task to
5
+ the Reviewer for verification. It reuses DevCouncil's real executors and Verifier;
6
+ without a ``--executor`` it performs a dry run so you can preview the campaign plan.
7
+
8
+ dev campaign run "Ship the settings page" --executor claude --workers 4
9
+ dev campaign status # print the campaign dashboard
10
+ dev campaign inbox coordinator # read an agent's mailbox
11
+ dev campaign roster # show the role hierarchy
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from devcouncil.utils.json_persist import dump_json
17
+ import logging
18
+ from pathlib import Path
19
+ from typing import List, Optional
20
+
21
+ import typer
22
+ from rich.console import Console
23
+ from rich.panel import Panel
24
+ from rich.table import Table
25
+
26
+ from devcouncil.campaign import (
27
+ Mailbox,
28
+ ROLES,
29
+ Rank,
30
+ Campaign,
31
+ build_coding_executor_factory,
32
+ build_verifier_fn,
33
+ )
34
+ from devcouncil.campaign.notify import Notifier, NullNotifier
35
+ from devcouncil.telemetry.stages import log_stage, log_step
36
+
37
+ app = typer.Typer(help="Run a multi-agent campaign over the planned task graph.")
38
+ console = Console()
39
+ logger = logging.getLogger(__name__)
40
+
41
+ # Task statuses that still need work (everything else is a satisfied prerequisite).
42
+ _ACTIONABLE = {"planned", "ready", "blocked", "running"}
43
+
44
+
45
+ def _load_plan(root: Path):
46
+ """Return ``(all_tasks, requirements)`` from the DevCouncil store."""
47
+ from devcouncil.storage.db import get_db
48
+ from devcouncil.storage.repositories import RequirementRepository, TaskRepository
49
+
50
+ db = get_db(root)
51
+ if not db:
52
+ return [], []
53
+ with db.get_session() as session:
54
+ tasks = TaskRepository(session).get_all()
55
+ reqs = RequirementRepository(session).get_all()
56
+ return tasks, reqs
57
+
58
+
59
+ def _persist_statuses(root: Path, tasks) -> None:
60
+ from devcouncil.storage.db import get_db
61
+ from devcouncil.storage.repositories import TaskRepository
62
+
63
+ db = get_db(root)
64
+ if not db:
65
+ return
66
+ with db.get_session() as session:
67
+ repo = TaskRepository(session)
68
+ for task in tasks:
69
+ repo.save(task)
70
+
71
+
72
+ @app.command("run")
73
+ def run_campaign(
74
+ goal: str = typer.Argument(..., help="The order to give the Director (a natural-language goal)."),
75
+ project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
76
+ workers: int = typer.Option(4, "--workers", "-n", min=1, help="Number of Worker (worker slots)."),
77
+ max_parallel: int = typer.Option(
78
+ 4,
79
+ "--max-parallel",
80
+ "-p",
81
+ min=1,
82
+ help=(
83
+ "Max tasks dispatched at once. Tasks with overlapping writable "
84
+ "planned_files are serialized automatically (shared git working tree)."
85
+ ),
86
+ ),
87
+ executor: Optional[str] = typer.Option(
88
+ None,
89
+ "--executor",
90
+ "-e",
91
+ help="Coding CLI the Worker use (claude, codex, …). Omit for a dry run (no changes).",
92
+ ),
93
+ profile: Optional[str] = typer.Option(None, "--profile", help="Executor profile (default/yolo/prod)."),
94
+ verify: bool = typer.Option(True, "--verify/--no-verify", help="Reviewer QC via the DevCouncil Verifier."),
95
+ serial_verify: bool = typer.Option(
96
+ True,
97
+ "--serial-verify/--parallel-verify",
98
+ help="Run Reviewer QC one task at a time (safe against git races) vs. concurrently.",
99
+ ),
100
+ stream: bool = typer.Option(False, "--stream", help="Stream executor output."),
101
+ ntfy_topic: Optional[str] = typer.Option(None, "--ntfy-topic", help="Push campaign updates to this ntfy topic."),
102
+ json_format: bool = typer.Option(False, "--json", help="Emit a machine-readable result."),
103
+ fail_on_blocking: bool = typer.Option(
104
+ False, "--fail-on-blocking", help="Exit non-zero if any task ends blocked (for CI/agents)."
105
+ ),
106
+ cost_budget: Optional[float] = typer.Option(
107
+ None, "--cost-budget", min=0.0, help="Halt campaign when model-call spend exceeds this USD cap."
108
+ ),
109
+ leases: Optional[bool] = typer.Option(
110
+ None, "--leases/--no-leases", help="Checkout per-task leases during execution (default: on with --executor)."
111
+ ),
112
+ ):
113
+ """Start the campaign and execute the plan for GOAL."""
114
+ from devcouncil.cli.commands.init import initialize_project
115
+
116
+ root = project_root.expanduser().resolve()
117
+ from devcouncil.telemetry.logging_setup import set_log_dir
118
+
119
+ set_log_dir(root)
120
+ logger.info("dev campaign run: executor=%s worker=%s", executor, workers)
121
+
122
+ with log_stage("campaign", project_root=root, subcommand="run"):
123
+ initialize_project(root, quiet=True)
124
+ log_step("campaign/1: loading plan", project_root=root, trace=True)
125
+ tasks, reqs = _load_plan(root)
126
+ if not tasks:
127
+ console.print(
128
+ "[yellow]No plan found. Run [bold]dev plan[/bold] first to create tasks for the Director.[/yellow]"
129
+ )
130
+ raise typer.Exit(code=0)
131
+
132
+ dry_run = executor is None
133
+ if len(tasks) >= 5 and not dry_run:
134
+ console.print(
135
+ f"[dim]Large plan ({len(tasks)} tasks) — campaign parallelizes dependency waves "
136
+ f"with Reviewer QC. See .devcouncil/campaign/dashboard.md for progress.[/dim]"
137
+ )
138
+
139
+ if dry_run:
140
+ console.print(
141
+ "[cyan]Dry run — no --executor given. The Coordinator will assign and route, "
142
+ "but Worker will not touch the repo.[/cyan]"
143
+ )
144
+ executor_factory = None # campaign default = echo executor
145
+ verify_fn = None # campaign default = pass-through QC
146
+ else:
147
+ assert executor is not None # dry_run is False iff --executor was given
148
+ executor_factory = build_coding_executor_factory(root, executor, profile=profile, stream=stream)
149
+ if verify:
150
+ from devcouncil.cli.commands.run import _build_verification_router
151
+
152
+ verify_fn = build_verifier_fn(root, _build_verification_router(root))
153
+ else:
154
+ verify_fn = None
155
+
156
+ notifier: Notifier = Notifier(topic=ntfy_topic) if ntfy_topic else NullNotifier()
157
+
158
+ use_leases = leases if leases is not None else (not dry_run)
159
+ budget = cost_budget
160
+ if budget is None:
161
+ try:
162
+ from devcouncil.app.config import load_config
163
+
164
+ budget = load_config(root).telemetry.cost_budget_usd
165
+ except Exception:
166
+ budget = None
167
+
168
+ campaign = Campaign(
169
+ root,
170
+ goal=goal,
171
+ tasks=tasks,
172
+ requirements=reqs,
173
+ num_workers=workers,
174
+ max_parallel=max_parallel,
175
+ executor_factory=executor_factory,
176
+ verify_fn=verify_fn,
177
+ notifier=notifier,
178
+ verify_serialized=serial_verify,
179
+ on_event=None if json_format else (lambda m: console.print(f" {m}")),
180
+ use_leases=use_leases,
181
+ cost_budget_usd=budget,
182
+ )
183
+
184
+ log_step("campaign/2: dispatching", project_root=root, trace=True)
185
+ result = campaign.run()
186
+
187
+ if not dry_run:
188
+ touched = {o.task_id for o in result.outcomes}
189
+ _persist_statuses(root, [t for t in tasks if t.id in touched])
190
+
191
+ if json_format:
192
+ payload = {
193
+ "goal": result.goal,
194
+ "success": result.success,
195
+ "halted": result.halted,
196
+ "halt_reason": result.halt_reason,
197
+ "verified": result.verified,
198
+ "blocked": result.blocked,
199
+ "skipped": result.skipped,
200
+ "dashboard": str(result.dashboard_path) if result.dashboard_path else None,
201
+ "outcomes": [
202
+ {
203
+ "task_id": o.task_id,
204
+ "title": o.title,
205
+ "owner": o.owner,
206
+ "bloom": o.bloom,
207
+ "status": o.status,
208
+ "blocking_gaps": o.blocking_gaps,
209
+ }
210
+ for o in result.outcomes
211
+ ],
212
+ }
213
+ typer.echo(dump_json(payload, indent=2))
214
+ else:
215
+ _render_result(result)
216
+
217
+ log_step("campaign/complete", project_root=root, trace=True)
218
+ if result.halted or (fail_on_blocking and result.blocked):
219
+ raise typer.Exit(code=1)
220
+
221
+
222
+ def _render_result(result) -> None:
223
+ table = Table(title="Director Campaign")
224
+ table.add_column("Task", style="cyan")
225
+ table.add_column("Owner")
226
+ table.add_column("Bloom")
227
+ table.add_column("Verdict")
228
+ verdict_style = {
229
+ "verified": "[green]verified[/green]",
230
+ "blocked": "[red]blocked[/red]",
231
+ "failed": "[red]failed[/red]",
232
+ "skipped": "[yellow]skipped[/yellow]",
233
+ }
234
+ for o in result.outcomes:
235
+ table.add_row(o.title[:48], o.owner, o.bloom, verdict_style.get(o.status, o.status))
236
+ console.print(table)
237
+ console.print(
238
+ Panel(
239
+ result.summary_line(),
240
+ title="Result",
241
+ expand=False,
242
+ border_style="green" if result.success else "yellow",
243
+ )
244
+ )
245
+ if result.dashboard_path:
246
+ console.print(f"[dim]Dashboard: {result.dashboard_path}[/dim]")
247
+
248
+
249
+ @app.command("status")
250
+ def status(
251
+ project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
252
+ ):
253
+ """Print the campaign dashboard (written by the Coordinator)."""
254
+ root = project_root.expanduser().resolve()
255
+ path = root / ".devcouncil" / "campaign" / "dashboard.md"
256
+ if not path.exists():
257
+ console.print("[yellow]No campaign has been run yet — no dashboard to show.[/yellow]")
258
+ raise typer.Exit(code=0)
259
+ console.print(path.read_text(encoding="utf-8"))
260
+
261
+
262
+ @app.command("inbox")
263
+ def inbox(
264
+ agent: str = typer.Argument(..., help="Agent id: director, coordinator, worker1…, reviewer."),
265
+ project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
266
+ unread_only: bool = typer.Option(False, "--unread", help="Show only unread messages."),
267
+ ):
268
+ """Read an agent's mailbox — the on-disk message bus."""
269
+ root = project_root.expanduser().resolve()
270
+ mailbox = Mailbox(root)
271
+ messages = mailbox.unread(agent) if unread_only else mailbox.all(agent)
272
+ if not messages:
273
+ console.print(f"[dim]{agent}'s mailbox is empty.[/dim]")
274
+ raise typer.Exit(code=0)
275
+ table = Table(title=f"{agent} — {len(messages)} message(s)")
276
+ table.add_column("From", style="cyan")
277
+ table.add_column("Type")
278
+ table.add_column("Content")
279
+ table.add_column("Read", justify="center")
280
+ for m in messages:
281
+ table.add_row(m.from_agent, m.type, m.content[:60], "•" if m.read else "[yellow]![/yellow]")
282
+ console.print(table)
283
+
284
+
285
+ @app.command("roster")
286
+ def roster():
287
+ """Show the role hierarchy and each rank's remit."""
288
+ order: List[Rank] = [Rank.DIRECTOR, Rank.COORDINATOR, Rank.WORKER, Rank.REVIEWER]
289
+ table = Table(title="Director Chain of Command")
290
+ table.add_column("Rank", style="magenta")
291
+ table.add_column("Title")
292
+ table.add_column("Reports to")
293
+ table.add_column("Remit")
294
+ for rank in order:
295
+ role = ROLES[rank]
296
+ table.add_row(
297
+ rank.value,
298
+ role.title,
299
+ role.reports_to.value if role.reports_to else "the operator",
300
+ role.summary,
301
+ )
302
+ console.print(table)