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,205 +0,0 @@
|
|
|
1
|
-
"""Semantic snapshots and diff classification."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import ast
|
|
6
|
-
import hashlib
|
|
7
|
-
import json
|
|
8
|
-
import re
|
|
9
|
-
from datetime import datetime, timezone
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
from devcouncil.indexing.ast_matcher import AstMatcher
|
|
13
|
-
from devcouncil.indexing.lsp import LspInspector
|
|
14
|
-
from devcouncil.storage.db import get_db
|
|
15
|
-
from devcouncil.storage.native import SemanticDiffRepository
|
|
16
|
-
|
|
17
|
-
_CONFIG_FILES = {
|
|
18
|
-
"pyproject.toml",
|
|
19
|
-
"package.json",
|
|
20
|
-
"uv.lock",
|
|
21
|
-
"schema.prisma",
|
|
22
|
-
"docker-compose.yml",
|
|
23
|
-
"Dockerfile",
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class SemanticIndex:
|
|
28
|
-
def __init__(self, project_root: Path):
|
|
29
|
-
self.project_root = project_root.resolve()
|
|
30
|
-
self.semantic_dir = self.project_root / ".devcouncil" / "semantic"
|
|
31
|
-
self.matcher = AstMatcher(self.project_root)
|
|
32
|
-
|
|
33
|
-
def snapshot_path(self, task_id: str, stage: str) -> Path:
|
|
34
|
-
return self.semantic_dir / task_id / f"{stage}.json"
|
|
35
|
-
|
|
36
|
-
def create_snapshot(self, task_id: str, stage: str) -> Path:
|
|
37
|
-
symbols = self._collect_symbols()
|
|
38
|
-
payload = {
|
|
39
|
-
"task_id": task_id,
|
|
40
|
-
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
41
|
-
"repo_map_path": str(self.project_root / ".devcouncil" / "repo_map.json"),
|
|
42
|
-
"files": self._config_file_entries(),
|
|
43
|
-
"source_files": self._source_file_entries(),
|
|
44
|
-
"symbols": symbols,
|
|
45
|
-
"imports": self._collect_imports(),
|
|
46
|
-
"public_symbols": [s for s in symbols if s.get("public")],
|
|
47
|
-
"lsp": json.loads(LspInspector(self.project_root).summary_json()),
|
|
48
|
-
}
|
|
49
|
-
path = self.snapshot_path(task_id, stage)
|
|
50
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
51
|
-
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
52
|
-
return path
|
|
53
|
-
|
|
54
|
-
def diff(self, task_id: str) -> dict:
|
|
55
|
-
before_path = self.snapshot_path(task_id, "before")
|
|
56
|
-
after_path = self.snapshot_path(task_id, "after")
|
|
57
|
-
if not after_path.exists():
|
|
58
|
-
self.create_snapshot(task_id, "after")
|
|
59
|
-
before = json.loads(before_path.read_text(encoding="utf-8")) if before_path.exists() else {}
|
|
60
|
-
after = json.loads(after_path.read_text(encoding="utf-8"))
|
|
61
|
-
classifications = self._classify(before, after)
|
|
62
|
-
summary = ", ".join(item["type"] for item in classifications) or "no semantic changes"
|
|
63
|
-
db = get_db(self.project_root)
|
|
64
|
-
if db:
|
|
65
|
-
with db.get_session() as session:
|
|
66
|
-
SemanticDiffRepository(session).save(
|
|
67
|
-
task_id,
|
|
68
|
-
str(before_path),
|
|
69
|
-
str(after_path),
|
|
70
|
-
classifications,
|
|
71
|
-
summary,
|
|
72
|
-
)
|
|
73
|
-
return {"classifications": classifications, "summary": summary}
|
|
74
|
-
|
|
75
|
-
def _collect_symbols(self) -> list[dict]:
|
|
76
|
-
symbols: list[dict] = []
|
|
77
|
-
for match in self.matcher.match(limit=500):
|
|
78
|
-
symbols.append({
|
|
79
|
-
"path": match.path,
|
|
80
|
-
"language": match.language,
|
|
81
|
-
"kind": match.kind,
|
|
82
|
-
"name": match.name,
|
|
83
|
-
"line": match.line,
|
|
84
|
-
"signature": match.text.strip(),
|
|
85
|
-
"public": match.name[:1].isupper() or "export" in match.text,
|
|
86
|
-
})
|
|
87
|
-
return symbols
|
|
88
|
-
|
|
89
|
-
def _collect_imports(self) -> list[dict]:
|
|
90
|
-
imports: list[dict] = []
|
|
91
|
-
for path in self.project_root.rglob("*"):
|
|
92
|
-
if not path.is_file() or path.suffix not in {".py", ".ts", ".tsx", ".js", ".go", ".rs"}:
|
|
93
|
-
continue
|
|
94
|
-
rel = path.relative_to(self.project_root).as_posix()
|
|
95
|
-
# Filter on the path relative to the project root — filtering on the
|
|
96
|
-
# absolute path would skip everything when the repo itself lives
|
|
97
|
-
# under a dot-directory.
|
|
98
|
-
rel_parts = Path(rel).parts
|
|
99
|
-
if self._is_ignored_path(rel) or any(part.startswith(".") for part in rel_parts):
|
|
100
|
-
continue
|
|
101
|
-
source = path.read_text(encoding="utf-8", errors="replace")
|
|
102
|
-
if path.suffix == ".py":
|
|
103
|
-
try:
|
|
104
|
-
tree = ast.parse(source)
|
|
105
|
-
except (SyntaxError, ValueError):
|
|
106
|
-
continue
|
|
107
|
-
for node in ast.walk(tree):
|
|
108
|
-
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
109
|
-
segment = ast.get_source_segment(source, node) or ""
|
|
110
|
-
imports.append({"path": rel, "statement": segment})
|
|
111
|
-
else:
|
|
112
|
-
for line in source.splitlines():
|
|
113
|
-
if re.match(r"^\s*(import|from)\s+", line):
|
|
114
|
-
imports.append({"path": rel, "statement": line.strip()})
|
|
115
|
-
return imports
|
|
116
|
-
|
|
117
|
-
def _classify(self, before: dict, after: dict) -> list[dict]:
|
|
118
|
-
before_symbols = {(s["path"], s["name"]): s for s in before.get("symbols", [])}
|
|
119
|
-
after_symbols = {(s["path"], s["name"]): s for s in after.get("symbols", [])}
|
|
120
|
-
results: list[dict] = []
|
|
121
|
-
|
|
122
|
-
for key, after_sym in after_symbols.items():
|
|
123
|
-
before_sym = before_symbols.get(key)
|
|
124
|
-
path = after_sym["path"]
|
|
125
|
-
if path.startswith("tests/") or "/test_" in path:
|
|
126
|
-
results.append({"type": "test_only_change", "path": path, "name": after_sym["name"]})
|
|
127
|
-
continue
|
|
128
|
-
if Path(path).name in _CONFIG_FILES:
|
|
129
|
-
results.append({"type": "config_schema_dependency_change", "path": path, "name": after_sym["name"]})
|
|
130
|
-
continue
|
|
131
|
-
if before_sym is None and after_sym.get("public"):
|
|
132
|
-
results.append({"type": "exported_symbol_added", "path": path, "name": after_sym["name"]})
|
|
133
|
-
elif before_sym and before_sym.get("signature") != after_sym.get("signature"):
|
|
134
|
-
if after_sym.get("public"):
|
|
135
|
-
results.append({"type": "public_api_change", "path": path, "name": after_sym["name"]})
|
|
136
|
-
else:
|
|
137
|
-
results.append({"type": "private_implementation_change", "path": path, "name": after_sym["name"]})
|
|
138
|
-
|
|
139
|
-
before_imports = {(i["path"], i["statement"]) for i in before.get("imports", [])}
|
|
140
|
-
after_imports = {(i["path"], i["statement"]) for i in after.get("imports", [])}
|
|
141
|
-
for added in after_imports - before_imports:
|
|
142
|
-
results.append({"type": "import_dependency_change", "path": added[0], "statement": added[1]})
|
|
143
|
-
|
|
144
|
-
for key in before_symbols:
|
|
145
|
-
if key not in after_symbols and before_symbols[key].get("public"):
|
|
146
|
-
results.append({
|
|
147
|
-
"type": "exported_symbol_removed",
|
|
148
|
-
"path": before_symbols[key]["path"],
|
|
149
|
-
"name": before_symbols[key]["name"],
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
before_files = {item["path"]: item.get("content", "") for item in before.get("files", []) if isinstance(item, dict)}
|
|
153
|
-
after_files = {item["path"]: item.get("content", "") for item in after.get("files", []) if isinstance(item, dict)}
|
|
154
|
-
for path, content in after_files.items():
|
|
155
|
-
if Path(path).name in _CONFIG_FILES and before_files.get(path) != content:
|
|
156
|
-
results.append({"type": "config_schema_dependency_change", "path": path})
|
|
157
|
-
|
|
158
|
-
classified_paths = {item.get("path") for item in results}
|
|
159
|
-
before_source = {
|
|
160
|
-
item["path"]: item.get("sha256", "")
|
|
161
|
-
for item in before.get("source_files", [])
|
|
162
|
-
if isinstance(item, dict)
|
|
163
|
-
}
|
|
164
|
-
after_source = {
|
|
165
|
-
item["path"]: item.get("sha256", "")
|
|
166
|
-
for item in after.get("source_files", [])
|
|
167
|
-
if isinstance(item, dict)
|
|
168
|
-
}
|
|
169
|
-
for path, digest in after_source.items():
|
|
170
|
-
if path in classified_paths:
|
|
171
|
-
continue
|
|
172
|
-
if before_source.get(path) == digest:
|
|
173
|
-
continue
|
|
174
|
-
if path.startswith("tests/") or "/test_" in path:
|
|
175
|
-
results.append({"type": "test_only_change", "path": path})
|
|
176
|
-
elif Path(path).name not in _CONFIG_FILES:
|
|
177
|
-
results.append({"type": "private_implementation_change", "path": path})
|
|
178
|
-
return results
|
|
179
|
-
|
|
180
|
-
def _config_file_entries(self) -> list[dict]:
|
|
181
|
-
entries: list[dict] = []
|
|
182
|
-
for name in _CONFIG_FILES:
|
|
183
|
-
path = self.project_root / name
|
|
184
|
-
if path.exists():
|
|
185
|
-
entries.append({
|
|
186
|
-
"path": name,
|
|
187
|
-
"content": path.read_text(encoding="utf-8", errors="replace"),
|
|
188
|
-
})
|
|
189
|
-
return entries
|
|
190
|
-
|
|
191
|
-
def _source_file_entries(self) -> list[dict]:
|
|
192
|
-
entries: list[dict] = []
|
|
193
|
-
for path in self.project_root.rglob("*"):
|
|
194
|
-
if not path.is_file() or path.suffix not in {".py", ".ts", ".tsx", ".js", ".go", ".rs"}:
|
|
195
|
-
continue
|
|
196
|
-
rel = path.relative_to(self.project_root).as_posix()
|
|
197
|
-
if self._is_ignored_path(rel):
|
|
198
|
-
continue
|
|
199
|
-
raw = path.read_bytes()
|
|
200
|
-
entries.append({"path": rel, "sha256": hashlib.sha256(raw).hexdigest()})
|
|
201
|
-
return sorted(entries, key=lambda item: item["path"])
|
|
202
|
-
|
|
203
|
-
def _is_ignored_path(self, rel_path: str) -> bool:
|
|
204
|
-
ignored_parts = {".git", ".devcouncil", "__pycache__", ".venv", "node_modules", "dist", "build", "target", "vendor"}
|
|
205
|
-
return any(part in ignored_parts for part in Path(rel_path).parts)
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import shutil
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
from devcouncil.integrations.check import build_integration_check_report
|
|
10
|
-
|
|
11
|
-
VALID_INTEGRATION_TARGETS = {
|
|
12
|
-
"all",
|
|
13
|
-
"hooks",
|
|
14
|
-
"codex",
|
|
15
|
-
"gemini",
|
|
16
|
-
"claude",
|
|
17
|
-
"cursor",
|
|
18
|
-
"opencode",
|
|
19
|
-
"antigravity",
|
|
20
|
-
"agy",
|
|
21
|
-
"warp",
|
|
22
|
-
"aider",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
TARGET_ALIASES = {
|
|
26
|
-
"agy": "antigravity",
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclass(frozen=True)
|
|
31
|
-
class IntegrationActionReport:
|
|
32
|
-
target: str
|
|
33
|
-
ok: bool
|
|
34
|
-
results: list[dict[str, Any]]
|
|
35
|
-
warnings: list[str]
|
|
36
|
-
check: dict[str, Any]
|
|
37
|
-
|
|
38
|
-
def as_dict(self) -> dict[str, Any]:
|
|
39
|
-
return {
|
|
40
|
-
"target": self.target,
|
|
41
|
-
"ok": self.ok,
|
|
42
|
-
"results": self.results,
|
|
43
|
-
"warnings": self.warnings,
|
|
44
|
-
"check": self.check,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
def to_json(self, *, indent: int | None = 2) -> str:
|
|
48
|
-
return json.dumps(self.as_dict(), indent=indent)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def normalize_apply_target(target: str) -> str:
|
|
52
|
-
normalized = (target or "").strip().lower().replace("_", "-")
|
|
53
|
-
normalized = TARGET_ALIASES.get(normalized, normalized)
|
|
54
|
-
if normalized not in VALID_INTEGRATION_TARGETS:
|
|
55
|
-
allowed = ", ".join(sorted(VALID_INTEGRATION_TARGETS))
|
|
56
|
-
raise ValueError(f"Unsupported integration target '{target}'. Expected one of: {allowed}.")
|
|
57
|
-
return normalized
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def apply_integration_target(
|
|
61
|
-
project_root: Path,
|
|
62
|
-
target: str,
|
|
63
|
-
*,
|
|
64
|
-
include_hooks: bool = True,
|
|
65
|
-
strict: bool = False,
|
|
66
|
-
gemini_scope: str = "project",
|
|
67
|
-
claude_scope: str = "local",
|
|
68
|
-
) -> IntegrationActionReport:
|
|
69
|
-
from devcouncil.cli.commands import integrate
|
|
70
|
-
|
|
71
|
-
root = project_root.expanduser().resolve()
|
|
72
|
-
normalized = normalize_apply_target(target)
|
|
73
|
-
warnings: list[str] = []
|
|
74
|
-
results: list[dict[str, Any]] = []
|
|
75
|
-
|
|
76
|
-
def add_result(name: str, ok: bool, path: Path | None = None, message: str = "") -> None:
|
|
77
|
-
results.append({
|
|
78
|
-
"target": name,
|
|
79
|
-
"ok": ok,
|
|
80
|
-
"path": str(path.relative_to(root)) if path and path.is_relative_to(root) else (str(path) if path else ""),
|
|
81
|
-
"message": message,
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
def apply_first_party(name: str) -> None:
|
|
85
|
-
command_builders = {
|
|
86
|
-
"codex": integrate._codex_command,
|
|
87
|
-
"gemini": lambda project: integrate._gemini_command(project, gemini_scope),
|
|
88
|
-
"claude": lambda project: integrate._claude_command(project, claude_scope),
|
|
89
|
-
}
|
|
90
|
-
command = command_builders[name](root)
|
|
91
|
-
if not shutil.which(command[0]):
|
|
92
|
-
warning = f"{name} CLI not found on PATH; skipped CLI MCP registration."
|
|
93
|
-
warnings.append(warning)
|
|
94
|
-
integrate.console.print(f"[yellow]{name} CLI not found on PATH. Skipping optional integration.[/yellow]")
|
|
95
|
-
add_result(name, True, None, "CLI not found; skipped optional global/client registration.")
|
|
96
|
-
return
|
|
97
|
-
code = integrate._run(command)
|
|
98
|
-
add_result(name, code == 0, None, "MCP registration command exited " + str(code))
|
|
99
|
-
|
|
100
|
-
def apply_project_file(name: str) -> None:
|
|
101
|
-
writers = {
|
|
102
|
-
"cursor": integrate._write_cursor_config,
|
|
103
|
-
"opencode": integrate._write_opencode_config,
|
|
104
|
-
"antigravity": integrate._write_antigravity_mcp_config,
|
|
105
|
-
"warp": integrate._write_warp_mcp_config,
|
|
106
|
-
}
|
|
107
|
-
recorders = {
|
|
108
|
-
"cursor": integrate._record_cursor_config,
|
|
109
|
-
"opencode": integrate._record_opencode_config,
|
|
110
|
-
"antigravity": integrate._record_antigravity_config,
|
|
111
|
-
"warp": integrate._record_warp_config,
|
|
112
|
-
}
|
|
113
|
-
path = writers[name](root)
|
|
114
|
-
recorders[name](root)
|
|
115
|
-
add_result(name, True, path, "Project integration config written.")
|
|
116
|
-
|
|
117
|
-
def apply_aider() -> None:
|
|
118
|
-
integrate._record_aider_config(root)
|
|
119
|
-
add_result("aider", True, root / ".devcouncil" / "config.yaml", "Aider executor enabled.")
|
|
120
|
-
|
|
121
|
-
def apply_hooks() -> None:
|
|
122
|
-
integrate._configure_native_hooks(root, "all", apply=True)
|
|
123
|
-
add_result("hooks", True, None, "Native hook files configured.")
|
|
124
|
-
|
|
125
|
-
if normalized == "all":
|
|
126
|
-
for name in ("codex", "gemini", "claude"):
|
|
127
|
-
apply_first_party(name)
|
|
128
|
-
# Batch the per-tool config.yaml record updates into one load/save.
|
|
129
|
-
with integrate._batched_raw_config(root):
|
|
130
|
-
for name in ("cursor", "opencode", "antigravity", "warp"):
|
|
131
|
-
apply_project_file(name)
|
|
132
|
-
apply_aider()
|
|
133
|
-
if include_hooks:
|
|
134
|
-
apply_hooks()
|
|
135
|
-
elif normalized in {"codex", "gemini", "claude"}:
|
|
136
|
-
apply_first_party(normalized)
|
|
137
|
-
elif normalized in {"cursor", "opencode", "antigravity", "warp"}:
|
|
138
|
-
apply_project_file(normalized)
|
|
139
|
-
elif normalized == "aider":
|
|
140
|
-
apply_aider()
|
|
141
|
-
elif normalized == "hooks":
|
|
142
|
-
apply_hooks()
|
|
143
|
-
|
|
144
|
-
check = build_integration_check_report(root, strict=strict).as_dict()
|
|
145
|
-
ok = all(item["ok"] for item in results) and (not strict or bool(check["ok"]))
|
|
146
|
-
return IntegrationActionReport(normalized, ok, results, warnings, check)
|