Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ on:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: ${{ matrix.python-version }}
- run: python -m pip install --upgrade pip
- run: python -m pip install -e '.[dev]'
- run: ruff check .
- run: pytest
- run: pytest -q
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ backup-*.tar.gz

# Local configuration
.env

# Generated release screenshots (avoid binary files in Codex diffs)
docs/screenshots/*-release.png
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Universal Project Compiler Agent

<p align="center">
<img src="public/badges/termux.svg" alt="Termux ready" />
<img src="public/badges/fastapi.svg" alt="FastAPI powered" />
<img src="public/badges/vercel.svg" alt="Vercel ready" />
</p>

<p align="center">
<strong>Android-first compiler agent for turning requirements into secure, runnable project scaffolds.</strong><br />
<a href="public/index.html">Preview the landing page</a> · <a href="https://vercel.com/new">Deploy on Vercel</a>
</p>

Android-first, Termux-first development agent that transforms documents, specifications, repositories, OCR text, Markdown, or natural language requests into complete, runnable, maintainable software project scaffolds.

The original product specification is preserved in [docs/SPECIFICATION.md](docs/SPECIFICATION.md).
Expand Down Expand Up @@ -58,14 +69,33 @@ tests/ Unit tests
.github/workflows/ CI checks
```


## Vercel landing page

A polished static introduction page is included in `public/` with animated SVG badges and Vercel routing/security headers in `vercel.json`.

```bash
npm i -g vercel
vercel deploy --prod
```

If deploying from CI, provide your Vercel token as an environment secret and run `vercel deploy --prod --token "$VERCEL_TOKEN"`.

## Development

```bash
python -m pip install -e '.[dev]'
ruff check .
pytest
./scripts/check.sh
```

`./scripts/check.sh` runs Ruff and the full pytest suite so release checks match CI. The Vercel landing page lives in `public/` and is served as a static site.

## Current release

- Version: 0.1.1
- Release date: 2026-06-02
- Release focus: CLI dry-run previews, safe output directory validation, dashboard route alignment, and test workflow hardening.

## Security model

- Never hardcode secrets in generated output.
Expand Down
2 changes: 1 addition & 1 deletion app/universal_compiler_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Universal Project Compiler Agent package."""

__all__ = ["__version__"]
__version__ = "0.1.0"
__version__ = "0.1.1"
22 changes: 19 additions & 3 deletions app/universal_compiler_agent/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ def _read_input(args: argparse.Namespace) -> str:
return "Universal Project Compiler Agent"


def _safe_output_dir(value: str) -> Path:
output_dir = Path(value)
if output_dir.is_absolute() or ".." in output_dir.parts:
msg = "output_dir must be a safe relative path"
raise argparse.ArgumentTypeError(msg)
return output_dir


def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Compile requirements into a runnable project scaffold."
Expand All @@ -37,7 +45,15 @@ def build_parser() -> argparse.ArgumentParser:
compile_cmd.add_argument("--text", help="Inline requirements text.")
compile_cmd.add_argument("--name", help="Override generated project name.")
compile_cmd.add_argument(
"--output-dir", default="generated", help="Directory that will receive output."
"--dry-run",
action="store_true",
help="Print the generated plan as JSON without writing files.",
)
compile_cmd.add_argument(
"--output-dir",
default=Path("generated"),
type=_safe_output_dir,
help="Safe relative directory that will receive output.",
)
return parser

Expand All @@ -47,12 +63,12 @@ def main(argv: list[str] | None = None) -> int:
args = parser.parse_args(argv)
requirements = _read_input(args)

if args.command == "plan":
if args.command == "plan" or args.dry_run:
plan = build_plan(requirements, args.name)
print(json.dumps(asdict(plan), indent=2))
return 0

result = compile_project(requirements, Path(args.output_dir), args.name)
result = compile_project(requirements, args.output_dir, args.name)
print(f"Generated {result.file_count} files in {result.root}")
return 0

Expand Down
54 changes: 17 additions & 37 deletions app/universal_compiler_agent/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

from .compiler import compile_project
from .planner import build_plan
from .templates import INDEX_HTML

APP_VERSION = "0.1.1"


class PlanRequest(BaseModel):
Expand All @@ -22,8 +25,15 @@ class CompileRequest(PlanRequest):
output_dir: str = Field(default="generated", max_length=240)


def _safe_output_dir(value: str) -> Path:
output_dir = Path(value)
if output_dir.is_absolute() or ".." in output_dir.parts:
raise HTTPException(status_code=400, detail="output_dir must be a safe relative path")
return output_dir


def create_app() -> FastAPI:
app = FastAPI(title="Universal Project Compiler Agent", version="0.1.0")
app = FastAPI(title="Universal Project Compiler Agent", version=APP_VERSION)

@app.middleware("http")
async def security_headers(request: Request, call_next): # type: ignore[no-untyped-def]
Expand All @@ -36,38 +46,7 @@ async def security_headers(request: Request, call_next): # type: ignore[no-unty

@app.get("/", response_class=HTMLResponse)
def index() -> str:
return """
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Universal Project Compiler Agent</title>
<script defer src="https://cdn.vercel-insights.com/v1/script.js"></script>
<style>
:root { color-scheme: light dark; --bg:#0b1020; --card:#11172b; --text:#eef2ff; --muted:#aab4d4; --accent:#7c3aed; }
* { box-sizing: border-box; }
body { margin:0; min-height:100vh; font-family: Inter, ui-sans-serif, system-ui, sans-serif; background: radial-gradient(circle at top, #1d2b64, var(--bg)); color:var(--text); }
main { width:min(1040px, 100%); margin:auto; padding: clamp(24px, 5vw, 72px); }
.hero { display:grid; gap:24px; }
.badge { width:max-content; border:1px solid #ffffff22; border-radius:999px; padding:8px 12px; color:var(--muted); background:#ffffff0d; }
h1 { font-size:clamp(42px, 8vw, 82px); line-height:.95; letter-spacing:-0.06em; margin:0; }
p { color:var(--muted); font-size:clamp(16px, 2vw, 20px); max-width:760px; }
.grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap:16px; margin-top:36px; }
.card { background: color-mix(in srgb, var(--card) 88%, white 12%); border:1px solid #ffffff14; border-radius:24px; padding:22px; box-shadow:0 20px 60px #0006; }
code { color:#c4b5fd; }
</style>
</head>
<body><main><section class="hero">
<div class="badge">Android-first • Termux-first • Production-ready</div>
<h1>Compile requirements into runnable software projects.</h1>
<p>Use <code>POST /plan</code> to analyze requirements or <code>POST /compile</code> to emit a secure, maintainable scaffold with docs, tests, and scripts.</p>
</section><section class="grid">
<div class="card"><h2>CLI</h2><p><code>upca compile --input-file spec.md</code></p></div>
<div class="card"><h2>API</h2><p>JSON endpoints for automation and future UI integrations.</p></div>
<div class="card"><h2>Security</h2><p>Path safety, secret redaction, and hardened HTTP headers.</p></div>
</section></main></body></html>
"""
return INDEX_HTML

@app.get("/health")
def health() -> dict[str, str]:
Expand All @@ -80,10 +59,11 @@ def plan(request: PlanRequest) -> JSONResponse:

@app.post("/compile")
def compile_endpoint(request: CompileRequest) -> dict[str, object]:
output_dir = Path(request.output_dir)
if output_dir.is_absolute() or ".." in output_dir.parts:
raise HTTPException(status_code=400, detail="output_dir must be a safe relative path")
result = compile_project(request.requirements, output_dir, request.project_name)
result = compile_project(
request.requirements,
_safe_output_dir(request.output_dir),
request.project_name,
)
return {"root": str(result.root), "file_count": result.file_count, "slug": result.plan.slug}

return app
Expand Down
5 changes: 2 additions & 3 deletions app/universal_compiler_agent/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Compile requirements into secure runnable project scaffolds." />
<title>Universal Project Compiler Agent</title>
<script defer src="https://cdn.vercel-insights.com/v1/script.js"></script>
<style>
:root {
color-scheme: dark;
Expand Down Expand Up @@ -148,8 +147,8 @@
}
}

planButton.addEventListener('click', () => submit('/api/plan', planButton));
compileButton.addEventListener('click', () => submit('/api/compile', compileButton));
planButton.addEventListener('click', () => submit('/plan', planButton));
compileButton.addEventListener('click', () => submit('/compile', compileButton));
</script>
</body>
</html>
Expand Down
7 changes: 7 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.1.1 - 2026-06-02

- Added CLI `compile --dry-run` support for release-safe plan previews.
- Rejected unsafe CLI and API output directories before project generation.
- Served the maintained dashboard template from the FastAPI root route and aligned its buttons with `/plan` and `/compile`.
- Updated release metadata and development dependencies for the current test client stack.

## 0.1.0

- Implemented the initial Universal Project Compiler Agent CLI and FastAPI service.
Expand Down
4 changes: 1 addition & 3 deletions docs/SPECIFICATION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
AGENTS.md

Universal Project Compiler Agent
# Universal Project Compiler Agent

Android-First • Termux-First • Codex CLI • Claude Code

Expand Down
15 changes: 15 additions & 0 deletions public/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
document.documentElement.classList.add('js');

const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
}
},
{ threshold: 0.18 }
);

document.querySelectorAll('.reveal').forEach((node) => observer.observe(node));
5 changes: 5 additions & 0 deletions public/badges/fastapi.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/badges/termux.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/badges/vercel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 99 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Universal Project Compiler Agent turns requirements into secure runnable project scaffolds." />
<meta property="og:title" content="Universal Project Compiler Agent" />
<meta property="og:description" content="A Termux-first compiler agent with planning, safe scaffolds, tests, docs, and FastAPI automation." />
<meta property="og:type" content="website" />
<title>Universal Project Compiler Agent</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@500;700;800;900&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="aurora aurora-one"></div>
<div class="aurora aurora-two"></div>
<header class="shell nav">
<a class="brand" href="#top" aria-label="Universal Project Compiler Agent home">
<span class="brand-mark">UP</span>
<span>Universal Project Compiler Agent</span>
</a>
<nav aria-label="Page sections">
<a href="#features">Features</a>
<a href="#workflow">Workflow</a>
<a href="#deploy">Deploy</a>
</nav>
</header>

<main id="top">
<section class="shell hero">
<div class="hero-copy reveal">
<p class="eyebrow">Android-first • Termux-ready • Secure by default</p>
<h1>Turn rough requirements into polished, runnable software scaffolds.</h1>
<p class="lede">Plan, compile, and ship maintainable Python/FastAPI projects with generated docs, tests, scripts, secret redaction, and low-resource workflows built for mobile developers.</p>
<div class="badge-row" aria-label="Project badges">
<img src="badges/termux.svg" alt="Termux ready" />
<img src="badges/fastapi.svg" alt="FastAPI powered" />
<img src="badges/vercel.svg" alt="Vercel ready" />
</div>
<div class="actions">
<a class="button primary" href="#features">Explore features</a>
<a class="button ghost" href="#deploy">Deploy on Vercel</a>
</div>
</div>
<aside class="terminal-card reveal" aria-label="Compilation preview">
<div class="terminal-bar"><span></span><span></span><span></span></div>
<pre><code>$ upca plan --text "# CRM Dashboard"
✓ Priority plan generated
✓ Security notes attached
✓ Tests and scripts mapped

$ upca compile --output-dir generated
✓ README.md
✓ docs/PLAN.md
✓ tests/test_smoke.py
✓ scripts/start.sh</code></pre>
</aside>
</section>

<section id="features" class="shell section reveal">
<div class="section-heading">
<p class="eyebrow">What ships</p>
<h2>Professional scaffolds with safety rails.</h2>
</div>
<div class="grid cards">
<article class="card"><span>01</span><h3>Planning engine</h3><p>Converts natural language into Critical/High/Medium/Low implementation tasks with impact and file scope.</p></article>
<article class="card"><span>02</span><h3>Safe generation</h3><p>Normalizes slugs, rejects unsafe paths, escapes Markdown, and redacts common secrets before writing files.</p></article>
<article class="card"><span>03</span><h3>Dual interface</h3><p>Use the local CLI for Termux workflows or the FastAPI service for browsers, automation, and future dashboards.</p></article>
</div>
</section>

<section id="workflow" class="shell section split reveal">
<div>
<p class="eyebrow">Workflow</p>
<h2>From prompt to project in three steps.</h2>
</div>
<ol class="steps">
<li><strong>Describe</strong><span>Paste a README, spec, OCR text, or a plain-language product idea.</span></li>
<li><strong>Review</strong><span>Inspect prioritized work items, security notes, and inferred architecture.</span></li>
<li><strong>Compile</strong><span>Generate a runnable scaffold with docs, tests, scripts, and package metadata.</span></li>
</ol>
</section>

<section id="deploy" class="shell deploy reveal">
<div>
<p class="eyebrow">Vercel-ready</p>
<h2>Static landing page configuration is included.</h2>
<p>Deploy the polished GitHub introduction page with Vercel after connecting the repository or by running the Vercel CLI with your account token.</p>
</div>
<a class="button primary" href="https://vercel.com/new" rel="noopener">Open Vercel import</a>
</section>
</main>

<footer class="shell footer">AGPL-3.0-only • Built for focused, mobile-friendly software generation.</footer>
<script src="app.js"></script>
</body>
</html>
Loading
Loading