diff --git a/README.md b/README.md index dd1dadf..d30efb1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # Universal Project Compiler Agent +

+ Termux ready + FastAPI powered + Vercel ready +

+ +

+ Android-first compiler agent for turning requirements into secure, runnable project scaffolds.
+ Preview the landing page · Deploy on Vercel +

+ 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). @@ -58,6 +69,18 @@ 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 @@ -65,7 +88,7 @@ python -m pip install -e '.[dev]' ./scripts/check.sh ``` -`./scripts/check.sh` runs Ruff and the full pytest suite so release checks match CI. +`./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 diff --git a/public/app.js b/public/app.js new file mode 100644 index 0000000..d1bfe86 --- /dev/null +++ b/public/app.js @@ -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)); diff --git a/public/badges/fastapi.svg b/public/badges/fastapi.svg new file mode 100644 index 0000000..09196c8 --- /dev/null +++ b/public/badges/fastapi.svg @@ -0,0 +1,5 @@ + + + + FASTAPI POWERED + diff --git a/public/badges/termux.svg b/public/badges/termux.svg new file mode 100644 index 0000000..cba805d --- /dev/null +++ b/public/badges/termux.svg @@ -0,0 +1,5 @@ + + + + TERMUX READY + diff --git a/public/badges/vercel.svg b/public/badges/vercel.svg new file mode 100644 index 0000000..64d9bf7 --- /dev/null +++ b/public/badges/vercel.svg @@ -0,0 +1,5 @@ + + + + VERCEL READY + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..efc804d --- /dev/null +++ b/public/index.html @@ -0,0 +1,99 @@ + + + + + + + + + + Universal Project Compiler Agent + + + + + + +
+
+ + +
+
+
+

Android-first • Termux-ready • Secure by default

+

Turn rough requirements into polished, runnable software scaffolds.

+

Plan, compile, and ship maintainable Python/FastAPI projects with generated docs, tests, scripts, secret redaction, and low-resource workflows built for mobile developers.

+
+ Termux ready + FastAPI powered + Vercel ready +
+ +
+ +
+ +
+
+

What ships

+

Professional scaffolds with safety rails.

+
+
+
01

Planning engine

Converts natural language into Critical/High/Medium/Low implementation tasks with impact and file scope.

+
02

Safe generation

Normalizes slugs, rejects unsafe paths, escapes Markdown, and redacts common secrets before writing files.

+
03

Dual interface

Use the local CLI for Termux workflows or the FastAPI service for browsers, automation, and future dashboards.

+
+
+ +
+
+

Workflow

+

From prompt to project in three steps.

+
+
    +
  1. DescribePaste a README, spec, OCR text, or a plain-language product idea.
  2. +
  3. ReviewInspect prioritized work items, security notes, and inferred architecture.
  4. +
  5. CompileGenerate a runnable scaffold with docs, tests, scripts, and package metadata.
  6. +
+
+ +
+
+

Vercel-ready

+

Static landing page configuration is included.

+

Deploy the polished GitHub introduction page with Vercel after connecting the repository or by running the Vercel CLI with your account token.

+
+ Open Vercel import +
+
+ + + + + diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..2f44b99 --- /dev/null +++ b/public/styles.css @@ -0,0 +1,132 @@ +:root { + color-scheme: dark; + --bg: #050816; + --panel: rgba(15, 23, 42, 0.72); + --panel-strong: rgba(15, 23, 42, 0.9); + --text: #f8fafc; + --muted: #a5b4fc; + --soft: #cbd5e1; + --line: rgba(255, 255, 255, 0.14); + --accent: #8b5cf6; + --accent-two: #06b6d4; + --accent-three: #22c55e; + --shadow: 0 28px 110px rgba(0, 0, 0, 0.48); +} + +* { box-sizing: border-box; } +html { scroll-behavior: smooth; } +body { + margin: 0; + min-height: 100vh; + overflow-x: hidden; + background: + radial-gradient(circle at 12% 10%, rgba(139, 92, 246, 0.28), transparent 32rem), + radial-gradient(circle at 88% 4%, rgba(6, 182, 212, 0.22), transparent 30rem), + linear-gradient(135deg, #050816 0%, #0f172a 50%, #111827 100%); + color: var(--text); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; +} + +a { color: inherit; text-decoration: none; } +.shell { width: min(1120px, calc(100% - 32px)); margin: 0 auto; } +.aurora { + position: fixed; + z-index: -1; + width: 24rem; + height: 24rem; + border-radius: 999px; + filter: blur(28px); + opacity: 0.35; + animation: float 13s ease-in-out infinite alternate; +} +.aurora-one { left: -7rem; top: 18rem; background: var(--accent); } +.aurora-two { right: -8rem; top: 8rem; background: var(--accent-two); animation-delay: -4s; } + +.nav { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 24px 0; } +.brand { display: inline-flex; align-items: center; gap: 12px; font-weight: 900; } +.brand-mark { + display: grid; + width: 42px; + height: 42px; + place-items: center; + border-radius: 14px; + background: linear-gradient(135deg, var(--accent), var(--accent-two)); + box-shadow: 0 14px 36px rgba(6, 182, 212, 0.22); +} +.nav nav { display: flex; gap: 18px; color: var(--soft); font-weight: 700; } +.nav nav a:hover { color: white; } + +.hero { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr); gap: 36px; align-items: center; padding: 72px 0 84px; } +.eyebrow { margin: 0 0 14px; color: var(--muted); font-weight: 900; letter-spacing: 0.12em; text-transform: uppercase; } +h1, h2, h3 { margin: 0; line-height: 1; letter-spacing: -0.055em; } +h1 { max-width: 820px; font-size: clamp(48px, 8vw, 94px); } +h2 { font-size: clamp(34px, 5vw, 58px); } +h3 { font-size: 24px; } +.lede { max-width: 720px; margin: 24px 0; color: var(--soft); font-size: clamp(18px, 2vw, 22px); line-height: 1.7; } +.badge-row { display: flex; flex-wrap: wrap; gap: 10px; margin: 18px 0 30px; } +.badge-row img { height: 34px; filter: drop-shadow(0 12px 20px rgba(0, 0, 0, 0.22)); } +.actions { display: flex; flex-wrap: wrap; gap: 14px; } +.button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 48px; + border-radius: 999px; + padding: 0 20px; + font-weight: 900; + transition: transform 0.2s ease, border-color 0.2s ease, filter 0.2s ease; +} +.button:hover { transform: translateY(-2px); filter: brightness(1.08); } +.primary { background: linear-gradient(135deg, var(--accent), var(--accent-two)); color: white; } +.ghost { border: 1px solid var(--line); background: rgba(255, 255, 255, 0.06); } + +.terminal-card { + border: 1px solid var(--line); + border-radius: 28px; + background: linear-gradient(180deg, var(--panel), rgba(2, 6, 23, 0.78)); + box-shadow: var(--shadow); + overflow: hidden; + transform: perspective(900px) rotateY(-5deg) rotateX(3deg); +} +.terminal-bar { display: flex; gap: 8px; padding: 18px; border-bottom: 1px solid var(--line); } +.terminal-bar span { width: 12px; height: 12px; border-radius: 999px; background: var(--accent-three); } +.terminal-bar span:nth-child(2) { background: #f59e0b; } +.terminal-bar span:nth-child(3) { background: #ef4444; } +pre { margin: 0; padding: 24px; overflow: auto; color: #dbeafe; font-size: 15px; line-height: 1.75; } + +.section { padding: 72px 0; } +.section-heading { max-width: 700px; margin-bottom: 28px; } +.grid { display: grid; gap: 18px; } +.cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.card, .deploy, .steps li { + border: 1px solid var(--line); + border-radius: 26px; + background: var(--panel); + box-shadow: 0 20px 70px rgba(0, 0, 0, 0.22); +} +.card { padding: 24px; } +.card span { color: var(--accent-three); font-weight: 900; } +.card p, .steps span, .deploy p { color: var(--soft); line-height: 1.65; } +.split { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 28px; align-items: start; } +.steps { display: grid; gap: 14px; margin: 0; padding: 0; list-style: none; counter-reset: step; } +.steps li { display: grid; gap: 8px; padding: 22px; } +.steps strong { font-size: 22px; } +.deploy { display: flex; justify-content: space-between; gap: 24px; align-items: center; margin: 72px auto; padding: 30px; background: linear-gradient(135deg, rgba(139, 92, 246, 0.2), rgba(6, 182, 212, 0.14)); } +.footer { padding: 24px 0 40px; color: var(--soft); } +.reveal { opacity: 1; transform: translateY(0); } +.js .reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.8s ease, transform 0.8s ease; } +.js .reveal.visible { opacity: 1; transform: translateY(0); } + +@keyframes float { + from { transform: translate3d(0, 0, 0) scale(1); } + to { transform: translate3d(3rem, -2rem, 0) scale(1.08); } +} + +@media (max-width: 860px) { + .nav { align-items: flex-start; flex-direction: column; } + .nav nav { flex-wrap: wrap; } + .hero, .split, .cards { grid-template-columns: 1fr; } + .hero { padding-top: 44px; } + .terminal-card { transform: none; } + .deploy { align-items: flex-start; flex-direction: column; } +} diff --git a/pyproject.toml b/pyproject.toml index 006121f..fe9c955 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,3 +47,7 @@ select = ["E", "F", "I", "B", "UP", "SIM"] [tool.ruff.lint.per-file-ignores] "app/universal_compiler_agent/server.py" = ["E501"] "app/universal_compiler_agent/templates.py" = ["E501"] + +[tool.codespell] +skip = "*.png,.git,.pytest_cache,.ruff_cache,.venv" +ignore-words-list = "thi" diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..f202d04 --- /dev/null +++ b/vercel.json @@ -0,0 +1,41 @@ +{ + "cleanUrls": true, + "trailingSlash": false, + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "Referrer-Policy", + "value": "strict-origin-when-cross-origin" + }, + { + "key": "Permissions-Policy", + "value": "camera=(), microphone=(), geolocation=()" + } + ] + } + ], + "rewrites": [ + { + "source": "/", + "destination": "/index.html" + }, + { + "source": "/styles.css", + "destination": "/public/styles.css" + }, + { + "source": "/app.js", + "destination": "/public/app.js" + }, + { + "source": "/badges/:path*", + "destination": "/public/badges/:path*" + } + ] +}