---
name: dev-start
description: Launch SmartStack dev environment (backend + frontend + admin credentials)
argument-hint: "[--stop] [--restart] [--reset] [--backend-only] [--frontend-only]"
model: haiku
allowed-tools: Bash, BashOutput, PowerShell
disable-model-invocation: true
---

## Current state (auto-injected — no tool calls)
- Status: !`ss dev status --json 2>/dev/null || echo '{}'`
- Cache: !`cat .claude/config/dev-run.json 2>/dev/null || echo '{}'`
- Session: !`cat .claude/dev-session.json 2>/dev/null || echo '{}'`

## Important notes

- **Bash tool runs Git Bash (POSIX) on Windows, NOT PowerShell.** Use `VAR=value cmd` for env vars, never `$env:VAR = "..."`. Use `&&`, `||`, `2>/dev/null`, `$(...)` POSIX subshell substitution.
- **Use PowerShell (not Bash) for `dotnet run` and `npm run` launches.** Git Bash on Windows cannot reliably resolve `node_modules/.bin` binaries (e.g. `vite`), and background Bash processes are fragile. Use Bash only for `ss dev *` CLI commands and `curl` probes.
- **`--reset` invalidates the cache only.** It never resets DB state. Admin credentials are read directly from `Security:InitialAdmin` in `appsettings.{Mode}.json` (SmartStack.app v3.46+ seeds the user from this section on backend start). No CLI admin reset is performed.
- **`appsettings.{Mode}.json` overrides `appsettings.json`** — `ss dev detect` deep-merges both files (override wins) when reading connection string, admin credentials, CORS origins, and FrontendUrl. The `sources` field tells you which file each value came from.
- **Launch profile is auto-selected.** If `appsettings.Local.json` exists, the `Local` profile is preferred (else `http`). Never override `--urls` or env vars manually.
- **Cache freshness is enforced.** `ss dev status` returns `cache_stale: true` when the cache is missing, older than 7 days, or any tracked source file (`appsettings*.json`, `Properties/launchSettings.json`, `.env.*`) has changed since the last detect. The `stale_reason` field tells you why.

## Routing

Parse `$ARGUMENTS` :
- Contains `--stop` → run `ss dev stop [--backend-only|--frontend-only]` (1 Bash) → print JSON output → STOP.
- Contains `--restart` → run the **Restart workflow** below, then STOP (do NOT also run the normal workflow). Honors `--reset`, `--backend-only`, `--frontend-only`.
- Contains `--reset` → run `ss dev detect --reset --json` (1 Bash) → continue normal workflow.
- Contains `--backend-only` or `--frontend-only` → note the scope (single launch below).
- Otherwise → use the injected state.

## Workflow normal

1. **Idempotent shortcut** :
   If `status.backend_running == true && status.frontend_running == true && status.cache_stale == false && session.status == "ready"` → print panel directly (see Output below). **0 tool call**.

2. **Detect (if needed)** :
   If `status.cache_stale == true` OR `--reset` OR the injected `Cache` has **no `config_status` field** (a pre-upgrade cache written before self-repair existed) → run `ss dev detect --json` (1 Bash) → parse JSON for `api_dir`, `web_dir`, `backend_cmd`, `frontend_cmd`, `api_port`, `web_port`, `mode`, `launch_profile`, `sources`, `warnings`, `admin_email`, `admin_source`, `log_dir`, `log_enabled`, and the self-repair fields `config_status`, `proposed_ports`, `git_branch`, `mismatch`, `ports_occupied`, `api_busy_pid`, `web_busy_pid`.
   Otherwise reuse the injected cache.

   **Print the resolved sources** so the user can debug a misconfig in one glance:
   ```
   [api_port]   5293               ← Properties/launchSettings.json:Local
   [database]   SmartStack-ia-test ← appsettings.Local.json
   [admin]      test@local.admin   ← appsettings.Local.json
   ```

2b. **Confirm ports (or self-repair)** :
   Read `config_status` from the detect JSON.

   **If `config_status == "ok"`** → present the detected configuration and wait for confirmation :
   ```
   Configuration :
     Backend  → http://localhost:<api_port>  (profile: <launch_profile>)
     Frontend → http://localhost:<web_port>
     Admin    → <admin_email>  (← <admin_source>)

   Proceed with these ports?
   ```
   - Confirm (or Enter) → continue to step 2c.
   - Decline → print: "Edit `Properties/launchSettings.json` (backend port) or `.env.*` / `VITE_PORT` (frontend port), then re-run `/dev-start --reset`." STOP.
   - Skipped when the idempotent shortcut (step 1) fired.

   **If `config_status != "ok"`** (`"mismatch"` | `"occupied"` | `"needs_setup"`) → **SELF-REPAIR — do NOT halt** :
   1. Explain in one line, citing detect fields :
      - `mismatch`    → "Backend launch port `<mismatch.api_port>` ≠ frontend VITE_API_URL `:<mismatch.vite_port>` (`<mismatch.vite_source>`)."
      - `occupied`    → "Default ports busy on branch `<git_branch>` (api pid `<api_busy_pid>` / web pid `<web_busy_pid>`)."
      - `needs_setup` → "Fresh worktree on branch `<git_branch>` with no `appsettings.Local.json` — needs isolated config."
   2. Show the proposed free ports from `proposed_ports` :
      ```
      Proposed free ports :
        Backend http  → <proposed_ports.api>
        Backend https → <proposed_ports.https>
        Frontend vite → <proposed_ports.web>
      ```
   3. Collect the MANDATORY SQL details (cannot be auto-generated — ask, accept inline answers) :
      - **Auth mode** : SQL login **or** Windows? (→ `authMode` = `"sql"` | `"windows"`)
      - **SQL Server** host (e.g. `localhost`, `.\SQLEXPRESS`).
      - **Database** — default = `SmartStack_<slug>`, where `<slug>` = `git_branch` with any
        `feature/`,`hotfix(es)/`,`release/`,`bugfix/` prefix removed and every non-alphanumeric run replaced by `_`
        (e.g. `hotfixes/licence-bug` → `SmartStack_licence_bug`). User may override.
      - If `authMode == "sql"` : **login** + **password**. (Skip both for Windows auth.)
      - **JWT secret is auto-generated by the CLI — do NOT ask for it.**
   4. Echo a summary (ports + server + database + login, password masked) and ask :
      "Apply this configuration? (creates `appsettings.Local.json` if absent — **gitignored**, it holds the SQL
      password + JWT — sets the launchSettings `Local` profile, and the frontend env)"
      Offer the manual escape : "— or reply `manual` to edit the files yourself; I'll list them and STOP."
   5. On confirm → ONE Bash (fill ports from `proposed_ports`, the rest from the answers) :
      ```
      ss dev configure --json --spec '{"apiPort":<proposed.api>,"httpsPort":<proposed.https>,"webPort":<proposed.web>,"authMode":"sql","sqlServer":"<server>","sqlDatabase":"<db>","sqlLogin":"<login>","sqlPassword":"<password>"}'
      ```
      For Windows auth, set `"authMode":"windows"` and **omit** `sqlLogin`/`sqlPassword`. Parse
      `{ success, written_files, jwt_generated }`; on `success:false` → print `error` and STOP.
   6. Re-detect : `ss dev detect --reset --json` → confirm `config_status == "ok"`. If still not ok → print the
      residual `warnings`/`mismatch` and STOP. Do **not** loop more than once (no infinite self-repair).
   7. Continue to step 2c → step 3 with the NEW ports.

   On `manual` → print: "Create/edit `appsettings.Local.json` (connection string + JWT) **and add it to `.gitignore`
   — it holds secrets**, set the `Properties/launchSettings.json` `Local` profile (applicationUrl +
   `ASPNETCORE_ENVIRONMENT=Local`), and `.env.local` (VITE_API_URL / VITE_PORT), then re-run `/dev-start --reset`." STOP.

2c. **Ensure frontend dependencies** :
   If detect reports `frontend_deps_ready == false` or `warnings` contain "dependencies not installed" → run `cd "<web_dir>" && npm install` (foreground Bash, timeout ~120s). On failure, print the npm error and STOP.

3. **Launch in PARALLEL (single message, two PowerShell with `run_in_background=true`)** — unless partial scope :
   - Backend : `Set-Location "<api_dir>"; <backend_cmd>` (PowerShell, run_in_background)
   - Frontend : `Set-Location "<web_dir>"; <frontend_cmd>` (PowerShell, run_in_background)

4. **Wait API (fail-fast on timeout)** : Bash curl loop (max 30 × 2s = 60s, accounts for cold-start seeding).
   Use the detect field `api_probe_endpoint` (default `/api/config/features` — anonymous GET, always mapped regardless of environment). Use `127.0.0.1` (not `localhost`) to avoid IPv6 ambiguity on Windows :
   ```bash
   API_READY=0
   for i in $(seq 1 30); do
     STATUS=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:<api_port><api_probe_endpoint>" 2>/dev/null || echo "000")
     if [ "$STATUS" != "000" ]; then
       API_READY=1; break
     fi
     sleep 2
   done
   if [ "$API_READY" = "0" ]; then
     echo "[FAIL] API did not respond on :$api_port within 60s"
     exit 1
   fi
   ```
   - Any non-`000` HTTP status (200, 401, 404, 500…) proves the server is listening → proceed.
   - `000` means connection refused (server not yet bound) → keep polling.
   - If the loop exhausts → print the failure line, **do NOT** print the credentials block, stop the workflow.

5. **Read credentials** : run `ss dev creds --json` (1 Bash). On non-zero exit → print error and stop.

6. **End-to-end healthcheck (login test)** : run `ss dev healthcheck --json` (1 Bash). Parses to `{ ok, status, hint, body }`.
   - If `ok == true` → proceed to step 7, print panel.
   - If `status == 401` → print `[FAIL] credentials don't match backend DB` plus the `hint` field, suggest `/dev-start --reset`. Do NOT print the credentials block.
   - If `status == 400` → print `[WARN] login returned 400` plus the `hint`. Wait 5s, retry once (admin seeding race). If still ≠ 2xx → print `[FAIL]` with `body` and stop.
   - If `status == 403` → print `[FAIL] admin account disabled/locked` plus `hint`.
   - If `status == 500 or 503` → print `[FAIL]` plus `hint`, suggest checking backend logs.
   - If `status == 0` → backend unreachable; treat like step 4 timeout.

7. **Output panel** :
   ```
   ╔════════════════════════════════════════════════════════════╗
   ║              SmartStack Dev Environment                     ║
   ╠════════════════════════════════════════════════════════════╣
   ║  Backend  : http://localhost:<api_port>  (<launch_profile>) ║
   ║  Frontend : http://localhost:<web_port>                    ║
   ║  Scalar   : http://localhost:<api_port>/scalar             ║
   ╠════════════════════════════════════════════════════════════╣
   ║  Admin Login (<admin_source>)                              ║
   ║  Email    : <email>                                        ║
   ║  Password : <password>                                     ║
   ║  Health   : ✓ login OK                                     ║
   ╚════════════════════════════════════════════════════════════╝
   ```
   Directly under the box, print the backend log folder (an absolute Windows path rarely fits the box, so it goes on its own line) :
   ```
   Logs : <log_dir>   (rolling daily)
   ```
   - `<log_dir>` is the folder from the cache/detect (`log_dir`) — never hardcode the file name prefix (it is configurable, e.g. `smartstack-*.log` or a client-specific prefix). If `log_enabled == false` → print `Logs : file sink disabled — console output only` instead of a path. If `log_dir` is absent from the injected cache (cache predates this field) → omit the Logs line entirely.
   If `warnings[]` is non-empty → print in yellow under the panel (CORS mismatch, mode mismatch, VITE_API_URL drift, etc.).

## Restart workflow (`--restart`)

Full cycle : **stop → clean rebuild → relaunch**. No idempotent shortcut — the cycle always runs. Honors `--backend-only` / `--frontend-only` (restart only that side) and `--reset` (force re-detect).

1. **Stop** : run `ss dev stop [--backend-only|--frontend-only] --json` (1 Bash). Stop runs FIRST (on the cached ports) so it kills the processes actually running, and frees the locked backend DLLs before the rebuild. If it returns `error: "No cache found…"` → nothing is running under management; ignore the error and continue to step 2.

2. **Resolve config** : if `status.cache_stale == true` OR `--reset` OR the cache is missing → run `ss dev detect --json` (1 Bash) and parse `api_dir`, `web_dir`, `backend_cmd`, `frontend_cmd`, `api_port`, `web_port`, `launch_profile`, `admin_email`, `admin_source`, `log_dir`, `log_enabled`, `warnings`. Otherwise reuse the injected cache.

2b. **Confirm ports** : if step 2 ran a fresh detect (cache was stale, missing, or `--reset`) → same confirmation prompt as normal workflow step 2b. If detect returns `config_status != "ok"`, run the normal-workflow self-repair flow (step 2b) before continuing. If the cache was reused unchanged → skip (user already confirmed these ports).

3. **Clean rebuild (fail-fast)** — run the in-scope sides in PARALLEL (single message, both foreground) :
   - Backend (skip if `--frontend-only`) : `cd "<api_dir>" && dotnet clean && dotnet build`. Use a generous Bash `timeout` (e.g. `300000` ms) — a clean build is slow. **If the build exits ≠ 0 → print the compiler error and STOP. Do NOT launch a backend with a broken build.**
   - Frontend (skip if `--backend-only`) : `cd "<web_dir>" && rm -rf node_modules/.vite` (clears the Vite dep-prebundle cache so the next start re-optimizes; never errors if the folder is absent).

4. **Relaunch in PARALLEL** (single message, two PowerShell with `run_in_background=true`), in-scope sides only :
   - Backend : `Set-Location "<api_dir>"; <backend_cmd>` (PowerShell, run_in_background)
   - Frontend : `Set-Location "<web_dir>"; <frontend_cmd>` (PowerShell, run_in_background)

5. **Wait API** (skip if `--frontend-only`) : same curl loop as normal workflow step 4 (max 30 × 2s). On timeout → print the failure line, do NOT print credentials, stop.

6. **Read credentials** : run `ss dev creds --json` (1 Bash). On non-zero exit → print error and stop.

7. **Healthcheck** (skip if `--frontend-only`) : run `ss dev healthcheck --json` (1 Bash). Same handling as normal workflow step 6.

8. **Output panel** : same panel as normal workflow step 7, but change the title line to `SmartStack Dev Environment (restarted)`.

## Constraints

- **Max 6 tool calls cold start** (detect + 2 launches + curl wait + creds + healthcheck). The self-repair path (step 2b, when `config_status != "ok"`) adds +2 (configure + re-detect); the happy path is unchanged.
- **Max ~9 tool calls for `--restart`** (stop + optional detect + 2 rebuilds + 2 launches + curl wait + creds + healthcheck). The restart cycle never takes the idempotent shortcut.
- **0 tool calls if everything is running and cache is fresh** (injected state suffices) — does not apply to `--restart`.
- Never re-Read the cache/session files manually — the injected state is the source of truth.
- Do not debug : if a launch crashes, print the BashOutput error and stop.
- SQL warning ≠ blocking error.
- Mode mismatch / occupied ports / fresh worktree are **NOT blocking** — they set `config_status != "ok"` and trigger the **self-repair flow** (step 2b): the CLI proposes free ports and writes a coherent backend+frontend config (`appsettings.Local.json` + launchSettings `Local` profile + frontend env) after the user supplies the mandatory SQL details (JWT auto-generated). `appsettings.Local.json` is created if absent and added to `.gitignore` (it holds the SQL password + JWT). A manual-escape option is always offered.
