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,70 +0,0 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
from pydantic import BaseModel
|
|
3
|
-
from devcouncil.domain.requirement import Requirement
|
|
4
|
-
from devcouncil.domain.assumption import Assumption
|
|
5
|
-
from devcouncil.llm.router import ModelRouter
|
|
6
|
-
|
|
7
|
-
class BlockingQuestion(BaseModel):
|
|
8
|
-
id: str
|
|
9
|
-
question: str
|
|
10
|
-
reason: str
|
|
11
|
-
|
|
12
|
-
class SpecOutput(BaseModel):
|
|
13
|
-
requirements: List[Requirement]
|
|
14
|
-
assumptions: List[Assumption]
|
|
15
|
-
blocking_questions: List[BlockingQuestion]
|
|
16
|
-
|
|
17
|
-
class SpecService:
|
|
18
|
-
def __init__(self, router: ModelRouter):
|
|
19
|
-
self.router = router
|
|
20
|
-
|
|
21
|
-
async def generate_spec(self, goal: str, repo_map_json: str) -> SpecOutput:
|
|
22
|
-
prompt = f"""
|
|
23
|
-
Goal: {goal}
|
|
24
|
-
|
|
25
|
-
Repository Map:
|
|
26
|
-
{repo_map_json}
|
|
27
|
-
|
|
28
|
-
Your task is to draft the initial software specification for this goal.
|
|
29
|
-
1. Identify functional and non-functional requirements.
|
|
30
|
-
2. Extract any assumptions you are making about the codebase or architecture.
|
|
31
|
-
3. List any blocking questions that the user must answer before implementation can proceed.
|
|
32
|
-
|
|
33
|
-
Each requirement MUST have clear, testable acceptance criteria with verification methods.
|
|
34
|
-
Be RIGOROUS about edge cases — a terse goal hides most of the real requirements.
|
|
35
|
-
For every behavior, add explicit acceptance criteria covering, where applicable:
|
|
36
|
-
- the normal/happy path with concrete example inputs and expected outputs;
|
|
37
|
-
- boundary and degenerate inputs (empty, single element, zero, negative, very large,
|
|
38
|
-
duplicate, already-sorted vs. unsorted, min/max);
|
|
39
|
-
- invalid or malformed inputs and the EXACT expected error behavior (e.g. raises
|
|
40
|
-
ValueError/TypeError) rather than silent or undefined behavior;
|
|
41
|
-
- non-mutation / no-unexpected-side-effects on inputs when the behavior is a pure
|
|
42
|
-
transformation;
|
|
43
|
-
- correct result TYPE (e.g. float vs int) when it matters.
|
|
44
|
-
Prefer several small, individually-verifiable acceptance criteria over one vague one.
|
|
45
|
-
|
|
46
|
-
Acceptance criteria MUST assert observable BEHAVIOR — return values, raised exceptions,
|
|
47
|
-
output, or side effects on supplied data — not repository state or tooling. DevCouncil's
|
|
48
|
-
own gates enforce file scope, clean diffs, and planned-file limits, so do NOT write
|
|
49
|
-
criteria about `git status`/`--porcelain` output, the exact set of changed/created files,
|
|
50
|
-
`git show HEAD` byte/append-only contents, commit shape, or whether flake8/mypy/ruff/
|
|
51
|
-
eslint/tsc/npm pass. Never require a tool the repo is not already configured for. Use the
|
|
52
|
-
`static_check` verification method ONLY for behavior expressible as a runnable assertion
|
|
53
|
-
(an importable function's result or raised exception), never to mean "a linter runs clean"
|
|
54
|
-
or "these files exist". If a criterion genuinely cannot be proven by running code
|
|
55
|
-
(architecture choices, repo scope, "works without extra configuration", subjective
|
|
56
|
-
quality), give it verification_method "manual" — it will be surfaced for human review
|
|
57
|
-
rather than block the automated gate. Prefer rewriting such a criterion as a concrete
|
|
58
|
-
behavioral one whenever possible.
|
|
59
|
-
|
|
60
|
-
Each assumption MUST have a confidence and impact level.
|
|
61
|
-
"""
|
|
62
|
-
messages = [
|
|
63
|
-
{"role": "user", "content": prompt}
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
return await self.router.complete_structured(
|
|
67
|
-
role="spec_writer",
|
|
68
|
-
messages=messages,
|
|
69
|
-
schema=SpecOutput
|
|
70
|
-
)
|
|
File without changes
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"""Scaffold a starter GitHub Actions workflow for a target repository.
|
|
2
|
-
|
|
3
|
-
DevCouncil already knows a project's test/lint/typecheck commands (config.yaml), so
|
|
4
|
-
it can emit a sensible CI starter that runs them. The workflow is a *template* the
|
|
5
|
-
user can adjust; scaffolding never overwrites an existing workflow unless forced.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
from devcouncil.app.config import load_config
|
|
13
|
-
|
|
14
|
-
WORKFLOW_RELPATH = Path(".github") / "workflows" / "devcouncil.yml"
|
|
15
|
-
|
|
16
|
-
_PYTHON_TOOLS = {
|
|
17
|
-
"pytest", "flake8", "ruff", "mypy", "tox", "python", "python3", "uv",
|
|
18
|
-
"poetry", "black", "isort", "pyright",
|
|
19
|
-
}
|
|
20
|
-
_NODE_TOOLS = {
|
|
21
|
-
"npm", "npx", "pnpm", "yarn", "bun", "eslint", "tsc", "jest", "vitest", "node",
|
|
22
|
-
}
|
|
23
|
-
_PYTHON_MARKERS = ("pyproject.toml", "requirements.txt", "setup.py", "setup.cfg", "Pipfile")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def detect_stacks(project_root: Path) -> set[str]:
|
|
27
|
-
"""Best-effort detection of the language stacks present in the repo."""
|
|
28
|
-
stacks: set[str] = set()
|
|
29
|
-
if any((project_root / marker).exists() for marker in _PYTHON_MARKERS):
|
|
30
|
-
stacks.add("python")
|
|
31
|
-
if (project_root / "package.json").exists():
|
|
32
|
-
stacks.add("node")
|
|
33
|
-
return stacks
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _command_stack(command: str) -> str | None:
|
|
37
|
-
tool = command.split()[0] if command.strip() else ""
|
|
38
|
-
if tool in _PYTHON_TOOLS:
|
|
39
|
-
return "python"
|
|
40
|
-
if tool in _NODE_TOOLS:
|
|
41
|
-
return "node"
|
|
42
|
-
return None
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _applicable_commands(commands: list[str], stacks: set[str]) -> list[str]:
|
|
46
|
-
"""Keep commands whose tool matches a detected stack; if none detected, keep all."""
|
|
47
|
-
if not stacks:
|
|
48
|
-
return list(commands)
|
|
49
|
-
kept = []
|
|
50
|
-
for command in commands:
|
|
51
|
-
stack = _command_stack(command)
|
|
52
|
-
if stack is None or stack in stacks:
|
|
53
|
-
kept.append(command)
|
|
54
|
-
return kept
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# Optional dependency-audit step per stack. Emitted only when the matching stack is
|
|
58
|
-
# detected, so a Python-only repo never gets an npm audit (and vice versa). These are
|
|
59
|
-
# non-blocking (continue-on-error) starters the user can tighten.
|
|
60
|
-
_AUDIT_STEPS: dict[str, list[str]] = {
|
|
61
|
-
"python": [
|
|
62
|
-
" - name: Dependency audit (pip-audit)",
|
|
63
|
-
" continue-on-error: true",
|
|
64
|
-
" run: pip-audit",
|
|
65
|
-
],
|
|
66
|
-
"node": [
|
|
67
|
-
" - name: Dependency audit (npm audit)",
|
|
68
|
-
" continue-on-error: true",
|
|
69
|
-
" run: npm audit --audit-level=high",
|
|
70
|
-
],
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _add_audit_steps(steps: list[str], stacks: set[str]) -> None:
|
|
75
|
-
"""Append an optional SCA audit step for each detected stack (only)."""
|
|
76
|
-
for stack in sorted(stacks):
|
|
77
|
-
steps.extend(_AUDIT_STEPS.get(stack, []))
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def _python_version(project_root: Path) -> str:
|
|
81
|
-
version_file = project_root / ".python-version"
|
|
82
|
-
if version_file.exists():
|
|
83
|
-
first = version_file.read_text(encoding="utf-8").strip().splitlines()
|
|
84
|
-
if first and first[0].strip():
|
|
85
|
-
return first[0].strip()
|
|
86
|
-
return "3.12"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def render_workflow(project_root: Path, default_branch: str = "main") -> str:
|
|
90
|
-
"""Render the workflow YAML text deterministically from config + detected stacks."""
|
|
91
|
-
config = load_config(project_root)
|
|
92
|
-
stacks = detect_stacks(project_root)
|
|
93
|
-
commands = config.commands
|
|
94
|
-
|
|
95
|
-
steps: list[str] = [
|
|
96
|
-
" - name: Checkout",
|
|
97
|
-
" uses: actions/checkout@v4",
|
|
98
|
-
]
|
|
99
|
-
if "python" in stacks:
|
|
100
|
-
steps += [
|
|
101
|
-
" - name: Set up Python",
|
|
102
|
-
" uses: actions/setup-python@v5",
|
|
103
|
-
" with:",
|
|
104
|
-
f' python-version: "{_python_version(project_root)}"',
|
|
105
|
-
]
|
|
106
|
-
if "node" in stacks:
|
|
107
|
-
steps += [
|
|
108
|
-
" - name: Set up Node",
|
|
109
|
-
" uses: actions/setup-node@v4",
|
|
110
|
-
" with:",
|
|
111
|
-
' node-version: "20"',
|
|
112
|
-
]
|
|
113
|
-
|
|
114
|
-
def add_command_steps(label: str, raw_commands: list[str]) -> None:
|
|
115
|
-
for command in _applicable_commands(raw_commands, stacks):
|
|
116
|
-
steps.append(f" - name: {label} ({command.split()[0]})")
|
|
117
|
-
steps.append(f" run: {command}")
|
|
118
|
-
|
|
119
|
-
add_command_steps("Lint", commands.lint)
|
|
120
|
-
add_command_steps("Typecheck", commands.typecheck)
|
|
121
|
-
add_command_steps("Test", commands.test)
|
|
122
|
-
_add_audit_steps(steps, stacks)
|
|
123
|
-
|
|
124
|
-
body = "\n".join(steps)
|
|
125
|
-
return (
|
|
126
|
-
"# Starter CI workflow generated by DevCouncil from .devcouncil/config.yaml.\n"
|
|
127
|
-
"# Adjust the setup steps, dependency install, and commands for your stack.\n"
|
|
128
|
-
"name: DevCouncil CI\n"
|
|
129
|
-
"\n"
|
|
130
|
-
"on:\n"
|
|
131
|
-
" push:\n"
|
|
132
|
-
f' branches: ["{default_branch}"]\n'
|
|
133
|
-
" pull_request:\n"
|
|
134
|
-
f' branches: ["{default_branch}"]\n'
|
|
135
|
-
"\n"
|
|
136
|
-
"jobs:\n"
|
|
137
|
-
" checks:\n"
|
|
138
|
-
" runs-on: ubuntu-latest\n"
|
|
139
|
-
" steps:\n"
|
|
140
|
-
f"{body}\n"
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def scaffold_ci(project_root: Path, force: bool = False) -> Path | None:
|
|
145
|
-
"""Write the starter workflow. Returns the path, or None if one already exists.
|
|
146
|
-
|
|
147
|
-
Does not overwrite an existing ``.github/workflows/devcouncil.yml`` unless
|
|
148
|
-
``force`` is set, so re-running is safe and user edits are preserved.
|
|
149
|
-
"""
|
|
150
|
-
project_root = project_root.resolve()
|
|
151
|
-
target = project_root / WORKFLOW_RELPATH
|
|
152
|
-
if target.exists() and not force:
|
|
153
|
-
return None
|
|
154
|
-
default_branch = load_config(project_root).project.default_branch or "main"
|
|
155
|
-
target.parent.mkdir(parents=True, exist_ok=True)
|
|
156
|
-
target.write_text(render_workflow(project_root, default_branch), encoding="utf-8")
|
|
157
|
-
return target
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
GITIGNORE_SECTIONS: tuple[tuple[str, tuple[str, ...]], ...] = (
|
|
7
|
-
(
|
|
8
|
-
"DevCouncil local state",
|
|
9
|
-
(
|
|
10
|
-
".devcouncil/*",
|
|
11
|
-
"!.devcouncil/",
|
|
12
|
-
"!.devcouncil/config.yaml",
|
|
13
|
-
"!.devcouncil/graphify.yaml",
|
|
14
|
-
),
|
|
15
|
-
),
|
|
16
|
-
(
|
|
17
|
-
"Local AI coding agents",
|
|
18
|
-
(
|
|
19
|
-
".agents/",
|
|
20
|
-
".codex/",
|
|
21
|
-
".aider*",
|
|
22
|
-
".gemini/",
|
|
23
|
-
".claude*",
|
|
24
|
-
".cursor/",
|
|
25
|
-
".openhands/",
|
|
26
|
-
".opencode/",
|
|
27
|
-
".conductor/",
|
|
28
|
-
".conducor/",
|
|
29
|
-
".antigravity/",
|
|
30
|
-
".warp/",
|
|
31
|
-
".gitnexus",
|
|
32
|
-
),
|
|
33
|
-
),
|
|
34
|
-
(
|
|
35
|
-
"Generated workspace guides (regenerated by 'dev map')",
|
|
36
|
-
(
|
|
37
|
-
"AGENTS.md",
|
|
38
|
-
"CLAUDE.md",
|
|
39
|
-
),
|
|
40
|
-
),
|
|
41
|
-
(
|
|
42
|
-
"Secrets and local databases",
|
|
43
|
-
(
|
|
44
|
-
"*.sqlite",
|
|
45
|
-
"*.sqlite-wal",
|
|
46
|
-
"*.sqlite-shm",
|
|
47
|
-
"*.db",
|
|
48
|
-
),
|
|
49
|
-
),
|
|
50
|
-
(
|
|
51
|
-
"Temporary, log, and dump artifacts",
|
|
52
|
-
(
|
|
53
|
-
"logs/",
|
|
54
|
-
"log/",
|
|
55
|
-
"tmp/",
|
|
56
|
-
"temp/",
|
|
57
|
-
".tmp/",
|
|
58
|
-
".temp/",
|
|
59
|
-
"scratch/",
|
|
60
|
-
"dumps/",
|
|
61
|
-
"dump/",
|
|
62
|
-
"*.tmp",
|
|
63
|
-
"*.temp",
|
|
64
|
-
"*.log",
|
|
65
|
-
"*.dmp",
|
|
66
|
-
"*.dump",
|
|
67
|
-
"*.bak",
|
|
68
|
-
"*.swp",
|
|
69
|
-
"*_results.txt",
|
|
70
|
-
"*_log.txt",
|
|
71
|
-
"*_output.txt",
|
|
72
|
-
),
|
|
73
|
-
),
|
|
74
|
-
(
|
|
75
|
-
"Environment, dependency, and cache directories",
|
|
76
|
-
(
|
|
77
|
-
"__pycache__/",
|
|
78
|
-
"*.py[cod]",
|
|
79
|
-
".venv/",
|
|
80
|
-
"venv/",
|
|
81
|
-
"node_modules/",
|
|
82
|
-
".env",
|
|
83
|
-
".env.local",
|
|
84
|
-
".env.*",
|
|
85
|
-
"!.env.example",
|
|
86
|
-
".pytest_cache/",
|
|
87
|
-
".mypy_cache/",
|
|
88
|
-
".ruff_cache/",
|
|
89
|
-
".DS_Store",
|
|
90
|
-
"Thumbs.db",
|
|
91
|
-
),
|
|
92
|
-
),
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def ensure_gitignore(project_root: Path) -> None:
|
|
97
|
-
gitignore_path = project_root / ".gitignore"
|
|
98
|
-
content = ""
|
|
99
|
-
if gitignore_path.exists():
|
|
100
|
-
try:
|
|
101
|
-
content = gitignore_path.read_text(encoding="utf-8")
|
|
102
|
-
except OSError:
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
existing_rules = {line.strip() for line in content.splitlines() if line.strip()}
|
|
106
|
-
chunks: list[str] = []
|
|
107
|
-
for heading, rules in GITIGNORE_SECTIONS:
|
|
108
|
-
missing_rules = [rule for rule in rules if rule not in existing_rules]
|
|
109
|
-
if missing_rules:
|
|
110
|
-
chunks.append("\n".join([f"# {heading}", *missing_rules]))
|
|
111
|
-
|
|
112
|
-
if not chunks:
|
|
113
|
-
return
|
|
114
|
-
|
|
115
|
-
prefix = ""
|
|
116
|
-
if content:
|
|
117
|
-
prefix = "" if content.endswith("\n") else "\n"
|
|
118
|
-
prefix += "\n"
|
|
119
|
-
|
|
120
|
-
try:
|
|
121
|
-
gitignore_path.write_text(content + prefix + "\n\n".join(chunks) + "\n", encoding="utf-8")
|
|
122
|
-
except OSError:
|
|
123
|
-
return
|