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,575 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import shutil
|
|
4
|
-
from dataclasses import dataclass, field
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from devcouncil.app.config import CliAgentProfileConfig, load_config
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
AGENT_ALIASES = {
|
|
12
|
-
"agy": "antigravity",
|
|
13
|
-
"agy-cli": "antigravity",
|
|
14
|
-
"antigravity-cli": "antigravity",
|
|
15
|
-
"google-antigravity": "antigravity",
|
|
16
|
-
"codex-cli": "codex",
|
|
17
|
-
"gemini-cli": "gemini",
|
|
18
|
-
"claude-cli": "claude",
|
|
19
|
-
"claude-code": "claude",
|
|
20
|
-
"opencode-cli": "opencode",
|
|
21
|
-
"open-code": "opencode",
|
|
22
|
-
"warp-cli": "warp",
|
|
23
|
-
"oz": "warp",
|
|
24
|
-
"oz-cli": "warp",
|
|
25
|
-
"cursor-agent": "cursor",
|
|
26
|
-
"cursor-cli": "cursor",
|
|
27
|
-
"copilot-cli": "copilot",
|
|
28
|
-
"github-copilot": "copilot",
|
|
29
|
-
"gh-copilot": "copilot",
|
|
30
|
-
"goose-cli": "goose",
|
|
31
|
-
"block-goose": "goose",
|
|
32
|
-
"amp-cli": "amp",
|
|
33
|
-
"sourcegraph-amp": "amp",
|
|
34
|
-
"qwen-code": "qwen",
|
|
35
|
-
"qwen-cli": "qwen",
|
|
36
|
-
"crush-cli": "crush",
|
|
37
|
-
"charm-crush": "crush",
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
BUILTIN_AGENT_NAMES = {
|
|
41
|
-
"aider",
|
|
42
|
-
"amp",
|
|
43
|
-
"antigravity",
|
|
44
|
-
"claude",
|
|
45
|
-
"codex",
|
|
46
|
-
"copilot",
|
|
47
|
-
"crush",
|
|
48
|
-
"cursor",
|
|
49
|
-
"gemini",
|
|
50
|
-
"goose",
|
|
51
|
-
"opencode",
|
|
52
|
-
"qwen",
|
|
53
|
-
"warp",
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
BUILTIN_CODING_EXECUTOR_NAMES = tuple(sorted(BUILTIN_AGENT_NAMES))
|
|
57
|
-
VALID_INPUT_MODES = {"stdin", "argument", "prompt-file"}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@dataclass(frozen=True)
|
|
61
|
-
class CliAgentSpec:
|
|
62
|
-
name: str
|
|
63
|
-
command: str
|
|
64
|
-
args: list[str] = field(default_factory=list)
|
|
65
|
-
input_mode: str = "stdin"
|
|
66
|
-
prompt_arg: str | None = None
|
|
67
|
-
timeout_seconds: int | None = None
|
|
68
|
-
env: dict[str, str] = field(default_factory=dict)
|
|
69
|
-
display_name: str | None = None
|
|
70
|
-
kind: str = "custom"
|
|
71
|
-
supports_mcp: bool = False
|
|
72
|
-
supports_diff_review: bool = False
|
|
73
|
-
default_profile: str = "default"
|
|
74
|
-
help_command: list[str] = field(default_factory=list)
|
|
75
|
-
built_in: bool = False
|
|
76
|
-
capabilities: list[str] = field(default_factory=list)
|
|
77
|
-
supports_handoff: bool = False
|
|
78
|
-
preferred_artifact_format: str = "json"
|
|
79
|
-
|
|
80
|
-
@property
|
|
81
|
-
def label(self) -> str:
|
|
82
|
-
return self.display_name or self.name
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def executable(self) -> str:
|
|
86
|
-
return self.command
|
|
87
|
-
|
|
88
|
-
def base_command(self) -> list[str]:
|
|
89
|
-
return [self.command, *self.args]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def normalize_agent_name(name: str) -> str:
|
|
93
|
-
normalized = (name or "").strip().lower().replace("_", "-")
|
|
94
|
-
return AGENT_ALIASES.get(normalized, normalized)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def resolve_cursor_agent_executable() -> str | None:
|
|
98
|
-
for candidate in ("cursor-agent", "agent"):
|
|
99
|
-
if shutil.which(candidate):
|
|
100
|
-
return candidate
|
|
101
|
-
return None
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
CODING_CLI_PROBE_ORDER: tuple[str, ...] = (
|
|
105
|
-
"codex",
|
|
106
|
-
"gemini",
|
|
107
|
-
"claude",
|
|
108
|
-
"cursor",
|
|
109
|
-
"opencode",
|
|
110
|
-
"antigravity",
|
|
111
|
-
"warp",
|
|
112
|
-
"aider",
|
|
113
|
-
"copilot",
|
|
114
|
-
"goose",
|
|
115
|
-
"amp",
|
|
116
|
-
"qwen",
|
|
117
|
-
"crush",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
@dataclass(frozen=True)
|
|
121
|
-
class CodingCliIntegrationInfo:
|
|
122
|
-
name: str
|
|
123
|
-
label: str
|
|
124
|
-
tier: int
|
|
125
|
-
headless: bool
|
|
126
|
-
mcp: bool
|
|
127
|
-
hooks: bool
|
|
128
|
-
launcher_shim: bool
|
|
129
|
-
notes: str
|
|
130
|
-
|
|
131
|
-
@property
|
|
132
|
-
def enforcement(self) -> str:
|
|
133
|
-
"""Honest containment posture: ``pre-action`` when a native hook blocks
|
|
134
|
-
unauthorized writes/commands before they happen, else ``verify-only`` — for
|
|
135
|
-
which "forbidden changes" are caught only post-hoc by verification, not blocked
|
|
136
|
-
up front. Surfaced so users aren't misled into assuming hard containment."""
|
|
137
|
-
return "pre-action" if self.hooks else "verify-only"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
# Tier 1 = headless executor + verify; tier 2 = MCP companion; hooks = native pre-tool policy.
|
|
141
|
-
CODING_CLI_INTEGRATION_INFO: dict[str, CodingCliIntegrationInfo] = {
|
|
142
|
-
"codex": CodingCliIntegrationInfo(
|
|
143
|
-
"codex", "Codex CLI", 1, True, True, True, True, "dev integrate codex --apply"
|
|
144
|
-
),
|
|
145
|
-
"gemini": CodingCliIntegrationInfo(
|
|
146
|
-
"gemini", "Gemini CLI", 1, True, True, True, True, "dev integrate gemini --apply"
|
|
147
|
-
),
|
|
148
|
-
"claude": CodingCliIntegrationInfo(
|
|
149
|
-
"claude", "Claude Code", 1, True, True, True, True, "dev integrate claude --apply"
|
|
150
|
-
),
|
|
151
|
-
"cursor": CodingCliIntegrationInfo(
|
|
152
|
-
"cursor",
|
|
153
|
-
"Cursor",
|
|
154
|
-
1,
|
|
155
|
-
True,
|
|
156
|
-
True,
|
|
157
|
-
True,
|
|
158
|
-
True,
|
|
159
|
-
"dev integrate cursor --apply; cursor-agent for headless",
|
|
160
|
-
),
|
|
161
|
-
"opencode": CodingCliIntegrationInfo(
|
|
162
|
-
"opencode", "OpenCode", 1, True, True, True, True, "dev integrate opencode --apply"
|
|
163
|
-
),
|
|
164
|
-
"antigravity": CodingCliIntegrationInfo(
|
|
165
|
-
"antigravity",
|
|
166
|
-
"Google Antigravity CLI",
|
|
167
|
-
1,
|
|
168
|
-
True,
|
|
169
|
-
True,
|
|
170
|
-
False,
|
|
171
|
-
True,
|
|
172
|
-
"dev integrate antigravity --apply",
|
|
173
|
-
),
|
|
174
|
-
"warp": CodingCliIntegrationInfo(
|
|
175
|
-
"warp", "Warp / Oz", 1, True, True, False, True, "dev integrate warp --apply"
|
|
176
|
-
),
|
|
177
|
-
"aider": CodingCliIntegrationInfo(
|
|
178
|
-
"aider", "Aider", 1, True, False, False, True, "dev integrate aider --apply"
|
|
179
|
-
),
|
|
180
|
-
"copilot": CodingCliIntegrationInfo(
|
|
181
|
-
"copilot", "GitHub Copilot CLI", 1, True, True, False, True, "dev run TASK-ID --executor copilot"
|
|
182
|
-
),
|
|
183
|
-
"goose": CodingCliIntegrationInfo(
|
|
184
|
-
"goose", "Goose", 1, True, True, False, True, "dev run TASK-ID --executor goose"
|
|
185
|
-
),
|
|
186
|
-
"amp": CodingCliIntegrationInfo(
|
|
187
|
-
"amp", "Amp (Sourcegraph)", 1, True, True, False, True, "dev run TASK-ID --executor amp"
|
|
188
|
-
),
|
|
189
|
-
"qwen": CodingCliIntegrationInfo(
|
|
190
|
-
"qwen", "Qwen Code", 1, True, True, False, True, "dev run TASK-ID --executor qwen"
|
|
191
|
-
),
|
|
192
|
-
"crush": CodingCliIntegrationInfo(
|
|
193
|
-
"crush", "Crush (Charm)", 1, True, True, False, True, "dev run TASK-ID --executor crush"
|
|
194
|
-
),
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
# First successful probe wins (cursor tries cursor-agent then cursor).
|
|
198
|
-
CODING_CLI_VERSION_COMMANDS: dict[str, tuple[tuple[str, ...], ...]] = {
|
|
199
|
-
"codex": (("codex", "--version"),),
|
|
200
|
-
"gemini": (("gemini", "--version"),),
|
|
201
|
-
"claude": (("claude", "--version"),),
|
|
202
|
-
"cursor": (("cursor-agent", "--version"), ("cursor", "--version")),
|
|
203
|
-
"opencode": (("opencode", "--version"),),
|
|
204
|
-
"antigravity": (("agy", "--version"),),
|
|
205
|
-
"warp": (("oz", "--version"),),
|
|
206
|
-
"aider": (("aider", "--version"),),
|
|
207
|
-
"copilot": (("copilot", "--version"),),
|
|
208
|
-
"goose": (("goose", "--version"),),
|
|
209
|
-
"amp": (("amp", "--version"),),
|
|
210
|
-
"qwen": (("qwen", "--version"),),
|
|
211
|
-
"crush": (("crush", "--version"),),
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def integration_tier_label(client: str) -> str:
|
|
216
|
-
info = CODING_CLI_INTEGRATION_INFO.get(normalize_agent_name(client))
|
|
217
|
-
tier = info.tier if info is not None else 3
|
|
218
|
-
if tier == 1:
|
|
219
|
-
return "Tier 1 (headless executor)"
|
|
220
|
-
if tier == 2:
|
|
221
|
-
return "Tier 2 (MCP companion)"
|
|
222
|
-
return "Tier 3 (sidecar)"
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
def resolve_coding_cli_executable(project_root: Path, client: str) -> str | None:
|
|
226
|
-
normalized = normalize_agent_name(client)
|
|
227
|
-
if normalized == "cursor":
|
|
228
|
-
return resolve_cursor_agent_executable()
|
|
229
|
-
spec = get_cli_agent_spec(project_root, normalized)
|
|
230
|
-
if not spec:
|
|
231
|
-
return None
|
|
232
|
-
return spec.executable if shutil.which(spec.executable) else None
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
def resolve_coding_cli_probe_order(project_root: Path) -> tuple[str, ...]:
|
|
236
|
-
try:
|
|
237
|
-
configured = load_config(project_root).execution.coding_cli_probe_order
|
|
238
|
-
if configured:
|
|
239
|
-
return tuple(normalize_agent_name(name) for name in configured)
|
|
240
|
-
except Exception:
|
|
241
|
-
pass
|
|
242
|
-
return CODING_CLI_PROBE_ORDER
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def detect_available_coding_cli(
|
|
246
|
-
project_root: Path,
|
|
247
|
-
probe_order: tuple[str, ...] | None = None,
|
|
248
|
-
) -> str | None:
|
|
249
|
-
for client in probe_order or resolve_coding_cli_probe_order(project_root):
|
|
250
|
-
if resolve_coding_cli_executable(project_root, client):
|
|
251
|
-
return client
|
|
252
|
-
return None
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
def resolve_automated_executor(
|
|
256
|
-
project_root: Path,
|
|
257
|
-
explicit: str | None = None,
|
|
258
|
-
*,
|
|
259
|
-
probe_order: tuple[str, ...] | None = None,
|
|
260
|
-
) -> str:
|
|
261
|
-
if explicit:
|
|
262
|
-
return normalize_agent_name(explicit)
|
|
263
|
-
try:
|
|
264
|
-
config = load_config(project_root).execution
|
|
265
|
-
configured = normalize_agent_name(config.default_executor)
|
|
266
|
-
if probe_order is None:
|
|
267
|
-
probe_order = resolve_coding_cli_probe_order(project_root)
|
|
268
|
-
except Exception:
|
|
269
|
-
configured = "manual"
|
|
270
|
-
if probe_order is None:
|
|
271
|
-
probe_order = CODING_CLI_PROBE_ORDER
|
|
272
|
-
if configured != "manual":
|
|
273
|
-
return configured
|
|
274
|
-
detected = detect_available_coding_cli(project_root, probe_order=probe_order)
|
|
275
|
-
return detected or configured
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
def is_reserved_agent_name(name: str) -> bool:
|
|
279
|
-
return normalize_agent_name(name) in BUILTIN_AGENT_NAMES
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
def builtin_agent_specs(project_root: Path) -> dict[str, CliAgentSpec]:
|
|
283
|
-
warp_mcp_path = project_root / ".devcouncil" / "integrations" / "warp-mcp.json"
|
|
284
|
-
return {
|
|
285
|
-
"codex": CliAgentSpec(
|
|
286
|
-
name="codex",
|
|
287
|
-
command="codex",
|
|
288
|
-
args=["exec", "-"],
|
|
289
|
-
display_name="Codex CLI",
|
|
290
|
-
kind="coding-cli",
|
|
291
|
-
supports_mcp=True,
|
|
292
|
-
supports_diff_review=True,
|
|
293
|
-
supports_handoff=True,
|
|
294
|
-
built_in=True,
|
|
295
|
-
),
|
|
296
|
-
"gemini": CliAgentSpec(
|
|
297
|
-
name="gemini",
|
|
298
|
-
command="gemini",
|
|
299
|
-
display_name="Gemini CLI",
|
|
300
|
-
kind="coding-cli",
|
|
301
|
-
supports_mcp=True,
|
|
302
|
-
built_in=True,
|
|
303
|
-
),
|
|
304
|
-
"claude": CliAgentSpec(
|
|
305
|
-
name="claude",
|
|
306
|
-
# `-p` runs headless; `--permission-mode acceptEdits` lets Claude Code
|
|
307
|
-
# actually apply file edits without an interactive approval prompt
|
|
308
|
-
# (otherwise it only describes the change and the diff stays empty).
|
|
309
|
-
command="claude",
|
|
310
|
-
args=["-p", "--permission-mode", "acceptEdits"],
|
|
311
|
-
display_name="Claude Code",
|
|
312
|
-
kind="coding-cli",
|
|
313
|
-
supports_mcp=True,
|
|
314
|
-
supports_diff_review=True,
|
|
315
|
-
supports_handoff=True,
|
|
316
|
-
built_in=True,
|
|
317
|
-
),
|
|
318
|
-
"opencode": CliAgentSpec(
|
|
319
|
-
name="opencode",
|
|
320
|
-
command="opencode",
|
|
321
|
-
args=[
|
|
322
|
-
"run",
|
|
323
|
-
"--file",
|
|
324
|
-
"{prompt_file}",
|
|
325
|
-
"Execute the DevCouncil task described in the attached prompt file.",
|
|
326
|
-
],
|
|
327
|
-
input_mode="prompt-file",
|
|
328
|
-
display_name="OpenCode",
|
|
329
|
-
kind="coding-cli",
|
|
330
|
-
supports_mcp=True,
|
|
331
|
-
supports_diff_review=True,
|
|
332
|
-
supports_handoff=True,
|
|
333
|
-
built_in=True,
|
|
334
|
-
),
|
|
335
|
-
"antigravity": CliAgentSpec(
|
|
336
|
-
name="antigravity",
|
|
337
|
-
command="agy",
|
|
338
|
-
args=[
|
|
339
|
-
"--print",
|
|
340
|
-
"--print-timeout",
|
|
341
|
-
"30m",
|
|
342
|
-
"Read and execute the DevCouncil task prompt at {prompt_file}.",
|
|
343
|
-
],
|
|
344
|
-
input_mode="prompt-file",
|
|
345
|
-
display_name="Google Antigravity CLI",
|
|
346
|
-
kind="coding-cli",
|
|
347
|
-
supports_mcp=True,
|
|
348
|
-
supports_diff_review=True,
|
|
349
|
-
supports_handoff=True,
|
|
350
|
-
built_in=True,
|
|
351
|
-
),
|
|
352
|
-
"warp": CliAgentSpec(
|
|
353
|
-
name="warp",
|
|
354
|
-
command="oz",
|
|
355
|
-
args=["agent", "run", "--cwd", str(project_root), "--mcp", str(warp_mcp_path), "--prompt"],
|
|
356
|
-
input_mode="argument",
|
|
357
|
-
display_name="Warp / Oz",
|
|
358
|
-
kind="agent-platform",
|
|
359
|
-
supports_mcp=True,
|
|
360
|
-
supports_diff_review=True,
|
|
361
|
-
supports_handoff=True,
|
|
362
|
-
built_in=True,
|
|
363
|
-
),
|
|
364
|
-
"cursor": CliAgentSpec(
|
|
365
|
-
name="cursor",
|
|
366
|
-
command=resolve_cursor_agent_executable() or "cursor-agent",
|
|
367
|
-
args=[
|
|
368
|
-
"--print",
|
|
369
|
-
"--trust",
|
|
370
|
-
"--workspace",
|
|
371
|
-
str(project_root),
|
|
372
|
-
"Read and execute the DevCouncil task prompt at {prompt_file}.",
|
|
373
|
-
],
|
|
374
|
-
input_mode="prompt-file",
|
|
375
|
-
display_name="Cursor Agent",
|
|
376
|
-
kind="coding-cli",
|
|
377
|
-
supports_mcp=True,
|
|
378
|
-
supports_diff_review=True,
|
|
379
|
-
supports_handoff=True,
|
|
380
|
-
built_in=True,
|
|
381
|
-
),
|
|
382
|
-
"aider": CliAgentSpec(
|
|
383
|
-
name="aider",
|
|
384
|
-
command="aider",
|
|
385
|
-
args=["--yes", "--no-show-model-warnings", "--message"],
|
|
386
|
-
input_mode="argument",
|
|
387
|
-
display_name="Aider",
|
|
388
|
-
kind="coding-cli",
|
|
389
|
-
supports_mcp=False,
|
|
390
|
-
supports_diff_review=True,
|
|
391
|
-
supports_handoff=True,
|
|
392
|
-
built_in=True,
|
|
393
|
-
),
|
|
394
|
-
"copilot": CliAgentSpec(
|
|
395
|
-
name="copilot",
|
|
396
|
-
command="copilot",
|
|
397
|
-
args=["--allow-all-tools", "-p"],
|
|
398
|
-
input_mode="argument",
|
|
399
|
-
display_name="GitHub Copilot CLI",
|
|
400
|
-
kind="coding-cli",
|
|
401
|
-
supports_mcp=True,
|
|
402
|
-
supports_diff_review=True,
|
|
403
|
-
supports_handoff=True,
|
|
404
|
-
built_in=True,
|
|
405
|
-
),
|
|
406
|
-
"goose": CliAgentSpec(
|
|
407
|
-
name="goose",
|
|
408
|
-
command="goose",
|
|
409
|
-
args=["run", "-i", "{prompt_file}"],
|
|
410
|
-
input_mode="prompt-file",
|
|
411
|
-
display_name="Goose",
|
|
412
|
-
kind="coding-cli",
|
|
413
|
-
supports_mcp=True,
|
|
414
|
-
supports_diff_review=True,
|
|
415
|
-
supports_handoff=True,
|
|
416
|
-
built_in=True,
|
|
417
|
-
),
|
|
418
|
-
"amp": CliAgentSpec(
|
|
419
|
-
name="amp",
|
|
420
|
-
command="amp",
|
|
421
|
-
args=["-x"],
|
|
422
|
-
input_mode="argument",
|
|
423
|
-
display_name="Amp (Sourcegraph)",
|
|
424
|
-
kind="coding-cli",
|
|
425
|
-
supports_mcp=True,
|
|
426
|
-
supports_diff_review=True,
|
|
427
|
-
supports_handoff=True,
|
|
428
|
-
built_in=True,
|
|
429
|
-
),
|
|
430
|
-
"qwen": CliAgentSpec(
|
|
431
|
-
name="qwen",
|
|
432
|
-
command="qwen",
|
|
433
|
-
display_name="Qwen Code",
|
|
434
|
-
kind="coding-cli",
|
|
435
|
-
supports_mcp=True,
|
|
436
|
-
supports_diff_review=True,
|
|
437
|
-
supports_handoff=True,
|
|
438
|
-
built_in=True,
|
|
439
|
-
),
|
|
440
|
-
"crush": CliAgentSpec(
|
|
441
|
-
name="crush",
|
|
442
|
-
command="crush",
|
|
443
|
-
args=["run"],
|
|
444
|
-
input_mode="argument",
|
|
445
|
-
display_name="Crush (Charm)",
|
|
446
|
-
kind="coding-cli",
|
|
447
|
-
supports_mcp=True,
|
|
448
|
-
supports_diff_review=True,
|
|
449
|
-
supports_handoff=True,
|
|
450
|
-
built_in=True,
|
|
451
|
-
),
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
def load_cli_agent_specs(project_root: Path) -> dict[str, CliAgentSpec]:
|
|
456
|
-
specs = builtin_agent_specs(project_root)
|
|
457
|
-
try:
|
|
458
|
-
configured = load_config(project_root).integrations.cli_agents.agents
|
|
459
|
-
except Exception:
|
|
460
|
-
configured = {}
|
|
461
|
-
|
|
462
|
-
for raw_name, agent in configured.items():
|
|
463
|
-
name = normalize_agent_name(raw_name)
|
|
464
|
-
if name in BUILTIN_AGENT_NAMES:
|
|
465
|
-
continue
|
|
466
|
-
data = agent.model_dump()
|
|
467
|
-
specs[name] = CliAgentSpec(
|
|
468
|
-
name=name,
|
|
469
|
-
command=data["command"],
|
|
470
|
-
args=list(data.get("args") or []),
|
|
471
|
-
input_mode=data.get("input_mode") or "stdin",
|
|
472
|
-
prompt_arg=data.get("prompt_arg"),
|
|
473
|
-
timeout_seconds=data.get("timeout_seconds"),
|
|
474
|
-
env=dict(data.get("env") or {}),
|
|
475
|
-
display_name=data.get("display_name"),
|
|
476
|
-
kind=data.get("kind") or "custom",
|
|
477
|
-
supports_mcp=bool(data.get("supports_mcp")),
|
|
478
|
-
supports_diff_review=bool(data.get("supports_diff_review")),
|
|
479
|
-
default_profile=data.get("default_profile") or "default",
|
|
480
|
-
help_command=list(data.get("help_command") or []),
|
|
481
|
-
built_in=False,
|
|
482
|
-
)
|
|
483
|
-
return specs
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
def get_cli_agent_spec(project_root: Path, name: str) -> CliAgentSpec | None:
|
|
487
|
-
return load_cli_agent_specs(project_root).get(normalize_agent_name(name))
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
def default_agent_profiles() -> dict[str, dict[str, Any]]:
|
|
491
|
-
return {
|
|
492
|
-
"default": {
|
|
493
|
-
"description": "Balanced local execution with DevCouncil verification.",
|
|
494
|
-
"timeout_seconds": None,
|
|
495
|
-
"prompt_preamble": "",
|
|
496
|
-
"require_explicit_confirmation": False,
|
|
497
|
-
},
|
|
498
|
-
"yolo": {
|
|
499
|
-
"description": "Faster local execution; DevCouncil still verifies the final diff.",
|
|
500
|
-
"timeout_seconds": 3600,
|
|
501
|
-
"prompt_preamble": (
|
|
502
|
-
"Profile: yolo. Move efficiently within the task scope, but keep all changes "
|
|
503
|
-
"inside the planned files and run the expected verification commands."
|
|
504
|
-
),
|
|
505
|
-
"require_explicit_confirmation": False,
|
|
506
|
-
# yolo leans on the CLI's most permissive auto-apply mode so the agent
|
|
507
|
-
# never stalls on an approval prompt.
|
|
508
|
-
"permission_mode": "auto",
|
|
509
|
-
},
|
|
510
|
-
"prod": {
|
|
511
|
-
"description": "Restrictive execution for high-risk repositories.",
|
|
512
|
-
"timeout_seconds": 1800,
|
|
513
|
-
"prompt_preamble": (
|
|
514
|
-
"Profile: prod. Treat this as high-risk work. Do not broaden scope, do not "
|
|
515
|
-
"change dependencies or schemas unless the task explicitly allows it, and "
|
|
516
|
-
"prefer minimal, easily reviewed edits."
|
|
517
|
-
),
|
|
518
|
-
"require_explicit_confirmation": True,
|
|
519
|
-
# prod drops blanket auto-approval: edits are gated rather than blindly
|
|
520
|
-
# applied, so the subprocess invocation is materially more contained than
|
|
521
|
-
# yolo's instead of being byte-for-byte identical.
|
|
522
|
-
"permission_mode": "gated",
|
|
523
|
-
},
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
def load_agent_profiles(project_root: Path) -> dict[str, CliAgentProfileConfig]:
|
|
528
|
-
raw_profiles = default_agent_profiles()
|
|
529
|
-
try:
|
|
530
|
-
configured = load_config(project_root).integrations.cli_agents.profiles
|
|
531
|
-
except Exception:
|
|
532
|
-
configured = {}
|
|
533
|
-
profiles = {
|
|
534
|
-
name: CliAgentProfileConfig.model_validate(data)
|
|
535
|
-
for name, data in raw_profiles.items()
|
|
536
|
-
}
|
|
537
|
-
profiles.update(configured)
|
|
538
|
-
return profiles
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
def agent_config_entry(
|
|
542
|
-
*,
|
|
543
|
-
command: str,
|
|
544
|
-
args: list[str] | None = None,
|
|
545
|
-
input_mode: str = "stdin",
|
|
546
|
-
prompt_arg: str | None = None,
|
|
547
|
-
timeout_seconds: int | None = None,
|
|
548
|
-
env: dict[str, str] | None = None,
|
|
549
|
-
display_name: str | None = None,
|
|
550
|
-
kind: str = "custom",
|
|
551
|
-
supports_mcp: bool = False,
|
|
552
|
-
supports_diff_review: bool = False,
|
|
553
|
-
default_profile: str = "default",
|
|
554
|
-
help_command: list[str] | None = None,
|
|
555
|
-
) -> dict[str, Any]:
|
|
556
|
-
entry: dict[str, Any] = {
|
|
557
|
-
"command": command,
|
|
558
|
-
"args": args or [],
|
|
559
|
-
"input_mode": input_mode,
|
|
560
|
-
"kind": kind,
|
|
561
|
-
"supports_mcp": supports_mcp,
|
|
562
|
-
"supports_diff_review": supports_diff_review,
|
|
563
|
-
"default_profile": default_profile,
|
|
564
|
-
}
|
|
565
|
-
if prompt_arg:
|
|
566
|
-
entry["prompt_arg"] = prompt_arg
|
|
567
|
-
if timeout_seconds is not None:
|
|
568
|
-
entry["timeout_seconds"] = timeout_seconds
|
|
569
|
-
if env:
|
|
570
|
-
entry["env"] = env
|
|
571
|
-
if display_name:
|
|
572
|
-
entry["display_name"] = display_name
|
|
573
|
-
if help_command:
|
|
574
|
-
entry["help_command"] = help_command
|
|
575
|
-
return entry
|