---
name: dev-start
description: Launch SmartStack dev environment (backend + frontend + admin credentials)
argument-hint: "[--stop] [--reset] [--backend-only] [--frontend-only]"
model: haiku
allowed-tools: Read, Bash, Write, TaskOutput
---

## Arguments
$ARGUMENTS

## Current state (auto-injected)

- Dev cache: !`cat .claude/config/dev-run.json 2>/dev/null || echo "no cache"`
- Dev session: !`cat .claude/dev-session.json 2>/dev/null || echo "no session"`
- Ports running: !`netstat.exe -ano 2>/dev/null | grep -E "LISTENING" | grep -E ":(5[0-9]{3}|6173|3000) " | head -5 || echo "none"`

<objective>
Launch SmartStack dev environment FAST. The injected state above is already loaded — use it directly, never re-read those files. Target: 0 tool calls if everything running, 2-3 to launch, 5 max cold start.
</objective>

<quick_start>
```bash
/dev-start                  # Launch all (idempotent, reuses cache+session)
/dev-start --stop           # Stop all services
/dev-start --reset          # Force re-detect config + reset password
/dev-start --backend-only   # Launch backend only
/dev-start --frontend-only  # Launch frontend only
```
</quick_start>

<subcommands>

| Command | Effect |
|---------|--------|
| `/dev-start` | Launch all services (0 tool calls if all running) |
| `/dev-start --stop` | Kill backend + frontend |
| `/dev-start --reset` | Re-detect config + reset admin password |
| `/dev-start --backend-only` | Launch backend only |
| `/dev-start --frontend-only` | Launch frontend only |

</subcommands>

<workflow>

## Handle --stop (Exit After)

Extract api_port, web_port from injected cache (default 5142, 6173). Single Bash:
```bash
AP={api_port from cache, default 5142}
WP={web_port from cache, default 6173}
APID=$(netstat.exe -ano | grep ":${AP} .*LISTENING" | awk '{print $5}' | head -1)
WPID=$(netstat.exe -ano | grep ":${WP} .*LISTENING" | awk '{print $5}' | head -1)
[ -n "$APID" ] && taskkill.exe /PID $APID /F 2>&1 && echo "Backend stopped (PID $APID)"
[ -n "$WPID" ] && taskkill.exe /PID $WPID /F 2>&1 && echo "Frontend stopped (PID $WPID)"
[ -z "$APID" ] && [ -z "$WPID" ] && echo "Nothing running"
```
Display result. Done. Exit.

---

## Step 0: Cache Decision

**CRITICAL: The injected state above IS your data source. NEVER re-read cache or session files.**

**If injected cache = "no cache" OR --reset flag → CACHE_HIT=false, go to Step 1 (cold start).**

**If injected cache has JSON values AND no --reset:**
- Extract directly from injected cache: api_dir, web_dir, api_port, web_port, backend_cmd, frontend_cmd, connection_string, sql_server, sql_database
- CACHE_HIT=true
- **Skip directly to Step 4 (check ports)**

---

## Step 1: Detect project (Cold Start Only)

**Only if CACHE_HIT=false.**

Single message with 3 PARALLEL tool calls:

1. **Read** `src/SmartStack.Api/appsettings.json` (limit: 40 lines — connection string, CORS, FrontendUrl)
2. **Read** `src/SmartStack.Api/Properties/launchSettings.json`
3. **Bash** — detect mode + frontend env + SQL check:
```bash
echo "=== MODE ==="
[ -f src/SmartStack.Api/appsettings.Local.json ] && echo "Local" || echo "Development"
echo "=== ENV ==="
cat web/smartstack-web/.env.development 2>/dev/null || cat web/smartstack-web/.env.standalone 2>/dev/null || echo "NONE"
echo "=== SQL ==="
CS=$(sed -n 's/.*"DefaultConnection":\s*"\([^"]*\)".*/\1/p' src/SmartStack.Api/appsettings.json 2>/dev/null)
SN=$(echo "$CS" | sed -n 's/.*[Ss]erver=\([^;]*\).*/\1/p' | sed 's/(local)/./')
DB=$(echo "$CS" | sed -n 's/.*[Dd]atabase=\([^;]*\).*/\1/p')
sqlcmd -S "$SN" -Q "SET NOCOUNT ON;SELECT name FROM sys.databases WHERE name='$DB'" -t 5 -E -C -h -1 -W 2>/dev/null | grep -q "$DB" && echo "ok $SN $DB" || echo "warn $SN $DB"
```

Determine config from results:

| | Development (no Local config) | Local (appsettings.Local.json exists) |
|--|-------------------------------|--------------------------------------|
| api_port | 5142 (launchSettings "http") | Port from launchSettings "Local" |
| web_port | 6173 | From .env.standalone or 5305 |
| backend_cmd | `dotnet run --launch-profile http` | `dotnet run --launch-profile Local` |
| frontend_cmd | `npm run dev` | `npm run local` |

## Step 2: Validate config coherence (Cold Start Only)

**Only if CACHE_HIT=false.**

Display one line per check, continue even on warnings:
- VITE_API_URL port matches api_port → `[OK]` or `[WARN] expected X, got Y`
- CorsOrigins includes `http://localhost:{web_port}` → `[OK]` or `[WARN]`
- Authentication:FrontendUrl = `http://localhost:{web_port}` → `[OK]` or `[WARN]`
- SQL status → `[OK] {server}/{db}` or `[WARN] SQL unreachable`

## Step 3: Write cache (Cold Start Only)

**Only if CACHE_HIT=false.**

Use Bash to write (avoids Write tool read-first requirement):
```bash
mkdir -p .claude/config && cat > .claude/config/dev-run.json << 'EOF'
{"version":2,"api_dir":"{api_dir}","web_dir":"{web_dir}","mode":"{mode}","api_port":{api_port},"web_port":{web_port},"backend_cmd":"{backend_cmd}","frontend_cmd":"{frontend_cmd}","connection_string":"{conn_str}","sql_server":"{srv}","sql_database":"{db}","sql_status":"{status}","cached_at":"{ISO now}"}
EOF
```

**IMPORTANT:** This Bash call can be parallelized with Step 5/6 launches in a single message.

## Step 4: Check if already running

Check the injected "Ports running" state to see what's already listening:
- Backend running = api_port appears in Ports
- Frontend running = web_port appears in Ports

If `--backend-only`: skip frontend checks.
If `--frontend-only`: skip backend checks.

**If both running → skip to Step 7.** ZERO tool calls needed.

## Step 5: Launch backend

If NOT already running and NOT `--frontend-only`:

```bash
cd {api_dir} && {backend_cmd}
```

Use `Bash(run_in_background=true)`. Track: `BACKEND_LAUNCHED=true`.

**Launch backend AND frontend in the SAME message as parallel Bash(run_in_background) calls.**

## Step 6: Launch frontend

If NOT already running and NOT `--backend-only`:

```bash
cd {web_dir} && {frontend_cmd}
```

Use `Bash(run_in_background=true)`.

**This MUST be in the same message as Step 5 (parallel launches).**
**If cold start, also include Step 3 (cache write) in the same message.**

## Step 7: Handle admin password

Choose ONE path based on BACKEND_LAUNCHED and session state:

### Path A: Backend just launched (ALWAYS reset — session or not)
The backend may have re-seeded the database on startup, invalidating any cached password.
Single Bash — wait for API then reset:
```bash
echo "Waiting for backend..."
for i in $(seq 1 20); do
  HTTP=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:{api_port}/scalar 2>/dev/null)
  [ "$HTTP" = "200" ] || [ "$HTTP" = "302" ] && echo "API ready" && break
  [ $i -eq 20 ] && echo "API timeout — continuing anyway"
  sleep 2
done
RESULT=$(timeout 20 smartstack admin reset --connection '{connection_string}' --force --json 2>&1 | grep -E '^\{')
EMAIL=$(echo "$RESULT" | sed -n 's/.*"email":"\([^"]*\)".*/\1/p')
PASS=$(echo "$RESULT" | sed -n 's/.*"password":"\([^"]*\)".*/\1/p')
cat > .claude/dev-session.json << SESSEOF
{"admin_email":"$EMAIL","admin_password":"$PASS","last_reset":"$(date -Iseconds)","api_port":{api_port},"web_port":{web_port},"environment":"{mode}","status":"ready"}
SESSEOF
echo "CREDENTIALS: $EMAIL / $PASS"
```

### Path B: Backend already running + no session (or --reset)
Single Bash — reset only (no wait needed):
```bash
RESULT=$(timeout 20 smartstack admin reset --connection '{connection_string}' --force --json 2>&1 | grep -E '^\{')
EMAIL=$(echo "$RESULT" | sed -n 's/.*"email":"\([^"]*\)".*/\1/p')
PASS=$(echo "$RESULT" | sed -n 's/.*"password":"\([^"]*\)".*/\1/p')
cat > .claude/dev-session.json << SESSEOF
{"admin_email":"$EMAIL","admin_password":"$PASS","last_reset":"$(date -Iseconds)","api_port":{api_port},"web_port":{web_port},"environment":"{mode}","status":"ready"}
SESSEOF
echo "CREDENTIALS: $EMAIL / $PASS"
```

### Path C: Everything running + session exists (no --reset)
ZERO tool calls. Use email + password from injected session directly.

## Step 8: Display connection info

```
================================================================
          SMARTSTACK DEV ENVIRONMENT
================================================================
  Backend:     http://localhost:{api_port}       [{RUNNING|LAUNCHED}]
  Frontend:    http://localhost:{web_port}       [{RUNNING|LAUNCHED}]
  Swagger:     http://localhost:{api_port}/scalar
  SQL Server:  {sql_server}/{sql_database}       [{OK|WARN}]

  Email:       {email}
  Password:    {password}

  Mode:        {Development|Local}
================================================================
```

</workflow>

<important_notes>

1. **Injected state IS source of truth** — Cache, session, and ports are already loaded above. NEVER re-read these files. This is the #1 speed optimization.
2. **NEVER debug or fix code** — If backend/frontend fails to start, display the error and STOP. No rebuilds, no NuGet restore, no diagnosis.
3. **Parallel launches** — Backend + frontend MUST be launched in the same message (parallel Bash background calls).
4. **Timeout = warn** — If API doesn't respond after 40s, warn user and show what you have.
5. **Admin reset needs --connection** — Single-quoted: `--connection '{conn_str}'`. Without it, CLI blocks on interactive picker.
6. **dev-session.json has secrets** — Must be in .gitignore.
7. **SQL check non-blocking** — Warnings don't prevent launch.
8. **Cold start writes cache** — So next run uses CACHED path (fast).
9. **Idempotent** — Running twice detects already-running processes via injected Ports.

</important_notes>

<success_criteria>
- Path C (all running + session): 0 tool calls — instant display
- Path A (cached, backend launched): 3 tool calls — launch + wait + reset ~25s
- Path B (running, no session): 1 tool call — reset only ~10s
- Cold start: 5 tool calls max — ~30s
- NEVER more than 5 tool calls total
</success_criteria>
