create-caspian-app 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,7 +24,7 @@ This is the top architectural requirement for this workspace. Treat it as a hard
24
24
 
25
25
  ## Global Rules
26
26
 
27
- - **Required frontend feedback loop:** for every UI change or browser-error fix, run `npm run logs` before editing and after reloading the affected route and exercising the changed interactions. It reads `.casp/browser-log.jsonl`; the developer's terminal is not your only source of frontend errors. Read both errors and warnings. A green Python/template/test gate or a visually correct first paint is not browser verification. If the browser or dev session is unavailable, report that verification gap explicitly.
27
+ - **Required frontend feedback loop:** for every UI change or browser-error fix, run `npm run logs` before editing. Make the whole related edit set while the dev hold queues changes, then run `npm run dev:resume` **once after the final edit**, reload the affected route, exercise the changed interactions, and run `npm run logs` again. Do not resume after each file: one editing run must produce one Python restart and one browser reload. The digest reads `.casp/browser-log.jsonl`; a green gate or visually correct first paint is not browser verification. If the browser or dev session is unavailable, report that gap explicitly. See `node_modules/caspian-utils/dist/docs/agent-development.md`.
28
28
  - **Read `pp.props` in the owning script, never directly in authored markup.** Forward incoming props onto the native root, then expose top-level script bindings: `const dialogOpen = !!pp.props.open; const isRotating = !!pp.props.rotate; const projectId = pp.props.projectId || '';`. Use `open="{dialogOpen}"`, `hidden="{isRotating}"`, and `project-id="{projectId}"`. Nested boundary/slot evaluation does not guarantee the script-local prop bag; `open="{!!pp.props.open}"` can throw even when root forwarding is correct. Script-defined callbacks/effects/RPC handlers may use `pp.props`. Do not mask this with optional chaining, replace browser `{...}` with Jinja `{{...}}`, or freeze derived props in state. Read `pulsepoint.md` "Read props in the script, bind names in markup" and `components.md` before editing prop-driven components.
29
29
 
30
30
  - Use this decision order: `caspian.config.json` for optional feature enablement, app runtime and app-owned code for current project behavior, matching workspace instruction files under `.github/instructions/**/*.instructions.md` for task-specific implementation guidance, installed `casp` runtime for framework internals, and packaged markdown docs for Caspian feature discovery and task routing.
@@ -177,11 +177,11 @@ This is the top architectural requirement for this workspace. Treat it as a hard
177
177
 
178
178
  - **An agent's whole editing run must cost one Python restart and one browser reload, not one per edit.** The change coordinator in `settings/bs-config.ts` batches on a 1500 ms quiet period tuned for a human's burst-save; an agent's gap between two edits is a tool round-trip and is always wider, so without a hold every edit restarts the server and reloads every open tab, re-running each route's Prisma queries against a pool the restart just discarded.
179
179
  - **The signal is explicit, because chokidar sees an inode change and not a writer.** `settings/dev-hold.ts` owns `.casp/dev-hold.json`; while it is active `SettledBatchWorker` keeps queueing and skips both the restart and the reload, then drains the run as one batch. Both expiry valves fail open (120 s without a refresh, 600 s absolute), so a crashed agent degrades to normal reloading rather than a frozen stack.
180
- - **Every agent host that reads this repo is wired, not just Claude Code.** `settings/dev-hold-hook.ts` runs on `PreToolUse` and `node settings/dev-hold.ts release --quiet` runs when the turn ends, configured in `.claude/settings.json`, `.github/hooks/dev-hold.json` (Copilot CLI and VS Code), and `.codex/hooks.json`. One script serves all three because they deliver the same PascalCase stdin payload (`tool_name`, `tool_input.command`); only the config file differs. Codex has no `SessionEnd`, so it releases on `Stop` alone and relies on the stale valve for a killed session.
180
+ - **Every supported agent host is wired, not just Claude Code.** `settings/dev-hold-hook.ts` runs on `PreToolUse` and `node settings/dev-hold.ts release --quiet` runs when the turn ends, configured in `.claude/settings.json`, `.github/hooks/dev-hold.json` (Copilot CLI and VS Code), and `.codex/hooks.json` (Codex CLI and compatible clients). One script serves all three because they deliver the same PascalCase stdin payload (`tool_name`, `tool_input.command`); only the config file differs. Codex has no `SessionEnd`, so it releases on `Stop` alone and relies on the stale valve for a killed session.
181
181
  - **`Bash` is in the matcher on purpose.** An agent told to prefer the shell for file changes edits with `cat > file <<'EOF'`, `sed -i`, or a throwaway Python script, none of which an `Edit|Write` matcher can see — that gap once cost a single feature branch eight restarts and five reloads. A blanket Bash match would over-correct, so `commandCanWrite` holds only for commands that can write, and never for the hold's own controls or the read-only quality gate: holding around `npm run logs` would print `DEV HOLD ACTIVE` over a perfectly current digest.
182
182
  - **The hook must never exit non-zero.** `PreToolUse` is fail-closed in GitHub Copilot, so a non-zero exit denies the agent's tool call outright. `dev-hold.ts` is therefore imported lazily inside the `try`/`catch`, the `.ts` extension in that specifier is load-bearing (this hook runs under plain `node`, which cannot resolve an extensionless relative import — unlike `bs-config.ts`, which runs under tsx), and nothing is written to stdout.
183
183
  - Tool names are normalised before matching, because the hosts spell the same tool `Edit`, `apply_patch`, and `insert_edit_into_file`. Adding a host means extending those sets plus that host's `matcher`, never forking the script.
184
- - Manual controls, and the fallback for a host without hooks: `npm run dev:hold`, `npm run dev:resume`, `npm run dev:hold:status`. **If `dev:resume` reports `No hold was active` during an editing run, the hook layer is not wired** — every edit so far has cost its own restart. Coverage is in `settings/dev-hold.test.ts` (including a test that the three config files exist and point at the hook) and `settings/utils.test.ts`, both run by the gate's `node` leg.
184
+ - Manual controls, and the fallback for a host without hooks: `npm run dev:hold`, `npm run dev:resume`, `npm run dev:hold:status`. Finish all related edits before the single explicit `dev:resume` used for in-turn browser verification. **If it reports `No hold was active` during an editing run, the current host's hook layer is not wired or did not fire** — check the applicable config and use the manual pair for the next batch. Coverage is in `settings/dev-hold.test.ts` and `settings/utils.test.ts`; the reusable workflow is in `node_modules/caspian-utils/dist/docs/agent-development.md`.
185
185
 
186
186
  ### `settings/build-static.py` and `settings/serve-static.py`
187
187
 
package/dist/AGENTS.md CHANGED
@@ -12,7 +12,7 @@ When you work here, use `caspian.config.json` and the code that actually runs as
12
12
 
13
13
  Do not treat the existence of a packaged doc as proof that the feature is enabled in this project.
14
14
 
15
- **Frontend verification is required for every UI change or browser-error fix.** Run `npm run logs` before editing to read the frontend reports from `.casp/browser-log.jsonl`, then **run `npm run dev:resume`** to apply your edits, reload the affected route, exercise the changed interactions, and run `npm run logs` again before reporting completion. The resume step is not optional: while an agent is editing, the dev stack holds its restart and reload on purpose (see "Dev hold" under Workspace Clarifications), so without it the route you reload and the log you read are still running the code from before your changes. A passing `npm run test` does not prove browser health: its browser-log section is informational and does not affect the exit code. Never skip this because Python tests passed or the first paint looks correct. See the browser-log rules under Workspace Clarifications for historical entries and unavailable browser access.
15
+ **Frontend verification is required for every UI change or browser-error fix.** Run `npm run logs` before editing to read the frontend reports from `.casp/browser-log.jsonl`. Make the complete related edit set while the agent hook holds development reloads, then **run `npm run dev:resume` once, after the final edit and before browser verification**. Let that single release apply the queued changes as one Python restart and one browser reload; only then exercise the affected route and interactions and run `npm run logs` again. Do not resume after each file or test against the pre-edit page while the hold is active. A passing `npm run test` does not prove browser health: its browser-log section is informational and does not affect the exit code. See "Dev hold" under Workspace Clarifications and `node_modules/caspian-utils/dist/docs/agent-development.md` for the full cross-host workflow.
16
16
 
17
17
  ## Caspian Core Contracts (Read Before Any Analysis)
18
18
 
@@ -358,18 +358,18 @@ Use `.github/copilot-instructions.md` for the repo-wide implementation rules. Th
358
358
  - **The signal is explicit, because the watcher cannot infer it.** chokidar sees an inode change, not a writer, so an agent's `Edit` and a human's save are indistinguishable. `settings/dev-hold.ts` owns a hold file at `.casp/dev-hold.json`; while it is active, `SettledBatchWorker` keeps queueing changes and skips both the restart and the reload, then drains the whole run as one batch with one settle. It lives in `.casp/` on purpose — `npm run dev` deletes that directory at startup, so a fresh stack cannot inherit a stale hold.
359
359
  - **It is set by hooks, not by agent discipline, and not only for Claude.** All three agent hosts that read this repo run `settings/dev-hold-hook.ts` on `PreToolUse` (~80 ms, dependency-free, run directly by Node) and `node settings/dev-hold.ts release --quiet` when the turn ends. One script serves all three, because they deliver the same PascalCase payload on stdin (`tool_name`, `tool_input.command`) — only the config file differs, and each host ignores the others':
360
360
 
361
- | Host | Config file | Release event |
362
- | ------------------------------ | ----------------------------- | --------------------------------------------------------------------------------- |
363
- | Claude Code | `.claude/settings.json` | `Stop`, `SessionEnd` |
364
- | GitHub Copilot (CLI + VS Code) | `.github/hooks/dev-hold.json` | `Stop`, `SessionEnd` |
365
- | Codex CLI | `.codex/hooks.json` | `Stop` (Codex has no `SessionEnd`; the 120 s stale valve covers a killed session) |
361
+ | Host | Config file | Release event |
362
+ | -------------------------------- | ----------------------------- | --------------------------------------------------------------------------------- |
363
+ | Claude Code | `.claude/settings.json` | `Stop`, `SessionEnd` |
364
+ | GitHub Copilot (CLI + VS Code) | `.github/hooks/dev-hold.json` | `Stop`, `SessionEnd` |
365
+ | Codex CLI and compatible clients | `.codex/hooks.json` | `Stop` (Codex has no `SessionEnd`; the 120 s stale valve covers a killed session) |
366
366
 
367
- VS Code Copilot also reads `.claude/settings.json` as a fallback hook source, so it is covered twice. There is deliberately no `SubagentStop` release anywhere: a subagent finishing does not mean the main agent has stopped editing. Do not rely on remembering to acquire the hold — but **do** run `npm run dev:resume` before browser verification, because the release event fires only after your turn ends. Any host without hook support still has the manual path: `npm run dev:hold` at the start of an editing run, `npm run dev:resume` at the end.
367
+ VS Code Copilot also reads `.claude/settings.json` as a fallback hook source, so it is covered twice. There is deliberately no `SubagentStop` release anywhere: a subagent finishing does not mean the main agent has stopped editing. Do not rely on remembering to acquire the hold — but **do finish the whole edit phase and run `npm run dev:resume` exactly once before browser verification**, because the automatic release fires only after the turn ends. Any host without hook support still has the manual path: `npm run dev:hold` at the start of an editing run, `npm run dev:resume` after its final edit. The reusable agent-facing workflow is documented in `node_modules/caspian-utils/dist/docs/agent-development.md`.
368
368
 
369
369
  - **Tool names are normalised, because the hosts spell them differently.** `Edit` (Claude), `apply_patch` (Codex) and `insert_edit_into_file` (Copilot) all mean the same thing, so `normalizeToolName` lower-cases and drops non-letters before matching. Adding a host means extending those sets and the `matcher` in that host's config — not forking the script.
370
370
  - **The hook must never exit non-zero.** `PreToolUse` is fail-_closed_ in GitHub Copilot: a non-zero exit denies the agent's tool call outright, so a broken hook would stop the session rather than merely lose a reload optimisation. That is why `dev-hold.ts` is imported lazily inside the `try`/`catch` rather than at the top of the file, why the `.ts` extension in that specifier is load-bearing (plain `node` cannot resolve an extensionless relative import, and this hook runs under plain `node`, unlike `bs-config.ts` which runs under tsx), and why nothing is printed to stdout. `dev-hold.test.ts` spawns the file for real to pin all three.
371
371
  - **`Bash` is in that matcher because the write tools are not the only writers.** An agent running under bypass-permissions mode is instructed to prefer the Bash tool for file changes, so it edits with `cat > file <<'EOF'`, `sed -i`, or a throwaway Python script — none of which an `Edit|Write|NotebookEdit` matcher can see. That gap is what let one feature branch cost eight full Python restarts and five reloads instead of one of each. A blanket `Bash` matcher would over-correct, though: holding on every `git status` and `npm run test` would make `npm run logs` print its `DEV HOLD ACTIVE` banner over a perfectly current digest, training the reader to ignore the one warning that matters. So `dev-hold-hook.ts` holds for a Bash command only when it can plausibly write (`commandCanWrite`), and never for the hold's own controls (`dev:resume`, `dev:hold*`, `npm run logs`) — re-acquiring there would undo the drain in the same breath that released it. The hook is fail-open by construction: unparseable input, an unknown tool, or a thrown error all exit 0 without a hold, because a missing hold costs reloads while a hook that blocks tool calls breaks the session.
372
- - **If `npm run dev:resume` reports `No hold was active`, the hook layer is not wired** — check that `.claude/settings.json` exists and still carries the `PreToolUse` entry. That message during an editing run means every edit so far has been costing its own restart; acquire manually with `npm run dev:hold` for the rest of the run and fix the wiring.
372
+ - **If `npm run dev:resume` reports `No hold was active`, the current host's hook layer is not wired or did not fire** — check `.claude/settings.json`, `.github/hooks/dev-hold.json`, or `.codex/hooks.json` as applicable and confirm its `PreToolUse` entry. That message during an editing run means every edit so far may have cost its own restart; use `npm run dev:hold` before the next edit batch and fix the applicable wiring.
373
373
  - **Manual controls:** `npm run dev:resume` (release and apply now — the one an agent needs), `npm run dev:hold` (acquire), `npm run dev:hold:status` (inspect).
374
374
  - **It fails open, never frozen.** A hold that stops being refreshed for 120 s is stale and ignored, so a crashed agent or killed session degrades to normal reloading rather than a dead stack. A hold that keeps being refreshed is capped at 10 minutes — and once capped it **stays** capped until an explicit release, because letting the next edit start a fresh window would let a steadily-editing agent reset the cap forever and it would never actually force a drain. A missing, corrupt, or structurally wrong hold file all read as no hold. The dev terminal prints the hold state on entry and exit, so silence never looks like a broken watcher.
375
375
  - **A human editing while an agent holds is deferred too.** This is intended: the tree is mid-edit and a reload would render a half-finished state. `npm run dev:resume` is the escape hatch. With no hold present, human editing behaves exactly as it always did.
@@ -427,6 +427,7 @@ If the task generates or edits route, layout, or component HTML templates, check
427
427
  - Database and seed flow: read `node_modules/caspian-utils/dist/docs/database.md` — start at "Two Generators, One Schema" for the required command order after schema changes (`npx prisma migrate dev` or `npx prisma db push`, then always `npx ppy generate`; `npx prisma generate` is Node-client-only and never a substitute). Verify against `prisma/schema.prisma`, `prisma/seed.ts`, and `src/lib/prisma/**`.
428
428
  - Static export (SSG) or previewing a static build: read `node_modules/caspian-utils/dist/docs/static-export.md`. Verify against `package.json` (`static`, `static:serve`), `settings/build-static.py`, `settings/serve-static.py`, and `settings/project-name.ts`. This is an app-owned convention, not a shipped Caspian feature and not gated by a `caspian.config.json` flag. `npm run static` = `npm run build && uv run python settings/build-static.py`, so it regenerates `settings/files-list.json` (via `projectName`) before the exporter walks that route index; do not reduce it back to `css:build` only. `npm run static:serve` runs `settings/serve-static.py`, which auto-selects a free port from a preferred default (8000) and binds loopback `127.0.0.1` — read the port it prints, not `settings/bs-config.json` (that is the dev BrowserSync source of truth, not the static preview).
429
429
  - Testing, type checking, linting, or the quality gate: read `tests/README.md` and `settings/check.py` for this workspace's gate, and `node_modules/caspian-utils/dist/docs/testing.md` for the general Caspian convention it implements. This is a workspace-adopted convention, not a shipped Caspian feature, so the project-specific details (the five tools including `templates` and `node`, the `F401` component-import guard as configured here, the browser-log digest) live in the workspace files; the packaged doc holds only the reusable shape. Verify against `pyproject.toml` (`[dependency-groups]`, `[tool.pyright]`, `[tool.ruff]`, `[tool.pytest.ini_options]`) and the `package.json` `test` script. **The single command is `npm run test`** (auto-fix: `npm run test:fix`) — there is no `npm run check` script, and a bare `pytest`/`ruff`/`pyright` run is a subset of the gate, not the gate.
430
+ - Agent editing, coordinated development refreshes, `dev:hold`, `dev:resume`, or hook integration for Claude Code, GitHub Copilot, or Codex: read `node_modules/caspian-utils/dist/docs/agent-development.md`. Verify the reusable contract against `settings/dev-hold.ts`, `settings/dev-hold-hook.ts`, `settings/bs-config.ts`, `package.json`, and the applicable host config. Finish the complete edit phase before one explicit `npm run dev:resume`; do not release after each file.
430
431
  - Styling, a stylesheet, CSS variables, a theme, or dark mode: edit `src/app/globals.css` and nothing else. Read `node_modules/caspian-utils/dist/docs/project-structure.md` "`src/app/globals.css` And `public/css/styles.css`" for the pipeline and `commands.md` "Compile the stylesheet" for the scripts. Verify against `postcss.config.js`, `settings/run-postcss.ts`, `caspian.config.json`, and the `<link href="/css/styles.css">` in `src/app/layout.py`.
431
432
  - Formatting code or markup: read `tests/README.md` "Formatting" and `settings/format.py`. App-owned tooling, not a shipped Caspian feature. The single command is `npm run format` (`npm run format:check` to report only); `npm run test:fix` runs it first, before the ruff fixes and the gate. It formats markup with **djLint** and Python with **`ruff format`**, in that order — reformatting a template changes how many lines its literal spans, which changes how ruff wraps the enclosing `html(...)` call, so ruff must run last for a single pass to converge. The house style is `html(r"""` on one line with the markup starting on the next; `ruff format` explodes that shape whenever the call has arguments besides the template, so `format.py` rejoins the opening afterwards and iterates the pair to a fixed point. A bare `ruff format` or an IDE format-on-save will re-split them — rerun `npm run format` rather than editing call sites by hand. Prettier is not usable on this markup: it has no Jinja awareness and de-indents `{% for %}` blocks to column 0. Verify against `settings/format.py`, `settings/_markup_equivalence.py`, and `tests/test_format.py`.
432
433
 
@@ -444,6 +445,7 @@ If the task generates or edits route, layout, or component HTML templates, check
444
445
  - When `caspian.config.json` has `tailwindcss: true`, document Tailwind class handling as the current contract: Python `merge_classes(...)` emits frontend `{twMerge(...)}` expressions and browser `twMerge(...)` resolves conflicts.
445
446
  - Keep repo-specific clarifications in this file or `.github/copilot-instructions.md` rather than embedding them in the packaged docs unless the behavior is truly framework-wide.
446
447
  - Keep `index.md` and cross-links aligned so AI can discover the right task doc quickly.
448
+ - Keep `agent-development.md`, `testing.md`, and `commands.md` aligned on the one-edit-batch → one-restart → one-browser-reload workflow. Host-specific hook filenames belong in the agent workflow guide; project-specific deviations stay in this file.
447
449
  - Continue validating `file-conventions.md`, `routing.md`, `components.md`, `auth.md`, `fetch-data.md`, `websockets.md`, `cache.md`, `pulsepoint.md`, `validation.md`, `database.md`, and `mcp.md` against the installed `casp` runtime before changing behavior claims.
448
450
  - Validate `static-export.md` against `package.json` (`static`, `static:serve`), `settings/build-static.py`, `settings/serve-static.py`, and `settings/project-name.ts` before changing its behavior claims. This is app-owned tooling, not installed `casp` runtime, so verify the scripts rather than a package module.
449
451
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-caspian-app",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",