devcouncil 0.2.0 → 0.2.2

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 (205) hide show
  1. package/README.md +144 -308
  2. package/bin/devcouncil.js +130 -32
  3. package/package.json +22 -19
  4. package/pyproject.toml +0 -66
  5. package/src/devcouncil/__init__.py +0 -0
  6. package/src/devcouncil/__main__.py +0 -4
  7. package/src/devcouncil/app/__init__.py +0 -28
  8. package/src/devcouncil/app/config.py +0 -320
  9. package/src/devcouncil/app/errors.py +0 -23
  10. package/src/devcouncil/app/events.py +0 -44
  11. package/src/devcouncil/app/orchestrator.py +0 -92
  12. package/src/devcouncil/app/project_status.py +0 -29
  13. package/src/devcouncil/app/run_context.py +0 -39
  14. package/src/devcouncil/app/state_machine.py +0 -108
  15. package/src/devcouncil/artifacts/__init__.py +0 -1
  16. package/src/devcouncil/artifacts/coverage.py +0 -96
  17. package/src/devcouncil/artifacts/graph.py +0 -163
  18. package/src/devcouncil/artifacts/migrations.py +0 -20
  19. package/src/devcouncil/artifacts/schemas.py +0 -23
  20. package/src/devcouncil/artifacts/serializer.py +0 -21
  21. package/src/devcouncil/artifacts/validators.py +0 -27
  22. package/src/devcouncil/assets/__init__.py +0 -1
  23. package/src/devcouncil/assets/devcouncil-logo.svg +0 -60
  24. package/src/devcouncil/assets/devcouncil_logo_premium.png +0 -0
  25. package/src/devcouncil/cli/__init__.py +0 -0
  26. package/src/devcouncil/cli/commands/__init__.py +0 -0
  27. package/src/devcouncil/cli/commands/agents.py +0 -292
  28. package/src/devcouncil/cli/commands/artifacts.py +0 -54
  29. package/src/devcouncil/cli/commands/ast.py +0 -22
  30. package/src/devcouncil/cli/commands/baseline.py +0 -35
  31. package/src/devcouncil/cli/commands/check.py +0 -209
  32. package/src/devcouncil/cli/commands/config.py +0 -115
  33. package/src/devcouncil/cli/commands/cost.py +0 -57
  34. package/src/devcouncil/cli/commands/dashboard.py +0 -31
  35. package/src/devcouncil/cli/commands/doctor.py +0 -340
  36. package/src/devcouncil/cli/commands/evidence.py +0 -48
  37. package/src/devcouncil/cli/commands/go.py +0 -656
  38. package/src/devcouncil/cli/commands/handoff.py +0 -69
  39. package/src/devcouncil/cli/commands/hook.py +0 -237
  40. package/src/devcouncil/cli/commands/init.py +0 -289
  41. package/src/devcouncil/cli/commands/integrate.py +0 -1515
  42. package/src/devcouncil/cli/commands/lsp.py +0 -20
  43. package/src/devcouncil/cli/commands/map.py +0 -112
  44. package/src/devcouncil/cli/commands/mcp_server.py +0 -18
  45. package/src/devcouncil/cli/commands/plan.py +0 -488
  46. package/src/devcouncil/cli/commands/prompt.py +0 -61
  47. package/src/devcouncil/cli/commands/repair.py +0 -89
  48. package/src/devcouncil/cli/commands/report.py +0 -137
  49. package/src/devcouncil/cli/commands/reset_demo_state.py +0 -33
  50. package/src/devcouncil/cli/commands/rollback.py +0 -59
  51. package/src/devcouncil/cli/commands/run.py +0 -289
  52. package/src/devcouncil/cli/commands/runs.py +0 -223
  53. package/src/devcouncil/cli/commands/scaffold.py +0 -32
  54. package/src/devcouncil/cli/commands/semantic.py +0 -47
  55. package/src/devcouncil/cli/commands/setup.py +0 -362
  56. package/src/devcouncil/cli/commands/shell.py +0 -73
  57. package/src/devcouncil/cli/commands/show.py +0 -76
  58. package/src/devcouncil/cli/commands/skills.py +0 -88
  59. package/src/devcouncil/cli/commands/status.py +0 -141
  60. package/src/devcouncil/cli/commands/tasks.py +0 -55
  61. package/src/devcouncil/cli/commands/trace.py +0 -88
  62. package/src/devcouncil/cli/commands/verify.py +0 -328
  63. package/src/devcouncil/cli/commands/version.py +0 -20
  64. package/src/devcouncil/cli/commands/watch.py +0 -574
  65. package/src/devcouncil/cli/commands/watch_fs.py +0 -40
  66. package/src/devcouncil/cli/main.py +0 -137
  67. package/src/devcouncil/council/__init__.py +0 -0
  68. package/src/devcouncil/council/prompts/__init__.py +0 -0
  69. package/src/devcouncil/council/prompts/arbiter.md +0 -19
  70. package/src/devcouncil/council/prompts/critic_a.md +0 -10
  71. package/src/devcouncil/council/prompts/critic_b.md +0 -10
  72. package/src/devcouncil/council/prompts/implementation_reviewer.md +0 -16
  73. package/src/devcouncil/council/prompts/planner_a.md +0 -16
  74. package/src/devcouncil/council/prompts/planner_b.md +0 -16
  75. package/src/devcouncil/council/prompts/rebuttal.md +0 -10
  76. package/src/devcouncil/council/prompts/spec_writer.md +0 -12
  77. package/src/devcouncil/domain/__init__.py +0 -0
  78. package/src/devcouncil/domain/assumption.py +0 -17
  79. package/src/devcouncil/domain/critique.py +0 -32
  80. package/src/devcouncil/domain/evidence.py +0 -47
  81. package/src/devcouncil/domain/gap.py +0 -52
  82. package/src/devcouncil/domain/requirement.py +0 -22
  83. package/src/devcouncil/domain/task.py +0 -55
  84. package/src/devcouncil/execution/__init__.py +0 -1
  85. package/src/devcouncil/execution/checkpoints.py +0 -246
  86. package/src/devcouncil/execution/context_builder.py +0 -60
  87. package/src/devcouncil/execution/executor.py +0 -15
  88. package/src/devcouncil/execution/fs_watcher.py +0 -180
  89. package/src/devcouncil/execution/handoff.py +0 -102
  90. package/src/devcouncil/execution/hook_policy.py +0 -253
  91. package/src/devcouncil/execution/patch.py +0 -77
  92. package/src/devcouncil/execution/paths.py +0 -14
  93. package/src/devcouncil/execution/permissions.py +0 -85
  94. package/src/devcouncil/execution/policy_engine.py +0 -343
  95. package/src/devcouncil/execution/prompt_builder.py +0 -671
  96. package/src/devcouncil/execution/shell_session.py +0 -225
  97. package/src/devcouncil/execution/task_runner.py +0 -170
  98. package/src/devcouncil/executors/__init__.py +0 -1
  99. package/src/devcouncil/executors/agent_registry.py +0 -575
  100. package/src/devcouncil/executors/coding_cli.py +0 -736
  101. package/src/devcouncil/executors/mini_swe.py +0 -73
  102. package/src/devcouncil/executors/native/__init__.py +0 -0
  103. package/src/devcouncil/executors/native/agent.py +0 -208
  104. package/src/devcouncil/executors/openhands.py +0 -71
  105. package/src/devcouncil/gating/__init__.py +0 -1
  106. package/src/devcouncil/gating/checks/__init__.py +0 -0
  107. package/src/devcouncil/gating/checks/clean_git.py +0 -52
  108. package/src/devcouncil/gating/checks/planned_files_check.py +0 -32
  109. package/src/devcouncil/gating/checks/requirement_coverage.py +0 -26
  110. package/src/devcouncil/gating/checks/secret_scan_check.py +0 -53
  111. package/src/devcouncil/gating/policy.py +0 -338
  112. package/src/devcouncil/hardware.py +0 -184
  113. package/src/devcouncil/indexing/__init__.py +0 -1
  114. package/src/devcouncil/indexing/ast_matcher.py +0 -168
  115. package/src/devcouncil/indexing/graph_index.py +0 -48
  116. package/src/devcouncil/indexing/lsp.py +0 -161
  117. package/src/devcouncil/indexing/repo_mapper.py +0 -1455
  118. package/src/devcouncil/indexing/semantic_index.py +0 -205
  119. package/src/devcouncil/integrations/actions.py +0 -146
  120. package/src/devcouncil/integrations/check.py +0 -423
  121. package/src/devcouncil/integrations/code_review_graph.py +0 -163
  122. package/src/devcouncil/integrations/github.py +0 -39
  123. package/src/devcouncil/integrations/github_intent.py +0 -142
  124. package/src/devcouncil/integrations/gitnexus.py +0 -62
  125. package/src/devcouncil/integrations/graphify.py +0 -34
  126. package/src/devcouncil/integrations/mcp/__init__.py +0 -0
  127. package/src/devcouncil/integrations/mcp/server.py +0 -2122
  128. package/src/devcouncil/integrations/opencode_devcouncil_plugin.mjs +0 -24
  129. package/src/devcouncil/integrations/pr_comments.py +0 -62
  130. package/src/devcouncil/live/__init__.py +0 -2
  131. package/src/devcouncil/live/cards.py +0 -349
  132. package/src/devcouncil/live/models.py +0 -63
  133. package/src/devcouncil/live/repair_prompt.py +0 -83
  134. package/src/devcouncil/live/reviewer.py +0 -70
  135. package/src/devcouncil/live/signals.py +0 -135
  136. package/src/devcouncil/live/summary.py +0 -34
  137. package/src/devcouncil/live/tasks.py +0 -18
  138. package/src/devcouncil/live/transcripts.py +0 -141
  139. package/src/devcouncil/llm/__init__.py +0 -1
  140. package/src/devcouncil/llm/cache.py +0 -42
  141. package/src/devcouncil/llm/model_defaults.yaml +0 -44
  142. package/src/devcouncil/llm/provider.py +0 -627
  143. package/src/devcouncil/llm/router.py +0 -310
  144. package/src/devcouncil/optimization/__init__.py +0 -1
  145. package/src/devcouncil/optimization/gepa_agent.py +0 -318
  146. package/src/devcouncil/planning/__init__.py +0 -1
  147. package/src/devcouncil/planning/arbiter_service.py +0 -57
  148. package/src/devcouncil/planning/correction_manifest.py +0 -303
  149. package/src/devcouncil/planning/critique_service.py +0 -71
  150. package/src/devcouncil/planning/plan_service.py +0 -60
  151. package/src/devcouncil/planning/prompt_enhancer_service.py +0 -167
  152. package/src/devcouncil/planning/repair_service.py +0 -39
  153. package/src/devcouncil/planning/spec_service.py +0 -70
  154. package/src/devcouncil/repo/__init__.py +0 -0
  155. package/src/devcouncil/repo/ci_scaffold.py +0 -157
  156. package/src/devcouncil/repo/gitignore.py +0 -123
  157. package/src/devcouncil/repo/sca.py +0 -374
  158. package/src/devcouncil/reporting/__init__.py +0 -0
  159. package/src/devcouncil/reporting/github_check.py +0 -32
  160. package/src/devcouncil/reporting/json_report.py +0 -30
  161. package/src/devcouncil/reporting/markdown_report.py +0 -83
  162. package/src/devcouncil/reporting/report_builder.py +0 -14
  163. package/src/devcouncil/skills/__init__.py +0 -19
  164. package/src/devcouncil/skills/library/README.md +0 -46
  165. package/src/devcouncil/skills/library/ai-training.md +0 -50
  166. package/src/devcouncil/skills/library/android.md +0 -50
  167. package/src/devcouncil/skills/library/backend.md +0 -52
  168. package/src/devcouncil/skills/library/core-engineering.md +0 -95
  169. package/src/devcouncil/skills/library/data-engineering.md +0 -47
  170. package/src/devcouncil/skills/library/desktop.md +0 -46
  171. package/src/devcouncil/skills/library/devops.md +0 -48
  172. package/src/devcouncil/skills/library/game-dev.md +0 -46
  173. package/src/devcouncil/skills/library/ios.md +0 -48
  174. package/src/devcouncil/skills/library/mobile-cross-platform.md +0 -46
  175. package/src/devcouncil/skills/library/security.md +0 -48
  176. package/src/devcouncil/skills/library/systems.md +0 -48
  177. package/src/devcouncil/skills/library/web.md +0 -47
  178. package/src/devcouncil/skills/library/windows.md +0 -47
  179. package/src/devcouncil/skills/registry.py +0 -330
  180. package/src/devcouncil/storage/__init__.py +0 -0
  181. package/src/devcouncil/storage/db.py +0 -147
  182. package/src/devcouncil/storage/models.py +0 -204
  183. package/src/devcouncil/storage/native.py +0 -557
  184. package/src/devcouncil/storage/repositories.py +0 -485
  185. package/src/devcouncil/telemetry/__init__.py +0 -0
  186. package/src/devcouncil/telemetry/cost.py +0 -140
  187. package/src/devcouncil/telemetry/model_pricing.yaml +0 -48
  188. package/src/devcouncil/telemetry/pricing.py +0 -28
  189. package/src/devcouncil/telemetry/traces.py +0 -146
  190. package/src/devcouncil/telemetry/tracker.py +0 -52
  191. package/src/devcouncil/ui/__init__.py +0 -1
  192. package/src/devcouncil/ui/dashboard.py +0 -423
  193. package/src/devcouncil/utils/__init__.py +0 -1
  194. package/src/devcouncil/utils/redaction.py +0 -147
  195. package/src/devcouncil/utils/subprocess_env.py +0 -69
  196. package/src/devcouncil/verification/__init__.py +0 -1
  197. package/src/devcouncil/verification/acceptance_compiler.py +0 -125
  198. package/src/devcouncil/verification/ad_hoc_check.py +0 -129
  199. package/src/devcouncil/verification/diff_coverage.py +0 -353
  200. package/src/devcouncil/verification/implementation_reviewer.py +0 -55
  201. package/src/devcouncil/verification/next_actions.py +0 -189
  202. package/src/devcouncil/verification/sandbox.py +0 -178
  203. package/src/devcouncil/verification/test_resolver.py +0 -91
  204. package/src/devcouncil/verification/verifier.py +0 -1548
  205. package/uv.lock +0 -1226
@@ -1,340 +0,0 @@
1
- import typer
2
- import subprocess
3
- import os
4
- import shutil
5
- from pathlib import Path
6
- from rich.console import Console
7
- from rich.table import Table
8
-
9
- from devcouncil.app.config import get_gcloud_access_token, load_config, load_local_secrets, provider_api_key_env_var
10
- from devcouncil.executors.agent_registry import (
11
- CODING_CLI_INTEGRATION_INFO,
12
- CODING_CLI_PROBE_ORDER,
13
- CODING_CLI_VERSION_COMMANDS,
14
- detect_available_coding_cli,
15
- resolve_automated_executor,
16
- )
17
- from devcouncil.llm.provider import SUPPORTED_MODEL_PROVIDERS, validate_model_provider
18
-
19
- app = typer.Typer()
20
- console = Console()
21
-
22
-
23
- def _probe_ollama(base_url: str) -> tuple[bool, str]:
24
- """Best-effort reachability check for a local Ollama server.
25
-
26
- ``base_url`` carries the OpenAI-compatible ``/v1`` suffix; the native
27
- ``/api/version`` endpoint lives one level up. Returns (reachable, detail);
28
- any failure is reported, never raised.
29
- """
30
- root = base_url.rstrip("/")
31
- if root.endswith("/v1"):
32
- root = root[: -len("/v1")].rstrip("/")
33
- try:
34
- import httpx
35
-
36
- resp = httpx.get(f"{root}/api/version", timeout=3.0)
37
- if resp.status_code < 400:
38
- version = ""
39
- try:
40
- version = (resp.json() or {}).get("version", "")
41
- except Exception:
42
- version = ""
43
- return True, f"Reachable at {root}" + (f" (v{version})." if version else ".")
44
- return False, f"Server at {root} returned HTTP {resp.status_code}."
45
- except Exception:
46
- return False, f"No Ollama server reachable at {root}."
47
-
48
-
49
- def _probe_ollama_models(base_url: str) -> tuple[bool, set[str]]:
50
- """Best-effort list of locally-pulled Ollama model tags via native ``/api/tags``.
51
-
52
- Returns (queried_ok, names). ``names`` holds the reported tags (e.g.
53
- ``qwen2.5-coder:7b``). Any failure returns (False, set()) and is never raised — a
54
- green liveness probe with no pulled model is the most common "configured but doesn't
55
- work" trap, so this turns it into an actionable row."""
56
- root = base_url.rstrip("/")
57
- if root.endswith("/v1"):
58
- root = root[: -len("/v1")].rstrip("/")
59
- try:
60
- import httpx
61
-
62
- resp = httpx.get(f"{root}/api/tags", timeout=3.0)
63
- if resp.status_code >= 400:
64
- return False, set()
65
- models = (resp.json() or {}).get("models", []) or []
66
- names = {str(m.get("name", "")).strip() for m in models if m.get("name")}
67
- return True, {n for n in names if n}
68
- except Exception:
69
- return False, set()
70
-
71
-
72
- def _ollama_model_present(model: str, pulled: set[str]) -> bool:
73
- """Whether ``model`` is among the pulled tags, tolerant of the implicit ``:latest``
74
- tag Ollama adds to untagged models."""
75
- if model in pulled:
76
- return True
77
- base = model.split(":", 1)[0]
78
- # configured "qwen2.5-coder" matches a pulled "qwen2.5-coder:latest", and vice versa.
79
- candidates = {model, f"{model}:latest", base, f"{base}:latest"}
80
- return any(tag in candidates or tag.split(":", 1)[0] == base and ":" not in model for tag in pulled)
81
-
82
-
83
- def render_doctor_check(project_root: Path = Path(".")):
84
- def _command_version(command: list[str]) -> str | None:
85
- executable = shutil.which(command[0])
86
- if not executable:
87
- return None
88
-
89
- resolved_command = [executable, *command[1:]]
90
- use_shell = os.name == "nt" and Path(executable).suffix.lower() in {".bat", ".cmd", ".ps1"}
91
- invocation = subprocess.list2cmdline(resolved_command) if use_shell else resolved_command
92
- try:
93
- return subprocess.check_output(
94
- invocation,
95
- stderr=subprocess.STDOUT,
96
- text=True,
97
- encoding="utf-8",
98
- errors="replace",
99
- shell=use_shell,
100
- timeout=10,
101
- ).splitlines()[0].strip()
102
- except Exception:
103
- return None
104
-
105
- table = Table(title="DevCouncil Doctor Check")
106
- table.add_column("Component", style="cyan")
107
- table.add_column("Status", style="magenta")
108
- table.add_column("Notes", style="green")
109
-
110
- # Check Git
111
- git_ver = _command_version(["git", "--version"])
112
- if git_ver:
113
- table.add_row("Git", "[green]OK[/green]", git_ver)
114
- else:
115
- table.add_row("Git", "[red]Missing[/red]", "Git is required for repo mapping and checkpoints.")
116
-
117
- # Check uv
118
- uv_ver = _command_version(["uv", "--version"])
119
- if uv_ver:
120
- table.add_row("uv", "[green]OK[/green]", uv_ver)
121
- else:
122
- table.add_row("uv", "[red]Missing[/red]", "Install uv to run or install DevCouncil.")
123
-
124
- # Check CLI shims
125
- if shutil.which("devcouncil"):
126
- table.add_row("devcouncil CLI", "[green]OK[/green]", "Found on PATH.")
127
- else:
128
- table.add_row("devcouncil CLI", "[yellow]Missing[/yellow]", "Run via 'uv run devcouncil' or install with 'uv tool install --force .'.")
129
-
130
- # Check ripgrep
131
- rg_ver = _command_version(["rg", "--version"])
132
- if rg_ver:
133
- table.add_row("ripgrep (rg)", "[green]OK[/green]", rg_ver)
134
- else:
135
- table.add_row("ripgrep (rg)", "[yellow]Missing[/yellow]", "ripgrep is highly recommended for fast repo mapping.")
136
-
137
- # Check supported coding CLIs (driven by the agent registry, so new
138
- # built-in agents show up here without doctor edits).
139
- for name in CODING_CLI_PROBE_ORDER:
140
- info = CODING_CLI_INTEGRATION_INFO.get(name)
141
- if info is None:
142
- continue
143
- version = None
144
- for probe in CODING_CLI_VERSION_COMMANDS.get(name, ()):
145
- version = _command_version(list(probe))
146
- if version:
147
- break
148
- if version:
149
- table.add_row(info.label, "[green]OK[/green]", f"{version}. Setup: {info.notes}.")
150
- else:
151
- table.add_row(
152
- info.label,
153
- "[yellow]Missing[/yellow]",
154
- f"Optional. Install {info.label}, then use: {info.notes}.",
155
- )
156
-
157
- detected = detect_available_coding_cli(project_root)
158
- resolved = resolve_automated_executor(project_root, None)
159
- if detected:
160
- table.add_row(
161
- "Recommended coding CLI",
162
- "[green]OK[/green]",
163
- f"Use --executor {resolved} for dev go / dev run (detected on PATH).",
164
- )
165
- else:
166
- table.add_row(
167
- "Recommended coding CLI",
168
- "[yellow]Missing[/yellow]",
169
- "No built-in coding CLI on PATH. Run dev integrate recommend after installing one.",
170
- )
171
-
172
- try:
173
- provider = load_config(project_root).models.provider
174
- except Exception:
175
- provider = "openrouter"
176
- try:
177
- provider = validate_model_provider(provider)
178
- except ValueError:
179
- supported = ", ".join(SUPPORTED_MODEL_PROVIDERS)
180
- table.add_row(
181
- "models.provider",
182
- "[red]Unsupported[/red]",
183
- f"{provider} is configured, but this runtime supports: {supported}.",
184
- )
185
- console.print(table)
186
- return
187
- if provider == "ollama":
188
- # Use the provider's own resolver so the displayed URL reflects OLLAMA_HOST
189
- # (with scheme/-/v1 normalization), not just OLLAMA_BASE_URL.
190
- from devcouncil.execution.prompt_builder import MAX_PROMPT_CHARS
191
- from devcouncil.llm.provider import OllamaProvider
192
-
193
- base_url = OllamaProvider._resolve_base_url()
194
- num_ctx = OllamaProvider._resolve_num_ctx()
195
- # DevCouncil's planning prompts reach ~MAX_PROMPT_CHARS chars (~4 chars/token);
196
- # recommend a context window that covers the prompt plus headroom for output.
197
- recommended_ctx = 16384
198
- min_ctx = max(8192, (MAX_PROMPT_CHARS // 4))
199
- table.add_row(
200
- "OLLAMA",
201
- "[green]OK[/green]",
202
- f"Local provider; no API key required (server: {base_url}).",
203
- )
204
-
205
- # Is the local Ollama server actually up? A native /api/version probe is
206
- # cheap and turns the most common failure ("provider configured but
207
- # `ollama serve` not running") into an actionable row instead of a
208
- # connection traceback on the first model call.
209
- reachable, detail = _probe_ollama(base_url)
210
- if reachable:
211
- table.add_row("OLLAMA server", "[green]OK[/green]", detail)
212
- else:
213
- table.add_row(
214
- "OLLAMA server",
215
- "[yellow]WARN[/yellow]",
216
- f"{detail} Start it with 'ollama serve' (or 'brew services start ollama').",
217
- )
218
-
219
- # A reachable server with the configured model NOT pulled is the most common
220
- # "all-green doctor, 404 on first call" trap. Verify the role models exist locally.
221
- if reachable:
222
- try:
223
- configured_models = sorted(
224
- {role.model for role in load_config(project_root).models.roles.values() if role.model}
225
- )
226
- except Exception:
227
- configured_models = []
228
- queried_ok, pulled = _probe_ollama_models(base_url)
229
- if not queried_ok:
230
- table.add_row(
231
- "OLLAMA models",
232
- "[yellow]WARN[/yellow]",
233
- "Could not list pulled models (/api/tags). Ensure each configured model is pulled.",
234
- )
235
- elif not configured_models:
236
- table.add_row(
237
- "OLLAMA models",
238
- "[yellow]WARN[/yellow]",
239
- "No role models configured; run 'dev setup --provider ollama --model <model>'.",
240
- )
241
- else:
242
- missing = [m for m in configured_models if not _ollama_model_present(m, pulled)]
243
- if missing:
244
- pulls = "; ".join(f"ollama pull {m}" for m in missing)
245
- table.add_row(
246
- "OLLAMA models",
247
- "[yellow]WARN[/yellow]",
248
- f"Configured model(s) not pulled: {', '.join(missing)}. Pull first: {pulls}.",
249
- )
250
- else:
251
- table.add_row(
252
- "OLLAMA models",
253
- "[green]OK[/green]",
254
- f"All configured models present locally ({', '.join(configured_models)}).",
255
- )
256
-
257
- if num_ctx is None:
258
- table.add_row(
259
- "OLLAMA num_ctx",
260
- "[yellow]WARN[/yellow]",
261
- f"OLLAMA_NUM_CTX not set — Ollama's small default (~2048-4096) will "
262
- f"truncate DevCouncil's large planning prompts. Set OLLAMA_NUM_CTX={recommended_ctx}.",
263
- )
264
- elif num_ctx < min_ctx:
265
- table.add_row(
266
- "OLLAMA num_ctx",
267
- "[yellow]WARN[/yellow]",
268
- f"OLLAMA_NUM_CTX={num_ctx} may be too small for planning prompts "
269
- f"(~{MAX_PROMPT_CHARS // 4} tokens); recommend >= {recommended_ctx}.",
270
- )
271
- else:
272
- table.add_row("OLLAMA num_ctx", "[green]OK[/green]", f"context window = {num_ctx} tokens.")
273
-
274
- # Local model size is bounded by host memory — unified RAM on Apple Silicon,
275
- # VRAM on a discrete-GPU box, system RAM otherwise. Surface a model that will
276
- # actually fit on this host (any OS), not a one-size default.
277
- from devcouncil import hardware
278
-
279
- host = hardware.describe_host()
280
- table.add_row(
281
- host.platform_label,
282
- "[green]OK[/green]",
283
- f"{host.chip_label}, {host.memory_label}. "
284
- f"Recommended local model: {host.recommended_ollama_model} "
285
- f"(dev setup --provider ollama --model {host.recommended_ollama_model}).",
286
- )
287
-
288
- console.print(table)
289
- return
290
- env_var = provider_api_key_env_var(provider)
291
- local_secrets = load_local_secrets(project_root)
292
- if os.environ.get(env_var):
293
- table.add_row(env_var, "[green]OK[/green]", f"Found in environment for {provider}.")
294
- elif local_secrets.get(env_var):
295
- table.add_row(env_var, "[green]OK[/green]", f"Found in .devcouncil/secrets.env for {provider}.")
296
- elif provider == "vertexai" and get_gcloud_access_token():
297
- table.add_row(env_var, "[green]OK[/green]", "Resolvable via gcloud auth print-access-token.")
298
- table.caption = "Resolvable via gcloud auth print-access-token."
299
- else:
300
- table.add_row(env_var, "[yellow]Missing[/yellow]", f"Required if using {provider} provider. Run 'dev setup'.")
301
-
302
- if provider == "vertexai":
303
- project = (
304
- os.environ.get("VERTEXAI_PROJECT")
305
- or os.environ.get("GOOGLE_CLOUD_PROJECT")
306
- or local_secrets.get("VERTEXAI_PROJECT")
307
- or local_secrets.get("GOOGLE_CLOUD_PROJECT")
308
- )
309
- if project:
310
- source = "environment" if os.environ.get("VERTEXAI_PROJECT") or os.environ.get("GOOGLE_CLOUD_PROJECT") else ".devcouncil/secrets.env"
311
- table.add_row("VERTEXAI_PROJECT", "[green]OK[/green]", f"Found in {source}.")
312
- else:
313
- table.add_row(
314
- "VERTEXAI_PROJECT",
315
- "[yellow]Missing[/yellow]",
316
- "Required for vertexai. Run 'dev setup --provider vertexai --vertex-project PROJECT_ID'.",
317
- )
318
-
319
- location = os.environ.get("VERTEXAI_LOCATION") or local_secrets.get("VERTEXAI_LOCATION", "global")
320
- table.add_row("VERTEXAI_LOCATION", "[green]OK[/green]", location)
321
-
322
- console.print(table)
323
-
324
-
325
- @app.callback(invoke_without_command=True)
326
- def doctor(
327
- ctx: typer.Context,
328
- project_root: Path = typer.Option(
329
- Path("."),
330
- "--project-root",
331
- help="Repository root containing .devcouncil/config.yaml.",
332
- ),
333
- ):
334
- """
335
- Check the environment for DevCouncil prerequisites.
336
- """
337
- if ctx.invoked_subcommand is not None:
338
- return
339
-
340
- render_doctor_check(project_root.expanduser().resolve())
@@ -1,48 +0,0 @@
1
- import json
2
- import typer
3
- from pathlib import Path
4
- from rich.console import Console
5
-
6
- from devcouncil.cli.commands.init import initialize_project
7
- from devcouncil.storage.db import get_db
8
- from devcouncil.storage.repositories import TaskRepository
9
- from devcouncil.verification.test_resolver import TestResolver
10
- from devcouncil.verification.verifier import Verifier
11
-
12
- app = typer.Typer(help="Evidence suggestion utilities.")
13
- console = Console()
14
-
15
-
16
- @app.command("suggest")
17
- def suggest(
18
- task_id: str = typer.Argument(...),
19
- apply: bool = typer.Option(False, "--apply"),
20
- include_low_confidence: bool = typer.Option(False, "--include-low-confidence"),
21
- project_root: Path = typer.Option(Path("."), "--project-root"),
22
- ):
23
- root = project_root.expanduser().resolve()
24
- initialize_project(root, quiet=True)
25
- db = get_db(root)
26
- if not db:
27
- console.print("[red]DevCouncil not initialized.[/red]")
28
- raise typer.Exit(code=1)
29
-
30
- with db.get_session() as session:
31
- task = TaskRepository(session).get_by_id(task_id)
32
- if not task:
33
- console.print(f"[red]Task {task_id} not found.[/red]")
34
- raise typer.Exit(code=1)
35
- changed = Verifier(root).get_task_changed_files(task_id)
36
- suggestions = TestResolver(root).suggest_for_task(task, changed)
37
- if not include_low_confidence:
38
- suggestions = [s for s in suggestions if s.confidence != "low"]
39
- if apply:
40
- for item in suggestions:
41
- if item.confidence == "high" and item.command not in task.expected_tests:
42
- task.expected_tests.append(item.command)
43
- TaskRepository(session).save(task)
44
- typer.echo(json.dumps({
45
- "task_id": task_id,
46
- "suggestions": [s.model_dump() for s in suggestions],
47
- "expected_tests": task.expected_tests,
48
- }, indent=2))