npm package: @klugeinteractive/create-kluge-app
Description: Scaffold a new app for the Kluge Studio OS platform
Latest version: 1.4.0

# create-kluge-app

Scaffold a new app for the **Kluge Studio OS** platform. Generates a project pre-wired for Kluge Studio: database connection, `0.0.0.0` binding, `/health` endpoint, idempotent migrations, and Kluge Studio OS SDK integration.

Every app includes both a **React/Vite frontend** and a **backend API** — choose your backend language.

---

## Usage

```bash
npx create-kluge-app my-app --type=fullstack-node    # Vite + Express
npx create-kluge-app my-app --type=fullstack-python  # Vite + FastAPI
npx create-kluge-app my-app --type=fullstack-flask   # Vite + Flask
```

Or interactively (no arguments needed):

```bash
npx create-kluge-app
# → prompts for name and template type
```

### Options

| Argument | Description |
|---|---|
| `name` | App name (positional). Must match `/^[a-z][a-z0-9-]*$/` — used as subdomain and Docker container name. |
| `--type` | Template: `fullstack-node`, `fullstack-python` (FastAPI), or `fullstack-flask` |

---

## Templates

### `fullstack-node` — Vite + Express + PostgreSQL

```bash
npx create-kluge-app my-app --type=fullstack-node
cd my-app
cd server && npm install
cd ../client && npm install
cp ../.env.example ../.env   # fill in DATABASE_URL
# Terminal 1 — API
cd server && npm run dev       # :3000
# Terminal 2 — UI
cd client && npm run dev       # :5173 (proxies /api → :3000)
```

**Structure:**
```
my-app/
  client/        — Vite + React, proxies /api to server
  server/        — Express, /health, DB migrations
  Dockerfile     — multi-stage (node build + node runtime)
  .env.example
```

**What's included:**
- `server/src/index.js` — Express app, `/health`, runs migrations on startup
- `server/src/db/migrate.js` — Idempotent migration runner with `_migrations` table
- `client/src/App.jsx` — `KlugeStudio.setNav()`, `getUser()`, `notify()` examples
- `client/index.html` — links `theme.css` and `sdk.js` with `data-dev="true"` for local dev

**Adding a migration:**

```js
// server/src/db/migrate.js
const MIGRATIONS = [
  {
    name: '001_create_items',
    sql: `CREATE TABLE IF NOT EXISTS items ( ... );`,
  },
];
```

---

### `fullstack-python` — Vite + FastAPI + PostgreSQL

```bash
npx create-kluge-app my-app --type=fullstack-python
cd my-app
cd client && npm install
cd ../server && python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp ../.env.example ../.env   # fill in DATABASE_URL
# Terminal 1 — API
uvicorn main:app --reload --port 8000   # :8000
# Terminal 2 — UI
cd client && npm run dev                # :5173 (proxies /api → :8000)
```

**Structure:**
```
my-app/
  client/              — Vite + React, proxies /api to server
  server/
    main.py            — FastAPI app, /health, serves static in prod
    database.py        — SQLAlchemy engine + session + Base
    requirements.txt
  Dockerfile           — multi-stage (node build + python runtime)
  .env.example
```

**Adding a model:**

```python
# server/models.py
from database import Base
from sqlalchemy import Column, Integer, String

class Item(Base):
    __tablename__ = "items"
    id   = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
```

Import in `main.py` before `Base.metadata.create_all()`:

```python
import models  # noqa — registers model with Base
```

---

### `fullstack-flask` — Vite + Flask + PostgreSQL

```bash
npx create-kluge-app my-app --type=fullstack-flask
cd my-app
cd client && npm install
cd ../server && python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp ../.env.example ../.env   # fill in DATABASE_URL
# Terminal 1 — API
python app.py                # Flask on :5000
# Terminal 2 — UI
cd ../client && npm run dev  # :5173 (proxies /api → :5000)
```

**Structure:**
```
my-app/
  client/              — Vite + React, proxies /api to server
  server/
    app.py             — Flask create_app() factory, /health, serves static in prod
    database.py        — flask-sqlalchemy db instance
    requirements.txt
  Dockerfile           — multi-stage (node build + python runtime)
  .env.example
```

**Adding a model:**

```python
# server/models.py
from database import db

class Item(db.Model):
    id   = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)
```

In `app.py`, import inside `create_app()` before `db.create_all()`:

```python
def create_app():
    ...
    with app.app_context():
        import models  # noqa
        db.create_all()
```

---

## Kluge Studio deploy requirements

Every template is pre-configured for these requirements:

| Requirement | How it's met |
|---|---|
| Binds to `0.0.0.0` | `app.listen(PORT, '0.0.0.0')` / `uvicorn --host 0.0.0.0` |
| `/health` returns `200` | Included in all templates |
| `DATABASE_URL` from env | Read from `process.env.DATABASE_URL` / `os.getenv` |
| RDS SSL compatibility | `sslmode` stripped from URL + `rejectUnauthorized: false` (node) |
| Idempotent startup | Migrations use `CREATE TABLE IF NOT EXISTS` / SQLAlchemy `create_all` |
| Auto-detected project type | `package.json` → node/vite, `requirements.txt` + fastapi → fastapi |

---

## Kluge Studio OS SDK reference

```js
// Register nav items (flat format) — icon is optional
KlugeHub.setNav([
  { label: 'Home',    url: '/',        icon: 'home' },
  { label: 'Reports', url: '/reports', icon: 'bar-chart' },
]);

// Register nav items with sections
KlugeHub.setNav([
  { section: 'Main', items: [
    { label: 'Home',    url: '/',        icon: 'home' },
    { label: 'Reports', url: '/reports', icon: 'bar-chart' },
  ]},
  { section: 'Account', items: [
    { label: 'Settings', url: '/settings', icon: 'settings' },
    { label: 'Log out',  url: '/logout',   icon: 'log-out' },
  ]},
]);

// Handle in-app SPA navigation from the sidebar
KlugeHub.onNavigate((path) => {
  router.push(path); // React Router, Vue Router, etc.
});

// Get the logged-in user
const user = KlugeHub.getUser();
// → { id, email, name, role, picture } | null

// Send a notification
KlugeHub.notify({ title: 'Done!', body: 'Export ready.', kind: 'success' });
// kind: 'info' | 'success' | 'error' | 'warning'

// Broadcast a message to all other open apps
KlugeHub.broadcast('data-updated', { table: 'orders' });

// Listen for broadcasts from other apps
KlugeHub.listen('data-updated', (payload) => console.log(payload));
KlugeHub.unlisten('data-updated', handler);

// Badge — show a count bubble on this app's hub icon
KlugeHub.setBadge(5);   // pink bubble on the app icon in the hub grid
KlugeHub.clearBadge();  // remove it

// Icons — 62 built-in Lucide SVG icons (no network request)
const icon = KlugeHub.icon('trash', 18); // → SVGElement, size optional (default 16)
button.prepend(icon);
console.log(KlugeHub.icons); // → ['home', 'menu', 'trash', ...]

// Confirm dialog — hub-native modal, keyboard-accessible
const ok = await KlugeHub.confirm('Delete this item?', {
  title: 'Are you sure?',
  confirmText: 'Delete',
  cancelText: 'Cancel',
  kind: 'danger',  // 'default' | 'danger'
});
if (ok) deleteItem();
```

### Kluge Studio OS theme CSS classes

Provided by `https://api.studio.klugeinteractive.com/theme.css`.

| Class | Description |
|---|---|
| `.k-card`, `.k-card-sm` | Glass card container |
| `.k-btn` | Base button |
| `.k-btn-primary` | Accent-colored button |
| `.k-btn-ghost` | Outline button |
| `.k-btn-danger` | Red destructive button |
| `.k-btn-sm` | Small button modifier |
| `.k-input`, `.k-textarea`, `.k-select` | Form inputs |
| `.k-label` | Form label |
| `.k-badge` | Neutral badge |
| `.k-badge-green/yellow/red/blue/accent` | Colored badges |
| `.k-table` | Styled table |
| `.k-grid-2`, `.k-grid-3` | 2/3-column grid |
| `.k-stack` | Vertical flex stack |
| `.k-row` | Horizontal flex row |
| `.k-muted` | Dimmed text |
| `.k-accent` | Accent-colored text |
| `.k-divider` | Horizontal rule |

---

## Naming rules

App names must match `/^[a-z][a-z0-9-]*$/`:

```
✔ my-app
✔ trading-dashboard
✔ api2
✘ My_App       (uppercase, underscore)
✘ 2fast        (starts with digit)
✘ my app       (space)
```

The name is used as the subdomain (`my-app.studio.klugeinteractive.com`) and Docker container name.