devcouncil 0.1.0 → 0.1.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/LICENSE +201 -201
- package/README.md +62 -543
- package/package.json +1 -1
- package/pyproject.toml +29 -26
- package/src/devcouncil/__main__.py +4 -4
- package/src/devcouncil/app/__init__.py +28 -28
- package/src/devcouncil/app/config.py +135 -108
- package/src/devcouncil/app/errors.py +23 -23
- package/src/devcouncil/app/events.py +44 -44
- package/src/devcouncil/app/orchestrator.py +67 -67
- package/src/devcouncil/app/project_status.py +29 -0
- package/src/devcouncil/app/run_context.py +39 -39
- package/src/devcouncil/app/state_machine.py +108 -108
- package/src/devcouncil/artifacts/__init__.py +1 -1
- package/src/devcouncil/artifacts/coverage.py +96 -96
- package/src/devcouncil/artifacts/graph.py +143 -143
- package/src/devcouncil/artifacts/migrations.py +20 -20
- package/src/devcouncil/artifacts/schemas.py +23 -23
- package/src/devcouncil/artifacts/serializer.py +21 -21
- package/src/devcouncil/artifacts/validators.py +27 -27
- package/src/devcouncil/cli/commands/artifacts.py +51 -48
- package/src/devcouncil/cli/commands/ast.py +22 -0
- package/src/devcouncil/cli/commands/baseline.py +35 -32
- package/src/devcouncil/cli/commands/config.py +76 -54
- package/src/devcouncil/cli/commands/dashboard.py +26 -0
- package/src/devcouncil/cli/commands/doctor.py +86 -42
- package/src/devcouncil/cli/commands/go.py +237 -0
- package/src/devcouncil/cli/commands/hook.py +96 -29
- package/src/devcouncil/cli/commands/init.py +67 -56
- package/src/devcouncil/cli/commands/integrate.py +320 -14
- package/src/devcouncil/cli/commands/lsp.py +20 -0
- package/src/devcouncil/cli/commands/map.py +25 -21
- package/src/devcouncil/cli/commands/plan.py +257 -206
- package/src/devcouncil/cli/commands/prompt.py +36 -33
- package/src/devcouncil/cli/commands/repair.py +72 -69
- package/src/devcouncil/cli/commands/report.py +112 -54
- package/src/devcouncil/cli/commands/reset_demo_state.py +31 -28
- package/src/devcouncil/cli/commands/rollback.py +49 -47
- package/src/devcouncil/cli/commands/run.py +252 -207
- package/src/devcouncil/cli/commands/setup.py +159 -18
- package/src/devcouncil/cli/commands/show.py +76 -57
- package/src/devcouncil/cli/commands/status.py +117 -105
- package/src/devcouncil/cli/commands/tasks.py +55 -41
- package/src/devcouncil/cli/commands/trace.py +2 -1
- package/src/devcouncil/cli/commands/verify.py +158 -128
- package/src/devcouncil/cli/commands/version.py +20 -20
- package/src/devcouncil/cli/commands/watch.py +574 -0
- package/src/devcouncil/cli/main.py +42 -24
- package/src/devcouncil/council/prompts/arbiter.md +19 -19
- package/src/devcouncil/council/prompts/critic_a.md +10 -10
- package/src/devcouncil/council/prompts/critic_b.md +10 -10
- package/src/devcouncil/council/prompts/implementation_reviewer.md +16 -16
- package/src/devcouncil/council/prompts/planner_a.md +16 -16
- package/src/devcouncil/council/prompts/planner_b.md +16 -16
- package/src/devcouncil/council/prompts/rebuttal.md +10 -10
- package/src/devcouncil/council/prompts/spec_writer.md +12 -12
- package/src/devcouncil/domain/assumption.py +17 -17
- package/src/devcouncil/domain/critique.py +32 -32
- package/src/devcouncil/domain/evidence.py +27 -27
- package/src/devcouncil/domain/gap.py +26 -26
- package/src/devcouncil/domain/requirement.py +22 -22
- package/src/devcouncil/domain/task.py +26 -26
- package/src/devcouncil/execution/__init__.py +1 -1
- package/src/devcouncil/execution/context_builder.py +54 -54
- package/src/devcouncil/execution/executor.py +15 -15
- package/src/devcouncil/execution/hook_policy.py +24 -3
- package/src/devcouncil/execution/patch.py +28 -28
- package/src/devcouncil/execution/permissions.py +44 -44
- package/src/devcouncil/execution/prompt_builder.py +23 -23
- package/src/devcouncil/execution/task_runner.py +63 -63
- package/src/devcouncil/executors/__init__.py +1 -1
- package/src/devcouncil/executors/coding_cli.py +112 -0
- package/src/devcouncil/executors/mini_swe.py +63 -63
- package/src/devcouncil/executors/native/agent.py +81 -81
- package/src/devcouncil/executors/openhands.py +56 -56
- package/src/devcouncil/gating/__init__.py +1 -1
- package/src/devcouncil/gating/checks/clean_git.py +50 -45
- package/src/devcouncil/gating/checks/planned_files_check.py +32 -32
- package/src/devcouncil/gating/checks/requirement_coverage.py +26 -26
- package/src/devcouncil/gating/checks/secret_scan_check.py +34 -34
- package/src/devcouncil/gating/policy.py +157 -157
- package/src/devcouncil/indexing/__init__.py +1 -1
- package/src/devcouncil/indexing/ast_matcher.py +168 -0
- package/src/devcouncil/indexing/graph_index.py +48 -48
- package/src/devcouncil/indexing/lsp.py +120 -0
- package/src/devcouncil/indexing/repo_mapper.py +208 -204
- package/src/devcouncil/integrations/github.py +35 -35
- package/src/devcouncil/integrations/gitnexus.py +27 -27
- package/src/devcouncil/integrations/graphify.py +34 -34
- package/src/devcouncil/integrations/mcp/server.py +549 -96
- package/src/devcouncil/integrations/pr_comments.py +62 -0
- package/src/devcouncil/live/__init__.py +2 -0
- package/src/devcouncil/live/cards.py +207 -0
- package/src/devcouncil/live/models.py +63 -0
- package/src/devcouncil/live/repair_prompt.py +83 -0
- package/src/devcouncil/live/reviewer.py +70 -0
- package/src/devcouncil/live/signals.py +135 -0
- package/src/devcouncil/live/summary.py +34 -0
- package/src/devcouncil/live/tasks.py +18 -0
- package/src/devcouncil/live/transcripts.py +138 -0
- package/src/devcouncil/llm/__init__.py +1 -1
- package/src/devcouncil/llm/cache.py +38 -38
- package/src/devcouncil/llm/provider.py +146 -125
- package/src/devcouncil/llm/router.py +111 -111
- package/src/devcouncil/planning/__init__.py +1 -1
- package/src/devcouncil/planning/arbiter_service.py +57 -57
- package/src/devcouncil/planning/critique_service.py +66 -66
- package/src/devcouncil/planning/plan_service.py +46 -46
- package/src/devcouncil/planning/prompt_enhancer_service.py +86 -0
- package/src/devcouncil/planning/repair_service.py +39 -39
- package/src/devcouncil/planning/spec_service.py +44 -44
- package/src/devcouncil/reporting/github_check.py +32 -32
- package/src/devcouncil/reporting/json_report.py +20 -17
- package/src/devcouncil/reporting/markdown_report.py +68 -46
- package/src/devcouncil/reporting/report_builder.py +14 -14
- package/src/devcouncil/storage/db.py +66 -66
- package/src/devcouncil/storage/models.py +83 -83
- package/src/devcouncil/storage/repositories.py +299 -222
- package/src/devcouncil/telemetry/cost.py +34 -34
- package/src/devcouncil/telemetry/tracker.py +49 -49
- package/src/devcouncil/ui/__init__.py +1 -0
- package/src/devcouncil/ui/dashboard.py +122 -0
- package/src/devcouncil/utils/__init__.py +1 -1
- package/src/devcouncil/utils/redaction.py +141 -141
- package/src/devcouncil/verification/__init__.py +1 -1
- package/src/devcouncil/verification/implementation_reviewer.py +55 -55
- package/src/devcouncil/verification/verifier.py +319 -302
- package/uv.lock +1 -1
|
@@ -1,69 +1,72 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
from rich.console import Console
|
|
3
|
-
from devcouncil.storage.db import get_db
|
|
4
|
-
from devcouncil.storage.repositories import TaskRepository, GapRepository
|
|
5
|
-
from devcouncil.planning.repair_service import RepairService
|
|
6
|
-
from devcouncil.execution.context_builder import ContextBuilder
|
|
7
|
-
from devcouncil.llm.provider import
|
|
8
|
-
from devcouncil.llm.router import ModelRouter
|
|
9
|
-
from devcouncil.app.config import load_config, get_api_key
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from devcouncil.storage.db import get_db
|
|
4
|
+
from devcouncil.storage.repositories import TaskRepository, GapRepository
|
|
5
|
+
from devcouncil.planning.repair_service import RepairService
|
|
6
|
+
from devcouncil.execution.context_builder import ContextBuilder
|
|
7
|
+
from devcouncil.llm.provider import create_provider, validate_model_provider
|
|
8
|
+
from devcouncil.llm.router import ModelRouter
|
|
9
|
+
from devcouncil.app.config import load_config, get_api_key
|
|
10
|
+
from devcouncil.cli.commands.init import initialize_project
|
|
11
|
+
import asyncio
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
app = typer.Typer()
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
async def run_repair_flow():
|
|
18
|
+
initialize_project(Path("."), quiet=True)
|
|
19
|
+
db = get_db()
|
|
20
|
+
if not db:
|
|
21
|
+
console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
with db.get_session() as session:
|
|
25
|
+
gap_repo = GapRepository(session)
|
|
26
|
+
task_repo = TaskRepository(session)
|
|
27
|
+
|
|
28
|
+
all_gaps = gap_repo.get_all()
|
|
29
|
+
blocking_gaps = [g for g in all_gaps if g.blocking]
|
|
30
|
+
|
|
31
|
+
if not blocking_gaps:
|
|
32
|
+
console.print("[green]No blocking gaps found. Nothing to repair![/green]")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
console.print(f"Found [bold]{len(blocking_gaps)}[/bold] blocking gaps. Orchestrating repair plan...")
|
|
36
|
+
|
|
37
|
+
# Load router
|
|
38
|
+
try:
|
|
39
|
+
config = load_config(Path("."))
|
|
40
|
+
validate_model_provider(config.models.provider)
|
|
41
|
+
api_key = get_api_key(config.models.provider, Path("."))
|
|
42
|
+
except (FileNotFoundError, ValueError) as e:
|
|
43
|
+
console.print(f"[red]{e}[/red]")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
provider = create_provider(config.models.provider, api_key)
|
|
47
|
+
role_config = {name: role.model_dump() for name, role in config.models.roles.items()}
|
|
48
|
+
router = ModelRouter(provider, role_config)
|
|
49
|
+
repair_service = RepairService(router)
|
|
50
|
+
context_builder = ContextBuilder(Path("."))
|
|
51
|
+
|
|
52
|
+
# Build minimal context for repair.
|
|
53
|
+
project_context = context_builder.get_structure_summary()
|
|
54
|
+
|
|
55
|
+
repair_output = await repair_service.generate_repair_plan(blocking_gaps, str(project_context))
|
|
56
|
+
|
|
57
|
+
for task in repair_output.suggested_tasks:
|
|
58
|
+
task.id = f"REPAIR-{task.id}"
|
|
59
|
+
task_repo.save(task)
|
|
60
|
+
console.print(f" - Created intelligent repair task [bold]{task.id}[/bold]: {task.title}")
|
|
61
|
+
|
|
62
|
+
console.print(f"\n[green]Successfully generated {len(repair_output.suggested_tasks)} repair tasks.[/green]")
|
|
63
|
+
|
|
64
|
+
@app.callback(invoke_without_command=True)
|
|
65
|
+
def repair(ctx: typer.Context):
|
|
66
|
+
"""
|
|
67
|
+
Convert blocking gaps into intelligent repair tasks using LLM inference.
|
|
68
|
+
"""
|
|
69
|
+
if ctx.invoked_subcommand is not None:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
asyncio.run(run_repair_flow())
|
|
@@ -1,71 +1,129 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
from rich.console import Console
|
|
3
|
-
from rich.markdown import Markdown
|
|
4
|
-
from devcouncil.storage.db import get_db
|
|
5
|
-
from devcouncil.storage.repositories import ArtifactGraphRepository
|
|
6
|
-
from devcouncil.reporting.report_builder import ReportBuilder
|
|
7
|
-
from devcouncil.integrations.github import GitHubIntegration
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.markdown import Markdown
|
|
4
|
+
from devcouncil.storage.db import get_db
|
|
5
|
+
from devcouncil.storage.repositories import ArtifactGraphRepository
|
|
6
|
+
from devcouncil.reporting.report_builder import ReportBuilder
|
|
7
|
+
from devcouncil.integrations.github import GitHubIntegration
|
|
8
|
+
from devcouncil.integrations.pr_comments import GitHubPRCommenter, GitLabMRCommenter, build_pr_comment_body
|
|
8
9
|
from devcouncil.artifacts.graph import ArtifactGraph
|
|
9
10
|
from devcouncil.telemetry.traces import TraceLogger
|
|
11
|
+
from devcouncil.cli.commands.init import initialize_project
|
|
12
|
+
from devcouncil.live.summary import live_review_summary
|
|
10
13
|
import asyncio
|
|
11
14
|
import os
|
|
12
15
|
import subprocess
|
|
13
16
|
from pathlib import Path
|
|
14
|
-
|
|
15
|
-
app = typer.Typer()
|
|
16
|
-
console = Console()
|
|
17
|
-
|
|
18
|
-
async def run_github_report(graph: ArtifactGraph):
|
|
19
|
-
token = os.environ.get("GITHUB_TOKEN")
|
|
20
|
-
repo = os.environ.get("GITHUB_REPOSITORY") # e.g. owner/repo
|
|
21
|
-
|
|
22
|
-
if not token or not repo:
|
|
23
|
-
console.print("[red]GITHUB_TOKEN and GITHUB_REPOSITORY must be set for GitHub reporting.[/red]")
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
try:
|
|
27
|
-
# Detect current SHA
|
|
28
|
-
sha = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip()
|
|
29
|
-
integration = GitHubIntegration(token, repo, sha)
|
|
30
|
-
await integration.report_verification(graph)
|
|
31
|
-
console.print(f"[green]Successfully reported to GitHub PR Checks for {repo} at {sha[:7]}[/green]")
|
|
32
|
-
except Exception as e:
|
|
33
|
-
console.print(f"[red]Failed to report to GitHub: {e}[/red]")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
17
|
+
|
|
18
|
+
app = typer.Typer()
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
async def run_github_report(graph: ArtifactGraph, project_root: Path):
|
|
22
|
+
token = os.environ.get("GITHUB_TOKEN")
|
|
23
|
+
repo = os.environ.get("GITHUB_REPOSITORY") # e.g. owner/repo
|
|
24
|
+
|
|
25
|
+
if not token or not repo:
|
|
26
|
+
console.print("[red]GITHUB_TOKEN and GITHUB_REPOSITORY must be set for GitHub reporting.[/red]")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Detect current SHA
|
|
31
|
+
sha = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=project_root).decode().strip()
|
|
32
|
+
integration = GitHubIntegration(token, repo, sha)
|
|
33
|
+
await integration.report_verification(graph)
|
|
34
|
+
console.print(f"[green]Successfully reported to GitHub PR Checks for {repo} at {sha[:7]}[/green]")
|
|
35
|
+
except Exception as e:
|
|
36
|
+
console.print(f"[red]Failed to report to GitHub: {e}[/red]")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def run_github_pr_comment(graph: ArtifactGraph, live_review: dict | None = None):
|
|
40
|
+
token = os.environ.get("GITHUB_TOKEN")
|
|
41
|
+
repo = os.environ.get("GITHUB_REPOSITORY")
|
|
42
|
+
pull_number = os.environ.get("GITHUB_PR_NUMBER") or os.environ.get("PR_NUMBER")
|
|
43
|
+
if not token or not repo or not pull_number:
|
|
44
|
+
console.print("[red]GITHUB_TOKEN, GITHUB_REPOSITORY, and GITHUB_PR_NUMBER must be set for GitHub PR comments.[/red]")
|
|
45
|
+
return
|
|
46
|
+
try:
|
|
47
|
+
pull_number_int = int(pull_number)
|
|
48
|
+
except ValueError:
|
|
49
|
+
console.print("[red]GITHUB_PR_NUMBER must be an integer.[/red]")
|
|
50
|
+
return
|
|
51
|
+
commenter = GitHubPRCommenter(token, repo, pull_number_int)
|
|
52
|
+
await commenter.post_comment(build_pr_comment_body(graph, live_review=live_review))
|
|
53
|
+
console.print(f"[green]Posted DevCouncil PR comment to GitHub PR #{pull_number}.[/green]")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def run_gitlab_mr_comment(graph: ArtifactGraph, live_review: dict | None = None):
|
|
57
|
+
token = os.environ.get("GITLAB_TOKEN")
|
|
58
|
+
project_id = os.environ.get("GITLAB_PROJECT_ID")
|
|
59
|
+
mr_iid = os.environ.get("GITLAB_MR_IID") or os.environ.get("CI_MERGE_REQUEST_IID")
|
|
60
|
+
base_url = os.environ.get("GITLAB_API_URL", "https://gitlab.com/api/v4")
|
|
61
|
+
if not token or not project_id or not mr_iid:
|
|
62
|
+
console.print("[red]GITLAB_TOKEN, GITLAB_PROJECT_ID, and GITLAB_MR_IID must be set for GitLab MR comments.[/red]")
|
|
63
|
+
return
|
|
64
|
+
try:
|
|
65
|
+
mr_iid_int = int(mr_iid)
|
|
66
|
+
except ValueError:
|
|
67
|
+
console.print("[red]GITLAB_MR_IID must be an integer.[/red]")
|
|
68
|
+
return
|
|
69
|
+
commenter = GitLabMRCommenter(token, project_id, mr_iid_int, base_url=base_url)
|
|
70
|
+
await commenter.post_comment(build_pr_comment_body(graph, live_review=live_review))
|
|
71
|
+
console.print(f"[green]Posted DevCouncil MR comment to GitLab MR !{mr_iid}.[/green]")
|
|
72
|
+
|
|
73
|
+
@app.callback(invoke_without_command=True)
|
|
74
|
+
def report(
|
|
75
|
+
ctx: typer.Context,
|
|
76
|
+
planning_only: bool = typer.Option(False, "--planning-only", help="Report only the planning phase status"),
|
|
77
|
+
json_format: bool = typer.Option(False, "--json", help="Output report in JSON format"),
|
|
78
|
+
github: bool = typer.Option(False, "--github", help="Post report to GitHub PR Checks"),
|
|
79
|
+
github_pr_comment: bool = typer.Option(False, "--github-pr-comment", help="Post report as a GitHub PR comment"),
|
|
80
|
+
gitlab_pr_comment: bool = typer.Option(False, "--gitlab-pr-comment", help="Post report as a GitLab merge request comment"),
|
|
81
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
Produce final evidence report.
|
|
85
|
+
"""
|
|
86
|
+
if ctx.invoked_subcommand is not None:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
root = project_root.expanduser().resolve()
|
|
90
|
+
initialize_project(root, quiet=True)
|
|
91
|
+
db = get_db(root)
|
|
92
|
+
if not db:
|
|
93
|
+
console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
|
|
94
|
+
raise typer.Exit(code=1)
|
|
95
|
+
|
|
96
|
+
with db.get_session() as session:
|
|
54
97
|
graph_repo = ArtifactGraphRepository(session)
|
|
55
98
|
graph = graph_repo.load_graph()
|
|
56
|
-
|
|
99
|
+
live_review = live_review_summary(root)
|
|
100
|
+
TraceLogger(root).log_event(
|
|
57
101
|
"report_generated",
|
|
58
|
-
{
|
|
102
|
+
{
|
|
103
|
+
"json": json_format,
|
|
104
|
+
"github": github,
|
|
105
|
+
"github_pr_comment": github_pr_comment,
|
|
106
|
+
"gitlab_pr_comment": gitlab_pr_comment,
|
|
107
|
+
"planning_only": planning_only,
|
|
108
|
+
},
|
|
59
109
|
summary="Generated DevCouncil report",
|
|
60
110
|
)
|
|
61
111
|
|
|
62
112
|
if github:
|
|
63
|
-
asyncio.run(run_github_report(graph))
|
|
64
|
-
return
|
|
65
|
-
|
|
113
|
+
asyncio.run(run_github_report(graph, root))
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
if github_pr_comment:
|
|
117
|
+
asyncio.run(run_github_pr_comment(graph, live_review=live_review))
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
if gitlab_pr_comment:
|
|
121
|
+
asyncio.run(run_gitlab_mr_comment(graph, live_review=live_review))
|
|
122
|
+
return
|
|
123
|
+
|
|
66
124
|
if json_format:
|
|
67
|
-
output = ReportBuilder.build_json(graph)
|
|
125
|
+
output = ReportBuilder.build_json(graph, live_review=live_review)
|
|
68
126
|
typer.echo(output)
|
|
69
127
|
else:
|
|
70
|
-
output = ReportBuilder.build_markdown(graph)
|
|
128
|
+
output = ReportBuilder.build_markdown(graph, live_review=live_review)
|
|
71
129
|
console.print(Markdown(output))
|
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
from rich.console import Console
|
|
3
|
-
from sqlmodel import delete
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from devcouncil.storage.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from sqlmodel import delete
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from devcouncil.storage.db import get_db
|
|
7
|
+
from devcouncil.storage.models import EvidenceModel, GapModel, RequirementModel, TaskModel
|
|
8
|
+
from devcouncil.cli.commands.init import initialize_project
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def reset_demo_state(
|
|
14
|
+
yes: bool = typer.Option(False, "--yes", help="Confirm clearing planning/demo artifacts."),
|
|
15
|
+
):
|
|
16
|
+
"""Clear demo planning artifacts from the local DevCouncil state database."""
|
|
17
|
+
if not yes:
|
|
18
|
+
console.print("[red]Refusing to clear state without --yes.[/red]")
|
|
19
|
+
raise typer.Exit(code=1)
|
|
20
|
+
|
|
21
|
+
initialize_project(Path("."), quiet=True)
|
|
22
|
+
db = get_db()
|
|
23
|
+
if not db:
|
|
24
|
+
console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
|
|
25
|
+
raise typer.Exit(code=1)
|
|
26
|
+
|
|
27
|
+
with db.get_session() as session:
|
|
28
|
+
for model in (EvidenceModel, GapModel, TaskModel, RequirementModel):
|
|
29
|
+
session.exec(delete(model))
|
|
30
|
+
|
|
31
|
+
console.print("[green]Cleared requirements, tasks, gaps, and evidence from local state.[/green]")
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
import subprocess
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from rich.console import Console
|
|
5
|
-
|
|
6
|
-
app = typer.Typer()
|
|
7
|
-
console = Console()
|
|
8
|
-
|
|
9
|
-
@app.callback(invoke_without_command=True)
|
|
10
|
-
def rollback(
|
|
11
|
-
ctx: typer.Context,
|
|
12
|
-
task_id: str = typer.Argument(..., help="ID of the task to rollback"),
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import typer
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
app = typer.Typer()
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
@app.callback(invoke_without_command=True)
|
|
10
|
+
def rollback(
|
|
11
|
+
ctx: typer.Context,
|
|
12
|
+
task_id: str = typer.Argument(..., help="ID of the task to rollback"),
|
|
13
|
+
project_root: Path = typer.Option(Path("."), "--project-root", help="Repository root containing .devcouncil/."),
|
|
14
|
+
):
|
|
15
|
+
"""
|
|
16
|
+
Revert changes using a task's git checkpoint.
|
|
17
|
+
"""
|
|
18
|
+
if ctx.invoked_subcommand is not None:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
root = project_root.expanduser().resolve()
|
|
22
|
+
checkpoint_file = root / ".devcouncil" / "checkpoints" / f"{task_id}-before.patch"
|
|
23
|
+
after_patch = root / ".devcouncil" / "checkpoints" / f"{task_id}-after.patch"
|
|
22
24
|
|
|
23
25
|
if not checkpoint_file.exists() and not after_patch.exists():
|
|
24
26
|
console.print(
|
|
@@ -28,31 +30,31 @@ def rollback(
|
|
|
28
30
|
raise typer.Exit(code=1)
|
|
29
31
|
|
|
30
32
|
console.print(f"Rolling back task [bold]{task_id}[/bold]...")
|
|
31
|
-
|
|
32
|
-
try:
|
|
33
|
+
|
|
34
|
+
try:
|
|
33
35
|
if after_patch.exists():
|
|
34
36
|
# Reverse-apply the task's changes only
|
|
35
|
-
console.print(f"Applying reverse patch from [bold]{after_patch}[/bold]...")
|
|
36
|
-
subprocess.check_call(
|
|
37
|
-
["git", "apply", "-R", str(after_patch)],
|
|
38
|
-
cwd=
|
|
39
|
-
)
|
|
40
|
-
console.print(f"[green]Successfully rolled back task {task_id} changes.[/green]")
|
|
41
|
-
else:
|
|
42
|
-
# No after-patch, but we have the before-patch — warn and offer manual reset
|
|
43
|
-
console.print(
|
|
44
|
-
f"[yellow]No after-patch found at {after_patch}.[/yellow]\n"
|
|
45
|
-
f"The before-patch at {checkpoint_file} captured the state before the task ran.\n"
|
|
46
|
-
f"To manually reset:\n"
|
|
47
|
-
f" 1. [bold]git stash[/bold] (if you want to keep current changes)\n"
|
|
48
|
-
f" 2. [bold]git checkout -- .[/bold] (discard working tree changes)\n"
|
|
49
|
-
f" 3. [bold]git apply {checkpoint_file}[/bold] (restore pre-task state)"
|
|
50
|
-
)
|
|
51
|
-
except subprocess.CalledProcessError as e:
|
|
52
|
-
console.print(f"[red]Failed to apply reverse patch: {e}[/red]")
|
|
53
|
-
console.print("[yellow]The patch may conflict with current changes. Try resolving manually:[/yellow]")
|
|
54
|
-
console.print(f" git apply -R --3way {after_patch}")
|
|
55
|
-
raise typer.Exit(code=1)
|
|
56
|
-
except Exception as e:
|
|
57
|
-
console.print(f"[red]Failed to rollback: {e}[/red]")
|
|
58
|
-
raise typer.Exit(code=1)
|
|
37
|
+
console.print(f"Applying reverse patch from [bold]{after_patch}[/bold]...")
|
|
38
|
+
subprocess.check_call(
|
|
39
|
+
["git", "apply", "-R", str(after_patch)],
|
|
40
|
+
cwd=root,
|
|
41
|
+
)
|
|
42
|
+
console.print(f"[green]Successfully rolled back task {task_id} changes.[/green]")
|
|
43
|
+
else:
|
|
44
|
+
# No after-patch, but we have the before-patch — warn and offer manual reset
|
|
45
|
+
console.print(
|
|
46
|
+
f"[yellow]No after-patch found at {after_patch}.[/yellow]\n"
|
|
47
|
+
f"The before-patch at {checkpoint_file} captured the state before the task ran.\n"
|
|
48
|
+
f"To manually reset:\n"
|
|
49
|
+
f" 1. [bold]git stash[/bold] (if you want to keep current changes)\n"
|
|
50
|
+
f" 2. [bold]git checkout -- .[/bold] (discard working tree changes)\n"
|
|
51
|
+
f" 3. [bold]git apply {checkpoint_file}[/bold] (restore pre-task state)"
|
|
52
|
+
)
|
|
53
|
+
except subprocess.CalledProcessError as e:
|
|
54
|
+
console.print(f"[red]Failed to apply reverse patch: {e}[/red]")
|
|
55
|
+
console.print("[yellow]The patch may conflict with current changes. Try resolving manually:[/yellow]")
|
|
56
|
+
console.print(f" git apply -R --3way {after_patch}")
|
|
57
|
+
raise typer.Exit(code=1)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
console.print(f"[red]Failed to rollback: {e}[/red]")
|
|
60
|
+
raise typer.Exit(code=1)
|