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,178 +0,0 @@
|
|
|
1
|
-
"""Verification sandbox abstraction."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import hashlib
|
|
6
|
-
import platform
|
|
7
|
-
import subprocess
|
|
8
|
-
import sys
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Literal
|
|
11
|
-
|
|
12
|
-
from pydantic import BaseModel
|
|
13
|
-
|
|
14
|
-
from devcouncil.app.config import DevCouncilConfig, load_config
|
|
15
|
-
from devcouncil.domain.task import Task
|
|
16
|
-
from devcouncil.storage.db import get_db
|
|
17
|
-
from devcouncil.storage.native import VerificationRunRepository
|
|
18
|
-
from devcouncil.verification.verifier import Verifier
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class SandboxResult(BaseModel):
|
|
22
|
-
sandbox: str
|
|
23
|
-
status: Literal["passed", "failed", "unsupported"]
|
|
24
|
-
environment: dict[str, str]
|
|
25
|
-
commands: list[dict]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class VerificationSandbox:
|
|
29
|
-
def run(self, task: Task, commands: list[str], requirements: list) -> SandboxResult:
|
|
30
|
-
raise NotImplementedError
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def _save_run(
|
|
34
|
-
project_root: Path,
|
|
35
|
-
task: Task,
|
|
36
|
-
sandbox: str,
|
|
37
|
-
environment: dict[str, str],
|
|
38
|
-
commands: list[dict],
|
|
39
|
-
status: Literal["passed", "failed", "unsupported"],
|
|
40
|
-
) -> None:
|
|
41
|
-
db = get_db(project_root)
|
|
42
|
-
if not db:
|
|
43
|
-
return
|
|
44
|
-
with db.get_session() as session:
|
|
45
|
-
VerificationRunRepository(session).save(
|
|
46
|
-
task.id,
|
|
47
|
-
sandbox,
|
|
48
|
-
environment,
|
|
49
|
-
commands,
|
|
50
|
-
status,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class LocalSandbox(VerificationSandbox):
|
|
55
|
-
def __init__(self, project_root: Path):
|
|
56
|
-
self.project_root = project_root
|
|
57
|
-
|
|
58
|
-
def run(self, task: Task, commands: list[str], requirements: list) -> SandboxResult:
|
|
59
|
-
import asyncio
|
|
60
|
-
|
|
61
|
-
gaps, _ = asyncio.run(Verifier(self.project_root).verify_task(task, requirements))
|
|
62
|
-
status: Literal["passed", "failed", "unsupported"] = (
|
|
63
|
-
"failed" if any(g.blocking for g in gaps) else "passed"
|
|
64
|
-
)
|
|
65
|
-
env = _environment_metadata(self.project_root)
|
|
66
|
-
command_results = [{"command": cmd, "status": status} for cmd in commands]
|
|
67
|
-
_save_run(self.project_root, task, "local", env, command_results, status)
|
|
68
|
-
return SandboxResult(sandbox="local", status=status, environment=env, commands=command_results)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class DockerSandbox(VerificationSandbox):
|
|
72
|
-
def __init__(self, project_root: Path, config: DevCouncilConfig):
|
|
73
|
-
self.project_root = project_root
|
|
74
|
-
self.config = config
|
|
75
|
-
|
|
76
|
-
def run(self, task: Task, commands: list[str], requirements: list) -> SandboxResult:
|
|
77
|
-
if not shutil_which("docker"):
|
|
78
|
-
result = SandboxResult(
|
|
79
|
-
sandbox="docker",
|
|
80
|
-
status="unsupported",
|
|
81
|
-
environment={},
|
|
82
|
-
commands=[{"reason": "docker not available"}],
|
|
83
|
-
)
|
|
84
|
-
_save_run(self.project_root, task, "docker", result.environment, result.commands, result.status)
|
|
85
|
-
return result
|
|
86
|
-
image = self.config.verification.sandbox.docker_image
|
|
87
|
-
setup = self.config.verification.sandbox.docker_setup_commands
|
|
88
|
-
results: list[dict] = []
|
|
89
|
-
for setup_cmd in setup:
|
|
90
|
-
proc = subprocess.run(
|
|
91
|
-
["docker", "run", "--rm", "-v", f"{self.project_root}:/work", "-w", "/work", image, "sh", "-c", setup_cmd],
|
|
92
|
-
capture_output=True,
|
|
93
|
-
text=True,
|
|
94
|
-
)
|
|
95
|
-
results.append({"command": setup_cmd, "exit_code": proc.returncode})
|
|
96
|
-
if proc.returncode != 0:
|
|
97
|
-
result = SandboxResult(sandbox="docker", status="failed", environment={"image": image}, commands=results)
|
|
98
|
-
_save_run(self.project_root, task, "docker", result.environment, result.commands, result.status)
|
|
99
|
-
return result
|
|
100
|
-
for cmd in commands:
|
|
101
|
-
proc = subprocess.run(
|
|
102
|
-
["docker", "run", "--rm", "-v", f"{self.project_root}:/work", "-w", "/work", image, "sh", "-c", cmd],
|
|
103
|
-
capture_output=True,
|
|
104
|
-
text=True,
|
|
105
|
-
)
|
|
106
|
-
results.append({"command": cmd, "exit_code": proc.returncode})
|
|
107
|
-
if proc.returncode != 0:
|
|
108
|
-
result = SandboxResult(sandbox="docker", status="failed", environment={"image": image}, commands=results)
|
|
109
|
-
_save_run(self.project_root, task, "docker", result.environment, result.commands, result.status)
|
|
110
|
-
return result
|
|
111
|
-
result = SandboxResult(sandbox="docker", status="passed", environment={"image": image}, commands=results)
|
|
112
|
-
_save_run(self.project_root, task, "docker", result.environment, result.commands, result.status)
|
|
113
|
-
return result
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class NixSandbox(VerificationSandbox):
|
|
117
|
-
def __init__(self, project_root: Path, config: DevCouncilConfig):
|
|
118
|
-
self.project_root = project_root
|
|
119
|
-
self.config = config
|
|
120
|
-
|
|
121
|
-
def run(self, task: Task, commands: list[str], requirements: list) -> SandboxResult:
|
|
122
|
-
if not (self.project_root / "flake.nix").exists() or not shutil_which("nix"):
|
|
123
|
-
result = SandboxResult(
|
|
124
|
-
sandbox="nix",
|
|
125
|
-
status="unsupported",
|
|
126
|
-
environment={},
|
|
127
|
-
commands=[{"reason": "nix or flake.nix unavailable"}],
|
|
128
|
-
)
|
|
129
|
-
_save_run(self.project_root, task, "nix", result.environment, result.commands, result.status)
|
|
130
|
-
return result
|
|
131
|
-
attr = self.config.verification.sandbox.nix_flake_attr or "devShells.default"
|
|
132
|
-
results: list[dict] = []
|
|
133
|
-
for cmd in commands:
|
|
134
|
-
proc = subprocess.run(
|
|
135
|
-
["nix", "develop", f".#{attr}", "-c", "sh", "-c", cmd],
|
|
136
|
-
cwd=self.project_root,
|
|
137
|
-
capture_output=True,
|
|
138
|
-
text=True,
|
|
139
|
-
)
|
|
140
|
-
results.append({"command": cmd, "exit_code": proc.returncode})
|
|
141
|
-
if proc.returncode != 0:
|
|
142
|
-
result = SandboxResult(sandbox="nix", status="failed", environment={"attr": attr}, commands=results)
|
|
143
|
-
_save_run(self.project_root, task, "nix", result.environment, result.commands, result.status)
|
|
144
|
-
return result
|
|
145
|
-
result = SandboxResult(sandbox="nix", status="passed", environment={"attr": attr}, commands=results)
|
|
146
|
-
_save_run(self.project_root, task, "nix", result.environment, result.commands, result.status)
|
|
147
|
-
return result
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def shutil_which(name: str) -> str | None:
|
|
151
|
-
import shutil
|
|
152
|
-
|
|
153
|
-
return shutil.which(name)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def _environment_metadata(project_root: Path) -> dict[str, str]:
|
|
157
|
-
env = {
|
|
158
|
-
"python": sys.version.split()[0],
|
|
159
|
-
"platform": platform.platform(),
|
|
160
|
-
}
|
|
161
|
-
try:
|
|
162
|
-
uv = subprocess.check_output(["uv", "--version"], text=True).strip()
|
|
163
|
-
env["uv"] = uv
|
|
164
|
-
except (FileNotFoundError, subprocess.CalledProcessError, OSError):
|
|
165
|
-
pass
|
|
166
|
-
lock = project_root / "uv.lock"
|
|
167
|
-
if lock.exists():
|
|
168
|
-
env["uv_lock_hash"] = hashlib.sha256(lock.read_bytes()).hexdigest()[:16]
|
|
169
|
-
return env
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def get_sandbox(name: str, project_root: Path) -> VerificationSandbox:
|
|
173
|
-
config = load_config(project_root)
|
|
174
|
-
if name == "docker":
|
|
175
|
-
return DockerSandbox(project_root, config)
|
|
176
|
-
if name == "nix":
|
|
177
|
-
return NixSandbox(project_root, config)
|
|
178
|
-
return LocalSandbox(project_root)
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
"""Evidence suggestion from changed files."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Literal
|
|
7
|
-
|
|
8
|
-
from pydantic import BaseModel
|
|
9
|
-
|
|
10
|
-
from devcouncil.domain.task import Task
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class EvidenceSuggestion(BaseModel):
|
|
14
|
-
command: str
|
|
15
|
-
confidence: Literal["high", "medium", "low"]
|
|
16
|
-
reason: str
|
|
17
|
-
paths: list[str] = []
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class TestResolver:
|
|
21
|
-
__test__ = False
|
|
22
|
-
|
|
23
|
-
def __init__(self, project_root: Path):
|
|
24
|
-
self.project_root = project_root.resolve()
|
|
25
|
-
|
|
26
|
-
def suggest_for_task(
|
|
27
|
-
self,
|
|
28
|
-
task: Task,
|
|
29
|
-
changed_files: list[str] | None = None,
|
|
30
|
-
) -> list[EvidenceSuggestion]:
|
|
31
|
-
files = changed_files or [pf.path for pf in task.planned_files]
|
|
32
|
-
suggestions: list[EvidenceSuggestion] = []
|
|
33
|
-
seen: set[str] = set()
|
|
34
|
-
|
|
35
|
-
for path in files:
|
|
36
|
-
normalized = path.replace("\\", "/")
|
|
37
|
-
for candidate in self._candidates_for_path(normalized):
|
|
38
|
-
if candidate in seen:
|
|
39
|
-
continue
|
|
40
|
-
seen.add(candidate)
|
|
41
|
-
confidence, reason = self._confidence_for(normalized, candidate)
|
|
42
|
-
suggestions.append(
|
|
43
|
-
EvidenceSuggestion(
|
|
44
|
-
command=candidate,
|
|
45
|
-
confidence=confidence,
|
|
46
|
-
reason=reason,
|
|
47
|
-
paths=[normalized],
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
return suggestions
|
|
51
|
-
|
|
52
|
-
def _candidates_for_path(self, path: str) -> list[str]:
|
|
53
|
-
candidates: list[str] = []
|
|
54
|
-
if path.startswith("src/") and path.endswith(".py"):
|
|
55
|
-
module = path.removeprefix("src/").removesuffix(".py")
|
|
56
|
-
parts = module.split("/")
|
|
57
|
-
if len(parts) >= 2:
|
|
58
|
-
nested = self.project_root / "tests" / parts[0] / f"test_{parts[-1]}.py"
|
|
59
|
-
if nested.exists():
|
|
60
|
-
candidates.append(f"pytest {nested.relative_to(self.project_root).as_posix()}")
|
|
61
|
-
flat = self.project_root / "tests" / f"test_{parts[-1]}.py"
|
|
62
|
-
if flat.exists():
|
|
63
|
-
candidates.append(f"pytest {flat.relative_to(self.project_root).as_posix()}")
|
|
64
|
-
if "cli/commands/" in path:
|
|
65
|
-
unit = self.project_root / "tests/unit/test_cli_commands.py"
|
|
66
|
-
if unit.exists():
|
|
67
|
-
candidates.append(f"pytest {unit.relative_to(self.project_root).as_posix()}")
|
|
68
|
-
if path.endswith("policy_engine.py"):
|
|
69
|
-
unit = self.project_root / "tests/unit/test_task_policy_engine.py"
|
|
70
|
-
if unit.exists():
|
|
71
|
-
candidates.append(f"pytest {unit.relative_to(self.project_root).as_posix()}")
|
|
72
|
-
if path == "src/auth.py":
|
|
73
|
-
auth_test = self.project_root / "tests/test_auth.py"
|
|
74
|
-
if auth_test.exists():
|
|
75
|
-
candidates.append("pytest tests/test_auth.py")
|
|
76
|
-
if not candidates:
|
|
77
|
-
candidates.append("pytest tests/unit")
|
|
78
|
-
return candidates
|
|
79
|
-
|
|
80
|
-
def _confidence_for(self, path: str, command: str) -> tuple[Literal["high", "medium", "low"], str]:
|
|
81
|
-
if "test_auth.py" in command and path.endswith("auth.py"):
|
|
82
|
-
return "high", "Direct auth module mapping"
|
|
83
|
-
if path.endswith("policy_engine.py") and "test_task_policy_engine.py" in command:
|
|
84
|
-
return "high", "Policy engine unit test exists"
|
|
85
|
-
if "cli/commands" in path and "test_cli_commands.py" in command:
|
|
86
|
-
return "high", "CLI command module maps to cli command tests"
|
|
87
|
-
if command.startswith("pytest tests/") and command != "pytest tests/unit":
|
|
88
|
-
return "high", "Nested module test path exists"
|
|
89
|
-
if "test_" in command:
|
|
90
|
-
return "medium", "Related test file match"
|
|
91
|
-
return "low", "Package-level fallback"
|