devcouncil 0.1.1 → 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.
Files changed (146) hide show
  1. package/README.md +218 -96
  2. package/bin/devcouncil.js +130 -32
  3. package/package.json +7 -11
  4. package/pyproject.toml +0 -34
  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 -158
  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 -143
  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/cli/__init__.py +0 -0
  23. package/src/devcouncil/cli/commands/__init__.py +0 -0
  24. package/src/devcouncil/cli/commands/artifacts.py +0 -51
  25. package/src/devcouncil/cli/commands/ast.py +0 -22
  26. package/src/devcouncil/cli/commands/baseline.py +0 -35
  27. package/src/devcouncil/cli/commands/config.py +0 -76
  28. package/src/devcouncil/cli/commands/dashboard.py +0 -26
  29. package/src/devcouncil/cli/commands/doctor.py +0 -140
  30. package/src/devcouncil/cli/commands/go.py +0 -237
  31. package/src/devcouncil/cli/commands/hook.py +0 -128
  32. package/src/devcouncil/cli/commands/init.py +0 -153
  33. package/src/devcouncil/cli/commands/integrate.py +0 -726
  34. package/src/devcouncil/cli/commands/lsp.py +0 -20
  35. package/src/devcouncil/cli/commands/map.py +0 -42
  36. package/src/devcouncil/cli/commands/mcp_server.py +0 -18
  37. package/src/devcouncil/cli/commands/plan.py +0 -327
  38. package/src/devcouncil/cli/commands/prompt.py +0 -50
  39. package/src/devcouncil/cli/commands/repair.py +0 -72
  40. package/src/devcouncil/cli/commands/report.py +0 -129
  41. package/src/devcouncil/cli/commands/reset_demo_state.py +0 -31
  42. package/src/devcouncil/cli/commands/rollback.py +0 -60
  43. package/src/devcouncil/cli/commands/run.py +0 -269
  44. package/src/devcouncil/cli/commands/setup.py +0 -223
  45. package/src/devcouncil/cli/commands/show.py +0 -76
  46. package/src/devcouncil/cli/commands/status.py +0 -117
  47. package/src/devcouncil/cli/commands/tasks.py +0 -55
  48. package/src/devcouncil/cli/commands/trace.py +0 -44
  49. package/src/devcouncil/cli/commands/verify.py +0 -193
  50. package/src/devcouncil/cli/commands/version.py +0 -20
  51. package/src/devcouncil/cli/commands/watch.py +0 -574
  52. package/src/devcouncil/cli/main.py +0 -88
  53. package/src/devcouncil/council/__init__.py +0 -0
  54. package/src/devcouncil/council/prompts/__init__.py +0 -0
  55. package/src/devcouncil/council/prompts/arbiter.md +0 -19
  56. package/src/devcouncil/council/prompts/critic_a.md +0 -10
  57. package/src/devcouncil/council/prompts/critic_b.md +0 -10
  58. package/src/devcouncil/council/prompts/implementation_reviewer.md +0 -16
  59. package/src/devcouncil/council/prompts/planner_a.md +0 -16
  60. package/src/devcouncil/council/prompts/planner_b.md +0 -16
  61. package/src/devcouncil/council/prompts/rebuttal.md +0 -10
  62. package/src/devcouncil/council/prompts/spec_writer.md +0 -12
  63. package/src/devcouncil/domain/__init__.py +0 -0
  64. package/src/devcouncil/domain/assumption.py +0 -17
  65. package/src/devcouncil/domain/critique.py +0 -32
  66. package/src/devcouncil/domain/evidence.py +0 -27
  67. package/src/devcouncil/domain/gap.py +0 -26
  68. package/src/devcouncil/domain/requirement.py +0 -22
  69. package/src/devcouncil/domain/task.py +0 -26
  70. package/src/devcouncil/execution/__init__.py +0 -1
  71. package/src/devcouncil/execution/context_builder.py +0 -60
  72. package/src/devcouncil/execution/executor.py +0 -15
  73. package/src/devcouncil/execution/hook_policy.py +0 -165
  74. package/src/devcouncil/execution/patch.py +0 -28
  75. package/src/devcouncil/execution/paths.py +0 -14
  76. package/src/devcouncil/execution/permissions.py +0 -92
  77. package/src/devcouncil/execution/prompt_builder.py +0 -59
  78. package/src/devcouncil/execution/task_runner.py +0 -166
  79. package/src/devcouncil/executors/__init__.py +0 -1
  80. package/src/devcouncil/executors/coding_cli.py +0 -112
  81. package/src/devcouncil/executors/mini_swe.py +0 -73
  82. package/src/devcouncil/executors/native/__init__.py +0 -0
  83. package/src/devcouncil/executors/native/agent.py +0 -107
  84. package/src/devcouncil/executors/openhands.py +0 -71
  85. package/src/devcouncil/gating/__init__.py +0 -1
  86. package/src/devcouncil/gating/checks/__init__.py +0 -0
  87. package/src/devcouncil/gating/checks/clean_git.py +0 -50
  88. package/src/devcouncil/gating/checks/planned_files_check.py +0 -32
  89. package/src/devcouncil/gating/checks/requirement_coverage.py +0 -26
  90. package/src/devcouncil/gating/checks/secret_scan_check.py +0 -34
  91. package/src/devcouncil/gating/policy.py +0 -190
  92. package/src/devcouncil/indexing/__init__.py +0 -1
  93. package/src/devcouncil/indexing/ast_matcher.py +0 -168
  94. package/src/devcouncil/indexing/graph_index.py +0 -48
  95. package/src/devcouncil/indexing/lsp.py +0 -120
  96. package/src/devcouncil/indexing/repo_mapper.py +0 -208
  97. package/src/devcouncil/indexing/symbol_index.py +0 -0
  98. package/src/devcouncil/integrations/code_review_graph.py +0 -163
  99. package/src/devcouncil/integrations/github.py +0 -39
  100. package/src/devcouncil/integrations/gitnexus.py +0 -27
  101. package/src/devcouncil/integrations/graphify.py +0 -34
  102. package/src/devcouncil/integrations/mcp/__init__.py +0 -0
  103. package/src/devcouncil/integrations/mcp/server.py +0 -599
  104. package/src/devcouncil/integrations/pr_comments.py +0 -62
  105. package/src/devcouncil/live/__init__.py +0 -2
  106. package/src/devcouncil/live/cards.py +0 -207
  107. package/src/devcouncil/live/models.py +0 -63
  108. package/src/devcouncil/live/repair_prompt.py +0 -83
  109. package/src/devcouncil/live/reviewer.py +0 -70
  110. package/src/devcouncil/live/signals.py +0 -135
  111. package/src/devcouncil/live/summary.py +0 -34
  112. package/src/devcouncil/live/tasks.py +0 -18
  113. package/src/devcouncil/live/transcripts.py +0 -138
  114. package/src/devcouncil/llm/__init__.py +0 -1
  115. package/src/devcouncil/llm/cache.py +0 -38
  116. package/src/devcouncil/llm/provider.py +0 -146
  117. package/src/devcouncil/llm/router.py +0 -125
  118. package/src/devcouncil/planning/__init__.py +0 -1
  119. package/src/devcouncil/planning/arbiter_service.py +0 -57
  120. package/src/devcouncil/planning/critique_service.py +0 -66
  121. package/src/devcouncil/planning/plan_service.py +0 -46
  122. package/src/devcouncil/planning/prompt_enhancer_service.py +0 -86
  123. package/src/devcouncil/planning/repair_service.py +0 -39
  124. package/src/devcouncil/planning/spec_service.py +0 -44
  125. package/src/devcouncil/repo/__init__.py +0 -0
  126. package/src/devcouncil/reporting/__init__.py +0 -0
  127. package/src/devcouncil/reporting/github_check.py +0 -32
  128. package/src/devcouncil/reporting/json_report.py +0 -20
  129. package/src/devcouncil/reporting/markdown_report.py +0 -68
  130. package/src/devcouncil/reporting/report_builder.py +0 -14
  131. package/src/devcouncil/storage/__init__.py +0 -0
  132. package/src/devcouncil/storage/db.py +0 -66
  133. package/src/devcouncil/storage/models.py +0 -83
  134. package/src/devcouncil/storage/repositories.py +0 -423
  135. package/src/devcouncil/telemetry/__init__.py +0 -0
  136. package/src/devcouncil/telemetry/cost.py +0 -34
  137. package/src/devcouncil/telemetry/traces.py +0 -91
  138. package/src/devcouncil/telemetry/tracker.py +0 -49
  139. package/src/devcouncil/ui/__init__.py +0 -1
  140. package/src/devcouncil/ui/dashboard.py +0 -122
  141. package/src/devcouncil/utils/__init__.py +0 -1
  142. package/src/devcouncil/utils/redaction.py +0 -141
  143. package/src/devcouncil/verification/__init__.py +0 -1
  144. package/src/devcouncil/verification/implementation_reviewer.py +0 -55
  145. package/src/devcouncil/verification/verifier.py +0 -530
  146. package/uv.lock +0 -1085
@@ -1,86 +0,0 @@
1
- from pydantic import BaseModel, Field
2
-
3
- from devcouncil.llm.router import ModelRouter
4
-
5
-
6
- class PromptEnhancement(BaseModel):
7
- original_goal: str
8
- enhanced_goal: str
9
- codebase_context: list[str] = Field(default_factory=list)
10
- debate_focus: list[str] = Field(default_factory=list)
11
- constraints: list[str] = Field(default_factory=list)
12
-
13
- def normalized(self, original_goal: str) -> "PromptEnhancement":
14
- enhanced_goal = self.enhanced_goal.strip() or original_goal
15
- return self.model_copy(
16
- update={
17
- "original_goal": original_goal,
18
- "enhanced_goal": enhanced_goal,
19
- "codebase_context": _clean_items(self.codebase_context),
20
- "debate_focus": _clean_items(self.debate_focus),
21
- "constraints": _clean_items(self.constraints),
22
- }
23
- )
24
-
25
- def debate_prompt(self) -> str:
26
- sections = [
27
- "# Enhanced Planning Prompt",
28
- "",
29
- "## Original user goal",
30
- self.original_goal,
31
- "",
32
- "## Codebase-specific goal",
33
- self.enhanced_goal,
34
- ]
35
- if self.codebase_context:
36
- sections.extend(["", "## Relevant codebase context"])
37
- sections.extend(f"- {item}" for item in self.codebase_context)
38
- if self.constraints:
39
- sections.extend(["", "## Constraints to preserve"])
40
- sections.extend(f"- {item}" for item in self.constraints)
41
- if self.debate_focus:
42
- sections.extend(["", "## Debate focus"])
43
- sections.extend(f"- {item}" for item in self.debate_focus)
44
- return "\n".join(sections)
45
-
46
-
47
- class PromptEnhancerService:
48
- def __init__(self, router: ModelRouter):
49
- self.router = router
50
-
51
- async def enhance_prompt(
52
- self,
53
- goal: str,
54
- repo_map_json: str,
55
- graph_context_json: str | None = None,
56
- ) -> PromptEnhancement:
57
- prompt = f"""
58
- Original user goal:
59
- {goal}
60
-
61
- Repository map:
62
- {repo_map_json}
63
-
64
- Code review graph context:
65
- {graph_context_json or "{}"}
66
-
67
- You are DevCouncil's codebase-specific prompt enhancer.
68
- Rewrite the user goal into a better planning prompt before it is sent to the council debate.
69
-
70
- Requirements:
71
- - Preserve the user's intent exactly; do not add unrelated features.
72
- - Make the goal specific to the mapped repository architecture, languages, tests, and likely ownership boundaries.
73
- - Identify constraints the planners and critics must preserve.
74
- - Identify debate focus areas that should force useful disagreement between pragmatic and production-readiness plans.
75
- - Keep the enhanced_goal concise enough to be used as the goal for spec, planning, critique, and arbitration.
76
- """
77
- enhancement = await self.router.complete_structured(
78
- role="prompt_enhancer",
79
- messages=[{"role": "user", "content": prompt}],
80
- schema=PromptEnhancement,
81
- )
82
- return enhancement.normalized(goal)
83
-
84
-
85
- def _clean_items(items: list[str]) -> list[str]:
86
- return [item.strip() for item in items if item.strip()]
@@ -1,39 +0,0 @@
1
- from typing import List
2
- import json
3
- from pydantic import BaseModel
4
- from devcouncil.domain.gap import Gap
5
- from devcouncil.domain.task import Task
6
- from devcouncil.llm.router import ModelRouter
7
-
8
- class RepairOutput(BaseModel):
9
- suggested_tasks: List[Task]
10
-
11
- class RepairService:
12
- """Uses LLM to infer focused repair tasks from blocking gaps."""
13
-
14
- def __init__(self, router: ModelRouter):
15
- self.router = router
16
-
17
- async def generate_repair_plan(self, gaps: List[Gap], project_context: str) -> RepairOutput:
18
- prompt = f"""
19
- The following blocking gaps were detected during verification.
20
- Gaps:
21
- {json.dumps([g.model_dump() for g in gaps], indent=2)}
22
-
23
- Project Context:
24
- {project_context}
25
-
26
- Your task is to generate focused implementation tasks to fix these gaps.
27
- - Each task must have a clear description and recommended fix.
28
- - Specify 'planned_files' that need modification (infer from gap evidence).
29
- - Link each task to the relevant 'requirement_id' mentioned in the gap.
30
-
31
- Return a JSON object with 'suggested_tasks'.
32
- """
33
- messages = [{"role": "user", "content": prompt}]
34
-
35
- return await self.router.complete_structured(
36
- role="planner_a", # Pragmatic tech lead is best suited for repair task generation
37
- messages=messages,
38
- schema=RepairOutput
39
- )
@@ -1,44 +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 acceptance criteria with verification methods.
34
- Each assumption MUST have a confidence and impact level.
35
- """
36
- messages = [
37
- {"role": "user", "content": prompt}
38
- ]
39
-
40
- return await self.router.complete_structured(
41
- role="spec_writer",
42
- messages=messages,
43
- schema=SpecOutput
44
- )
File without changes
File without changes
@@ -1,32 +0,0 @@
1
- from devcouncil.artifacts.graph import ArtifactGraph
2
-
3
- class GitHubCheckGenerator:
4
- """Generates GitHub Checks API payloads."""
5
-
6
- @staticmethod
7
- def generate(graph: ArtifactGraph) -> dict:
8
- summary = graph.coverage_summary()
9
- blocking_gaps = graph.blocking_gaps()
10
-
11
- status = "completed"
12
- conclusion = "failure" if summary["blocking_gaps"] > 0 else "success"
13
-
14
- text = f"**Requirements**: {summary['total_requirements']} | "
15
- text += f"**Tasks**: {summary['total_tasks']} | "
16
- text += f"**Gaps**: {summary['blocking_gaps']} blocking\n\n"
17
-
18
- if blocking_gaps:
19
- text += "### Blocking Gaps\n"
20
- for gap in blocking_gaps:
21
- text += f"- **{gap.id}**: {gap.description}\n"
22
-
23
- return {
24
- "name": "DevCouncil Verification",
25
- "status": status,
26
- "conclusion": conclusion,
27
- "output": {
28
- "title": f"DevCouncil: {conclusion.capitalize()}",
29
- "summary": f"Found {summary['blocking_gaps']} blocking gaps.",
30
- "text": text
31
- }
32
- }
@@ -1,20 +0,0 @@
1
- import json
2
- from devcouncil.artifacts.graph import ArtifactGraph
3
-
4
- class JsonReportGenerator:
5
- """Generates a JSON evidence report."""
6
-
7
- @staticmethod
8
- def generate(graph: ArtifactGraph, live_review: dict | None = None) -> str:
9
- summary = graph.coverage_summary()
10
- live_blockers = len((live_review or {}).get("blocking_cards", []))
11
-
12
- report = {
13
- "verdict": "blocked" if summary["blocking_gaps"] > 0 or live_blockers > 0 else "passed",
14
- "coverage_summary": summary,
15
- "blocking_gaps": [g.model_dump() for g in graph.blocking_gaps()]
16
- }
17
- if live_review is not None:
18
- report["live_review"] = live_review
19
-
20
- return json.dumps(report, indent=2)
@@ -1,68 +0,0 @@
1
- from devcouncil.artifacts.graph import ArtifactGraph
2
-
3
- class MarkdownReportGenerator:
4
- """Generates a Markdown evidence report."""
5
-
6
- MAX_INLINE_GAPS = 25
7
-
8
- @staticmethod
9
- def generate(graph: ArtifactGraph, live_review: dict | None = None) -> str:
10
- summary = graph.coverage_summary()
11
- live_blockers = (live_review or {}).get("blocking_cards", [])
12
-
13
- md_output = "# DevCouncil Report\n\n"
14
- md_output += "## Verdict\n"
15
- if summary["blocking_gaps"] > 0 or live_blockers:
16
- parts = []
17
- if summary["blocking_gaps"] > 0:
18
- parts.append(f"{summary['blocking_gaps']} high-severity gap(s)")
19
- if live_blockers:
20
- parts.append(f"{len(live_blockers)} live-review blocker(s)")
21
- md_output += f"**Blocked**: {', '.join(parts)} remain.\n\n"
22
- else:
23
- md_output += "**Passed**: Ready for release.\n\n"
24
-
25
- md_output += "## Coverage Summary\n"
26
- md_output += f"- **Requirements**: {summary['total_requirements']} ({summary['requirements_without_tasks']} unmapped)\n"
27
- md_output += f"- **Tasks**: {summary['total_tasks']} ({summary['tasks_without_requirements']} orphaned)\n"
28
- md_output += f"- **Evidence**: {summary['total_ac'] - summary['ac_without_evidence']}/{summary['total_ac']} AC verified\n\n"
29
-
30
- md_output += "## Requirements Coverage Table\n"
31
- md_output += "| Requirement | Task Mapping | Status |\n"
32
- md_output += "|---|---|---|\n"
33
-
34
- for req in graph.requirements.values():
35
- linked_tasks = [t for t in graph.tasks.values() if req.id in t.requirement_ids]
36
- task_str = ", ".join([t.id for t in linked_tasks]) if linked_tasks else "*None*"
37
- status_str = "Covered" if linked_tasks else "**Unmapped**"
38
- md_output += f"| {req.id} {req.title} | {task_str} | {status_str} |\n"
39
-
40
- md_output += "\n## Blocking Gaps\n"
41
- blocking_gaps = graph.blocking_gaps()
42
- if not blocking_gaps:
43
- md_output += "None.\n"
44
- else:
45
- for gap in blocking_gaps[:MarkdownReportGenerator.MAX_INLINE_GAPS]:
46
- md_output += f"### {gap.id}: {gap.description}\n"
47
- md_output += f"**Recommended fix**: {gap.recommended_fix}\n\n"
48
- if len(blocking_gaps) > MarkdownReportGenerator.MAX_INLINE_GAPS:
49
- remaining = len(blocking_gaps) - MarkdownReportGenerator.MAX_INLINE_GAPS
50
- md_output += f"_Omitted {remaining} additional blocking gap(s). Use JSON output for the full list._\n"
51
-
52
- if live_review is not None:
53
- md_output += "\n## Live Review\n"
54
- cards = live_review.get("cards", {})
55
- md_output += f"- **Pending signals**: {live_review.get('pending_signals', 0)}\n"
56
- md_output += f"- **Open cards**: {cards.get('open', 0)}\n"
57
- md_output += f"- **Open critical cards**: {cards.get('critical_open', 0)}\n"
58
- if not live_blockers:
59
- md_output += "- **Blocking cards in scope**: None.\n"
60
- else:
61
- md_output += "\n### Blocking Live-Review Cards\n"
62
- for card in live_blockers[:MarkdownReportGenerator.MAX_INLINE_GAPS]:
63
- md_output += f"- **{card['id']}**"
64
- if card.get("task_id"):
65
- md_output += f" (`{card['task_id']}`)"
66
- md_output += f": {card['summary']}\n"
67
-
68
- return md_output
@@ -1,14 +0,0 @@
1
- from devcouncil.artifacts.graph import ArtifactGraph
2
- from devcouncil.reporting.markdown_report import MarkdownReportGenerator
3
- from devcouncil.reporting.json_report import JsonReportGenerator
4
-
5
- class ReportBuilder:
6
- """Builds reports in various formats from the artifact graph."""
7
-
8
- @staticmethod
9
- def build_markdown(graph: ArtifactGraph, live_review: dict | None = None) -> str:
10
- return MarkdownReportGenerator.generate(graph, live_review=live_review)
11
-
12
- @staticmethod
13
- def build_json(graph: ArtifactGraph, live_review: dict | None = None) -> str:
14
- return JsonReportGenerator.generate(graph, live_review=live_review)
File without changes
@@ -1,66 +0,0 @@
1
- from contextlib import contextmanager
2
-
3
- from sqlmodel import SQLModel, create_engine, Session
4
- from sqlalchemy.exc import OperationalError
5
- from pathlib import Path
6
- from typing import Optional
7
-
8
- from devcouncil.storage.models import SchemaVersionModel
9
-
10
-
11
- SCHEMA_VERSION = 1
12
-
13
-
14
- class Database:
15
- def __init__(self, db_path: Path):
16
- self.db_path = db_path
17
- self.engine = create_engine(f"sqlite:///{db_path}")
18
-
19
- def create_db_and_tables(self):
20
- self._create_tables()
21
- self.ensure_schema_version()
22
-
23
- def ensure_schema_version(self):
24
- self._create_tables()
25
- with Session(self.engine) as session:
26
- current = session.get(SchemaVersionModel, "singleton")
27
- if current is None:
28
- session.add(SchemaVersionModel(id="singleton", version=SCHEMA_VERSION))
29
- session.commit()
30
- return
31
- if current.version != SCHEMA_VERSION:
32
- raise RuntimeError(
33
- f"Unsupported DevCouncil schema version {current.version}; "
34
- f"expected {SCHEMA_VERSION}."
35
- )
36
-
37
- def _create_tables(self):
38
- try:
39
- SQLModel.metadata.create_all(self.engine)
40
- except OperationalError as exc:
41
- if "already exists" not in str(exc):
42
- raise
43
-
44
- @contextmanager
45
- def get_session(self):
46
- """Yield a session with automatic commit/rollback/close."""
47
- session = Session(self.engine)
48
- try:
49
- yield session
50
- session.commit()
51
- except Exception:
52
- session.rollback()
53
- raise
54
- finally:
55
- session.close()
56
-
57
-
58
- def get_db(project_root: Path = Path(".")) -> Optional[Database]:
59
- dev_dir = project_root / ".devcouncil"
60
- if not dev_dir.exists():
61
- return None
62
-
63
- db_path = dev_dir / "state.sqlite"
64
- db = Database(db_path)
65
- db.ensure_schema_version()
66
- return db
@@ -1,83 +0,0 @@
1
- from sqlmodel import SQLModel, Field
2
- from typing import Optional
3
-
4
-
5
- class SchemaVersionModel(SQLModel, table=True):
6
- __tablename__ = "schema_version"
7
- id: str = Field(primary_key=True, default="singleton")
8
- version: int
9
-
10
- class RequirementModel(SQLModel, table=True):
11
- __tablename__ = "requirements"
12
- id: str = Field(primary_key=True)
13
- title: str
14
- description: str
15
- priority: str
16
- source: str
17
- # Store complex lists as JSON strings for simplicity in SQLite
18
- acceptance_criteria_json: str = Field(default="[]")
19
-
20
- class AssumptionModel(SQLModel, table=True):
21
- __tablename__ = "assumptions"
22
- id: str = Field(primary_key=True)
23
- statement: str
24
- confidence: str
25
- impact: str
26
- reversible: bool
27
- requires_user_confirmation: bool
28
- linked_requirement_ids_json: str = Field(default="[]")
29
- status: str = "open"
30
-
31
- class TaskModel(SQLModel, table=True):
32
- __tablename__ = "tasks"
33
- id: str = Field(primary_key=True)
34
- title: str
35
- description: str
36
- requirement_ids_json: str = Field(default="[]")
37
- acceptance_criterion_ids_json: str = Field(default="[]")
38
- planned_files_json: str = Field(default="[]")
39
- expected_tests_json: str = Field(default="[]")
40
- allowed_commands_json: str = Field(default="[]")
41
- forbidden_changes_json: str = Field(default="[]")
42
- status: str = "planned"
43
-
44
- class EvidenceModel(SQLModel, table=True):
45
- __tablename__ = "evidence"
46
- id: Optional[int] = Field(default=None, primary_key=True)
47
- type: str # "command", "diff", "test"
48
- task_id: Optional[str] = None
49
- requirement_id: Optional[str] = None
50
- acceptance_criterion_id: Optional[str] = None
51
- data_json: str
52
-
53
- class GapModel(SQLModel, table=True):
54
- __tablename__ = "gaps"
55
- id: str = Field(primary_key=True)
56
- severity: str
57
- gap_type: str
58
- requirement_id: Optional[str] = None
59
- task_id: Optional[str] = None
60
- description: str
61
- evidence_json: str = Field(default="[]")
62
- recommended_fix: str
63
- blocking: bool
64
-
65
- class CritiqueFindingModel(SQLModel, table=True):
66
- __tablename__ = "critique_findings"
67
- id: str = Field(primary_key=True)
68
- source_agent: str
69
- target_plan_id: str
70
- severity: str
71
- finding_type: str
72
- claim: str
73
- linked_requirement_id: Optional[str] = None
74
- suggested_requirement: Optional[str] = None
75
- suggested_task: Optional[str] = None
76
- falsifiable_check: str
77
- status: str = "open"
78
-
79
- class ProjectStateModel(SQLModel, table=True):
80
- __tablename__ = "project_state"
81
- id: str = Field(primary_key=True, default="singleton")
82
- current_phase: str
83
- history_json: str = Field(default="[]")