
Spec To Repo
- 561 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
spec-to-repo is an agent skill that parses ambiguous natural-language product specs into structured requirements tables, stack defaults, and resolved ambiguities before repository scaffolding.
About
spec-to-repo is a spec-parsing skill from alirezarezvani/claude-skills for developers facing messy, conversational product descriptions. It reads the full spec twice—first for context, then to populate a structured interpretation table—following a four-tier extraction priority: explicit statements, strong signals, contextual inference, and sensible defaults. Explicit choices like PostgreSQL or Next.js are non-negotiable; signals like sign-up imply auth and database needs. Developers invoke spec-to-repo when they have a natural-language brief and need a requirements matrix, technology defaults, and flagged ambiguities before generating a repo scaffold. The skill minimizes premature clarification by inferring reasonable defaults.
- Two-pass parsing: full read, then structured interpretation table without over-questioning
- Four-tier extraction priority: explicit statements, strong signals, inference, domain defaults
- Default stack matrix for web UI, API-only, mobile, CLI, performance, and lightweight specs
- Database defaults keyed to auth, persistence, and scale signals—including when not to add a DB
- Ambiguity resolution rules so agents do not stall on unspecified frameworks
Spec To Repo by the numbers
- 561 all-time installs (skills.sh)
- Ranked #705 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill spec-to-repoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 561 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
How do you turn a messy spec into requirements?
Install this when you have a messy natural-language product spec and need a structured requirements table, sensible stack defaults, and ambiguity resolved before scaffolding a repo.
Who is it for?
Developers with conversational or incomplete product specs who need a structured requirements matrix and stack defaults before scaffolding a repository.
Skip if: Developers who already have formal PRDs, OpenAPI specs, or ticket backlogs ready for direct implementation.
When should I use this skill?
A natural-language product spec needs parsing into structured requirements, stack choices, and resolved gaps before repo creation.
What you get
Structured requirements table, chosen stack defaults, inferred feature list, and documented ambiguity resolutions.
- requirements table
- stack defaults document
- ambiguity resolution log
Files
Spec to Repo
Turn a natural-language project specification into a complete, runnable starter repository. Not a template filler — a spec interpreter that generates real, working code for any stack.
When to Use
- User provides a text description of an app and wants code
- User has a PRD, requirements doc, or feature list and needs a codebase
- User says "build me an app that...", "scaffold this", "bootstrap a project"
- User wants a working starter repo, not just a file tree
Not this skill when the user wants a SaaS app with Stripe + Auth specifically — use product-team/saas-scaffolder instead.
Core Workflow
Phase 1 — Parse & Interpret
Read the spec. Extract these fields silently:
| Field | Source | Required |
|---|---|---|
| App name | Explicit or infer from description | yes |
| Description | First sentence of spec | yes |
| Features | Bullet points or sentences describing behavior | yes |
| Tech stack | Explicit ("use FastAPI") or infer from context | yes |
| Auth | "login", "users", "accounts", "roles" | if mentioned |
| Database | "store", "save", "persist", "records", "schema" | if mentioned |
| API surface | "endpoint", "API", "REST", "GraphQL" | if mentioned |
| Deploy target | "Vercel", "Docker", "AWS", "Railway" | if mentioned |
Stack inference rules (when user doesn't specify):
| Signal | Inferred stack |
|---|---|
| "web app", "dashboard", "SaaS" | Next.js + TypeScript |
| "API", "backend", "microservice" | FastAPI (Python) or Express (Node) |
| "mobile app" | Flutter or React Native |
| "CLI tool" | Go or Python |
| "data pipeline" | Python |
| "high performance", "systems" | Rust or Go |
After parsing, present a structured interpretation back to the user:
## Spec Interpretation
**App:** [name]
**Stack:** [framework + language]
**Features:**
1. [feature]
2. [feature]
**Database:** [yes/no — engine]
**Auth:** [yes/no — method]
**Deploy:** [target]
Does this match your intent? Any corrections before I generate?Flag ambiguities. Ask at most 3 clarifying questions. If the user says "just build it", proceed with best-guess defaults.
Phase 2 — Architecture
Design the project before writing any files:
1. Select template — Match to a stack template from references/stack-templates.md 2. Define file tree — List every file that will be created 3. Map features to files — Each feature gets at minimum one file/component 4. Design database schema — If applicable, define tables/collections with fields and types 5. Identify dependencies — List every package with version constraints 6. Plan API routes — If applicable, list every endpoint with method, path, request/response shape
Present the file tree to the user before generating:
project-name/
├── README.md
├── .env.example
├── .gitignore
├── .github/workflows/ci.yml
├── package.json / requirements.txt / go.mod
├── src/
│ ├── ...
├── tests/
│ ├── ...
└── ...Phase 3 — Generate
Write every file. Rules:
- Real code, not stubs. Every function has a real implementation. No
// TODO: implementorpassplaceholders. - Syntactically valid. Every file must parse without errors in its language.
- Imports match dependencies. Every import must correspond to a package in the manifest (package.json, requirements.txt, go.mod, etc.).
- Types included. TypeScript projects use types. Python projects use type hints. Go projects use typed structs.
- Environment variables. Generate
.env.examplewith every required variable, commented with purpose. - README.md. Include: project description, prerequisites, setup steps (clone, install, configure env, run), and available scripts/commands.
- CI config. Generate
.github/workflows/ci.ymlwith: install, lint (if linter in deps), test, build. - .gitignore. Stack-appropriate ignores (node_modules, __pycache__, .env, build artifacts).
File generation order: 1. Manifest (package.json / requirements.txt / go.mod) 2. Config files (.env.example, .gitignore, CI) 3. Database schema / migrations 4. Core business logic 5. API routes / endpoints 6. UI components (if applicable) 7. Tests 8. README.md
Phase 4 — Validate
After generation, run through this checklist:
- [ ] Every imported package exists in the manifest
- [ ] Every file referenced by an import exists in the tree
- [ ]
.env.examplelists every env var used in code - [ ]
.gitignorecovers build artifacts and secrets - [ ] README has setup instructions that actually work
- [ ] No hardcoded secrets, API keys, or passwords
- [ ] At least one test file exists
- [ ] Build/start command is documented and would work
Run scripts/validate_project.py against the generated directory to catch common issues.
Examples
Example 1: Task Management API
Input spec:
"Build me a task management API. Users can create, list, update, and delete tasks. Tasks have a title, description, status (todo/in-progress/done), and due date. Use FastAPI with SQLite. Add basic auth with API keys."
Output file tree:
task-api/
├── README.md
├── .env.example # API_KEY, DATABASE_URL
├── .gitignore
├── .github/workflows/ci.yml
├── requirements.txt # fastapi, uvicorn, sqlalchemy, pytest
├── main.py # FastAPI app, CORS, lifespan
├── models.py # SQLAlchemy Task model
├── schemas.py # Pydantic request/response schemas
├── database.py # SQLite engine + session
├── auth.py # API key middleware
├── routers/
│ └── tasks.py # CRUD endpoints
└── tests/
└── test_tasks.py # Smoke tests for each endpointExample 2: Recipe Sharing Web App
Input spec:
"I want a recipe sharing website. Users sign up, post recipes with ingredients and steps, browse other recipes, and save favorites. Use Next.js with Tailwind. Store data in PostgreSQL."
Output file tree:
recipe-share/
├── README.md
├── .env.example # DATABASE_URL, NEXTAUTH_SECRET, NEXTAUTH_URL
├── .gitignore
├── .github/workflows/ci.yml
├── package.json # next, react, tailwindcss, prisma, next-auth
├── tailwind.config.ts
├── tsconfig.json
├── next.config.ts
├── prisma/
│ └── schema.prisma # User, Recipe, Ingredient, Favorite models
├── src/
│ ├── app/
│ │ ├── layout.tsx
│ │ ├── page.tsx # Homepage — recipe feed
│ │ ├── recipes/
│ │ │ ├── page.tsx # Browse recipes
│ │ │ ├── [id]/page.tsx # Recipe detail
│ │ │ └── new/page.tsx # Create recipe form
│ │ └── api/
│ │ ├── auth/[...nextauth]/route.ts
│ │ └── recipes/route.ts
│ ├── components/
│ │ ├── RecipeCard.tsx
│ │ ├── RecipeForm.tsx
│ │ └── Navbar.tsx
│ └── lib/
│ ├── prisma.ts
│ └── auth.ts
└── tests/
└── recipes.test.tsExample 3: CLI Expense Tracker
Input spec:
"Python CLI tool for tracking expenses. Commands: add, list, summary, export-csv. Store in a local SQLite file. No external API."
Output file tree:
expense-tracker/
├── README.md
├── .gitignore
├── .github/workflows/ci.yml
├── pyproject.toml
├── src/
│ └── expense_tracker/
│ ├── __init__.py
│ ├── cli.py # argparse commands
│ ├── database.py # SQLite operations
│ ├── models.py # Expense dataclass
│ └── formatters.py # Table + CSV output
└── tests/
└── test_cli.pyAnti-Patterns
| Anti-pattern | Fix |
|---|---|
Placeholder code — // TODO: implement, pass, empty function bodies | Every function has a real implementation. If complex, implement a working simplified version. |
| Stack override — picking Next.js when the user said Flask | Always honor explicit tech preferences. Only infer when the user doesn't specify. |
| Missing .gitignore — committing node_modules or .env | Generate stack-appropriate .gitignore as one of the first files. |
| Phantom imports — importing packages not in the manifest | Cross-check every import against package.json / requirements.txt before finishing. |
| Over-engineering MVP — adding Redis caching, rate limiting, WebSockets to a v1 | Build the minimum that works. The user can iterate. |
| Ignoring stated preferences — user says "PostgreSQL" and you generate MongoDB | Parse the spec carefully. Explicit preferences are non-negotiable. |
Missing env vars — code reads process.env.X but .env.example doesn't list it | Every env var used in code must appear in .env.example with a comment. |
| No tests — shipping a repo with zero test files | At minimum: one smoke test per API endpoint or one test per core function. |
| Hallucinated APIs — generating code that calls library methods that don't exist | Stick to well-documented, stable APIs. When unsure, use the simplest approach. |
Validation Script
scripts/validate_project.py
Checks a generated project directory for common issues:
# Validate a generated project
python3 scripts/validate_project.py /path/to/generated-project
# JSON output
python3 scripts/validate_project.py /path/to/generated-project --format jsonChecks performed:
- README.md exists and is non-empty
- .gitignore exists
- .env.example exists (if code references env vars)
- Package manifest exists (package.json, requirements.txt, go.mod, Cargo.toml, pubspec.yaml)
- No .env file committed (secrets leak)
- At least one test file exists
- No TODO/FIXME placeholders in generated code
Progressive Enhancement
For complex specs, generate in stages:
1. MVP — Core feature only, working end-to-end 2. Auth — Add authentication if requested 3. Polish — Error handling, validation, loading states 4. Deploy — Docker, CI, deploy config
Ask the user after MVP: "Core is working. Want me to add auth/polish/deploy next, or iterate on what's here?"
Cross-References
- Related:
product-team/saas-scaffolder— SaaS-specific scaffolding (Next.js + Stripe + Auth) - Related:
engineering/spec-driven-workflow— spec-first development methodology - Related:
engineering/database-designer— database schema design patterns - Related:
engineering-team/senior-fullstack— full-stack implementation patterns
Spec Parsing Guide
How to extract structured requirements from ambiguous, incomplete, or conversational natural-language specifications.
---
Parsing Strategy
Read the full spec once. On the second pass, extract fields into the structured interpretation table. Don't ask questions for anything you can reasonably infer.
Extraction Priority
1. Explicit statements — "Use PostgreSQL", "Build with Next.js" — non-negotiable 2. Strong signals — "users can sign up" implies auth + user model + database 3. Contextual inference — "dashboard" implies web app; "track expenses" implies CRUD + database 4. Defaults — When nothing is specified, pick the most common choice for the domain
---
Ambiguity Resolution
Stack Not Specified
| Spec pattern | Default stack | Reasoning |
|---|---|---|
| Web app with UI | Next.js + TypeScript | Most versatile, SSR + API routes |
| API / backend only | FastAPI | Fast to scaffold, great DX, typed |
| Mobile app | Flutter | Cross-platform, single codebase |
| CLI tool | Python | Fastest to ship, stdlib-rich |
| "Simple" / "lightweight" | Express or Flask | Minimal overhead |
| "Fast" / "performance" | Go | Compiled, concurrent |
Database Not Specified
| Signal | Default |
|---|---|
| User accounts, persistent data | PostgreSQL |
| Small project, local-only, CLI | SQLite |
| Document-oriented, flexible schema | MongoDB (only if user signals) |
| No data persistence mentioned | No database — don't add one |
Auth Not Specified
| Signal | Default |
|---|---|
| "Users", "accounts", "login" | Yes — session-based or JWT |
| "Admin panel", "roles" | Yes — with role-based access |
| API with "API keys" | Yes — API key middleware |
| No user-facing features | No auth — don't add one |
---
Common Spec Shapes
Shape 1: Stream of Consciousness
"I want an app where people can post recipes and other people can comment on them and save their favorites, maybe add a rating system too, and it should look nice on mobile"
Extract:
- Features: post recipes, comment, favorites, ratings
- UI: responsive / mobile-friendly
- Implies: auth (users), database (recipes, comments, favorites, ratings), web app
Shape 2: Feature List
"Features: 1. User registration 2. Create projects 3. Invite team members 4. Kanban board 5. File uploads"
Extract:
- Features: numbered list, each gets a route/component
- Auth: yes (registration)
- Database: yes (users, projects, teams, files)
- Complex features: kanban (drag-drop), file uploads (storage)
Shape 3: Technical Spec
"FastAPI backend with PostgreSQL. Endpoints: POST /items, GET /items, GET /items/{id}, PUT /items/{id}, DELETE /items/{id}. Use SQLAlchemy ORM. Add JWT auth."
Extract:
- Stack: explicit (FastAPI, PostgreSQL, SQLAlchemy, JWT)
- API: 5 CRUD endpoints, fully defined
- Minimal inference needed — generate exactly what's asked
Shape 4: Existing PRD
[Multi-page document with overview, user personas, feature requirements, acceptance criteria]
Extract:
- Read the overview first for scope
- Map feature requirements to files
- Use acceptance criteria as test case seeds
- Ignore personas, market analysis, timelines — they don't affect code generation
---
What to Ask vs. What to Infer
Ask (max 3 questions):
- Stack preference when the spec is truly ambiguous and could go multiple ways
- Database choice when both SQL and NoSQL are equally valid
- Deploy target when it materially affects the code (serverless vs. container)
Infer silently:
- Auth method (JWT for APIs, session for web apps)
- Testing framework (most popular for the stack)
- Linter / formatter (stack default)
- CSS approach (Tailwind for React/Next, stack default otherwise)
- Package versions (latest stable)
Never ask:
- "What folder structure do you want?" — use the stack convention
- "Do you want TypeScript?" — yes, always for JS projects
- "Should I add error handling?" — yes, always
- "Do you want tests?" — yes, always
Stack Templates
Quick-reference templates for common tech stacks. Each template defines: file structure, manifest, entry point, and build/run commands.
---
Next.js (TypeScript + Tailwind)
When: Web apps, dashboards, SaaS, landing pages with dynamic content.
Manifest: package.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest"
}
}Core deps: next, react, react-dom, tailwindcss, postcss, autoprefixer Auth: next-auth (default) or clerk Database: prisma (ORM) + @prisma/client Testing: jest, @testing-library/react
File structure:
src/app/layout.tsx — Root layout with providers
src/app/page.tsx — Homepage
src/app/api/*/route.ts — API routes
src/components/*.tsx — Shared components
src/lib/*.ts — Utilities, DB client
prisma/schema.prisma — Database schema (if DB)Config files: tsconfig.json, tailwind.config.ts, next.config.ts, postcss.config.mjs
---
FastAPI (Python)
When: REST APIs, backends, microservices, data-driven services.
Manifest: requirements.txt
fastapi>=0.110.0
uvicorn>=0.29.0
sqlalchemy>=2.0.0
pydantic>=2.0.0
pytest>=8.0.0
httpx>=0.27.0File structure:
main.py — FastAPI app, CORS, lifespan
models.py — SQLAlchemy models
schemas.py — Pydantic schemas
database.py — Engine, session factory
routers/*.py — Route modules
tests/test_*.py — pytest testsRun: uvicorn main:app --reload Test: pytest
---
Express (TypeScript)
When: Node.js APIs, middleware-heavy backends, real-time services.
Manifest: package.json
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "jest"
}
}Core deps: express, cors, dotenv Dev deps: typescript, tsx, @types/express, @types/node, jest, ts-jest
File structure:
src/index.ts — App setup, middleware, listen
src/routes/*.ts — Route handlers
src/middleware/*.ts — Auth, validation, error handling
src/models/*.ts — Data models / ORM entities
src/lib/*.ts — Utilities
tests/*.test.ts — Jest tests---
Go (net/http or Gin)
When: High-performance APIs, CLI tools, systems programming.
Manifest: go.mod
File structure (API):
main.go — Entry point, router setup
handlers/*.go — HTTP handlers
models/*.go — Data structs
middleware/*.go — Auth, logging
db/*.go — Database connection
*_test.go — Table-driven testsFile structure (CLI):
main.go — Entry point, flag parsing
cmd/*.go — Subcommands
internal/*.go — Business logic
*_test.go — TestsRun: go run . Test: go test ./... Build: go build -o app .
---
Rust (Actix-web or Axum)
When: High-performance, safety-critical APIs, systems.
Manifest: Cargo.toml
File structure:
src/main.rs — Entry point, server setup
src/routes/*.rs — Route handlers
src/models/*.rs — Data structs, serde
src/db.rs — Database pool
src/error.rs — Error types
tests/*.rs — Integration testsRun: cargo run Test: cargo test
---
Flutter (Dart)
When: Cross-platform mobile apps (iOS + Android).
Manifest: pubspec.yaml
File structure:
lib/main.dart — Entry point, MaterialApp
lib/screens/*.dart — Screen widgets
lib/widgets/*.dart — Reusable components
lib/models/*.dart — Data classes
lib/services/*.dart — API clients, storage
lib/providers/*.dart — State management
test/*_test.dart — Widget testsRun: flutter run Test: flutter test
---
Rails (Ruby)
When: Full-stack web apps, CRUD-heavy applications, rapid prototyping.
Manifest: Gemfile
File structure: Standard Rails conventions (app/, config/, db/, spec/).
Run: bin/rails server Test: bin/rspec
---
Django (Python)
When: Full-stack Python web apps, admin-heavy apps, content management.
Manifest: requirements.txt
django>=5.0
djangorestframework>=3.15
pytest-django>=4.8File structure:
manage.py
config/settings.py — Settings
config/urls.py — Root URL config
apps/<name>/models.py — Models
apps/<name>/views.py — Views or ViewSets
apps/<name>/serializers.py — DRF serializers
apps/<name>/urls.py — App URL config
tests/test_*.pyRun: python manage.py runserver Test: pytest
---
CI Template (.github/workflows/ci.yml)
Adapt per stack:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup [runtime]
uses: actions/setup-[runtime]@v5
with:
[runtime]-version: '[version]'
- name: Install
run: [install command]
- name: Lint
run: [lint command]
- name: Test
run: [test command]
- name: Build
run: [build command]---
.gitignore Essentials by Stack
| Stack | Must ignore |
|---|---|
| Node/Next.js | node_modules/, .next/, .env, dist/, .turbo/ |
| Python | __pycache__/, *.pyc, .venv/, .env, *.egg-info/ |
| Go | Binary name, .env, vendor/ (if not committed) |
| Rust | target/, .env |
| Flutter | .dart_tool/, build/, .env, *.iml |
| Rails | log/, tmp/, .env, storage/, node_modules/ |
All stacks: .env, .DS_Store, *.log, IDE folders (.idea/, .vscode/)
#!/usr/bin/env python3
"""
validate_project.py — Validate a generated project directory for common issues.
Checks:
- README.md exists and is non-empty
- .gitignore exists
- .env.example exists (if code references env vars)
- Package manifest exists (package.json, requirements.txt, go.mod, etc.)
- No .env file committed (secrets leak)
- At least one test file exists
- No TODO/FIXME placeholders in generated code
Usage:
python3 validate_project.py /path/to/project
python3 validate_project.py /path/to/project --format json
python3 validate_project.py /path/to/project --strict
"""
import argparse
import json
import os
import re
import sys
MANIFESTS = [
"package.json",
"requirements.txt",
"pyproject.toml",
"go.mod",
"Cargo.toml",
"pubspec.yaml",
"Gemfile",
"pom.xml",
"build.gradle",
"build.gradle.kts",
]
CODE_EXTENSIONS = {
".py", ".js", ".ts", ".tsx", ".jsx", ".go", ".rs", ".rb",
".dart", ".java", ".kt", ".swift", ".cs", ".cpp", ".c",
}
TEST_PATTERNS = [
r"test_.*\.py$",
r".*_test\.py$",
r".*\.test\.[jt]sx?$",
r".*\.spec\.[jt]sx?$",
r".*_test\.go$",
r".*_test\.rs$",
r".*_test\.dart$",
r"test/.*",
r"tests/.*",
r"spec/.*",
r"__tests__/.*",
]
PLACEHOLDER_PATTERNS = [
r"\bTODO\b",
r"\bFIXME\b",
r"\bHACK\b",
r"//\s*implement",
r"#\s*implement",
r'raise NotImplementedError',
r"pass\s*$",
r"\.\.\. # placeholder",
]
ENV_VAR_PATTERNS = [
r"process\.env\.\w+",
r"os\.environ\[",
r"os\.getenv\(",
r"env\(",
r"std::env::var",
r"os\.Getenv\(",
r"ENV\[",
r"Platform\.environment\[",
]
def find_files(root):
"""Walk directory, skip hidden dirs and common vendor dirs."""
skip = {".git", "node_modules", ".next", "__pycache__", "target", ".dart_tool",
"build", "dist", ".venv", "venv", "vendor", ".turbo"}
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in skip]
for f in filenames:
yield os.path.join(dirpath, f)
def check_readme(root):
path = os.path.join(root, "README.md")
if not os.path.isfile(path):
return {"name": "readme", "status": "FAIL", "message": "README.md missing"}
size = os.path.getsize(path)
if size < 50:
return {"name": "readme", "status": "WARN", "message": f"README.md is only {size} bytes — likely incomplete"}
return {"name": "readme", "status": "PASS", "message": f"README.md exists ({size} bytes)"}
def check_gitignore(root):
path = os.path.join(root, ".gitignore")
if not os.path.isfile(path):
return {"name": "gitignore", "status": "FAIL", "message": ".gitignore missing"}
return {"name": "gitignore", "status": "PASS", "message": ".gitignore exists"}
def check_env_example(root, all_files):
uses_env = False
for filepath in all_files:
ext = os.path.splitext(filepath)[1]
if ext not in CODE_EXTENSIONS:
continue
try:
content = open(filepath, "r", encoding="utf-8", errors="ignore").read()
except (OSError, UnicodeDecodeError):
continue
for pattern in ENV_VAR_PATTERNS:
if re.search(pattern, content):
uses_env = True
break
if uses_env:
break
if not uses_env:
return {"name": "env_example", "status": "PASS", "message": "No env vars detected — .env.example not required"}
path = os.path.join(root, ".env.example")
if not os.path.isfile(path):
return {"name": "env_example", "status": "FAIL", "message": "Code references env vars but .env.example is missing"}
return {"name": "env_example", "status": "PASS", "message": ".env.example exists"}
def check_no_env_file(root):
path = os.path.join(root, ".env")
if os.path.isfile(path):
return {"name": "no_env_committed", "status": "FAIL", "message": ".env file found — secrets may be committed"}
return {"name": "no_env_committed", "status": "PASS", "message": "No .env file committed"}
def check_manifest(root):
for manifest in MANIFESTS:
if os.path.isfile(os.path.join(root, manifest)):
return {"name": "manifest", "status": "PASS", "message": f"Package manifest found: {manifest}"}
return {"name": "manifest", "status": "FAIL", "message": "No package manifest found (package.json, requirements.txt, go.mod, etc.)"}
def check_tests(all_files, root):
for filepath in all_files:
rel = os.path.relpath(filepath, root)
for pattern in TEST_PATTERNS:
if re.search(pattern, rel):
return {"name": "tests", "status": "PASS", "message": f"Test file found: {rel}"}
return {"name": "tests", "status": "FAIL", "message": "No test files found"}
def check_placeholders(all_files, root):
findings = []
for filepath in all_files:
ext = os.path.splitext(filepath)[1]
if ext not in CODE_EXTENSIONS:
continue
try:
lines = open(filepath, "r", encoding="utf-8", errors="ignore").readlines()
except (OSError, UnicodeDecodeError):
continue
for i, line in enumerate(lines, 1):
for pattern in PLACEHOLDER_PATTERNS:
if re.search(pattern, line):
rel = os.path.relpath(filepath, root)
findings.append(f"{rel}:{i}")
break
if not findings:
return {"name": "placeholders", "status": "PASS", "message": "No TODO/FIXME/placeholder code found"}
if len(findings) <= 3:
return {"name": "placeholders", "status": "WARN",
"message": f"{len(findings)} placeholder(s) found: {', '.join(findings)}"}
return {"name": "placeholders", "status": "FAIL",
"message": f"{len(findings)} placeholders found (showing first 5): {', '.join(findings[:5])}"}
def run_checks(root, strict):
all_files = list(find_files(root))
checks = [
check_readme(root),
check_gitignore(root),
check_manifest(root),
check_env_example(root, all_files),
check_no_env_file(root),
check_tests(all_files, root),
check_placeholders(all_files, root),
]
passes = sum(1 for c in checks if c["status"] == "PASS")
warns = sum(1 for c in checks if c["status"] == "WARN")
fails = sum(1 for c in checks if c["status"] == "FAIL")
if strict:
overall = "PASS" if fails == 0 and warns == 0 else "FAIL"
else:
overall = "PASS" if fails == 0 else "FAIL"
return {
"project": root,
"files_scanned": len(all_files),
"checks": checks,
"summary": {"pass": passes, "warn": warns, "fail": fails},
"overall": overall,
}
def print_report(result):
print("=" * 60)
print("PROJECT VALIDATION REPORT")
print("=" * 60)
print(f"Project: {result['project']}")
print(f"Files scanned: {result['files_scanned']}")
print()
for check in result["checks"]:
icon = {"PASS": " \u2705", "WARN": " \u26a0\ufe0f", "FAIL": " \u274c"}[check["status"]]
print(f"{icon} [{check['status']}] {check['name']}: {check['message']}")
s = result["summary"]
print()
print(f"Results: {s['pass']} pass, {s['warn']} warn, {s['fail']} fail")
indicator = "\u2705" if result["overall"] == "PASS" else "\u274c"
print(f"Overall: {indicator} {result['overall']}")
print("=" * 60)
def main():
parser = argparse.ArgumentParser(
description="Validate a generated project directory for common issues."
)
parser.add_argument("path", help="Path to the project directory to validate")
parser.add_argument("--format", choices=["text", "json"], default="text",
help="Output format (default: text)")
parser.add_argument("--strict", action="store_true",
help="Treat warnings as failures")
args = parser.parse_args()
if not os.path.isdir(args.path):
print(f"Error: not a directory: {args.path}", file=sys.stderr)
sys.exit(1)
result = run_checks(os.path.abspath(args.path), args.strict)
if args.format == "json":
print(json.dumps(result, indent=2))
else:
print_report(result)
sys.exit(0 if result["overall"] == "PASS" else 1)
if __name__ == "__main__":
main()
Related skills
How it compares
Pick spec-to-repo over coding skills when the input is an unstructured brief and the output needed is requirements—not generated source files.
FAQ
How does spec-to-repo handle ambiguous specifications?
spec-to-repo applies a four-tier extraction priority—explicit statements, strong signals, contextual inference, then defaults—and only asks questions when reasonable inference is impossible, populating a structured interpretation table on the second pass.
What stack decisions does spec-to-repo infer automatically?
spec-to-repo treats explicit technology choices as non-negotiable while inferring needs like auth, user models, and databases from phrases such as users can sign up, defaulting to common choices when unspecified.
Is Spec To Repo safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.