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,56 @@
1
+ """Task scope update MCP tool handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from mcp.types import TextContent
8
+
9
+ from devcouncil.integrations.mcp.util import (
10
+ json_text,
11
+ optional_string_list_argument,
12
+ parse_cli_json,
13
+ required_string_argument,
14
+ run_cli_command,
15
+ )
16
+
17
+
18
+ async def handle_update_task_scope(root: Path, db: object, arguments: dict) -> list[TextContent]:
19
+ del db # routed through CLI service layer
20
+ task_id, arg_error = required_string_argument(arguments, "task_id")
21
+ if arg_error:
22
+ return arg_error
23
+ lease_token, arg_error = required_string_argument(arguments, "lease_token")
24
+ if arg_error:
25
+ return arg_error
26
+ assert task_id is not None and lease_token is not None
27
+ expected_tests, arg_error = optional_string_list_argument(arguments, "expected_tests")
28
+ if arg_error:
29
+ return arg_error
30
+ allowed_commands, arg_error = optional_string_list_argument(arguments, "allowed_commands")
31
+ if arg_error:
32
+ return arg_error
33
+ planned_files, arg_error = optional_string_list_argument(arguments, "planned_files")
34
+ if arg_error:
35
+ return arg_error
36
+ create_planned_files, arg_error = optional_string_list_argument(arguments, "create_planned_files")
37
+ if arg_error:
38
+ return arg_error
39
+ cli_args = [
40
+ "scope", "update", task_id,
41
+ "--lease-token", lease_token,
42
+ "--json",
43
+ ]
44
+ for test in expected_tests:
45
+ cli_args.extend(["--expected-test", test])
46
+ for cmd in allowed_commands:
47
+ cli_args.extend(["--allowed-command", cmd])
48
+ for path in planned_files:
49
+ cli_args.extend(["--planned-file", path])
50
+ for path in create_planned_files:
51
+ cli_args.extend(["--planned-file-create", path])
52
+ payload, cli_error = parse_cli_json(run_cli_command(cli_args, root))
53
+ if cli_error:
54
+ return cli_error
55
+ assert payload is not None
56
+ return json_text(payload)
@@ -0,0 +1,199 @@
1
+ """Status, gaps, and next-actions MCP tool handlers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from mcp.types import TextContent
10
+
11
+ from devcouncil.integrations.mcp.util import (
12
+ error_text,
13
+ int_argument,
14
+ json_text,
15
+ optional_string_argument,
16
+ parse_cli_json,
17
+ required_string_argument,
18
+ run_cli_command,
19
+ run_cli_json,
20
+ )
21
+
22
+ # Default MCP list/status projections target a ~32 KB agent context budget.
23
+ MCP_CONTEXT_BUDGET_CHARS = 32_000
24
+
25
+ _LIST_TASK_FIELDS = ("id", "title", "status", "priority", "requirement_ids", "lease")
26
+ _GAP_FIELDS = (
27
+ "id",
28
+ "severity",
29
+ "gap_type",
30
+ "blocking",
31
+ "task_id",
32
+ "requirement_id",
33
+ "file",
34
+ "line",
35
+ "acceptance_criterion_id",
36
+ )
37
+ _ACTION_FIELDS = (
38
+ "gap_id",
39
+ "gap_type",
40
+ "category",
41
+ "severity",
42
+ "blocking",
43
+ "action",
44
+ "file",
45
+ "line",
46
+ "suggested_command",
47
+ "acceptance_criterion_id",
48
+ )
49
+
50
+
51
+ def _compact_task_row(task: object) -> dict[str, Any]:
52
+ if not isinstance(task, dict):
53
+ return {}
54
+ row: dict[str, Any] = {key: task.get(key) for key in _LIST_TASK_FIELDS}
55
+ row["requirements"] = task.get("requirement_ids") or []
56
+ row.pop("requirement_ids", None)
57
+ return row
58
+
59
+
60
+ def _compact_gap_row(gap: object) -> dict[str, Any]:
61
+ if not isinstance(gap, dict):
62
+ return {}
63
+ return {key: gap.get(key) for key in _GAP_FIELDS}
64
+
65
+
66
+ def _compact_action_row(action: object) -> dict[str, Any]:
67
+ if not isinstance(action, dict):
68
+ return {}
69
+ return {key: action.get(key) for key in _ACTION_FIELDS}
70
+
71
+
72
+ def _status_cli_error(cli_error: list[TextContent]) -> list[TextContent]:
73
+ try:
74
+ err = json.loads(cli_error[0].text)
75
+ except (IndexError, json.JSONDecodeError, TypeError):
76
+ return cli_error
77
+ code = err.get("code")
78
+ if code == "cli_parse_error":
79
+ return error_text("status command returned invalid JSON", code="cli_parse_error")
80
+ if code == "cli_failed":
81
+ return error_text(str(err.get("error") or "status command failed"), code="cli_failed")
82
+ return cli_error
83
+
84
+
85
+ async def handle_status(root: Path, db: object, arguments: dict) -> list[TextContent]:
86
+ del db, arguments
87
+ payload, cli_error = parse_cli_json(run_cli_command(["status", "--json"], root, truncate=False))
88
+ if cli_error:
89
+ return _status_cli_error(cli_error)
90
+ assert payload is not None
91
+ if not payload.get("initialized"):
92
+ return error_text("DevCouncil not initialized in this directory.", code="not_initialized")
93
+ summary = payload.get("coverage_summary") or {}
94
+ live = payload.get("live_review") or {}
95
+ phase = payload.get("phase", "UNKNOWN")
96
+ lines = [
97
+ f"Phase: {phase}",
98
+ (
99
+ f"Requirements: {summary.get('total_requirements', 0)} "
100
+ f"({summary.get('requirements_without_tasks', 0)} unmapped)"
101
+ ),
102
+ (
103
+ f"Tasks: {summary.get('total_tasks', 0)} "
104
+ f"({summary.get('tasks_without_requirements', 0)} orphaned)"
105
+ ),
106
+ (
107
+ f"Gaps: {summary.get('total_gaps', 0)} "
108
+ f"({summary.get('blocking_gaps', 0)} blocking)"
109
+ ),
110
+ f"Live signals: {live.get('pending_signals', 0)}",
111
+ ]
112
+ return [TextContent(type="text", text="\n".join(lines) + "\n")]
113
+
114
+
115
+ async def handle_report(root: Path, db: object, arguments: dict) -> list[TextContent]:
116
+ del db, arguments
117
+ result = run_cli_command(["report"], root, truncate=True)
118
+ if not result.get("ok"):
119
+ stderr = str(result.get("stderr") or "report command failed")
120
+ return error_text(stderr, code="cli_failed")
121
+ return [TextContent(type="text", text=str(result.get("stdout") or ""))]
122
+
123
+
124
+ async def handle_get_gaps(root: Path, db: object, arguments: dict) -> list[TextContent]:
125
+ del db
126
+ task_id, arg_error = required_string_argument(arguments, "task_id")
127
+ if arg_error:
128
+ return arg_error
129
+ assert task_id is not None
130
+ blocking_only = bool(arguments.get("blocking_only", False))
131
+ cli_args = ["gaps", "--json", "--task-id", task_id]
132
+ if blocking_only:
133
+ cli_args.append("--blocking-only")
134
+ payload, cli_error = run_cli_json(cli_args, root)
135
+ if cli_error:
136
+ return cli_error
137
+ assert payload is not None
138
+ if not payload.get("initialized", True):
139
+ return error_text("DevCouncil not initialized in this directory.", code="not_initialized")
140
+ gaps = payload.get("gaps")
141
+ if isinstance(gaps, list):
142
+ payload = {
143
+ **payload,
144
+ "gaps": [_compact_gap_row(gap) for gap in gaps],
145
+ "gap_count": len(gaps),
146
+ }
147
+ return json_text(payload)
148
+
149
+
150
+ async def handle_get_next_actions(root: Path, db: object, arguments: dict) -> list[TextContent]:
151
+ del db
152
+ task_id, arg_error = required_string_argument(arguments, "task_id")
153
+ if arg_error:
154
+ return arg_error
155
+ assert task_id is not None
156
+ payload, cli_error = run_cli_json(["gaps", "--json", "--task-id", task_id, "--next-actions"], root)
157
+ if cli_error:
158
+ return cli_error
159
+ assert payload is not None
160
+ if not payload.get("ok", True):
161
+ return error_text(
162
+ str(payload.get("error") or "next-actions command failed"),
163
+ code="cli_failed",
164
+ )
165
+ next_actions = payload.get("next_actions")
166
+ advisory = payload.get("advisory_actions")
167
+ updates: dict[str, Any] = {}
168
+ if isinstance(next_actions, list):
169
+ updates["next_actions"] = [_compact_action_row(a) for a in next_actions]
170
+ updates["next_action_count"] = len(next_actions)
171
+ if isinstance(advisory, list):
172
+ updates["advisory_actions"] = [_compact_action_row(a) for a in advisory]
173
+ updates["advisory_action_count"] = len(advisory)
174
+ if updates:
175
+ payload = {**payload, **updates}
176
+ return json_text(payload)
177
+
178
+
179
+ async def handle_list_tasks(root: Path, db: object, arguments: dict) -> list[TextContent]:
180
+ del db
181
+ status_filter = optional_string_argument(arguments, "status")
182
+ if status_filter == "":
183
+ return error_text("status must be a string", code="invalid_arguments", argument="status")
184
+ limit = int_argument(arguments, "limit", 100, minimum=1, maximum=500)
185
+ offset = int_argument(arguments, "offset", 0, minimum=0, maximum=1_000_000)
186
+ cli_args = ["tasks", "--json", "--limit", str(limit), "--offset", str(offset)]
187
+ if status_filter:
188
+ cli_args.extend(["--status", status_filter])
189
+ payload, cli_error = run_cli_json(cli_args, root)
190
+ if cli_error:
191
+ return cli_error
192
+ assert payload is not None
193
+ tasks = payload.get("tasks")
194
+ if isinstance(tasks, list):
195
+ payload = {
196
+ **payload,
197
+ "tasks": [_compact_task_row(task) for task in tasks],
198
+ }
199
+ return json_text(payload)
@@ -0,0 +1,88 @@
1
+ """Task lookup and prompt MCP tool handlers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from mcp.types import TextContent
8
+
9
+ from devcouncil.integrations.mcp.util import (
10
+ error_text,
11
+ json_text,
12
+ required_string_argument,
13
+ run_cli_json,
14
+ )
15
+ from devcouncil.utils.json_persist import dump_json
16
+
17
+
18
+ async def handle_get_task(root: Path, db: object, arguments: dict) -> list[TextContent]:
19
+ del db # routed through CLI service layer
20
+ task_id, arg_error = required_string_argument(arguments, "task_id")
21
+ if arg_error:
22
+ return arg_error
23
+ assert task_id is not None
24
+ payload, cli_error = run_cli_json(["show", task_id, "--json"], root)
25
+ if cli_error:
26
+ return cli_error
27
+ assert payload is not None
28
+ task = payload.get("task")
29
+ if not isinstance(task, dict):
30
+ return error_text(f"Task {task_id} not found.", code="not_found", task_id=str(task_id))
31
+ return [TextContent(type="text", text=dump_json(task, indent=2))]
32
+
33
+
34
+ async def handle_get_prompt(root: Path, db: object, arguments: dict) -> list[TextContent]:
35
+ del db # routed through CLI service layer
36
+ task_id, arg_error = required_string_argument(arguments, "task_id")
37
+ if arg_error:
38
+ return arg_error
39
+ assert task_id is not None
40
+ payload, cli_error = run_cli_json(["prompt", task_id, "--json"], root)
41
+ if cli_error:
42
+ return cli_error
43
+ assert payload is not None
44
+ if not payload.get("ok"):
45
+ return error_text(
46
+ str(payload.get("error") or f"Task {task_id} not found."),
47
+ code="not_found",
48
+ task_id=str(task_id),
49
+ )
50
+ prompt = payload.get("prompt")
51
+ if not isinstance(prompt, str):
52
+ return error_text("prompt command returned no prompt text", code="cli_parse_error")
53
+ return [TextContent(type="text", text=prompt)]
54
+
55
+
56
+ async def handle_prepare_execution(root: Path, db: object, arguments: dict) -> list[TextContent]:
57
+ del db # routed through CLI service layer
58
+ task_id, arg_error = required_string_argument(arguments, "task_id")
59
+ if arg_error:
60
+ return arg_error
61
+ assert task_id is not None
62
+ show_payload, show_error = run_cli_json(["show", task_id, "--json"], root)
63
+ if show_error:
64
+ return show_error
65
+ assert show_payload is not None
66
+ task = show_payload.get("task")
67
+ if not isinstance(task, dict):
68
+ return error_text(f"Task {task_id} not found.", code="not_found", task_id=str(task_id))
69
+ prompt_payload, prompt_error = run_cli_json(["prompt", task_id, "--json"], root)
70
+ if prompt_error:
71
+ return prompt_error
72
+ assert prompt_payload is not None
73
+ if not prompt_payload.get("ok"):
74
+ return error_text(
75
+ str(prompt_payload.get("error") or f"Task {task_id} not found."),
76
+ code="not_found",
77
+ task_id=str(task_id),
78
+ )
79
+ prompt = prompt_payload.get("prompt")
80
+ if not isinstance(prompt, str):
81
+ return error_text("prompt command returned no prompt text", code="cli_parse_error")
82
+ return json_text({
83
+ "task_id": task.get("id", task_id),
84
+ "prompt": prompt,
85
+ "planned_files": task.get("planned_files") or [],
86
+ "allowed_commands": task.get("allowed_commands") or [],
87
+ "expected_tests": task.get("expected_tests") or [],
88
+ })