devcouncil 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +144 -308
  2. package/bin/devcouncil.js +130 -32
  3. package/package.json +22 -19
  4. package/pyproject.toml +0 -66
  5. package/src/devcouncil/__init__.py +0 -0
  6. package/src/devcouncil/__main__.py +0 -4
  7. package/src/devcouncil/app/__init__.py +0 -28
  8. package/src/devcouncil/app/config.py +0 -320
  9. package/src/devcouncil/app/errors.py +0 -23
  10. package/src/devcouncil/app/events.py +0 -44
  11. package/src/devcouncil/app/orchestrator.py +0 -92
  12. package/src/devcouncil/app/project_status.py +0 -29
  13. package/src/devcouncil/app/run_context.py +0 -39
  14. package/src/devcouncil/app/state_machine.py +0 -108
  15. package/src/devcouncil/artifacts/__init__.py +0 -1
  16. package/src/devcouncil/artifacts/coverage.py +0 -96
  17. package/src/devcouncil/artifacts/graph.py +0 -163
  18. package/src/devcouncil/artifacts/migrations.py +0 -20
  19. package/src/devcouncil/artifacts/schemas.py +0 -23
  20. package/src/devcouncil/artifacts/serializer.py +0 -21
  21. package/src/devcouncil/artifacts/validators.py +0 -27
  22. package/src/devcouncil/assets/__init__.py +0 -1
  23. package/src/devcouncil/assets/devcouncil-logo.svg +0 -60
  24. package/src/devcouncil/assets/devcouncil_logo_premium.png +0 -0
  25. package/src/devcouncil/cli/__init__.py +0 -0
  26. package/src/devcouncil/cli/commands/__init__.py +0 -0
  27. package/src/devcouncil/cli/commands/agents.py +0 -292
  28. package/src/devcouncil/cli/commands/artifacts.py +0 -54
  29. package/src/devcouncil/cli/commands/ast.py +0 -22
  30. package/src/devcouncil/cli/commands/baseline.py +0 -35
  31. package/src/devcouncil/cli/commands/check.py +0 -209
  32. package/src/devcouncil/cli/commands/config.py +0 -115
  33. package/src/devcouncil/cli/commands/cost.py +0 -57
  34. package/src/devcouncil/cli/commands/dashboard.py +0 -31
  35. package/src/devcouncil/cli/commands/doctor.py +0 -340
  36. package/src/devcouncil/cli/commands/evidence.py +0 -48
  37. package/src/devcouncil/cli/commands/go.py +0 -656
  38. package/src/devcouncil/cli/commands/handoff.py +0 -69
  39. package/src/devcouncil/cli/commands/hook.py +0 -237
  40. package/src/devcouncil/cli/commands/init.py +0 -289
  41. package/src/devcouncil/cli/commands/integrate.py +0 -1515
  42. package/src/devcouncil/cli/commands/lsp.py +0 -20
  43. package/src/devcouncil/cli/commands/map.py +0 -112
  44. package/src/devcouncil/cli/commands/mcp_server.py +0 -18
  45. package/src/devcouncil/cli/commands/plan.py +0 -488
  46. package/src/devcouncil/cli/commands/prompt.py +0 -61
  47. package/src/devcouncil/cli/commands/repair.py +0 -89
  48. package/src/devcouncil/cli/commands/report.py +0 -137
  49. package/src/devcouncil/cli/commands/reset_demo_state.py +0 -33
  50. package/src/devcouncil/cli/commands/rollback.py +0 -59
  51. package/src/devcouncil/cli/commands/run.py +0 -289
  52. package/src/devcouncil/cli/commands/runs.py +0 -223
  53. package/src/devcouncil/cli/commands/scaffold.py +0 -32
  54. package/src/devcouncil/cli/commands/semantic.py +0 -47
  55. package/src/devcouncil/cli/commands/setup.py +0 -362
  56. package/src/devcouncil/cli/commands/shell.py +0 -73
  57. package/src/devcouncil/cli/commands/show.py +0 -76
  58. package/src/devcouncil/cli/commands/skills.py +0 -88
  59. package/src/devcouncil/cli/commands/status.py +0 -141
  60. package/src/devcouncil/cli/commands/tasks.py +0 -55
  61. package/src/devcouncil/cli/commands/trace.py +0 -88
  62. package/src/devcouncil/cli/commands/verify.py +0 -328
  63. package/src/devcouncil/cli/commands/version.py +0 -20
  64. package/src/devcouncil/cli/commands/watch.py +0 -574
  65. package/src/devcouncil/cli/commands/watch_fs.py +0 -40
  66. package/src/devcouncil/cli/main.py +0 -137
  67. package/src/devcouncil/council/__init__.py +0 -0
  68. package/src/devcouncil/council/prompts/__init__.py +0 -0
  69. package/src/devcouncil/council/prompts/arbiter.md +0 -19
  70. package/src/devcouncil/council/prompts/critic_a.md +0 -10
  71. package/src/devcouncil/council/prompts/critic_b.md +0 -10
  72. package/src/devcouncil/council/prompts/implementation_reviewer.md +0 -16
  73. package/src/devcouncil/council/prompts/planner_a.md +0 -16
  74. package/src/devcouncil/council/prompts/planner_b.md +0 -16
  75. package/src/devcouncil/council/prompts/rebuttal.md +0 -10
  76. package/src/devcouncil/council/prompts/spec_writer.md +0 -12
  77. package/src/devcouncil/domain/__init__.py +0 -0
  78. package/src/devcouncil/domain/assumption.py +0 -17
  79. package/src/devcouncil/domain/critique.py +0 -32
  80. package/src/devcouncil/domain/evidence.py +0 -47
  81. package/src/devcouncil/domain/gap.py +0 -52
  82. package/src/devcouncil/domain/requirement.py +0 -22
  83. package/src/devcouncil/domain/task.py +0 -55
  84. package/src/devcouncil/execution/__init__.py +0 -1
  85. package/src/devcouncil/execution/checkpoints.py +0 -246
  86. package/src/devcouncil/execution/context_builder.py +0 -60
  87. package/src/devcouncil/execution/executor.py +0 -15
  88. package/src/devcouncil/execution/fs_watcher.py +0 -180
  89. package/src/devcouncil/execution/handoff.py +0 -102
  90. package/src/devcouncil/execution/hook_policy.py +0 -253
  91. package/src/devcouncil/execution/patch.py +0 -77
  92. package/src/devcouncil/execution/paths.py +0 -14
  93. package/src/devcouncil/execution/permissions.py +0 -85
  94. package/src/devcouncil/execution/policy_engine.py +0 -343
  95. package/src/devcouncil/execution/prompt_builder.py +0 -671
  96. package/src/devcouncil/execution/shell_session.py +0 -225
  97. package/src/devcouncil/execution/task_runner.py +0 -170
  98. package/src/devcouncil/executors/__init__.py +0 -1
  99. package/src/devcouncil/executors/agent_registry.py +0 -575
  100. package/src/devcouncil/executors/coding_cli.py +0 -736
  101. package/src/devcouncil/executors/mini_swe.py +0 -73
  102. package/src/devcouncil/executors/native/__init__.py +0 -0
  103. package/src/devcouncil/executors/native/agent.py +0 -208
  104. package/src/devcouncil/executors/openhands.py +0 -71
  105. package/src/devcouncil/gating/__init__.py +0 -1
  106. package/src/devcouncil/gating/checks/__init__.py +0 -0
  107. package/src/devcouncil/gating/checks/clean_git.py +0 -52
  108. package/src/devcouncil/gating/checks/planned_files_check.py +0 -32
  109. package/src/devcouncil/gating/checks/requirement_coverage.py +0 -26
  110. package/src/devcouncil/gating/checks/secret_scan_check.py +0 -53
  111. package/src/devcouncil/gating/policy.py +0 -338
  112. package/src/devcouncil/hardware.py +0 -184
  113. package/src/devcouncil/indexing/__init__.py +0 -1
  114. package/src/devcouncil/indexing/ast_matcher.py +0 -168
  115. package/src/devcouncil/indexing/graph_index.py +0 -48
  116. package/src/devcouncil/indexing/lsp.py +0 -161
  117. package/src/devcouncil/indexing/repo_mapper.py +0 -1455
  118. package/src/devcouncil/indexing/semantic_index.py +0 -205
  119. package/src/devcouncil/integrations/actions.py +0 -146
  120. package/src/devcouncil/integrations/check.py +0 -423
  121. package/src/devcouncil/integrations/code_review_graph.py +0 -163
  122. package/src/devcouncil/integrations/github.py +0 -39
  123. package/src/devcouncil/integrations/github_intent.py +0 -142
  124. package/src/devcouncil/integrations/gitnexus.py +0 -62
  125. package/src/devcouncil/integrations/graphify.py +0 -34
  126. package/src/devcouncil/integrations/mcp/__init__.py +0 -0
  127. package/src/devcouncil/integrations/mcp/server.py +0 -2122
  128. package/src/devcouncil/integrations/opencode_devcouncil_plugin.mjs +0 -24
  129. package/src/devcouncil/integrations/pr_comments.py +0 -62
  130. package/src/devcouncil/live/__init__.py +0 -2
  131. package/src/devcouncil/live/cards.py +0 -349
  132. package/src/devcouncil/live/models.py +0 -63
  133. package/src/devcouncil/live/repair_prompt.py +0 -83
  134. package/src/devcouncil/live/reviewer.py +0 -70
  135. package/src/devcouncil/live/signals.py +0 -135
  136. package/src/devcouncil/live/summary.py +0 -34
  137. package/src/devcouncil/live/tasks.py +0 -18
  138. package/src/devcouncil/live/transcripts.py +0 -141
  139. package/src/devcouncil/llm/__init__.py +0 -1
  140. package/src/devcouncil/llm/cache.py +0 -42
  141. package/src/devcouncil/llm/model_defaults.yaml +0 -44
  142. package/src/devcouncil/llm/provider.py +0 -627
  143. package/src/devcouncil/llm/router.py +0 -310
  144. package/src/devcouncil/optimization/__init__.py +0 -1
  145. package/src/devcouncil/optimization/gepa_agent.py +0 -318
  146. package/src/devcouncil/planning/__init__.py +0 -1
  147. package/src/devcouncil/planning/arbiter_service.py +0 -57
  148. package/src/devcouncil/planning/correction_manifest.py +0 -303
  149. package/src/devcouncil/planning/critique_service.py +0 -71
  150. package/src/devcouncil/planning/plan_service.py +0 -60
  151. package/src/devcouncil/planning/prompt_enhancer_service.py +0 -167
  152. package/src/devcouncil/planning/repair_service.py +0 -39
  153. package/src/devcouncil/planning/spec_service.py +0 -70
  154. package/src/devcouncil/repo/__init__.py +0 -0
  155. package/src/devcouncil/repo/ci_scaffold.py +0 -157
  156. package/src/devcouncil/repo/gitignore.py +0 -123
  157. package/src/devcouncil/repo/sca.py +0 -374
  158. package/src/devcouncil/reporting/__init__.py +0 -0
  159. package/src/devcouncil/reporting/github_check.py +0 -32
  160. package/src/devcouncil/reporting/json_report.py +0 -30
  161. package/src/devcouncil/reporting/markdown_report.py +0 -83
  162. package/src/devcouncil/reporting/report_builder.py +0 -14
  163. package/src/devcouncil/skills/__init__.py +0 -19
  164. package/src/devcouncil/skills/library/README.md +0 -46
  165. package/src/devcouncil/skills/library/ai-training.md +0 -50
  166. package/src/devcouncil/skills/library/android.md +0 -50
  167. package/src/devcouncil/skills/library/backend.md +0 -52
  168. package/src/devcouncil/skills/library/core-engineering.md +0 -95
  169. package/src/devcouncil/skills/library/data-engineering.md +0 -47
  170. package/src/devcouncil/skills/library/desktop.md +0 -46
  171. package/src/devcouncil/skills/library/devops.md +0 -48
  172. package/src/devcouncil/skills/library/game-dev.md +0 -46
  173. package/src/devcouncil/skills/library/ios.md +0 -48
  174. package/src/devcouncil/skills/library/mobile-cross-platform.md +0 -46
  175. package/src/devcouncil/skills/library/security.md +0 -48
  176. package/src/devcouncil/skills/library/systems.md +0 -48
  177. package/src/devcouncil/skills/library/web.md +0 -47
  178. package/src/devcouncil/skills/library/windows.md +0 -47
  179. package/src/devcouncil/skills/registry.py +0 -330
  180. package/src/devcouncil/storage/__init__.py +0 -0
  181. package/src/devcouncil/storage/db.py +0 -147
  182. package/src/devcouncil/storage/models.py +0 -204
  183. package/src/devcouncil/storage/native.py +0 -557
  184. package/src/devcouncil/storage/repositories.py +0 -485
  185. package/src/devcouncil/telemetry/__init__.py +0 -0
  186. package/src/devcouncil/telemetry/cost.py +0 -140
  187. package/src/devcouncil/telemetry/model_pricing.yaml +0 -48
  188. package/src/devcouncil/telemetry/pricing.py +0 -28
  189. package/src/devcouncil/telemetry/traces.py +0 -146
  190. package/src/devcouncil/telemetry/tracker.py +0 -52
  191. package/src/devcouncil/ui/__init__.py +0 -1
  192. package/src/devcouncil/ui/dashboard.py +0 -423
  193. package/src/devcouncil/utils/__init__.py +0 -1
  194. package/src/devcouncil/utils/redaction.py +0 -147
  195. package/src/devcouncil/utils/subprocess_env.py +0 -69
  196. package/src/devcouncil/verification/__init__.py +0 -1
  197. package/src/devcouncil/verification/acceptance_compiler.py +0 -125
  198. package/src/devcouncil/verification/ad_hoc_check.py +0 -129
  199. package/src/devcouncil/verification/diff_coverage.py +0 -353
  200. package/src/devcouncil/verification/implementation_reviewer.py +0 -55
  201. package/src/devcouncil/verification/next_actions.py +0 -189
  202. package/src/devcouncil/verification/sandbox.py +0 -178
  203. package/src/devcouncil/verification/test_resolver.py +0 -91
  204. package/src/devcouncil/verification/verifier.py +0 -1548
  205. package/uv.lock +0 -1226
@@ -1,27 +0,0 @@
1
- from devcouncil.domain.requirement import Requirement
2
- from devcouncil.domain.task import Task
3
- from devcouncil.app.errors import GatingError
4
-
5
- class ArtifactValidator:
6
- """Validates DevCouncil artifacts (tasks, requirements, etc.)."""
7
-
8
- @staticmethod
9
- def validate_requirement(req: Requirement) -> None:
10
- if not req.title:
11
- raise GatingError(f"Requirement {req.id} missing title.")
12
- if not req.acceptance_criteria:
13
- raise GatingError(f"Requirement {req.id} must have at least one acceptance criterion.")
14
- for ac in req.acceptance_criteria:
15
- if not ac.verification_method:
16
- raise GatingError(f"Acceptance criterion {ac.id} in {req.id} missing verification method.")
17
-
18
- @staticmethod
19
- def validate_task(task: Task) -> None:
20
- if not task.requirement_ids:
21
- raise GatingError(f"Task {task.id} must map to at least one requirement.")
22
- if not task.planned_files:
23
- raise GatingError(f"Task {task.id} must have at least one planned file.")
24
- if not task.acceptance_criterion_ids:
25
- raise GatingError(f"Task {task.id} must map to at least one acceptance criterion.")
26
- if not task.allowed_commands and not task.expected_tests:
27
- raise GatingError(f"Task {task.id} must define allowed commands or expected tests.")
@@ -1 +0,0 @@
1
- """Packaged visual assets for DevCouncil."""
@@ -1,60 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" width="100%" height="100%">
2
- <defs>
3
- <linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
4
- <stop offset="0%" stop-color="#1a0000" />
5
- <stop offset="100%" stop-color="#000000" />
6
- </linearGradient>
7
- <linearGradient id="redGlow" x1="0%" y1="0%" x2="100%" y2="100%">
8
- <stop offset="0%" stop-color="#ff3333" />
9
- <stop offset="100%" stop-color="#990000" />
10
- </linearGradient>
11
- <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
12
- <feGaussianBlur stdDeviation="10" result="blur" />
13
- <feComposite in="SourceGraphic" in2="blur" operator="over" />
14
- </filter>
15
- </defs>
16
-
17
- <rect width="500" height="500" fill="transparent" />
18
-
19
- <g transform="translate(250, 250)">
20
- <!-- Outer wireframe hexagon representing the bounds of the orchestrator -->
21
- <polygon points="0,-180 155.88,-90 155.88,90 0,180 -155.88,90 -155.88,-90"
22
- fill="none" stroke="#330000" stroke-width="8" stroke-linejoin="round" />
23
-
24
- <!-- Inner geometric structure - Top Face -->
25
- <polygon points="0,-180 155.88,-90 0,0 -155.88,-90" fill="#0d0000" stroke="#ff0000" stroke-width="2" />
26
-
27
- <!-- Inner geometric structure - Right Face -->
28
- <polygon points="0,0 155.88,-90 155.88,90 0,180" fill="#000000" stroke="#cc0000" stroke-width="2" />
29
-
30
- <!-- Inner geometric structure - Left Face -->
31
- <polygon points="0,0 0,180 -155.88,90 -155.88,-90" fill="#1a0000" stroke="#ff3333" stroke-width="2" />
32
-
33
- <!-- The glowing core node (3D Isometric) -->
34
- <!-- Top core face -->
35
- <polygon points="0,-80 69.28,-40 0,0 -69.28,-40" fill="#ff4d4d" />
36
-
37
- <!-- Right core face -->
38
- <polygon points="0,0 69.28,-40 69.28,40 0,80" fill="#b30000" />
39
-
40
- <!-- Left core face -->
41
- <polygon points="0,0 0,80 -69.28,40 -69.28,-40" fill="#800000" />
42
-
43
- <!-- Power lines radiating from the core -->
44
- <line x1="0" y1="-80" x2="0" y2="-180" stroke="#ff0000" stroke-width="4" filter="url(#glow)" />
45
- <line x1="69.28" y1="40" x2="155.88" y2="90" stroke="#ff0000" stroke-width="4" filter="url(#glow)" />
46
- <line x1="-69.28" y1="40" x2="-155.88" y2="90" stroke="#ff0000" stroke-width="4" filter="url(#glow)" />
47
-
48
- <line x1="0" y1="80" x2="0" y2="180" stroke="#ff0000" stroke-width="2" stroke-dasharray="5,5" />
49
- <line x1="-69.28" y1="-40" x2="-155.88" y2="-90" stroke="#ff0000" stroke-width="2" stroke-dasharray="5,5" />
50
- <line x1="69.28" y1="-40" x2="155.88" y2="-90" stroke="#ff0000" stroke-width="2" stroke-dasharray="5,5" />
51
-
52
- <!-- Terminal connection dots -->
53
- <circle cx="0" cy="-180" r="8" fill="#ff3333" filter="url(#glow)" />
54
- <circle cx="155.88" cy="90" r="8" fill="#ff3333" filter="url(#glow)" />
55
- <circle cx="-155.88" cy="90" r="8" fill="#ff3333" filter="url(#glow)" />
56
- <circle cx="0" cy="180" r="5" fill="#800000" />
57
- <circle cx="-155.88" cy="-90" r="5" fill="#800000" />
58
- <circle cx="155.88" cy="-90" r="5" fill="#800000" />
59
- </g>
60
- </svg>
File without changes
File without changes
@@ -1,292 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import subprocess
4
- import sys
5
- from pathlib import Path
6
-
7
- import typer
8
- from rich.console import Console
9
- from rich.table import Table
10
-
11
- from devcouncil.cli.commands import run as run_command
12
- from devcouncil.cli.commands.integrate import _load_raw_config, _project_root, _save_raw_config
13
- from devcouncil.executors.agent_registry import (
14
- VALID_INPUT_MODES,
15
- agent_config_entry,
16
- detect_available_coding_cli,
17
- is_reserved_agent_name,
18
- load_agent_profiles,
19
- load_cli_agent_specs,
20
- normalize_agent_name,
21
- resolve_automated_executor,
22
- resolve_cursor_agent_executable,
23
- )
24
- from devcouncil.optimization.gepa_agent import GepaUnavailableError, optimize_agent_profile
25
-
26
- app = typer.Typer(help="Manage DevCouncil CLI agents.")
27
- console = Console()
28
-
29
-
30
- @app.callback(invoke_without_command=True)
31
- def list_agents(
32
- ctx: typer.Context,
33
- project_root: Path | None = typer.Option(None, "--project-root", help="Repository root containing .devcouncil/."),
34
- ):
35
- """List built-in and configured CLI agents."""
36
- if ctx.invoked_subcommand is not None:
37
- return
38
-
39
- root = _project_root(project_root)
40
- table = Table(title="DevCouncil Agents")
41
- table.add_column("Agent", style="cyan")
42
- table.add_column("Type")
43
- table.add_column("Command")
44
- table.add_column("Profile")
45
- table.add_column("MCP")
46
- table.add_column("Diff Review")
47
-
48
- for name, spec in sorted(load_cli_agent_specs(root).items()):
49
- table.add_row(
50
- name,
51
- "built-in" if spec.built_in else spec.kind,
52
- " ".join(spec.base_command()),
53
- spec.default_profile,
54
- "yes" if spec.supports_mcp else "no",
55
- "yes" if spec.supports_diff_review else "no",
56
- )
57
- console.print(table)
58
-
59
-
60
- @app.command("add")
61
- def add_agent(
62
- name: str = typer.Argument(..., help="Agent name, for example opencode or aider."),
63
- command: str = typer.Option(..., "--command", help="Executable to launch."),
64
- arg: list[str] | None = typer.Option(None, "--arg", help="Argument to pass to the CLI. Repeat for multiple args."),
65
- input_mode: str = typer.Option("stdin", "--input-mode", help="Prompt input mode: stdin, argument, or prompt-file."),
66
- prompt_arg: str | None = typer.Option(None, "--prompt-arg", help="Flag used before the prompt or prompt file."),
67
- timeout_seconds: int | None = typer.Option(None, "--timeout-seconds", help="Agent-specific timeout override."),
68
- display_name: str | None = typer.Option(None, "--display-name", help="Human-readable agent name."),
69
- kind: str = typer.Option("custom", "--kind", help="Agent kind, for example coding-cli or review-cli."),
70
- supports_mcp: bool = typer.Option(False, "--supports-mcp", help="Mark this agent as MCP-capable."),
71
- supports_diff_review: bool = typer.Option(False, "--supports-diff-review", help="Mark this agent as able to review diffs."),
72
- default_profile: str = typer.Option("default", "--default-profile", help="Default execution profile for this agent."),
73
- help_arg: list[str] | None = typer.Option(None, "--help-arg", help="Argument for the agent help command. Repeat for multiple args."),
74
- project_root: Path | None = typer.Option(None, "--project-root", help="Repository root containing .devcouncil/."),
75
- ):
76
- """Register an arbitrary prompt-taking CLI as a DevCouncil agent."""
77
- if input_mode not in VALID_INPUT_MODES:
78
- console.print("[red]--input-mode must be one of: stdin, argument, prompt-file.[/red]")
79
- raise typer.Exit(code=2)
80
- if not name.strip():
81
- console.print("[red]Agent name cannot be empty.[/red]")
82
- raise typer.Exit(code=2)
83
- if not command.strip():
84
- console.print("[red]--command cannot be empty.[/red]")
85
- raise typer.Exit(code=2)
86
-
87
- root = _project_root(project_root)
88
- if is_reserved_agent_name(name):
89
- console.print(f"[red]'{name}' is reserved for a built-in DevCouncil agent.[/red]")
90
- raise typer.Exit(code=2)
91
- if default_profile not in load_agent_profiles(root):
92
- console.print(f"[red]Unknown --default-profile '{default_profile}'.[/red]")
93
- raise typer.Exit(code=2)
94
-
95
- normalized = normalize_agent_name(name)
96
- entry = agent_config_entry(
97
- command=command,
98
- args=arg or [],
99
- input_mode=input_mode,
100
- prompt_arg=prompt_arg,
101
- timeout_seconds=timeout_seconds,
102
- display_name=display_name,
103
- kind=kind,
104
- supports_mcp=supports_mcp,
105
- supports_diff_review=supports_diff_review,
106
- default_profile=default_profile,
107
- help_command=[command, *(help_arg or [])] if help_arg else [],
108
- )
109
- config = _load_raw_config(root)
110
- agents = config.setdefault("integrations", {}).setdefault("cli_agents", {}).setdefault("agents", {})
111
- agents[normalized] = entry
112
- _save_raw_config(root, config)
113
- console.print(f"[green]Registered CLI agent '{normalized}' in .devcouncil/config.yaml.[/green]")
114
-
115
-
116
- @app.command("doctor")
117
- def doctor(
118
- project_root: Path | None = typer.Option(None, "--project-root", help="Repository root containing .devcouncil/."),
119
- ):
120
- """Check configured CLI agents and execution profiles."""
121
- root = _project_root(project_root)
122
- profiles = load_agent_profiles(root)
123
- table = Table(title="DevCouncil Agent Doctor")
124
- table.add_column("Agent", style="cyan")
125
- table.add_column("Status")
126
- table.add_column("Details", no_wrap=True)
127
-
128
- for name, spec in sorted(load_cli_agent_specs(root).items()):
129
- if spec.name == "cursor":
130
- executable = resolve_cursor_agent_executable()
131
- else:
132
- executable = _which(spec.executable)
133
- mode_ok = spec.input_mode in VALID_INPUT_MODES
134
- profile_ok = spec.default_profile in profiles
135
- help_ok, help_detail = _check_help(spec.help_command or [spec.executable, "--help"])
136
-
137
- if executable and mode_ok and profile_ok:
138
- status = "[green]OK[/green]"
139
- elif executable:
140
- status = "[red]Invalid[/red]"
141
- else:
142
- status = "[yellow]Missing[/yellow]"
143
-
144
- details = []
145
- details.append(executable or f"{spec.executable} not found on PATH")
146
- if not mode_ok:
147
- details.append(f"invalid input_mode={spec.input_mode}")
148
- if not profile_ok:
149
- details.append(f"missing profile={spec.default_profile}")
150
- if help_ok:
151
- details.append("help command OK")
152
- elif not spec.built_in:
153
- details.append(help_detail)
154
- table.add_row(name, status, "; ".join(details))
155
-
156
- console.print(table)
157
- detected = detect_available_coding_cli(root)
158
- if detected:
159
- resolved = resolve_automated_executor(root, None)
160
- console.print(
161
- f"\n[dim]Auto-pick for dev go / dev run:[/dim] [cyan]{resolved}[/cyan] "
162
- f"(first built-in CLI on PATH in probe order)"
163
- )
164
- else:
165
- console.print("\n[dim]No built-in coding CLI on PATH for auto-pick.[/dim]")
166
-
167
-
168
- @app.command("run")
169
- def run_agent(
170
- task_id: str = typer.Argument(..., help="ID of the task to run."),
171
- agent: str = typer.Option(..., "--agent", "-a", help="Agent name to execute."),
172
- profile: str | None = typer.Option(None, "--profile", help="Execution profile: default, yolo, prod, or configured."),
173
- stream: bool = typer.Option(
174
- False,
175
- "--stream",
176
- help="Stream coding CLI stdout/stderr live (also enabled by execution.stream_cli_output).",
177
- ),
178
- project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
179
- ):
180
- """Run a DevCouncil task with a named CLI agent and profile."""
181
- run_command.run(task_id, executor=agent, profile=profile, stream=stream, project_root=project_root)
182
-
183
-
184
- def _which(command: str) -> str | None:
185
- from shutil import which
186
-
187
- return which(command)
188
-
189
-
190
- @app.command("help")
191
- def agent_help(
192
- name: str = typer.Argument(..., help="Agent name, for example codex or opencode."),
193
- project_root: Path | None = typer.Option(None, "--project-root", help="Repository root containing .devcouncil/."),
194
- ):
195
- """Show the underlying CLI agent's own help output."""
196
- root = _project_root(project_root)
197
- spec = load_cli_agent_specs(root).get(normalize_agent_name(name))
198
- if spec is None:
199
- console.print(f"[red]Unknown agent '{name}'. Use 'dev agents' to list available agents.[/red]")
200
- raise typer.Exit(code=1)
201
- command = list(spec.help_command) or [spec.executable, "--help"]
202
- executable = _which(command[0])
203
- if not executable:
204
- console.print(f"[red]{command[0]} is not installed or not on PATH.[/red]")
205
- raise typer.Exit(code=1)
206
- resolved = [executable, *command[1:]]
207
- use_shell = sys.platform == "win32" and Path(executable).suffix.lower() in {".bat", ".cmd", ".ps1"}
208
- try:
209
- result = subprocess.run(
210
- subprocess.list2cmdline(resolved) if use_shell else resolved,
211
- capture_output=True,
212
- text=True,
213
- encoding="utf-8",
214
- errors="replace",
215
- timeout=15,
216
- shell=use_shell,
217
- )
218
- except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as exc:
219
- console.print(f"[red]Failed to run {' '.join(command)}: {exc}[/red]")
220
- raise typer.Exit(code=1)
221
- output = (result.stdout or "") + (result.stderr or "")
222
- typer.echo(output.strip())
223
- raise typer.Exit(code=result.returncode)
224
-
225
-
226
- def _check_help(command: list[str]) -> tuple[bool, str]:
227
- executable = _which(command[0]) if command else None
228
- if not command or not executable:
229
- return False, "help command unavailable"
230
- resolved = [executable, *command[1:]]
231
- use_shell = sys.platform == "win32" and Path(executable).suffix.lower() in {".bat", ".cmd", ".ps1"}
232
- try:
233
- result = subprocess.run(
234
- subprocess.list2cmdline(resolved) if use_shell else resolved,
235
- capture_output=True,
236
- text=True,
237
- encoding="utf-8",
238
- errors="replace",
239
- timeout=5,
240
- shell=use_shell,
241
- )
242
- except subprocess.TimeoutExpired:
243
- return False, "help command timed out"
244
- return result.returncode == 0, f"help command exited {result.returncode}"
245
-
246
-
247
- @app.command("optimize")
248
- def optimize_agent(
249
- agent: str = typer.Option(..., "--agent", "-a", help="Agent name to optimize, for example codex or opencode."),
250
- profile_name: str = typer.Option("default", "--profile", help="Profile name to optimize: default, yolo, prod, or configured."),
251
- evals_path: Path = typer.Option(..., "--evals", help="JSON or JSONL GEPA evaluation dataset."),
252
- max_metric_calls: int = typer.Option(40, "--max-metric-calls", min=1, help="Maximum GEPA metric calls."),
253
- objective: str | None = typer.Option(None, "--objective", help="Override the default optimization objective."),
254
- apply: bool = typer.Option(
255
- False,
256
- "--apply/--dry-run",
257
- help="Write the optimized preamble into .devcouncil/config.yaml. Defaults to dry-run.",
258
- ),
259
- output_path: Path | None = typer.Option(None, "--output", help="Write the optimization artifact to this path."),
260
- project_root: Path | None = typer.Option(None, "--project-root", help="Repository root containing .devcouncil/."),
261
- ):
262
- """
263
- Optimize a CLI-agent profile prompt preamble with GEPA.
264
- """
265
- root = _project_root(project_root)
266
- try:
267
- result = optimize_agent_profile(
268
- project_root=root,
269
- agent=agent,
270
- profile_name=profile_name,
271
- evals_path=evals_path,
272
- max_metric_calls=max_metric_calls,
273
- objective=objective,
274
- apply=apply,
275
- output_path=output_path,
276
- )
277
- except GepaUnavailableError as exc:
278
- console.print(f"[red]{exc}[/red]")
279
- raise typer.Exit(code=1) from exc
280
- except ValueError as exc:
281
- console.print(f"[red]{exc}[/red]")
282
- raise typer.Exit(code=2) from exc
283
-
284
- mode = "applied" if result.applied else "dry-run"
285
- score = "unknown" if result.best_score is None else f"{result.best_score:.3f}"
286
- console.print(
287
- f"[green]GEPA optimization complete ({mode}) for {result.agent}:{result.profile_name}.[/green]"
288
- )
289
- console.print(f"Score: [bold]{score}[/bold]")
290
- console.print(f"Artifact: [dim]{result.artifact_path}[/dim]")
291
- console.print("\n[bold]Optimized preamble[/bold]")
292
- console.print(result.best_preamble or "[dim](empty)[/dim]")
@@ -1,54 +0,0 @@
1
- import typer
2
- from rich.console import Console
3
- from rich.table import Table
4
- from pathlib import Path
5
-
6
- from devcouncil.app.errors import GatingError
7
- from devcouncil.artifacts.validators import ArtifactValidator
8
- from devcouncil.storage.db import get_db
9
- from devcouncil.storage.repositories import RequirementRepository, TaskRepository
10
- from devcouncil.cli.commands.init import initialize_project
11
-
12
- app = typer.Typer()
13
- console = Console()
14
-
15
-
16
- @app.command(name="validate")
17
- def validate(
18
- project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
19
- ):
20
- """Validate requirements and tasks stored in the artifact graph."""
21
- root = project_root.expanduser().resolve()
22
- initialize_project(root, quiet=True)
23
- db = get_db(root)
24
- if not db:
25
- console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
26
- raise typer.Exit(code=1)
27
-
28
- errors: list[str] = []
29
- with db.get_session() as session:
30
- req_repo = RequirementRepository(session)
31
- task_repo = TaskRepository(session)
32
-
33
- for req in req_repo.get_all():
34
- try:
35
- ArtifactValidator.validate_requirement(req)
36
- except GatingError as exc:
37
- errors.append(str(exc))
38
-
39
- for task in task_repo.get_all():
40
- try:
41
- ArtifactValidator.validate_task(task)
42
- except GatingError as exc:
43
- errors.append(str(exc))
44
-
45
- if not errors:
46
- console.print("[green]Artifacts are valid.[/green]")
47
- return
48
-
49
- table = Table(title="Artifact Validation Errors")
50
- table.add_column("Error", style="red")
51
- for error in errors:
52
- table.add_row(error)
53
- console.print(table)
54
- raise typer.Exit(code=1)
@@ -1,22 +0,0 @@
1
- import json
2
- from pathlib import Path
3
-
4
- import typer
5
-
6
- from devcouncil.indexing.ast_matcher import AstMatcher
7
-
8
- app = typer.Typer(help="Run structural AST searches.")
9
-
10
-
11
- @app.command("match")
12
- def match(
13
- query: str = typer.Argument("", help="Name or source text to match."),
14
- language: str | None = typer.Option(None, "--language", "-l", help="Language filter."),
15
- kind: str | None = typer.Option(None, "--kind", "-k", help="Symbol kind filter."),
16
- limit: int = typer.Option(100, "--limit", help="Maximum matches."),
17
- project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root."),
18
- ):
19
- """Find function/class/type symbols using tree-sitter when available and fallbacks otherwise."""
20
- root = project_root.expanduser().resolve()
21
- matches = AstMatcher(root).match(query=query, language=language, kind=kind, limit=max(1, limit))
22
- typer.echo(json.dumps({"matches": [item.model_dump() for item in matches]}, indent=2))
@@ -1,35 +0,0 @@
1
- import json
2
- from pathlib import Path
3
-
4
- import typer
5
- from rich.console import Console
6
-
7
- from devcouncil.cli.commands.init import initialize_project
8
- from devcouncil.storage.db import get_db
9
- from devcouncil.verification.verifier import Verifier
10
-
11
- console = Console()
12
-
13
-
14
- def baseline(
15
- force: bool = typer.Option(False, "--force", help="Overwrite an existing baseline snapshot."),
16
- project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
17
- ):
18
- """Capture the current repo state as DevCouncil's verification baseline."""
19
- root = project_root.expanduser().resolve()
20
- initialize_project(root, quiet=True)
21
- if not get_db(root):
22
- raise typer.Exit(code=1)
23
-
24
- baseline_path = root / ".devcouncil" / "baseline.json"
25
- if baseline_path.exists() and not force:
26
- console.print("[yellow]Baseline already exists. Use --force to replace it.[/yellow]")
27
- raise typer.Exit(code=1)
28
-
29
- changed_files = Verifier(root).get_changed_files()
30
- payload = {
31
- "changed_files": changed_files,
32
- "note": "Files present in this snapshot are excluded from future task-scoped verification diffs.",
33
- }
34
- baseline_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
35
- console.print(f"[green]Captured baseline with {len(changed_files)} changed file(s).[/green]")