devcouncil 0.3.0 → 0.4.0

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