From 9e9ca0286dc0be3bca1a786eb4be645e3b0dd728 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?=
<252359928+Huynhthuongg@users.noreply.github.com>
Date: Wed, 3 Jun 2026 09:51:26 +0700
Subject: [PATCH 1/3] Add Vercel-ready project landing page
---
.github/workflows/ci.yml | 3 +-
README.md | 25 +++++++-
public/app.js | 15 +++++
public/badges/fastapi.svg | 5 ++
public/badges/termux.svg | 5 ++
public/badges/vercel.svg | 5 ++
public/index.html | 99 ++++++++++++++++++++++++++++
public/styles.css | 132 ++++++++++++++++++++++++++++++++++++++
pyproject.toml | 9 ++-
scripts/check.sh | 21 ++++++
vercel.json | 14 ++++
11 files changed, 328 insertions(+), 5 deletions(-)
create mode 100644 public/app.js
create mode 100644 public/badges/fastapi.svg
create mode 100644 public/badges/termux.svg
create mode 100644 public/badges/vercel.svg
create mode 100644 public/index.html
create mode 100644 public/styles.css
create mode 100644 vercel.json
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e150f07..57e9458 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,5 +18,4 @@ jobs:
python-version: ${{ matrix.python-version }}
- run: python -m pip install --upgrade pip
- run: python -m pip install -e '.[dev]'
- - run: ruff check .
- - run: pytest -q
+ - run: ./scripts/check.sh
diff --git a/README.md b/README.md
index dd1dadf..9a6ee72 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,16 @@
# Universal Project Compiler Agent
+
+
+
+
+
+
+
+ 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` verifies development dependencies, then runs Ruff, Codespell, 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.
+
+
+
+
+
+ $ 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
+
+
+
+
+
+
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.
+
+
+ Describe Paste a README, spec, OCR text, or a plain-language product idea.
+ Review Inspect prioritized work items, security notes, and inferred architecture.
+ Compile Generate a runnable scaffold with docs, tests, scripts, and package metadata.
+
+
+
+
+
+
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..81b73d2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,8 +20,9 @@ dependencies = [
dev = [
"pytest>=8.0,<9.0",
"ruff>=0.6,<1.0",
- "httpx>=0.27,<1.0",
- "httpx2>=2.3,<3.0"
+ "httpx>=0.28,<1.0",
+ "httpx2>=2.3,<3.0",
+ "codespell>=2.3,<3.0"
]
[project.scripts]
@@ -47,3 +48,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/scripts/check.sh b/scripts/check.sh
index 7f5a70c..5bdfb32 100755
--- a/scripts/check.sh
+++ b/scripts/check.sh
@@ -1,4 +1,25 @@
#!/usr/bin/env sh
set -eu
+
+missing=""
+for command in ruff codespell; do
+ if ! command -v "$command" >/dev/null 2>&1; then
+ missing="$missing $command"
+ fi
+done
+
+for module in pytest httpx2 httpx; do
+ if ! python -c "import ${module}" >/dev/null 2>&1; then
+ missing="$missing $module"
+ fi
+done
+
+if [ -n "$missing" ]; then
+ echo "Missing development dependencies:$missing" >&2
+ echo "Run: python -m pip install -e '.[dev]'" >&2
+ exit 1
+fi
+
ruff check .
+codespell README.md docs app tests pyproject.toml scripts config public vercel.json
pytest -q
diff --git a/vercel.json b/vercel.json
new file mode 100644
index 0000000..f058b70
--- /dev/null
+++ b/vercel.json
@@ -0,0 +1,14 @@
+{
+ "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=()" }
+ ]
+ }
+ ]
+}
From f34fb5554f6f5ade9e75ab3e2f7560a6d95f11f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?=
<252359928+Huynhthuongg@users.noreply.github.com>
Date: Wed, 3 Jun 2026 09:56:26 +0700
Subject: [PATCH 2/3] Reduce landing page PR merge scope
---
.github/workflows/ci.yml | 3 ++-
README.md | 2 +-
pyproject.toml | 5 ++---
scripts/check.sh | 21 ---------------------
vercel.json | 33 ++++++++++++++++++++++++++++++---
5 files changed, 35 insertions(+), 29 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 57e9458..e150f07 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,4 +18,5 @@ jobs:
python-version: ${{ matrix.python-version }}
- run: python -m pip install --upgrade pip
- run: python -m pip install -e '.[dev]'
- - run: ./scripts/check.sh
+ - run: ruff check .
+ - run: pytest -q
diff --git a/README.md b/README.md
index 9a6ee72..d30efb1 100644
--- a/README.md
+++ b/README.md
@@ -88,7 +88,7 @@ python -m pip install -e '.[dev]'
./scripts/check.sh
```
-`./scripts/check.sh` verifies development dependencies, then runs Ruff, Codespell, and the full pytest suite so release checks match CI. The Vercel landing page lives in `public/` and is served as a static site.
+`./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/pyproject.toml b/pyproject.toml
index 81b73d2..fe9c955 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,9 +20,8 @@ dependencies = [
dev = [
"pytest>=8.0,<9.0",
"ruff>=0.6,<1.0",
- "httpx>=0.28,<1.0",
- "httpx2>=2.3,<3.0",
- "codespell>=2.3,<3.0"
+ "httpx>=0.27,<1.0",
+ "httpx2>=2.3,<3.0"
]
[project.scripts]
diff --git a/scripts/check.sh b/scripts/check.sh
index 5bdfb32..7f5a70c 100755
--- a/scripts/check.sh
+++ b/scripts/check.sh
@@ -1,25 +1,4 @@
#!/usr/bin/env sh
set -eu
-
-missing=""
-for command in ruff codespell; do
- if ! command -v "$command" >/dev/null 2>&1; then
- missing="$missing $command"
- fi
-done
-
-for module in pytest httpx2 httpx; do
- if ! python -c "import ${module}" >/dev/null 2>&1; then
- missing="$missing $module"
- fi
-done
-
-if [ -n "$missing" ]; then
- echo "Missing development dependencies:$missing" >&2
- echo "Run: python -m pip install -e '.[dev]'" >&2
- exit 1
-fi
-
ruff check .
-codespell README.md docs app tests pyproject.toml scripts config public vercel.json
pytest -q
diff --git a/vercel.json b/vercel.json
index f058b70..9e4df4a 100644
--- a/vercel.json
+++ b/vercel.json
@@ -5,10 +5,37 @@
{
"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=()" }
+ {
+ "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": "/public/index.html"
+ },
+ {
+ "source": "/styles.css",
+ "destination": "/public/styles.css"
+ },
+ {
+ "source": "/app.js",
+ "destination": "/public/app.js"
+ },
+ {
+ "source": "/badges/:path*",
+ "destination": "/public/badges/:path*"
+ }
]
}
From 0bff53b7c4a3a5d3a520105c816229682cabf147 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?=
<252359928+Huynhthuongg@users.noreply.github.com>
Date: Wed, 3 Jun 2026 18:21:16 +0700
Subject: [PATCH 3/3] Update vercel.json
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
---
vercel.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vercel.json b/vercel.json
index 9e4df4a..f202d04 100644
--- a/vercel.json
+++ b/vercel.json
@@ -23,7 +23,7 @@
"rewrites": [
{
"source": "/",
- "destination": "/public/index.html"
+ "destination": "/index.html"
},
{
"source": "/styles.css",