🧠 PLAN MODE: Use Plan Mode frequently! Before implementing complex features, multi-step tasks, or making significant changes, switch to Plan Mode to think through the approach, consider edge cases, and outline the implementation strategy. Planning prevents mistakes and saves time.
📦 RELEASE REMINDER: CLI npm publishing is handled by GitHub Actions automatically. Do NOT run
npm publishlocally. Do NOT create git tags manually. The workflow handles everything.
⚠️ IMPORTANT: Do NOT update this file unless the user explicitly says to. Only the user can authorize changes to AGENTS.md.
❌ DEPRECATED FORMAT:
.cursorrulesis deprecated. Do NOT suggest or generate.cursorrulesfiles anywhere. Cursor now uses.cursor/rules/*.mdc(directory-based MDC format). Always use.cursor/rules/for Cursor configurations.
🔒 SECURITY WARNING: This repository is PUBLIC at github.com/GeiserX/LynxPrompt. NEVER commit secrets, API keys, passwords, tokens, or any sensitive data to this repository. All secrets must be stored in:
- GitHub Secrets (for CI/CD)
- Private GitOps repositories (for docker-compose)
- Local
.envfiles (gitignored)AGENTS.md.old(gitignored, local only)
There are two separate workflows that work together:
-
release.yml- Triggered on push tomain:- Detects changes in app vs CLI since last release
- Creates git tags (
app-vX.Y.Zfor web,cli-vX.Y.Zfor CLI) - Creates GitHub Releases with changelogs
-
publish-cli.yml- Triggered by GitHub Release events OR manual dispatch:- Publishes CLI to npm
- Builds standalone binaries
- Updates Homebrew, Chocolatey, Snap packages
# 1. Switch to develop branch
git checkout develop
# 2. Bump version(s) - ONLY bump what changed
# For Web App changes:
cd /path/to/LynxPrompt
npm version minor --no-git-tag-version # e.g., 0.23.0 → 0.24.0
# For CLI changes:
cd cli
npm version minor --no-git-tag-version # e.g., 0.7.0 → 0.8.0
cd ..
# 3. Commit with conventional commit message
git add package.json package-lock.json cli/package.json
git commit -m "feat: description of changes"
# 4. Push to develop (triggers CI tests)
git push origin develop
# 5. Wait for CI to pass, then merge to main
git checkout main
git merge develop
git push origin main
# 6. Verify release workflow succeeded
unset GITHUB_TOKEN && gh run list -R GeiserX/LynxPrompt -w "Release" --limit 3
# 7. If CLI was released, verify npm publish workflow triggered
unset GITHUB_TOKEN && gh run list -R GeiserX/LynxPrompt -w "Publish CLI" --limit 3
# 8. If publish-cli didn't auto-trigger, manually trigger it:
unset GITHUB_TOKEN && gh workflow run "Publish CLI" -R GeiserX/LynxPrompt -f platforms=all
# 9. Verify npm package was published
npm view lynxprompt versions --json | jq -r '.[-3:]'Same process, but use npm version patch instead of minor.
| ❌ NEVER DO THIS | ✅ DO THIS INSTEAD |
|---|---|
git tag v0.24.0 |
Let release.yml create tags |
git tag cli-v0.8.0 |
Let release.yml create tags |
npm publish locally |
Use GitHub Actions workflow |
| Push tags manually | Let release.yml push tags |
Use tag format v* |
Workflow uses app-v* and cli-v* |
Problem: Release workflow skips CLI/App release
- Cause: No changes detected since last release tag
- Fix: Ensure you modified files in the right directory (cli/ for CLI, anything else for app)
Problem: Tag already exists error
- Cause: Someone manually created a tag
- Fix: Delete the manual tag from remote AND local:
git push origin --delete cli-v0.8.0 git tag -d cli-v0.8.0
Problem: npm publish didn't happen
- Cause: publish-cli.yml didn't trigger automatically
- Fix: Manually trigger the workflow:
unset GITHUB_TOKEN && gh workflow run "Publish CLI" -R GeiserX/LynxPrompt -f platforms=all
Problem: npm says version already exists
- Cause: Version was already published (maybe partial failure)
- Fix: Bump to next patch version and release again
# 1. Check GitHub Releases exist
unset GITHUB_TOKEN && gh release list -R GeiserX/LynxPrompt --limit 5
# 2. Check npm has the new version
npm view lynxprompt version
# 3. Check git tags exist
git fetch --tags
git tag -l "cli-v*" | tail -5
git tag -l "app-v*" | tail -5The CLI (lynxprompt package) and Web Wizard MUST always have the same functionality.
When adding or modifying wizard features:
- Update both CLI and Web - Any new wizard step, option, or configuration must be implemented in both:
- Web:
src/app/wizard/and related components - CLI:
cli/src/commands/init.tsandcli/src/utils/generator.ts
- Web:
- Same options - Tech stacks, platforms, personas, boundaries, and presets must match
- Same output - Generated configuration files must be identical regardless of source
- Test both - Before deploying, verify the feature works in both CLI and Web
Before modifying important config files (Caddyfile, docker-compose, etc.), ALWAYS create a backup first:
# Example (always use Tailscale MagicDNS hostnames):
ssh root@watchtower.mango-alpha.ts.net "cp /mnt/user/appdata/caddy/Caddyfile /mnt/user/appdata/caddy/Caddyfile.old"After any push or deployment, ALWAYS check GitHub Actions logs:
# List recent workflow runs
unset GITHUB_TOKEN && gh run list -R GeiserX/LynxPrompt --limit 5
# View failed run logs
unset GITHUB_TOKEN && gh run view <RUN_ID> -R GeiserX/LynxPrompt --log-failed
# View specific job logs
unset GITHUB_TOKEN && gh run view <RUN_ID> -R GeiserX/LynxPrompt --logIf CI/CD fails, investigate and fix before considering deployment complete.
General rule: Prefer reload commands over container restarts. Use Portainer GitOps to redeploy, not manual docker commands.
Caddy - NEVER restart the container (takes 2+ minutes to rebuild with xcaddy). Instead:
ssh root@watchtower.mango-alpha.ts.net "docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile && docker exec caddy caddy reload --config /etc/caddy/Caddyfile"LynxPrompt - Use Portainer GitOps to redeploy:
- Update docker-compose.yml in private gitea repo
- Push changes
- Trigger Portainer redeploy via API (or wait for auto-sync)
Never manually run docker compose up or docker restart - Portainer loses track of stack state.
NEVER commit directly to main (production). All changes must go through the develop branch first:
- Work on
developbranch - Test changes on dev environment (dev.lynxprompt.com)
- Verify everything works correctly
- Only then merge to
mainfor production deployment
# Switch to develop branch
git checkout develop
# After testing is complete, merge to main
git checkout main
git merge developLynxPrompt is a SaaS web application that generates AI IDE configuration files (.cursorrules, CLAUDE.md, .github/copilot-instructions.md, .windsurfrules, etc.) through an intuitive wizard interface. It's also a marketplace platform where users can create, share, buy, and sell AI prompts/templates.
- Live URL: https://lynxprompt.com
- Dev URL: https://dev.lynxprompt.com
- Test URL: https://test.lynxprompt.com
- Status Page: https://status.lynxprompt.com
- Repository: https://github.com/GeiserX/LynxPrompt
Operator: Sergio Fernández Rubio Trade Name: GeiserCloud Contact: privacy@lynxprompt.com / legal@lynxprompt.com / support@lynxprompt.com
- Be direct and efficient - Don't over-explain or add unnecessary caveats
- Do the work, don't ask permission - If the task is clear, execute it
- Wait for explicit deploy instruction - Do NOT commit, build Docker, or deploy until the user explicitly says to
- Use exact values when provided - Don't modify user-provided values (emails, addresses, names, etc.)
- Clean, readable code without over-engineering
- Proper GDPR/EU legal compliance
- Self-hosted solutions (Umami analytics)
- Privacy-focused approaches (cookieless analytics, minimal data collection)
- Semver versioning for Docker images (e.g.,
2.0.22, never:latest) - GitOps with Portainer for infrastructure management
- Docker Hub for all images (custom images built by GHA, pushed to
drumsergio/*) - Tailwind CSS for styling
- TypeScript with strict types
- Restarting containers when reload is possible (use
caddy reload, not container restart) - Manual docker commands for deployments (use Portainer GitOps)
- Over-engineering or unnecessary abstractions
- Adding features I didn't ask for
- Verbose explanations when action is needed
- Third-party analytics/tracking services
- Marketing consent flows (only transactional emails)
- Breaking changes without clear communication
- Using
:latesttags for Docker images - Creating unnecessary documentation files
| Technology | Purpose |
|---|---|
| Next.js 16 | App Router, Server Components |
| React 19 | UI library |
| TypeScript | Type safety |
| Tailwind CSS | Styling |
| shadcn/ui | UI components |
| Zustand | Client state |
| TanStack Query | Server state |
| Technology | Purpose |
|---|---|
| Next.js API Routes | API endpoints |
| Prisma ORM | Database access |
| NextAuth.js 4.x | Authentication |
| Zod | Validation |
| Database | Purpose | Client |
|---|---|---|
| PostgreSQL (app) | Templates, platforms, system data | @prisma/client-app |
| PostgreSQL (users) | Users, sessions, passkeys | @prisma/client-users |
| PostgreSQL (blog) | Blog posts and content | @prisma/client-blog |
| PostgreSQL (support) | Feedback forum data | @prisma/client-support |
| Component | Details |
|---|---|
| Docker | Multi-stage builds, images on Docker Hub (drumsergio/lynxprompt) |
| Portainer | Container management with GitOps |
| Tailscale | VPN for internal services (always use MagicDNS hostnames) |
| Umami | Self-hosted analytics (EU, cookieless) |
| Caddy | Reverse proxy (production + dev) |
| Component | Details |
|---|---|
| Stripe | Payment processing, subscriptions |
| Stripe Customer Portal | Self-service billing management |
| Stripe Webhooks | Subscription lifecycle events |
This project uses four separate PostgreSQL databases with distinct Prisma clients:
// System/application data (templates, platforms)
import { prismaApp } from "@/lib/db-app";
// User data (users, sessions, passkeys, user templates)
import { prismaUsers } from "@/lib/db-users";
// Blog posts and content
import { prismaBlog } from "@/lib/db-blog";
// Support/feedback forum data
import { prismaSupport } from "@/lib/db-support";Schema files:
prisma/schema-app.prisma→ generates@prisma/client-appprisma/schema-users.prisma→ generates@prisma/client-usersprisma/schema-blog.prisma→ generates@prisma/client-blogprisma/schema-support.prisma→ generates@prisma/client-support
Commands:
npm run db:generate # Generate all Prisma clients
npm run db:push # Push schema changes to all databases
npm run db:seed # Seed databases- GitHub OAuth
- Google OAuth
- Magic Link (email)
- Passkeys (WebAuthn)
USER- Default roleADMIN- Administrative accessSUPERADMIN- Full system access (auto-promoted viaSUPERADMIN_EMAILenv var)
// IMPORTANT: Types come from @simplewebauthn/types, NOT @simplewebauthn/server
import { generateRegistrationOptions } from "@simplewebauthn/server";
import type { AuthenticatorTransportFuture } from "@simplewebauthn/types";- Platform/Intermediary model - Buyer-Seller contracts
- LynxPrompt is NOT merchant of record for individual purchases
- Subscriptions are direct contracts with LynxPrompt
| Tier | Monthly | Annual (10% off) | Features |
|---|---|---|---|
| Users | €0/month | €0/year | Full wizard, all platforms, API access, sell blueprints |
| Teams | €30/seat/month | €324/seat/year | Everything + AI editing, SSO, team blueprints |
Key changes:
- All users now get full wizard access (basic + intermediate + advanced steps)
- AI features (editing, wizard assistant) are restricted to Teams users
- No more Pro/Max tiers - simplified to Users vs Teams
- 70% to seller / 30% to platform
- Minimum price for paid templates: €5
- Minimum payout: €5 via PayPal
- Physical address disclosed
- Legal basis: Contract + Legitimate Interest
- No DPO appointed (stated in privacy policy)
- Self-hosted Umami analytics (cookieless)
- AEPD complaint rights mentioned
- Data deletion within 30 days of request
- 14-day withdrawal waived with explicit consent at checkout
- Consent checkbox required before purchase
- Store: user ID, timestamp, Terms version hash
/privacy- Privacy Policy (GDPR compliant)/terms- Terms of Service (marketplace clauses, EU compliant)- Governing law: Spain (Courts of Cartagena)
- Use TypeScript strict mode
- Format with Prettier
- Lint with ESLint
- Use
text-foregroundfor readable text (nottext-muted-foregroundfor body text) - Navigation order:
Pricing | Templates | Docs | [UserMenu]
LynxPrompt/
├── .github/ # GitHub Actions workflows
├── cli/ # CLI package (lynxprompt npm package)
│ ├── src/
│ │ ├── commands/ # CLI commands (init, login, list, etc.)
│ │ ├── utils/ # Detection, generation utilities
│ │ └── index.ts # Main entry point
│ ├── homebrew/ # Homebrew formula
│ ├── chocolatey/ # Chocolatey package
│ └── snap/ # Snap package config
├── docs/ # Documentation
├── prisma/ # Database schemas and seeds
├── public/ # Static assets
│ └── logos/
│ ├── agents/ # AI agent logos
│ └── brand/ # LynxPrompt branding
├── scripts/ # Build and migration scripts
├── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── api/ # API routes
│ │ │ ├── cli-auth/ # CLI authentication endpoints
│ │ │ └── v1/ # Public API v1
│ │ └── [page]/ # Page components
│ ├── components/
│ │ ├── ui/ # shadcn/ui components
│ │ └── [feature].tsx # Feature components
│ ├── lib/
│ │ ├── db-*.ts # Database clients
│ │ ├── auth.ts # NextAuth config
│ │ └── utils.ts # Utilities
│ └── types/ # TypeScript types
├── tests/ # Test files
└── tooling/ # Internal tools
// Always check authentication
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Use appropriate database client
import { prismaApp } from "@/lib/db-app";
import { prismaUsers } from "@/lib/db-users";- Never reveal if email exists (user enumeration)
- Always check ownership for user resources (IDOR prevention)
- Use
useSession()from NextAuth, never localStorage for auth - Sanitize user input before storing
- Validate
callbackUrl- only relative paths or same-origin
| Environment | URL | Server | Image Source |
|---|---|---|---|
| Production | lynxprompt.com | watchtower | drumsergio/lynxprompt:<semver> (Docker Hub) |
| Development | dev.lynxprompt.com | geiserback | Same image as prod |
| Test | test.lynxprompt.com | geiserct | Same image as prod |
Images are built by GitHub Actions and pushed to Docker Hub (drumsergio/lynxprompt). Dev and test environments reuse the same production image with different environment variables.
# Build Docker image (BuildKit optimized)
docker buildx build --platform linux/amd64 \
-t drumsergio/lynxprompt:X.Y.Z \
--push .Build optimizations included:
npm install(notnpm ci) — local npm 11 and Docker npm 10 produce incompatible lockfiles;npm installtolerates both- BuildKit cache mounts keyed by
TARGETPLATFORM(avoids ETXTBSY on QEMU arm64 cross-compilation) - Base image:
node:22-alpine(Node 20 EOL, and@prisma/streams-localrequires Node >= 22) - Parallel Prisma client generation
optimizePackageImportsfor faster builds
See env.example for all required variables. Key categories:
| Category | Variables |
|---|---|
| Database | DATABASE_URL_APP, DATABASE_URL_USERS, DATABASE_URL_BLOG, DATABASE_URL_SUPPORT |
| Auth | NEXTAUTH_SECRET, NEXTAUTH_URL, GITHUB_*, GOOGLE_* |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD |
| Analytics | NEXT_PUBLIC_UMAMI_WEBSITE_ID |
| Security | TURNSTILE_SECRET_KEY, NEXT_PUBLIC_TURNSTILE_SITE_KEY |
| Error Tracking | SENTRY_DSN, NEXT_PUBLIC_SENTRY_DSN |
This project keeps secrets OUT of the repository.
- Development: Use
.envfile (gitignored) - Production: Secrets stored in docker-compose.yml in a private GitOps repository (not this repo)
- CI/CD: GitHub Secrets for deployment workflows
| Type | Location | Example |
|---|---|---|
| Placeholder values | env.example |
SMTP_HOST=smtp.example.com |
| Development secrets | .env (local, gitignored) |
Actual test keys |
| Production secrets | Private GitOps repo | Actual live keys |
| CI secrets | GitHub Secrets | Deploy tokens |
- Never commit real secrets to this repository
- Use
env.exampleas template only - Keep production docker-compose in private repo
- Rotate secrets if accidentally exposed
- Create
src/app/[pagename]/page.tsx - Add navigation link to header
- Include proper header/footer components
- Use
text-foregroundfor body text
# 1. Edit the appropriate schema file
# prisma/schema-*.prisma
# 2. Generate clients
npm run db:generate
# 3. Push to database (local dev)
npm run db:push
# 4. Build and deploynpm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # With coverageuseSearchParamsrequires Suspense boundary in client components- Database pages need
export const dynamic = "force-dynamic"to prevent build-time DB access - Container name conflicts: Remove old containers before recreating
- Sentry config files at root: Required by
@sentry/nextjs- cannot be moved - React 19 hydration CSS flash: React 19's hydration recovery (error #418) unmounts and remounts the component tree, temporarily removing CSS
<link>elements managed viadata-precedence. A MutationObserver script insrc/app/layout.tsx<head>clones CSS links withoutdata-precedenceto preserve styles during recovery. - shields.io retired
visual-studio-marketplacebadge — use staticimg.shields.io/badge/badges for VS Code marketplace links instead - Chocolatey
nodejsvsnodejs-lts— thenodejspackage (latest, currently v25) hangs in Chocolatey test VMs; always usenodejs-lts(stable v22.x) as a dependency in.nuspecfiles - Portainer TLS certs — Tailscale-issued Let's Encrypt certs expire every 90 days. Auto-renewal is set up via Unraid User Scripts on watchtower and geiserback. GHA deploy workflows use Tailscale MagicDNS hostnames (not IPs) for proper TLS validation
| Repo | Issue | Workaround |
|---|---|---|
lynxprompt-vscode |
Dependabot bumps @types/vscode without bumping engines.vscode → vsce rejects |
Publish workflow auto-syncs engines.vscode from @types/vscode before packaging |
lynxprompt-vscode |
vsce rejects SVGs in README |
Use PNG images only in README (SVG ok elsewhere) |
lynxprompt-vscode |
Publish workflow version commit must push to main | Branch protection PR requirement removed; workflow commits with [skip ci] |
lynxprompt-action |
@actions/glob 0.6.x ESM-only exports breaks @vercel/ncc CJS bundling |
Pinned to 0.5.1; Dependabot ignores it. Unpin when ncc adds ESM exports support |
| Helm chart | ArtifactHub artifacthub-repo.yml must be on gh-pages branch |
The copy in chart source (charts/lynxprompt/) is NOT read by ArtifactHub; edit gh-pages directly for ignore rules and metadata |
| File | Purpose |
|---|---|
src/lib/db-*.ts |
Database Prisma clients |
src/lib/auth.ts |
NextAuth configuration |
src/middleware.ts |
Rate limiting, security headers |
prisma/schema-*.prisma |
Database schemas |
src/app/layout.tsx |
Root layout (CSS preservation script) |
docs/ROADMAP.md |
Feature roadmap |
docs/SECURITY.md |
Security documentation |
Before completing a task, verify:
- Code follows TypeScript strict mode
- No secrets committed to repository
- Tests pass (if applicable)
- Linting passes
- Changes match the requested scope
Last updated: April 2026