devcouncil 0.2.0 → 0.2.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.
- package/README.md +191 -253
- package/bin/devcouncil.js +130 -32
- package/package.json +6 -17
- package/pyproject.toml +0 -66
- package/src/devcouncil/__init__.py +0 -0
- package/src/devcouncil/__main__.py +0 -4
- package/src/devcouncil/app/__init__.py +0 -28
- package/src/devcouncil/app/config.py +0 -320
- package/src/devcouncil/app/errors.py +0 -23
- package/src/devcouncil/app/events.py +0 -44
- package/src/devcouncil/app/orchestrator.py +0 -92
- package/src/devcouncil/app/project_status.py +0 -29
- package/src/devcouncil/app/run_context.py +0 -39
- package/src/devcouncil/app/state_machine.py +0 -108
- package/src/devcouncil/artifacts/__init__.py +0 -1
- package/src/devcouncil/artifacts/coverage.py +0 -96
- package/src/devcouncil/artifacts/graph.py +0 -163
- package/src/devcouncil/artifacts/migrations.py +0 -20
- package/src/devcouncil/artifacts/schemas.py +0 -23
- package/src/devcouncil/artifacts/serializer.py +0 -21
- package/src/devcouncil/artifacts/validators.py +0 -27
- package/src/devcouncil/assets/__init__.py +0 -1
- package/src/devcouncil/assets/devcouncil-logo.svg +0 -60
- package/src/devcouncil/assets/devcouncil_logo_premium.png +0 -0
- package/src/devcouncil/cli/__init__.py +0 -0
- package/src/devcouncil/cli/commands/__init__.py +0 -0
- package/src/devcouncil/cli/commands/agents.py +0 -292
- package/src/devcouncil/cli/commands/artifacts.py +0 -54
- package/src/devcouncil/cli/commands/ast.py +0 -22
- package/src/devcouncil/cli/commands/baseline.py +0 -35
- package/src/devcouncil/cli/commands/check.py +0 -209
- package/src/devcouncil/cli/commands/config.py +0 -115
- package/src/devcouncil/cli/commands/cost.py +0 -57
- package/src/devcouncil/cli/commands/dashboard.py +0 -31
- package/src/devcouncil/cli/commands/doctor.py +0 -340
- package/src/devcouncil/cli/commands/evidence.py +0 -48
- package/src/devcouncil/cli/commands/go.py +0 -656
- package/src/devcouncil/cli/commands/handoff.py +0 -69
- package/src/devcouncil/cli/commands/hook.py +0 -237
- package/src/devcouncil/cli/commands/init.py +0 -289
- package/src/devcouncil/cli/commands/integrate.py +0 -1515
- package/src/devcouncil/cli/commands/lsp.py +0 -20
- package/src/devcouncil/cli/commands/map.py +0 -112
- package/src/devcouncil/cli/commands/mcp_server.py +0 -18
- package/src/devcouncil/cli/commands/plan.py +0 -488
- package/src/devcouncil/cli/commands/prompt.py +0 -61
- package/src/devcouncil/cli/commands/repair.py +0 -89
- package/src/devcouncil/cli/commands/report.py +0 -137
- package/src/devcouncil/cli/commands/reset_demo_state.py +0 -33
- package/src/devcouncil/cli/commands/rollback.py +0 -59
- package/src/devcouncil/cli/commands/run.py +0 -289
- package/src/devcouncil/cli/commands/runs.py +0 -223
- package/src/devcouncil/cli/commands/scaffold.py +0 -32
- package/src/devcouncil/cli/commands/semantic.py +0 -47
- package/src/devcouncil/cli/commands/setup.py +0 -362
- package/src/devcouncil/cli/commands/shell.py +0 -73
- package/src/devcouncil/cli/commands/show.py +0 -76
- package/src/devcouncil/cli/commands/skills.py +0 -88
- package/src/devcouncil/cli/commands/status.py +0 -141
- package/src/devcouncil/cli/commands/tasks.py +0 -55
- package/src/devcouncil/cli/commands/trace.py +0 -88
- package/src/devcouncil/cli/commands/verify.py +0 -328
- package/src/devcouncil/cli/commands/version.py +0 -20
- package/src/devcouncil/cli/commands/watch.py +0 -574
- package/src/devcouncil/cli/commands/watch_fs.py +0 -40
- package/src/devcouncil/cli/main.py +0 -137
- package/src/devcouncil/council/__init__.py +0 -0
- package/src/devcouncil/council/prompts/__init__.py +0 -0
- package/src/devcouncil/council/prompts/arbiter.md +0 -19
- package/src/devcouncil/council/prompts/critic_a.md +0 -10
- package/src/devcouncil/council/prompts/critic_b.md +0 -10
- package/src/devcouncil/council/prompts/implementation_reviewer.md +0 -16
- package/src/devcouncil/council/prompts/planner_a.md +0 -16
- package/src/devcouncil/council/prompts/planner_b.md +0 -16
- package/src/devcouncil/council/prompts/rebuttal.md +0 -10
- package/src/devcouncil/council/prompts/spec_writer.md +0 -12
- package/src/devcouncil/domain/__init__.py +0 -0
- package/src/devcouncil/domain/assumption.py +0 -17
- package/src/devcouncil/domain/critique.py +0 -32
- package/src/devcouncil/domain/evidence.py +0 -47
- package/src/devcouncil/domain/gap.py +0 -52
- package/src/devcouncil/domain/requirement.py +0 -22
- package/src/devcouncil/domain/task.py +0 -55
- package/src/devcouncil/execution/__init__.py +0 -1
- package/src/devcouncil/execution/checkpoints.py +0 -246
- package/src/devcouncil/execution/context_builder.py +0 -60
- package/src/devcouncil/execution/executor.py +0 -15
- package/src/devcouncil/execution/fs_watcher.py +0 -180
- package/src/devcouncil/execution/handoff.py +0 -102
- package/src/devcouncil/execution/hook_policy.py +0 -253
- package/src/devcouncil/execution/patch.py +0 -77
- package/src/devcouncil/execution/paths.py +0 -14
- package/src/devcouncil/execution/permissions.py +0 -85
- package/src/devcouncil/execution/policy_engine.py +0 -343
- package/src/devcouncil/execution/prompt_builder.py +0 -671
- package/src/devcouncil/execution/shell_session.py +0 -225
- package/src/devcouncil/execution/task_runner.py +0 -170
- package/src/devcouncil/executors/__init__.py +0 -1
- package/src/devcouncil/executors/agent_registry.py +0 -575
- package/src/devcouncil/executors/coding_cli.py +0 -736
- package/src/devcouncil/executors/mini_swe.py +0 -73
- package/src/devcouncil/executors/native/__init__.py +0 -0
- package/src/devcouncil/executors/native/agent.py +0 -208
- package/src/devcouncil/executors/openhands.py +0 -71
- package/src/devcouncil/gating/__init__.py +0 -1
- package/src/devcouncil/gating/checks/__init__.py +0 -0
- package/src/devcouncil/gating/checks/clean_git.py +0 -52
- package/src/devcouncil/gating/checks/planned_files_check.py +0 -32
- package/src/devcouncil/gating/checks/requirement_coverage.py +0 -26
- package/src/devcouncil/gating/checks/secret_scan_check.py +0 -53
- package/src/devcouncil/gating/policy.py +0 -338
- package/src/devcouncil/hardware.py +0 -184
- package/src/devcouncil/indexing/__init__.py +0 -1
- package/src/devcouncil/indexing/ast_matcher.py +0 -168
- package/src/devcouncil/indexing/graph_index.py +0 -48
- package/src/devcouncil/indexing/lsp.py +0 -161
- package/src/devcouncil/indexing/repo_mapper.py +0 -1455
- package/src/devcouncil/indexing/semantic_index.py +0 -205
- package/src/devcouncil/integrations/actions.py +0 -146
- package/src/devcouncil/integrations/check.py +0 -423
- package/src/devcouncil/integrations/code_review_graph.py +0 -163
- package/src/devcouncil/integrations/github.py +0 -39
- package/src/devcouncil/integrations/github_intent.py +0 -142
- package/src/devcouncil/integrations/gitnexus.py +0 -62
- package/src/devcouncil/integrations/graphify.py +0 -34
- package/src/devcouncil/integrations/mcp/__init__.py +0 -0
- package/src/devcouncil/integrations/mcp/server.py +0 -2122
- package/src/devcouncil/integrations/opencode_devcouncil_plugin.mjs +0 -24
- package/src/devcouncil/integrations/pr_comments.py +0 -62
- package/src/devcouncil/live/__init__.py +0 -2
- package/src/devcouncil/live/cards.py +0 -349
- package/src/devcouncil/live/models.py +0 -63
- package/src/devcouncil/live/repair_prompt.py +0 -83
- package/src/devcouncil/live/reviewer.py +0 -70
- package/src/devcouncil/live/signals.py +0 -135
- package/src/devcouncil/live/summary.py +0 -34
- package/src/devcouncil/live/tasks.py +0 -18
- package/src/devcouncil/live/transcripts.py +0 -141
- package/src/devcouncil/llm/__init__.py +0 -1
- package/src/devcouncil/llm/cache.py +0 -42
- package/src/devcouncil/llm/model_defaults.yaml +0 -44
- package/src/devcouncil/llm/provider.py +0 -627
- package/src/devcouncil/llm/router.py +0 -310
- package/src/devcouncil/optimization/__init__.py +0 -1
- package/src/devcouncil/optimization/gepa_agent.py +0 -318
- package/src/devcouncil/planning/__init__.py +0 -1
- package/src/devcouncil/planning/arbiter_service.py +0 -57
- package/src/devcouncil/planning/correction_manifest.py +0 -303
- package/src/devcouncil/planning/critique_service.py +0 -71
- package/src/devcouncil/planning/plan_service.py +0 -60
- package/src/devcouncil/planning/prompt_enhancer_service.py +0 -167
- package/src/devcouncil/planning/repair_service.py +0 -39
- package/src/devcouncil/planning/spec_service.py +0 -70
- package/src/devcouncil/repo/__init__.py +0 -0
- package/src/devcouncil/repo/ci_scaffold.py +0 -157
- package/src/devcouncil/repo/gitignore.py +0 -123
- package/src/devcouncil/repo/sca.py +0 -374
- package/src/devcouncil/reporting/__init__.py +0 -0
- package/src/devcouncil/reporting/github_check.py +0 -32
- package/src/devcouncil/reporting/json_report.py +0 -30
- package/src/devcouncil/reporting/markdown_report.py +0 -83
- package/src/devcouncil/reporting/report_builder.py +0 -14
- package/src/devcouncil/skills/__init__.py +0 -19
- package/src/devcouncil/skills/library/README.md +0 -46
- package/src/devcouncil/skills/library/ai-training.md +0 -50
- package/src/devcouncil/skills/library/android.md +0 -50
- package/src/devcouncil/skills/library/backend.md +0 -52
- package/src/devcouncil/skills/library/core-engineering.md +0 -95
- package/src/devcouncil/skills/library/data-engineering.md +0 -47
- package/src/devcouncil/skills/library/desktop.md +0 -46
- package/src/devcouncil/skills/library/devops.md +0 -48
- package/src/devcouncil/skills/library/game-dev.md +0 -46
- package/src/devcouncil/skills/library/ios.md +0 -48
- package/src/devcouncil/skills/library/mobile-cross-platform.md +0 -46
- package/src/devcouncil/skills/library/security.md +0 -48
- package/src/devcouncil/skills/library/systems.md +0 -48
- package/src/devcouncil/skills/library/web.md +0 -47
- package/src/devcouncil/skills/library/windows.md +0 -47
- package/src/devcouncil/skills/registry.py +0 -330
- package/src/devcouncil/storage/__init__.py +0 -0
- package/src/devcouncil/storage/db.py +0 -147
- package/src/devcouncil/storage/models.py +0 -204
- package/src/devcouncil/storage/native.py +0 -557
- package/src/devcouncil/storage/repositories.py +0 -485
- package/src/devcouncil/telemetry/__init__.py +0 -0
- package/src/devcouncil/telemetry/cost.py +0 -140
- package/src/devcouncil/telemetry/model_pricing.yaml +0 -48
- package/src/devcouncil/telemetry/pricing.py +0 -28
- package/src/devcouncil/telemetry/traces.py +0 -146
- package/src/devcouncil/telemetry/tracker.py +0 -52
- package/src/devcouncil/ui/__init__.py +0 -1
- package/src/devcouncil/ui/dashboard.py +0 -423
- package/src/devcouncil/utils/__init__.py +0 -1
- package/src/devcouncil/utils/redaction.py +0 -147
- package/src/devcouncil/utils/subprocess_env.py +0 -69
- package/src/devcouncil/verification/__init__.py +0 -1
- package/src/devcouncil/verification/acceptance_compiler.py +0 -125
- package/src/devcouncil/verification/ad_hoc_check.py +0 -129
- package/src/devcouncil/verification/diff_coverage.py +0 -353
- package/src/devcouncil/verification/implementation_reviewer.py +0 -55
- package/src/devcouncil/verification/next_actions.py +0 -189
- package/src/devcouncil/verification/sandbox.py +0 -178
- package/src/devcouncil/verification/test_resolver.py +0 -91
- package/src/devcouncil/verification/verifier.py +0 -1548
- 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))
|