devcouncil 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -6
- package/package.json +9 -2
- package/pyproject.toml +34 -2
- package/src/devcouncil/app/config.py +348 -12
- package/src/devcouncil/app/orchestrator.py +10 -6
- package/src/devcouncil/app/state_machine.py +4 -0
- package/src/devcouncil/artifacts/graph.py +32 -5
- package/src/devcouncil/assets/__init__.py +1 -0
- package/src/devcouncil/assets/devcouncil-logo.svg +60 -0
- package/src/devcouncil/assets/devcouncil_logo_premium.png +0 -0
- package/src/devcouncil/cli/commands/agents.py +292 -0
- package/src/devcouncil/cli/commands/artifacts.py +6 -3
- package/src/devcouncil/cli/commands/check.py +220 -0
- package/src/devcouncil/cli/commands/config.py +43 -4
- package/src/devcouncil/cli/commands/cost.py +57 -0
- package/src/devcouncil/cli/commands/dashboard.py +6 -1
- package/src/devcouncil/cli/commands/design.py +186 -0
- package/src/devcouncil/cli/commands/doctor.py +379 -22
- package/src/devcouncil/cli/commands/evidence.py +48 -0
- package/src/devcouncil/cli/commands/go.py +532 -33
- package/src/devcouncil/cli/commands/handoff.py +69 -0
- package/src/devcouncil/cli/commands/hook.py +296 -15
- package/src/devcouncil/cli/commands/init.py +161 -20
- package/src/devcouncil/cli/commands/integrate.py +1371 -124
- package/src/devcouncil/cli/commands/logs.py +106 -0
- package/src/devcouncil/cli/commands/map.py +80 -10
- package/src/devcouncil/cli/commands/okf.py +245 -0
- package/src/devcouncil/cli/commands/plan.py +256 -55
- package/src/devcouncil/cli/commands/prompt.py +18 -7
- package/src/devcouncil/cli/commands/repair.py +50 -24
- package/src/devcouncil/cli/commands/report.py +8 -0
- package/src/devcouncil/cli/commands/reset_demo_state.py +4 -2
- package/src/devcouncil/cli/commands/rollback.py +27 -28
- package/src/devcouncil/cli/commands/run.py +195 -54
- package/src/devcouncil/cli/commands/runs.py +223 -0
- package/src/devcouncil/cli/commands/scaffold.py +32 -0
- package/src/devcouncil/cli/commands/semantic.py +47 -0
- package/src/devcouncil/cli/commands/setup.py +145 -6
- package/src/devcouncil/cli/commands/shell.py +73 -0
- package/src/devcouncil/cli/commands/skills.py +267 -0
- package/src/devcouncil/cli/commands/status.py +30 -15
- package/src/devcouncil/cli/commands/trace.py +47 -3
- package/src/devcouncil/cli/commands/verify.py +144 -3
- package/src/devcouncil/cli/commands/watch.py +32 -12
- package/src/devcouncil/cli/commands/watch_fs.py +40 -0
- package/src/devcouncil/cli/main.py +91 -7
- package/src/devcouncil/domain/evidence.py +29 -2
- package/src/devcouncil/domain/gap.py +27 -1
- package/src/devcouncil/domain/task.py +31 -2
- package/src/devcouncil/execution/checkpoints.py +256 -0
- package/src/devcouncil/execution/context_builder.py +1 -1
- package/src/devcouncil/execution/fs_watcher.py +205 -0
- package/src/devcouncil/execution/handoff.py +102 -0
- package/src/devcouncil/execution/hook_policy.py +162 -74
- package/src/devcouncil/execution/patch.py +65 -10
- package/src/devcouncil/execution/permissions.py +24 -24
- package/src/devcouncil/execution/policy_engine.py +350 -0
- package/src/devcouncil/execution/prompt_builder.py +751 -23
- package/src/devcouncil/execution/shell_session.py +231 -0
- package/src/devcouncil/execution/task_runner.py +24 -9
- package/src/devcouncil/executors/agent_registry.py +596 -0
- package/src/devcouncil/executors/coding_cli.py +791 -39
- package/src/devcouncil/executors/mini_swe.py +6 -0
- package/src/devcouncil/executors/native/agent.py +135 -19
- package/src/devcouncil/executors/openhands.py +6 -0
- package/src/devcouncil/gating/checks/clean_git.py +3 -1
- package/src/devcouncil/gating/checks/secret_scan_check.py +47 -21
- package/src/devcouncil/gating/policy.py +190 -11
- package/src/devcouncil/hardware.py +184 -0
- package/src/devcouncil/indexing/ast_matcher.py +17 -7
- package/src/devcouncil/indexing/lsp.py +45 -4
- package/src/devcouncil/indexing/repo_mapper.py +1284 -15
- package/src/devcouncil/indexing/semantic_index.py +221 -0
- package/src/devcouncil/integrations/actions.py +166 -0
- package/src/devcouncil/integrations/check.py +426 -0
- package/src/devcouncil/integrations/claude_assets.py +444 -0
- package/src/devcouncil/integrations/code_review_graph.py +13 -2
- package/src/devcouncil/integrations/github_intent.py +149 -0
- package/src/devcouncil/integrations/gitnexus.py +45 -2
- package/src/devcouncil/integrations/mcp/server.py +1944 -32
- package/src/devcouncil/integrations/opencode_devcouncil_plugin.mjs +24 -0
- package/src/devcouncil/integrations/pr_comments.py +9 -0
- package/src/devcouncil/knowledge/__init__.py +23 -0
- package/src/devcouncil/knowledge/design.py +374 -0
- package/src/devcouncil/knowledge/design_conformance.py +317 -0
- package/src/devcouncil/knowledge/fetch.py +223 -0
- package/src/devcouncil/knowledge/frontmatter.py +51 -0
- package/src/devcouncil/knowledge/okf.py +202 -0
- package/src/devcouncil/knowledge/skill_bridge.py +96 -0
- package/src/devcouncil/knowledge/sources.py +239 -0
- package/src/devcouncil/live/cards.py +181 -25
- package/src/devcouncil/live/repair_prompt.py +29 -6
- package/src/devcouncil/live/reviewer.py +72 -13
- package/src/devcouncil/live/signals.py +2 -2
- package/src/devcouncil/live/summary.py +18 -8
- package/src/devcouncil/live/transcripts.py +47 -11
- package/src/devcouncil/llm/cache.py +20 -8
- package/src/devcouncil/llm/model_defaults.yaml +44 -0
- package/src/devcouncil/llm/provider.py +617 -49
- package/src/devcouncil/llm/router.py +337 -53
- package/src/devcouncil/optimization/__init__.py +1 -0
- package/src/devcouncil/optimization/gepa_agent.py +318 -0
- package/src/devcouncil/optimization/skillopt.py +673 -0
- package/src/devcouncil/planning/arbiter_service.py +10 -2
- package/src/devcouncil/planning/correction_manifest.py +346 -0
- package/src/devcouncil/planning/critique_service.py +16 -4
- package/src/devcouncil/planning/plan_service.py +86 -6
- package/src/devcouncil/planning/prompt_enhancer_service.py +206 -1
- package/src/devcouncil/planning/repair_service.py +8 -2
- package/src/devcouncil/planning/spec_service.py +37 -3
- package/src/devcouncil/repo/ci_scaffold.py +165 -0
- package/src/devcouncil/repo/gitignore.py +123 -0
- package/src/devcouncil/repo/sca.py +384 -0
- package/src/devcouncil/reporting/json_report.py +22 -1
- package/src/devcouncil/reporting/markdown_report.py +29 -1
- package/src/devcouncil/reporting/okf_bundle_writer.py +364 -0
- package/src/devcouncil/reporting/okf_html.py +323 -0
- package/src/devcouncil/reporting/report_builder.py +18 -1
- package/src/devcouncil/skills/__init__.py +19 -0
- package/src/devcouncil/skills/library/README.md +46 -0
- package/src/devcouncil/skills/library/ai-training.md +50 -0
- package/src/devcouncil/skills/library/android.md +50 -0
- package/src/devcouncil/skills/library/backend.md +52 -0
- package/src/devcouncil/skills/library/core-engineering.md +95 -0
- package/src/devcouncil/skills/library/data-engineering.md +47 -0
- package/src/devcouncil/skills/library/desktop.md +46 -0
- package/src/devcouncil/skills/library/devops.md +48 -0
- package/src/devcouncil/skills/library/game-dev.md +46 -0
- package/src/devcouncil/skills/library/ios.md +48 -0
- package/src/devcouncil/skills/library/mobile-cross-platform.md +46 -0
- package/src/devcouncil/skills/library/security.md +48 -0
- package/src/devcouncil/skills/library/systems.md +48 -0
- package/src/devcouncil/skills/library/web.md +47 -0
- package/src/devcouncil/skills/library/windows.md +47 -0
- package/src/devcouncil/skills/registry.py +408 -0
- package/src/devcouncil/storage/db.py +140 -3
- package/src/devcouncil/storage/models.py +125 -0
- package/src/devcouncil/storage/native.py +559 -0
- package/src/devcouncil/storage/repositories.py +157 -78
- package/src/devcouncil/telemetry/cost.py +123 -17
- package/src/devcouncil/telemetry/logging_setup.py +244 -0
- package/src/devcouncil/telemetry/model_pricing.yaml +48 -0
- package/src/devcouncil/telemetry/pricing.py +28 -0
- package/src/devcouncil/telemetry/stages.py +141 -0
- package/src/devcouncil/telemetry/traces.py +62 -7
- package/src/devcouncil/telemetry/tracker.py +24 -10
- package/src/devcouncil/ui/dashboard.py +393 -28
- package/src/devcouncil/utils/redaction.py +9 -3
- package/src/devcouncil/utils/subprocess_env.py +69 -0
- package/src/devcouncil/verification/acceptance_compiler.py +253 -0
- package/src/devcouncil/verification/ad_hoc_check.py +135 -0
- package/src/devcouncil/verification/diff_coverage.py +353 -0
- package/src/devcouncil/verification/implementation_reviewer.py +11 -2
- package/src/devcouncil/verification/next_actions.py +189 -0
- package/src/devcouncil/verification/sandbox.py +181 -0
- package/src/devcouncil/verification/test_resolver.py +91 -0
- package/src/devcouncil/verification/verifier.py +1549 -143
- package/uv.lock +205 -64
- package/src/devcouncil/indexing/symbol_index.py +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
from typing import Any
|
|
2
3
|
import typer
|
|
3
4
|
import yaml
|
|
4
5
|
from rich.console import Console
|
|
@@ -6,10 +7,42 @@ from pathlib import Path
|
|
|
6
7
|
from devcouncil.storage.db import Database
|
|
7
8
|
from devcouncil.integrations.gitnexus import GitNexusIntegration
|
|
8
9
|
from devcouncil.integrations.graphify import GraphifyIntegration
|
|
10
|
+
from devcouncil.llm.provider import build_role_model_config, validate_model_provider
|
|
11
|
+
from devcouncil.repo.gitignore import ensure_gitignore
|
|
9
12
|
|
|
10
13
|
app = typer.Typer()
|
|
11
14
|
console = Console()
|
|
12
15
|
|
|
16
|
+
# Per-stack default verification commands. A fresh project gets ONLY the commands
|
|
17
|
+
# for the stack(s) actually detected in the repo, so the verifier never inherits a
|
|
18
|
+
# cross-stack gate (e.g. `npm test`/`eslint`/`tsc` on a Python repo) that it would run
|
|
19
|
+
# as a blocking fallback and fail for tooling/stack reasons instead of a real defect.
|
|
20
|
+
_STACK_COMMAND_DEFAULTS: dict[str, dict[str, list[str]]] = {
|
|
21
|
+
"python": {"test": ["pytest"], "lint": ["ruff check ."], "typecheck": ["mypy ."]},
|
|
22
|
+
"node": {"test": ["npm test"], "lint": ["eslint ."], "typecheck": ["tsc --noEmit"]},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _stack_aware_commands(project_root: Path) -> dict[str, list[str]]:
|
|
27
|
+
"""Default test/lint/typecheck commands scoped to the repo's detected stack(s).
|
|
28
|
+
|
|
29
|
+
Returns empty lists when no stack is detected — empty is safe (no speculative
|
|
30
|
+
fallback gates) and far better than guessing wrong-stack tools."""
|
|
31
|
+
from devcouncil.repo.ci_scaffold import detect_stacks
|
|
32
|
+
|
|
33
|
+
commands: dict[str, list[str]] = {"test": [], "lint": [], "typecheck": []}
|
|
34
|
+
try:
|
|
35
|
+
stacks = detect_stacks(project_root)
|
|
36
|
+
except Exception:
|
|
37
|
+
return commands
|
|
38
|
+
for stack in sorted(stacks):
|
|
39
|
+
for key, cmds in _STACK_COMMAND_DEFAULTS.get(stack, {}).items():
|
|
40
|
+
for command in cmds:
|
|
41
|
+
if command not in commands[key]:
|
|
42
|
+
commands[key].append(command)
|
|
43
|
+
return commands
|
|
44
|
+
|
|
45
|
+
|
|
13
46
|
DEFAULT_CONFIG = {
|
|
14
47
|
"project": {
|
|
15
48
|
"name": "devcouncil-project",
|
|
@@ -18,23 +51,12 @@ DEFAULT_CONFIG = {
|
|
|
18
51
|
},
|
|
19
52
|
"models": {
|
|
20
53
|
"provider": "openrouter",
|
|
21
|
-
"roles":
|
|
22
|
-
"spec_writer": {"model": "anthropic/claude-3.5-sonnet"},
|
|
23
|
-
"prompt_enhancer": {"model": "anthropic/claude-3.5-sonnet"},
|
|
24
|
-
"planner_a": {"model": "anthropic/claude-3.5-sonnet"},
|
|
25
|
-
"planner_b": {"model": "google/gemini-pro-1.5"},
|
|
26
|
-
"critic_a": {"model": "openai/gpt-4o"},
|
|
27
|
-
"critic_b": {"model": "anthropic/claude-3-opus"},
|
|
28
|
-
"arbiter": {"model": "openai/gpt-4o"},
|
|
29
|
-
"native_agent": {"model": "anthropic/claude-3.5-sonnet"},
|
|
30
|
-
"implementation_reviewer": {"model": "openai/gpt-4o"},
|
|
31
|
-
"live_reviewer": {"model": "openai/gpt-4o"},
|
|
32
|
-
}
|
|
54
|
+
"roles": build_role_model_config("openrouter"),
|
|
33
55
|
},
|
|
34
56
|
"commands": {
|
|
35
|
-
"test": [
|
|
36
|
-
"lint": [
|
|
37
|
-
"typecheck": [
|
|
57
|
+
"test": [],
|
|
58
|
+
"lint": [],
|
|
59
|
+
"typecheck": [],
|
|
38
60
|
},
|
|
39
61
|
"gates": {
|
|
40
62
|
"require_clean_git_before_task": True,
|
|
@@ -45,9 +67,12 @@ DEFAULT_CONFIG = {
|
|
|
45
67
|
"block_failed_commands": True
|
|
46
68
|
},
|
|
47
69
|
"execution": {
|
|
48
|
-
"default_executor": "
|
|
70
|
+
"default_executor": "manual",
|
|
49
71
|
"max_repair_attempts": 3,
|
|
50
|
-
"checkpoint_before_each_task": True
|
|
72
|
+
"checkpoint_before_each_task": True,
|
|
73
|
+
"stream_cli_output": False,
|
|
74
|
+
"cursor_resume_mode": "off",
|
|
75
|
+
"coding_cli_probe_order": [],
|
|
51
76
|
},
|
|
52
77
|
"privacy": {
|
|
53
78
|
"redact_env_vars": True,
|
|
@@ -71,15 +96,90 @@ DEFAULT_CONFIG = {
|
|
|
71
96
|
"signals_path": ".devcouncil/live/signals",
|
|
72
97
|
"default_client": "claude",
|
|
73
98
|
},
|
|
99
|
+
"cli_agents": {
|
|
100
|
+
"enabled": True,
|
|
101
|
+
"profiles": {
|
|
102
|
+
"default": {
|
|
103
|
+
"description": "Balanced local execution with DevCouncil verification.",
|
|
104
|
+
},
|
|
105
|
+
"yolo": {
|
|
106
|
+
"description": "Faster local execution; DevCouncil still verifies the final diff.",
|
|
107
|
+
"timeout_seconds": 3600,
|
|
108
|
+
"prompt_preamble": "Profile: yolo. Move efficiently within the task scope.",
|
|
109
|
+
},
|
|
110
|
+
"prod": {
|
|
111
|
+
"description": "Restrictive execution for high-risk repositories.",
|
|
112
|
+
"timeout_seconds": 1800,
|
|
113
|
+
"prompt_preamble": "Profile: prod. Keep edits minimal and explicitly within task scope.",
|
|
114
|
+
"require_explicit_confirmation": True,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
"agents": {},
|
|
118
|
+
},
|
|
74
119
|
}
|
|
75
120
|
}
|
|
76
121
|
|
|
77
122
|
|
|
123
|
+
def parse_role_model_overrides(values: list[str] | None) -> dict[str, str]:
|
|
124
|
+
overrides: dict[str, str] = {}
|
|
125
|
+
for value in values or []:
|
|
126
|
+
if "=" not in value:
|
|
127
|
+
raise ValueError(f"Invalid --role-model value '{value}'. Use ROLE=MODEL.")
|
|
128
|
+
role, model = value.split("=", 1)
|
|
129
|
+
role = role.strip()
|
|
130
|
+
model = model.strip()
|
|
131
|
+
if not role or not model:
|
|
132
|
+
raise ValueError(f"Invalid --role-model value '{value}'. Use ROLE=MODEL.")
|
|
133
|
+
overrides[role] = model
|
|
134
|
+
return overrides
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _generate_initial_map(project_root: Path, quiet: bool) -> None:
|
|
138
|
+
"""Best-effort repo map + agent guide generation on fresh init.
|
|
139
|
+
|
|
140
|
+
Imported lazily to avoid a circular import with the map command, and wrapped
|
|
141
|
+
so a mapping failure never blocks initialization.
|
|
142
|
+
"""
|
|
143
|
+
try:
|
|
144
|
+
from devcouncil.cli.commands.map import generate_map_artifacts
|
|
145
|
+
|
|
146
|
+
generate_map_artifacts(project_root, project_root / ".devcouncil" / "repo_map.json")
|
|
147
|
+
if not quiet:
|
|
148
|
+
console.print("[green]Generated .devcouncil/repo_map.json and agent guides (AGENTS.md, CLAUDE.md).[/green]")
|
|
149
|
+
except Exception as exc: # mapping is best-effort, never fatal
|
|
150
|
+
if not quiet:
|
|
151
|
+
console.print(f"[yellow]Skipped repo map generation: {exc}. Run 'dev map' later.[/yellow]")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _scaffold_initial_skills(project_root: Path, quiet: bool) -> None:
|
|
155
|
+
"""Best-effort scaffolding of applicable engineering skills into .claude/skills/.
|
|
156
|
+
|
|
157
|
+
Always writes the core-engineering skill; adds domain skills (android, ios, web,
|
|
158
|
+
...) whose file triggers match the repository. Never fatal.
|
|
159
|
+
"""
|
|
160
|
+
try:
|
|
161
|
+
from devcouncil.skills.registry import scaffold_skills, select_skills
|
|
162
|
+
|
|
163
|
+
selected = select_skills(project_root=project_root)
|
|
164
|
+
written = scaffold_skills(project_root, selected)
|
|
165
|
+
if written and not quiet:
|
|
166
|
+
names = ", ".join(sorted(skill.name for skill in selected))
|
|
167
|
+
console.print(f"[green]Scaffolded {len(written)} skill(s) into .claude/skills/ ({names}).[/green]")
|
|
168
|
+
except Exception as exc: # skill scaffolding is best-effort, never fatal
|
|
169
|
+
if not quiet:
|
|
170
|
+
console.print(f"[yellow]Skipped skill scaffolding: {exc}. Run 'dev skills scaffold' later.[/yellow]")
|
|
171
|
+
|
|
172
|
+
|
|
78
173
|
def initialize_project(
|
|
79
174
|
project_root: Path = Path("."),
|
|
80
175
|
project_name: str | None = None,
|
|
176
|
+
model_provider: str = "openrouter",
|
|
177
|
+
model: str | None = None,
|
|
178
|
+
role_models: dict[str, str] | None = None,
|
|
81
179
|
with_gitnexus: bool = False,
|
|
82
180
|
with_graphify: bool = False,
|
|
181
|
+
with_map: bool = True,
|
|
182
|
+
with_skills: bool = True,
|
|
83
183
|
quiet: bool = False,
|
|
84
184
|
) -> bool:
|
|
85
185
|
"""Initialize DevCouncil project state.
|
|
@@ -90,7 +190,10 @@ def initialize_project(
|
|
|
90
190
|
dev_dir = project_root / ".devcouncil"
|
|
91
191
|
created = False
|
|
92
192
|
|
|
93
|
-
|
|
193
|
+
# Detect "already initialized" by the config file, NOT the .devcouncil/ directory:
|
|
194
|
+
# logging creates .devcouncil/logs/ on every CLI startup, so the directory exists
|
|
195
|
+
# before init runs. Keying on config.yaml ensures a first init still writes config.
|
|
196
|
+
if not (dev_dir / "config.yaml").exists():
|
|
94
197
|
if not quiet:
|
|
95
198
|
console.print("Initializing DevCouncil...")
|
|
96
199
|
dev_dir.mkdir(exist_ok=True)
|
|
@@ -100,11 +203,20 @@ def initialize_project(
|
|
|
100
203
|
(dev_dir / "logs").mkdir(exist_ok=True)
|
|
101
204
|
|
|
102
205
|
config_path = dev_dir / "config.yaml"
|
|
103
|
-
config = copy.deepcopy(DEFAULT_CONFIG)
|
|
206
|
+
config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG)
|
|
207
|
+
# Scope default verification commands to the repo's actual stack(s).
|
|
208
|
+
config["commands"] = _stack_aware_commands(project_root)
|
|
104
209
|
if project_name:
|
|
105
210
|
config["project"]["name"] = project_name
|
|
106
211
|
else:
|
|
107
212
|
config["project"]["name"] = project_root.name
|
|
213
|
+
provider = validate_model_provider(model_provider)
|
|
214
|
+
config["models"]["provider"] = provider
|
|
215
|
+
config["models"]["roles"] = build_role_model_config(
|
|
216
|
+
provider,
|
|
217
|
+
model=model,
|
|
218
|
+
role_models=role_models,
|
|
219
|
+
)
|
|
108
220
|
|
|
109
221
|
with open(config_path, "w") as f:
|
|
110
222
|
yaml.dump(config, f, default_flow_style=False)
|
|
@@ -115,6 +227,11 @@ def initialize_project(
|
|
|
115
227
|
console.print(f"[green]Successfully initialized DevCouncil in {dev_dir}[/green]")
|
|
116
228
|
created = True
|
|
117
229
|
|
|
230
|
+
if with_map:
|
|
231
|
+
_generate_initial_map(project_root, quiet)
|
|
232
|
+
if with_skills:
|
|
233
|
+
_scaffold_initial_skills(project_root, quiet)
|
|
234
|
+
|
|
118
235
|
if with_gitnexus:
|
|
119
236
|
nexus = GitNexusIntegration(project_root)
|
|
120
237
|
nexus.initialize()
|
|
@@ -123,6 +240,7 @@ def initialize_project(
|
|
|
123
240
|
graphify = GraphifyIntegration(project_root)
|
|
124
241
|
graphify.initialize()
|
|
125
242
|
|
|
243
|
+
ensure_gitignore(project_root)
|
|
126
244
|
return created
|
|
127
245
|
|
|
128
246
|
|
|
@@ -130,8 +248,17 @@ def initialize_project(
|
|
|
130
248
|
def init(
|
|
131
249
|
ctx: typer.Context,
|
|
132
250
|
project_name: str = typer.Option(None, "--name", "-n", help="Project name"),
|
|
251
|
+
provider: str = typer.Option("openrouter", "--provider", help="Model provider for generated config."),
|
|
252
|
+
model: str | None = typer.Option(None, "--model", "-m", help="Model id to use for every default role."),
|
|
253
|
+
role_model: list[str] | None = typer.Option(
|
|
254
|
+
None,
|
|
255
|
+
"--role-model",
|
|
256
|
+
help="Per-role model override in ROLE=MODEL form. Can be repeated.",
|
|
257
|
+
),
|
|
133
258
|
with_gitnexus: bool = typer.Option(False, "--gitnexus", help="Initialize GitNexus structural awareness"),
|
|
134
259
|
with_graphify: bool = typer.Option(False, "--graphify", help="Initialize Graphify knowledge graph engine"),
|
|
260
|
+
skip_map: bool = typer.Option(False, "--skip-map", help="Skip generating repo_map.json and agent guides on init."),
|
|
261
|
+
skip_skills: bool = typer.Option(False, "--skip-skills", help="Skip scaffolding engineering skills into .claude/skills/ on init."),
|
|
135
262
|
):
|
|
136
263
|
"""
|
|
137
264
|
Initialize DevCouncil in the current directory.
|
|
@@ -140,14 +267,28 @@ def init(
|
|
|
140
267
|
return
|
|
141
268
|
|
|
142
269
|
dev_dir = Path(".devcouncil")
|
|
143
|
-
|
|
270
|
+
# "Already initialized" means a config.yaml exists — not merely the .devcouncil/
|
|
271
|
+
# directory, which the logging setup pre-creates (.devcouncil/logs/) on startup.
|
|
272
|
+
if (dev_dir / "config.yaml").exists() and not (with_gitnexus or with_graphify):
|
|
144
273
|
console.print("[yellow]DevCouncil is already initialized in this directory.[/yellow]")
|
|
145
274
|
console.print("Use --gitnexus or --graphify to add upgrade paths.")
|
|
146
275
|
raise typer.Exit()
|
|
147
276
|
|
|
277
|
+
try:
|
|
278
|
+
role_models = parse_role_model_overrides(role_model)
|
|
279
|
+
model_provider = validate_model_provider(provider)
|
|
280
|
+
except ValueError as e:
|
|
281
|
+
console.print(f"[red]{e}[/red]")
|
|
282
|
+
raise typer.Exit(code=2) from e
|
|
283
|
+
|
|
148
284
|
initialize_project(
|
|
149
285
|
Path("."),
|
|
150
286
|
project_name=project_name,
|
|
287
|
+
model_provider=model_provider,
|
|
288
|
+
model=model,
|
|
289
|
+
role_models=role_models,
|
|
151
290
|
with_gitnexus=with_gitnexus,
|
|
152
291
|
with_graphify=with_graphify,
|
|
292
|
+
with_map=not skip_map,
|
|
293
|
+
with_skills=not skip_skills,
|
|
153
294
|
)
|