agileforagents 0.3.0__tar.gz

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.
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: agileforagents
3
+ Version: 0.3.0
4
+ Summary: Zero-config MCP server that turns prompts into verified agile task boards for AI coding agents
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/FAJU85/AgileForAgents
7
+ Project-URL: Repository, https://github.com/FAJU85/AgileForAgents
8
+ Project-URL: Issues, https://github.com/FAJU85/AgileForAgents/issues
9
+ Keywords: mcp,agile,ai-agents,claude-code,cursor,codex
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Build Tools
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: mcp>=1.9.0
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
24
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
25
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
27
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
28
+ Requires-Dist: pip-audit>=2.7.0; extra == "dev"
29
+
30
+ # AgileForAgents
31
+
32
+ **Turn any project prompt into a verified, sprint-ready agile task board — right inside your AI coding agent.**
33
+
34
+ AgileForAgents is a zero-config [MCP](https://modelcontextprotocol.io) server for
35
+ Claude Code, Cursor, Codex CLI, and any MCP-compatible host. You describe a project
36
+ in your IDE as you normally would; AgileForAgents turns it into a structured agile
37
+ board (epics → sprints → user stories → subtasks) where **every subtask has an
38
+ observable done-criteria** and **every assumption is made explicit** — following
39
+ Andrej Karpathy's Spec methodology.
40
+
41
+ ## How it works
42
+
43
+ ```
44
+ ┌──────────────────────────┐ stdio ┌────────────────────────┐ HTTPS ┌──────────────────────────────┐
45
+ │ Your IDE │◀──────────▶│ agileforagents │───────────▶│ Hosted backend (HF Space) │
46
+ │ Claude Code / Cursor / │ MCP tool │ MCP server │ /api/ │ • runs a free HF model │
47
+ │ Codex — you type here │ call │ (stdlib-only client) │ generate │ • holds the token server-side│
48
+ └──────────────────────────┘ └────────────────────────┘◀───────────│ • returns a verified board │
49
+ └───────────────┬──────────────┘
50
+ │ logs metrics
51
+
52
+ ┌──────────────────────────┐
53
+ │ Gradio Control Panel │
54
+ │ (monitoring dashboard, │
55
+ │ no prompt input) │
56
+ └──────────────────────────┘
57
+ ```
58
+
59
+ - **You never leave your IDE.** Prompts come from your editor through the MCP tool —
60
+ not from a web form.
61
+ - **No API key, no token, no heavy dependencies.** The MCP server installs with just
62
+ the MCP SDK + pydantic. All model inference happens on the hosted backend, which
63
+ holds the Hugging Face token as a server-side secret.
64
+ - **The Gradio Space is a control panel**, not an input form — it shows how the
65
+ generator is performing (volume, average verification score, recent activity).
66
+ - **Works offline, degraded.** If the backend is unreachable, a local heuristic
67
+ fallback still returns a valid scaffold board so the tool never hard-fails.
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ pip install agileforagents
73
+ ```
74
+
75
+ Then register the `agileforagents` stdio server with your MCP host.
76
+
77
+ **Claude Code** (`~/.claude/mcp.json` or project `.mcp.json`):
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "agileforagents": {
83
+ "command": "agileforagents"
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ **Cursor / Codex CLI:** point your MCP config at the `agileforagents` command. No
90
+ environment variables are required.
91
+
92
+ ## Tools
93
+
94
+ | Tool | What it does |
95
+ |---|---|
96
+ | `generate_agile_tasks` | Turn a prompt into a verified agile board. Args: `prompt`, `format` (`claude` \| `json` \| `cursor`), `detail_level` (`standard` \| `detailed`). |
97
+ | `get_agent_rules` | Return the AgileForAgents working protocol to paste into `CLAUDE.md` / `.cursorrules`. |
98
+ | `feedback_stats` | Local generation count + backend health. |
99
+
100
+ ## Configuration (optional)
101
+
102
+ The MCP server works with no configuration. For self-hosting or testing you can override:
103
+
104
+ | Env var | Purpose | Default |
105
+ |---|---|---|
106
+ | `AFA_BACKEND_URL` | Hosted backend base URL | `https://vooom-agileforagents.hf.space` |
107
+ | `AFA_BACKEND_TIMEOUT` | Request timeout (seconds) | `120` |
108
+
109
+ ## Repository layout
110
+
111
+ | Path | Purpose |
112
+ |---|---|
113
+ | `src/agileforagents/` | The installable stdio MCP server (thin client + formatters + fallback). |
114
+ | `space/` | The Hugging Face Space: FastAPI inference backend + Gradio monitoring dashboard. |
115
+ | `tests/` | Unit + E2E tests. |
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ pip install -e ".[dev]"
121
+ make format-check && make lint && make typecheck && make test
122
+ ```
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,97 @@
1
+ # AgileForAgents
2
+
3
+ **Turn any project prompt into a verified, sprint-ready agile task board — right inside your AI coding agent.**
4
+
5
+ AgileForAgents is a zero-config [MCP](https://modelcontextprotocol.io) server for
6
+ Claude Code, Cursor, Codex CLI, and any MCP-compatible host. You describe a project
7
+ in your IDE as you normally would; AgileForAgents turns it into a structured agile
8
+ board (epics → sprints → user stories → subtasks) where **every subtask has an
9
+ observable done-criteria** and **every assumption is made explicit** — following
10
+ Andrej Karpathy's Spec methodology.
11
+
12
+ ## How it works
13
+
14
+ ```
15
+ ┌──────────────────────────┐ stdio ┌────────────────────────┐ HTTPS ┌──────────────────────────────┐
16
+ │ Your IDE │◀──────────▶│ agileforagents │───────────▶│ Hosted backend (HF Space) │
17
+ │ Claude Code / Cursor / │ MCP tool │ MCP server │ /api/ │ • runs a free HF model │
18
+ │ Codex — you type here │ call │ (stdlib-only client) │ generate │ • holds the token server-side│
19
+ └──────────────────────────┘ └────────────────────────┘◀───────────│ • returns a verified board │
20
+ └───────────────┬──────────────┘
21
+ │ logs metrics
22
+
23
+ ┌──────────────────────────┐
24
+ │ Gradio Control Panel │
25
+ │ (monitoring dashboard, │
26
+ │ no prompt input) │
27
+ └──────────────────────────┘
28
+ ```
29
+
30
+ - **You never leave your IDE.** Prompts come from your editor through the MCP tool —
31
+ not from a web form.
32
+ - **No API key, no token, no heavy dependencies.** The MCP server installs with just
33
+ the MCP SDK + pydantic. All model inference happens on the hosted backend, which
34
+ holds the Hugging Face token as a server-side secret.
35
+ - **The Gradio Space is a control panel**, not an input form — it shows how the
36
+ generator is performing (volume, average verification score, recent activity).
37
+ - **Works offline, degraded.** If the backend is unreachable, a local heuristic
38
+ fallback still returns a valid scaffold board so the tool never hard-fails.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pip install agileforagents
44
+ ```
45
+
46
+ Then register the `agileforagents` stdio server with your MCP host.
47
+
48
+ **Claude Code** (`~/.claude/mcp.json` or project `.mcp.json`):
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "agileforagents": {
54
+ "command": "agileforagents"
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ **Cursor / Codex CLI:** point your MCP config at the `agileforagents` command. No
61
+ environment variables are required.
62
+
63
+ ## Tools
64
+
65
+ | Tool | What it does |
66
+ |---|---|
67
+ | `generate_agile_tasks` | Turn a prompt into a verified agile board. Args: `prompt`, `format` (`claude` \| `json` \| `cursor`), `detail_level` (`standard` \| `detailed`). |
68
+ | `get_agent_rules` | Return the AgileForAgents working protocol to paste into `CLAUDE.md` / `.cursorrules`. |
69
+ | `feedback_stats` | Local generation count + backend health. |
70
+
71
+ ## Configuration (optional)
72
+
73
+ The MCP server works with no configuration. For self-hosting or testing you can override:
74
+
75
+ | Env var | Purpose | Default |
76
+ |---|---|---|
77
+ | `AFA_BACKEND_URL` | Hosted backend base URL | `https://vooom-agileforagents.hf.space` |
78
+ | `AFA_BACKEND_TIMEOUT` | Request timeout (seconds) | `120` |
79
+
80
+ ## Repository layout
81
+
82
+ | Path | Purpose |
83
+ |---|---|
84
+ | `src/agileforagents/` | The installable stdio MCP server (thin client + formatters + fallback). |
85
+ | `space/` | The Hugging Face Space: FastAPI inference backend + Gradio monitoring dashboard. |
86
+ | `tests/` | Unit + E2E tests. |
87
+
88
+ ## Development
89
+
90
+ ```bash
91
+ pip install -e ".[dev]"
92
+ make format-check && make lint && make typecheck && make test
93
+ ```
94
+
95
+ ## License
96
+
97
+ MIT
@@ -0,0 +1,92 @@
1
+ [build-system]
2
+ requires = ["setuptools>=45", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "agileforagents"
7
+ version = "0.3.0"
8
+ description = "Zero-config MCP server that turns prompts into verified agile task boards for AI coding agents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ keywords = ["mcp", "agile", "ai-agents", "claude-code", "cursor", "codex"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Software Development :: Build Tools",
21
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
22
+ ]
23
+ # The installable MCP server is intentionally dependency-light: only the MCP SDK
24
+ # and pydantic. All LLM work happens on the hosted backend (a Hugging Face Space),
25
+ # reached over the standard library, so users need no API keys or heavy ML deps.
26
+ dependencies = [
27
+ "mcp>=1.9.0",
28
+ "pydantic>=2.0.0",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/FAJU85/AgileForAgents"
33
+ Repository = "https://github.com/FAJU85/AgileForAgents"
34
+ Issues = "https://github.com/FAJU85/AgileForAgents/issues"
35
+
36
+ [project.scripts]
37
+ agileforagents = "agileforagents.server:main"
38
+ agile-for-agents = "agileforagents.server:main"
39
+
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "pytest>=8.0.0",
43
+ "pytest-cov>=5.0.0",
44
+ "pytest-asyncio>=0.23.0",
45
+ "ruff>=0.4.0",
46
+ "mypy>=1.10.0",
47
+ "pip-audit>=2.7.0",
48
+ ]
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["src"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ python_files = ["test_*.py"]
56
+ asyncio_mode = "auto"
57
+
58
+ [tool.ruff]
59
+ line-length = 100
60
+ target-version = "py311"
61
+
62
+ [tool.ruff.lint]
63
+ select = ["E", "F", "W", "I", "N", "UP", "B", "T20"]
64
+ # T20 catches print() statements in production code per policy
65
+ ignore = []
66
+
67
+ [tool.mypy]
68
+ python_version = "3.11"
69
+ strict = true
70
+ ignore_missing_imports = true
71
+ files = ["src/"]
72
+
73
+ # ── third-party without stubs ────────────────────────────────────────────────
74
+ [[tool.mypy.overrides]]
75
+ module = [
76
+ "mcp", "mcp.*",
77
+ "pydantic", "pydantic.*",
78
+ ]
79
+ ignore_errors = true
80
+
81
+ [[tool.mypy.overrides]]
82
+ module = ["agileforagents.models"]
83
+ disable_error_code = ["misc"]
84
+
85
+ # The mcp SDK's @app.list_tools() / @app.call_tool() decorators are untyped, and
86
+ # which error code mypy emits for them ("untyped-decorator" vs "no-untyped-call")
87
+ # varies by installed mcp version. Disable both here so the gate is deterministic
88
+ # across environments, and turn off unused-ignore warnings for this module.
89
+ [[tool.mypy.overrides]]
90
+ module = ["agileforagents.server"]
91
+ disable_error_code = ["untyped-decorator", "no-untyped-call", "misc"]
92
+ warn_unused_ignores = false
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """AgileForAgents: Verified agile task generation via Karpathy's three-layer methodology."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,63 @@
1
+ """Backend client — talks to the hosted AgileForAgents inference service.
2
+
3
+ Uses only the Python standard library (``urllib``) so the MCP server installs
4
+ with zero heavy dependencies. The hosted backend (a Hugging Face Space) holds
5
+ the model token server-side, so the end user never supplies any credentials.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import urllib.error
13
+ import urllib.request
14
+ from typing import Any
15
+
16
+ # Public Hugging Face Space backend. Overridable for self-hosting / testing.
17
+ DEFAULT_BACKEND_URL = "https://vooom-agileforagents.hf.space"
18
+ BACKEND_URL = os.getenv("AFA_BACKEND_URL", DEFAULT_BACKEND_URL).rstrip("/")
19
+
20
+ # The backend can be slow on a cold start (model spin-up), so allow generous time.
21
+ REQUEST_TIMEOUT_SECONDS = float(os.getenv("AFA_BACKEND_TIMEOUT", "120"))
22
+
23
+
24
+ class BackendError(RuntimeError):
25
+ """Raised when the hosted backend is unreachable or returns an error."""
26
+
27
+
28
+ def generate_board(prompt: str, detail_level: str = "standard") -> dict[str, Any]:
29
+ """POST a prompt to the backend and return ``{"board": ..., "verification": ...}``.
30
+
31
+ Raises :class:`BackendError` on any network or protocol failure so the caller
32
+ can fall back to local generation.
33
+ """
34
+ payload = json.dumps({"prompt": prompt, "detail_level": detail_level}).encode("utf-8")
35
+ request = urllib.request.Request(
36
+ f"{BACKEND_URL}/api/generate",
37
+ data=payload,
38
+ headers={"Content-Type": "application/json", "Accept": "application/json"},
39
+ method="POST",
40
+ )
41
+ try:
42
+ with urllib.request.urlopen(request, timeout=REQUEST_TIMEOUT_SECONDS) as response:
43
+ body = response.read().decode("utf-8")
44
+ except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, OSError) as exc:
45
+ raise BackendError(f"Backend request failed: {exc}") from exc
46
+
47
+ try:
48
+ data: dict[str, Any] = json.loads(body)
49
+ except json.JSONDecodeError as exc:
50
+ raise BackendError(f"Backend returned invalid JSON: {exc}") from exc
51
+
52
+ if "board" not in data:
53
+ raise BackendError(f"Backend response missing 'board': {data}")
54
+ return data
55
+
56
+
57
+ def health() -> bool:
58
+ """Return ``True`` if the backend health endpoint responds OK."""
59
+ try:
60
+ with urllib.request.urlopen(f"{BACKEND_URL}/health", timeout=10) as response:
61
+ return bool(response.status == 200)
62
+ except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, OSError):
63
+ return False
@@ -0,0 +1,115 @@
1
+ """Layer 3 — Environment Layer: Formats output and injects agent guardrails."""
2
+
3
+ import json
4
+
5
+ from .models import AgileTaskBoard, VerificationResult
6
+ from .prompts import ENVIRONMENT_RULES
7
+
8
+
9
+ class EnvironmentLayer:
10
+ """Adapts the verified AgileTaskBoard for different agent environments."""
11
+
12
+ SUPPORTED_FORMATS = ("claude", "json", "cursor")
13
+
14
+ def format(
15
+ self,
16
+ board: AgileTaskBoard,
17
+ verification: VerificationResult,
18
+ output_format: str = "claude",
19
+ ) -> str:
20
+ if output_format not in self.SUPPORTED_FORMATS:
21
+ raise ValueError(
22
+ f"Unknown format '{output_format}'. Choose from: {self.SUPPORTED_FORMATS}"
23
+ )
24
+
25
+ if output_format == "json":
26
+ return self._format_json(board, verification)
27
+ if output_format == "cursor":
28
+ return self._format_cursor(board)
29
+ return self._format_claude(board, verification)
30
+
31
+ # ------------------------------------------------------------------
32
+ # Private formatters
33
+ # ------------------------------------------------------------------
34
+
35
+ def _format_claude(self, board: AgileTaskBoard, verification: VerificationResult) -> str:
36
+ lines = [ENVIRONMENT_RULES]
37
+
38
+ lines.append(f"## Project Goal\n{board.project_goal}\n")
39
+ lines.append(f"## Tech Stack\n{', '.join(board.tech_stack)}\n")
40
+
41
+ lines.append("## Assumptions")
42
+ for a in board.assumptions:
43
+ lines.append(f"- {a}")
44
+ lines.append("")
45
+
46
+ lines.append("## Epics")
47
+ for e in board.epics:
48
+ lines.append(f"- {e}")
49
+ lines.append("")
50
+
51
+ for sprint in board.sprints:
52
+ lines.append(f"## Sprint {sprint.number}: {sprint.goal}")
53
+ lines.append(f"**Done when:** {sprint.done_criteria}\n")
54
+
55
+ for story in sprint.user_stories:
56
+ lines.append(f"### {story.id} — {story.title} ({story.story_points} pts)")
57
+ lines.append(
58
+ f"As a **{story.as_a}**, I want **{story.i_want}** "
59
+ f"so that **{story.so_that}**.\n"
60
+ )
61
+ lines.append("**Acceptance Criteria:**")
62
+ for ac in story.acceptance_criteria:
63
+ lines.append(f"- {ac}")
64
+ lines.append("")
65
+ lines.append("**Subtasks:**")
66
+ for st in story.subtasks:
67
+ lines.append(f"- [ ] `{st.id}` {st.title} ({st.estimated_hours}h)")
68
+ lines.append(f" - {st.description}")
69
+ lines.append(f" - **Done when:** {st.done_criteria}")
70
+ lines.append("")
71
+
72
+ lines.append("---")
73
+ lines.append(
74
+ f"*Verification score: {verification.score:.0%} — "
75
+ f"{'✓ Passed' if verification.passed else '⚠ Partial'}*"
76
+ )
77
+
78
+ return "\n".join(lines)
79
+
80
+ def _format_json(self, board: AgileTaskBoard, verification: VerificationResult) -> str:
81
+ return json.dumps(
82
+ {
83
+ "board": board.model_dump(),
84
+ "verification": verification.model_dump(),
85
+ },
86
+ indent=2,
87
+ )
88
+
89
+ def _format_cursor(self, board: AgileTaskBoard) -> str:
90
+ """Cursor Rules format: compact task list for .cursorrules injection."""
91
+ lines = [
92
+ "# Project: " + board.project_goal,
93
+ "",
94
+ "## Rules",
95
+ "- Complete one user story at a time before starting the next.",
96
+ "- Every subtask has a done_criteria — self-test against it before marking [x].",
97
+ "- State any new assumptions explicitly before acting on them.",
98
+ "",
99
+ "## Assumptions",
100
+ ]
101
+ for a in board.assumptions:
102
+ lines.append(f"- {a}")
103
+ lines.append("")
104
+
105
+ for sprint in board.sprints:
106
+ lines.append(f"## Sprint {sprint.number}: {sprint.goal}")
107
+ for story in sprint.user_stories:
108
+ lines.append(f"\n### {story.id}: {story.title}")
109
+ for st in story.subtasks:
110
+ lines.append(f"- [ ] {st.id}: {st.title} | done: {st.done_criteria}")
111
+ return "\n".join(lines)
112
+
113
+ def get_rules_only(self) -> str:
114
+ """Returns the injected agent rules without any task content."""
115
+ return ENVIRONMENT_RULES.strip()
@@ -0,0 +1,156 @@
1
+ """Local fallback generator — used only when the hosted backend is unreachable.
2
+
3
+ This is a deterministic, dependency-free heuristic decomposition. It is
4
+ intentionally conservative: it produces a valid, well-structured AgileTaskBoard
5
+ so the MCP tool never hard-fails, but it does not attempt LLM-quality planning.
6
+ The hosted backend is always preferred when available.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from typing import Any
13
+
14
+ from .models import AgileTaskBoard, Sprint, Subtask, UserStory, VerificationResult
15
+
16
+ # Lightweight tech-stack detection so the fallback board reflects the prompt.
17
+ _TECH_KEYWORDS = {
18
+ "python": "Python",
19
+ "fastapi": "FastAPI",
20
+ "django": "Django",
21
+ "flask": "Flask",
22
+ "react": "React",
23
+ "next": "Next.js",
24
+ "vue": "Vue",
25
+ "node": "Node.js",
26
+ "typescript": "TypeScript",
27
+ "postgres": "PostgreSQL",
28
+ "mysql": "MySQL",
29
+ "sqlite": "SQLite",
30
+ "mongo": "MongoDB",
31
+ "redis": "Redis",
32
+ "docker": "Docker",
33
+ "tailwind": "Tailwind CSS",
34
+ }
35
+
36
+
37
+ def _detect_tech_stack(prompt: str) -> list[str]:
38
+ lower = prompt.lower()
39
+ found = [label for key, label in _TECH_KEYWORDS.items() if key in lower]
40
+ return found or ["TBD — confirm with stakeholder"]
41
+
42
+
43
+ def _first_sentence(prompt: str) -> str:
44
+ cleaned = prompt.strip().rstrip(".")
45
+ match = re.split(r"[.!?\n]", cleaned, maxsplit=1)
46
+ return match[0].strip() if match and match[0].strip() else cleaned
47
+
48
+
49
+ def generate_board(prompt: str, detail_level: str = "standard") -> dict[str, Any]:
50
+ """Return ``{"board": ..., "verification": ...}`` matching the backend contract."""
51
+ goal = _first_sentence(prompt)
52
+ tech_stack = _detect_tech_stack(prompt)
53
+
54
+ board = AgileTaskBoard(
55
+ project_goal=f"Deliver: {goal}.",
56
+ tech_stack=tech_stack,
57
+ assumptions=[
58
+ "Generated locally (hosted backend was unreachable) — review before use.",
59
+ "Single small team; no external compliance constraints assumed.",
60
+ "Scope limited to a demonstrable MVP first.",
61
+ ],
62
+ epics=["Foundation & Setup", "Core Feature", "Polish & Ship"],
63
+ sprints=[
64
+ Sprint(
65
+ number=1,
66
+ goal="Stand up a runnable skeleton that proves the core idea.",
67
+ done_criteria="App boots locally and the primary happy path is demonstrable.",
68
+ user_stories=[
69
+ UserStory(
70
+ id="US-1",
71
+ title="Project skeleton",
72
+ as_a="developer",
73
+ i_want="a runnable project skeleton with the chosen stack",
74
+ so_that="feature work has a verified foundation",
75
+ story_points=3,
76
+ acceptance_criteria=[
77
+ "Given the repo is cloned, when setup is run, then the app starts "
78
+ "without errors.",
79
+ ],
80
+ subtasks=[
81
+ Subtask(
82
+ id="ST-1",
83
+ title="Initialise project & dependencies",
84
+ description="Scaffold the project structure and install deps.",
85
+ done_criteria="The dev server / entry point runs and prints a "
86
+ "startup message.",
87
+ estimated_hours=2.0,
88
+ ),
89
+ Subtask(
90
+ id="ST-2",
91
+ title="Define the core data model",
92
+ description="Model the central entity implied by the prompt.",
93
+ done_criteria="The model is created and a smoke test reads/writes "
94
+ "one record.",
95
+ estimated_hours=3.0,
96
+ ),
97
+ ],
98
+ )
99
+ ],
100
+ ),
101
+ Sprint(
102
+ number=2,
103
+ goal="Implement the primary user-facing capability end to end.",
104
+ done_criteria="A user can complete the main task the product exists to serve.",
105
+ user_stories=[
106
+ UserStory(
107
+ id="US-2",
108
+ title="Core capability",
109
+ as_a="end user",
110
+ i_want="to accomplish the product's primary task",
111
+ so_that="the product delivers its core value",
112
+ story_points=5,
113
+ acceptance_criteria=[
114
+ "Given the app is running, when I perform the main action, then I see "
115
+ "a correct, persisted result.",
116
+ ],
117
+ subtasks=[
118
+ Subtask(
119
+ id="ST-3",
120
+ title="Implement core action endpoint/handler",
121
+ description="Build the primary action path identified in the goal.",
122
+ done_criteria="The action returns a successful result and persists "
123
+ "state.",
124
+ estimated_hours=4.0,
125
+ ),
126
+ Subtask(
127
+ id="ST-4",
128
+ title="Wire up the user interface / interface surface",
129
+ description="Expose the core action to the user.",
130
+ done_criteria="The action is reachable and usable from the "
131
+ "interface.",
132
+ estimated_hours=4.0,
133
+ ),
134
+ ],
135
+ )
136
+ ],
137
+ ),
138
+ ],
139
+ )
140
+
141
+ verification = VerificationResult(
142
+ passed=False,
143
+ score=0.5,
144
+ issues=["Generated by the offline fallback — not LLM-verified."],
145
+ feedback=(
146
+ "This board was produced locally because the hosted backend was unreachable. "
147
+ "Treat it as a starting scaffold and refine the sprints for your specific project."
148
+ ),
149
+ suggested_fixes=["Retry once the backend is reachable for a fully verified board."],
150
+ )
151
+
152
+ return {
153
+ "board": board.model_dump(),
154
+ "verification": verification.model_dump(),
155
+ "source": "local-fallback",
156
+ }