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,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