create-nifra 2.0.0 → 2.2.0

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.
package/dist/auth.js CHANGED
@@ -33,7 +33,7 @@ export const AUTH_PRESETS = {
33
33
  label: "better-auth",
34
34
  note: "email/password + sessions, mounted at /api/auth/* — add OAuth providers in auth.ts.",
35
35
  // @nifrajs/better-auth mounts it; better-auth is the implementation (peer-installed alongside).
36
- deps: { "@nifrajs/better-auth": "^2.0.0", "better-auth": "^1.2.0" },
36
+ deps: { "@nifrajs/better-auth": "^2.2.0", "better-auth": "^1.2.0" },
37
37
  },
38
38
  };
39
39
  /** The better-auth `database` adapter expression + its import line, for the scaffolded DB's ORM. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nifra",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Scaffold a new nifra app — `bun create nifra <dir>`.",
5
5
  "keywords": [
6
6
  "bun",
@@ -29,6 +29,7 @@
29
29
  },
30
30
  "files": [
31
31
  "dist",
32
+ "src/agent-files.ts",
32
33
  "template",
33
34
  "template-site",
34
35
  "template-site-vue",
@@ -0,0 +1,149 @@
1
+ /**
2
+ * The canonical generators for the agent-discovery files an app ships so a coding agent auto-discovers
3
+ * the project's nifra MCP server and prefers it over writing nifra from memory:
4
+ *
5
+ * .mcp.json — Claude Code's project MCP registry ({ mcpServers: { nifra: { command, args } } })
6
+ * .cursor/mcp.json — Cursor's MCP registry (same server config)
7
+ * CLAUDE.md — Claude's preamble (use the MCP first; gate on `nifra check`) + `@AGENTS.md` import
8
+ * AGENTS.md "MCP" — a section for non-Claude agents pointing at the same server
9
+ *
10
+ * Single source of truth: both `create-nifra` (scaffold time) and `@nifrajs/cli`'s `nifra init-agents`
11
+ * (retrofit an existing app) import these, so the four files can never drift apart. This module is
12
+ * dependency-free (pure string + object generation) on purpose — `create-nifra` ships with no runtime
13
+ * deps, and `@nifrajs/cli` imports it as a workspace dependency.
14
+ *
15
+ * Why `bunx @nifrajs/cli mcp` (not `bunx nifra mcp`): the `nifra` binary is provided by the
16
+ * `@nifrajs/cli` package. A scaffolded `site` app carries `@nifrajs/cli` as a devDependency, so either
17
+ * spelling would resolve `node_modules/.bin/nifra` locally — BUT the `api`/`isr` templates do NOT carry
18
+ * `@nifrajs/cli`, and the bare npm package literally named `nifra` (this monorepo's `@nifrajs/web` shim)
19
+ * exposes NO `nifra` bin. So `bunx nifra mcp` would fetch the wrong package and fail there. Naming the
20
+ * bin-owning package, `bunx @nifrajs/cli mcp`, resolves the locally-installed bin when present and
21
+ * otherwise fetches the package that actually provides the `nifra` command — robust across every
22
+ * template and for any existing app.
23
+ */
24
+
25
+ import { readFileSync } from "node:fs"
26
+
27
+ /** The MCP launch command, shared by `.mcp.json` and `.cursor/mcp.json`. See the module header for why
28
+ * the package is named explicitly rather than relying on the bare `nifra` bin. */
29
+ export const MCP_SERVER_COMMAND = "bunx" as const
30
+
31
+ /**
32
+ * The `@nifrajs/cli` version the launch command pins to — DERIVED at load time from this package's own
33
+ * `version`, never hardcoded. `fixed` changeset versioning ([["@nifrajs/*", "create-nifra", "nifra"]] in
34
+ * `.changeset/config.json`) bumps `create-nifra` and `@nifrajs/cli` in lockstep, so `create-nifra@x`
35
+ * always means `@nifrajs/cli@x`. Deriving it here means a release bump has nothing to forget to update —
36
+ * there is no stale literal to drift (the 1.0.0 cut shipped a beta pin exactly this way). `check:publish`
37
+ * still asserts it equals `@nifrajs/cli`'s version as a belt-and-suspenders guard on the `fixed` link.
38
+ *
39
+ * Resolves correctly under BOTH export conditions: the `bun` export runs `src/agent-files.ts`
40
+ * (`../package.json` → the package root) and the tsc `default` export runs `dist/agent-files.js`
41
+ * (`../package.json` → the same root, since `dist/` sits beside the manifest). npm always ships a
42
+ * package's own `package.json`, so the read can't miss at install time.
43
+ *
44
+ * Why pin at all instead of `bunx @nifrajs/cli mcp`: `bunx` keys its cache on the exact version spec.
45
+ * An UNPINNED spec resolves to the `latest` tag once, then `bunx` reuses that cached copy on every
46
+ * later spawn WITHOUT re-checking the registry — so an editor that once launched an older `@nifrajs/cli`
47
+ * keeps respawning the stale binary even after a newer one is published (the MCP server silently runs
48
+ * old code). Pinning the exact version makes the version part of the cache key, so each release fetches
49
+ * fresh and a stale cache can never shadow it. The cost: an already-scaffolded app's `.mcp.json` freezes
50
+ * at its scaffold-time version until `nifra init-agents` is re-run — an acceptable, deterministic trade.
51
+ */
52
+ export const MCP_CLI_VERSION: string = (
53
+ JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as {
54
+ version: string
55
+ }
56
+ ).version
57
+ export const MCP_SERVER_ARGS = [`@nifrajs/cli@${MCP_CLI_VERSION}`, "mcp"] as const
58
+
59
+ /** The server entry registered under the `nifra` key in both Claude Code's and Cursor's MCP config. */
60
+ export interface McpServerConfig {
61
+ readonly command: string
62
+ readonly args: readonly string[]
63
+ }
64
+
65
+ /** Claude Code / Cursor MCP config shape: a map of server name → launch config. */
66
+ export interface McpConfig {
67
+ readonly mcpServers: Readonly<Record<string, McpServerConfig>>
68
+ }
69
+
70
+ /** The one canonical MCP config object both registries serialize — the anti-drift seam. */
71
+ export const MCP_CONFIG: McpConfig = {
72
+ mcpServers: {
73
+ nifra: { command: MCP_SERVER_COMMAND, args: [...MCP_SERVER_ARGS] },
74
+ },
75
+ }
76
+
77
+ /** Serialize the canonical MCP config as the JSON written to `.mcp.json` and `.cursor/mcp.json`.
78
+ * Trailing newline so the file is POSIX-clean and diffs don't flag a missing EOL. */
79
+ export function mcpJson(): string {
80
+ return `${JSON.stringify(MCP_CONFIG, null, 2)}\n`
81
+ }
82
+
83
+ /**
84
+ * `CLAUDE.md` — Claude Code reads this automatically. It is deliberately NOT a copy of `AGENTS.md`:
85
+ * a short preamble that (1) tells Claude this project ships a nifra MCP, registered in `.mcp.json`, and
86
+ * to PREFER it, and (2) pulls in the full cookbook with Claude Code's `@file` import directive on its
87
+ * own line — `@AGENTS.md` — so the conventions live in exactly one place and can't drift between the
88
+ * two files. Keep this short; the depth is in `AGENTS.md`.
89
+ */
90
+ export function claudeMd(): string {
91
+ return `# CLAUDE.md
92
+
93
+ This project ships a **nifra MCP server**, registered for Claude Code in \`.mcp.json\` (it launches with
94
+ \`${MCP_SERVER_COMMAND} ${MCP_SERVER_ARGS.join(" ")}\`). **Prefer the MCP tools** over writing nifra from memory — they are
95
+ typechecked against *this* project and this installed version, so they beat training-data recall:
96
+
97
+ - \`nifra_docs\` / \`nifra_example\` — exact signatures + verified, compiling snippets. Reach for these
98
+ before hand-writing a route, a loader, or a client call; a remembered API is often stale or wrong.
99
+ - \`nifra_context\` — this project's route index + conventions, so changes fit the existing surface.
100
+ - \`nifra_types\` — the EXACT TypeScript of any \`@nifrajs/*\` symbol (e.g. \`nifra_types({ name: "RateLimitStore" })\`).
101
+ Call it for a precise type; **never read \`node_modules/@nifrajs/**/*.d.ts\`** — the tool is the authoritative, complete source.
102
+ - **\`nifra_check\` is the done-gate.** Before declaring any change complete, run \`nifra_check\` (or
103
+ \`nifra check --json\` in a terminal). It typechecks the frontend↔backend contract and flags drift
104
+ (hand-rolled \`fetch()\` to this app's own API, server-only imports in \`routes/\`). A failing check
105
+ means the work isn't done — fix it, don't ship around it.
106
+
107
+ The full nifra cookbook for this app — backend rules, the typed never-throwing client, file routing, and
108
+ the gotchas — lives in \`AGENTS.md\`, imported here so it stays the single source of truth:
109
+
110
+ @AGENTS.md
111
+ `
112
+ }
113
+
114
+ /**
115
+ * The "## MCP server" section appended to a scaffolded (or retrofitted) `AGENTS.md`, so non-Claude
116
+ * agents (Cursor, and anything that reads `AGENTS.md`) also learn the MCP exists and what to prefer.
117
+ * Mirrors the CLAUDE.md preamble's guidance without the Claude-specific `@import`.
118
+ */
119
+ export function agentsMcpSection(): string {
120
+ return `## MCP server
121
+
122
+ This project ships a **nifra MCP server** — launch it with \`${MCP_SERVER_COMMAND} ${MCP_SERVER_ARGS.join(" ")}\`. It's registered for
123
+ Claude Code in \`.mcp.json\` and for Cursor in \`.cursor/mcp.json\`; other agents can point their MCP client
124
+ at that command. **Prefer its tools** over writing nifra from memory — they're typechecked against this
125
+ project + the installed version, so they beat training-data recall:
126
+
127
+ - \`nifra_docs\` / \`nifra_example\` — exact signatures + verified, compiling snippets.
128
+ - \`nifra_context\` / \`nifra_routes\` — this project's route index + per-route schemas.
129
+ - \`nifra_types\` — the EXACT TypeScript of any \`@nifrajs/*\` symbol (interface/type/function). Call it for a
130
+ precise type; **never read \`@nifrajs\` \`.d.ts\` files** — this is the authoritative, complete source.
131
+ - \`nifra_check\` — the done-gate (typecheck + drift lints). Run it before calling work complete; a
132
+ failing check means it isn't done. (\`nifra check --json\` in a terminal does the same.)`
133
+ }
134
+
135
+ /** Identifies a generated agent-discovery file: where it goes (relative to the project root) and how to
136
+ * produce its content. `merge` is for files that augment an existing one (AGENTS.md) rather than own it. */
137
+ export interface AgentFileSpec {
138
+ /** Path relative to the project root (POSIX-style; the caller joins onto the cwd). */
139
+ readonly path: string
140
+ /** A human label for the "wrote/skipped" report. */
141
+ readonly label: string
142
+ }
143
+
144
+ /** The standalone files this module fully owns (whole-file generators). AGENTS.md is handled separately
145
+ * because create-nifra builds it from `agents.ts` and the retrofit command appends a section to it. */
146
+ export const MCP_JSON_PATH = ".mcp.json"
147
+ export const CURSOR_MCP_JSON_PATH = ".cursor/mcp.json"
148
+ export const CLAUDE_MD_PATH = "CLAUDE.md"
149
+ export const AGENTS_MD_PATH = "AGENTS.md"
@@ -10,9 +10,9 @@
10
10
  "typecheck": "tsc --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "@nifrajs/client": "^2.0.0",
14
- "@nifrajs/core": "^2.0.0",
15
- "@nifrajs/schema": "^2.0.0"
13
+ "@nifrajs/client": "^2.2.0",
14
+ "@nifrajs/core": "^2.2.0",
15
+ "@nifrajs/schema": "^2.2.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/bun": "latest",
@@ -9,16 +9,16 @@
9
9
  "dev:bun": "bun run server.ts"
10
10
  },
11
11
  "dependencies": {
12
- "@nifrajs/client": "^2.0.0",
13
- "@nifrajs/core": "^2.0.0",
14
- "@nifrajs/schema": "^2.0.0",
15
- "@nifrajs/web": "^2.0.0",
16
- "@nifrajs/web-react": "^2.0.0",
12
+ "@nifrajs/client": "^2.2.0",
13
+ "@nifrajs/core": "^2.2.0",
14
+ "@nifrajs/schema": "^2.2.0",
15
+ "@nifrajs/web": "^2.2.0",
16
+ "@nifrajs/web-react": "^2.2.0",
17
17
  "react": "^19.0.0",
18
18
  "react-dom": "^19.0.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@nifrajs/cli": "^2.0.0",
21
+ "@nifrajs/cli": "^2.2.0",
22
22
  "@types/react": "^19.0.0",
23
23
  "@types/react-dom": "^19.0.0",
24
24
  "typescript": "^6.0.3"
@@ -17,20 +17,20 @@
17
17
  "deploy:vercel": "vercel deploy --prebuilt"
18
18
  },
19
19
  "dependencies": {
20
- "@nifrajs/client": "^2.0.0",
21
- "@nifrajs/core": "^2.0.0",
22
- "@nifrajs/schema": "^2.0.0",
23
- "@nifrajs/deno": "^2.0.0",
24
- "@nifrajs/node": "^2.0.0",
25
- "@nifrajs/web": "^2.0.0",
26
- "@nifrajs/web-react": "^2.0.0",
20
+ "@nifrajs/client": "^2.2.0",
21
+ "@nifrajs/core": "^2.2.0",
22
+ "@nifrajs/schema": "^2.2.0",
23
+ "@nifrajs/deno": "^2.2.0",
24
+ "@nifrajs/node": "^2.2.0",
25
+ "@nifrajs/web": "^2.2.0",
26
+ "@nifrajs/web-react": "^2.2.0",
27
27
  "react": "^19.0.0",
28
28
  "react-dom": "^19.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/react": "^19.0.0",
32
32
  "@types/react-dom": "^19.0.0",
33
- "@nifrajs/cli": "^2.0.0",
33
+ "@nifrajs/cli": "^2.2.0",
34
34
  "@vitejs/plugin-react": "^4.3.0",
35
35
  "typescript": "^6.0.3",
36
36
  "vite": "^8.0.0"
@@ -17,18 +17,18 @@
17
17
  "deploy:vercel": "vercel deploy --prebuilt"
18
18
  },
19
19
  "dependencies": {
20
- "@nifrajs/client": "^2.0.0",
21
- "@nifrajs/core": "^2.0.0",
22
- "@nifrajs/schema": "^2.0.0",
23
- "@nifrajs/deno": "^2.0.0",
24
- "@nifrajs/node": "^2.0.0",
25
- "@nifrajs/web": "^2.0.0",
26
- "@nifrajs/web-preact": "^2.0.0",
20
+ "@nifrajs/client": "^2.2.0",
21
+ "@nifrajs/core": "^2.2.0",
22
+ "@nifrajs/schema": "^2.2.0",
23
+ "@nifrajs/deno": "^2.2.0",
24
+ "@nifrajs/node": "^2.2.0",
25
+ "@nifrajs/web": "^2.2.0",
26
+ "@nifrajs/web-preact": "^2.2.0",
27
27
  "preact": "^10.25.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@preact/preset-vite": "^2.9.0",
31
- "@nifrajs/cli": "^2.0.0",
31
+ "@nifrajs/cli": "^2.2.0",
32
32
  "typescript": "^6.0.3",
33
33
  "vite": "^8.0.0"
34
34
  }
@@ -17,17 +17,17 @@
17
17
  "deploy:vercel": "vercel deploy --prebuilt"
18
18
  },
19
19
  "dependencies": {
20
- "@nifrajs/client": "^2.0.0",
21
- "@nifrajs/core": "^2.0.0",
22
- "@nifrajs/schema": "^2.0.0",
23
- "@nifrajs/deno": "^2.0.0",
24
- "@nifrajs/node": "^2.0.0",
25
- "@nifrajs/web": "^2.0.0",
26
- "@nifrajs/web-solid": "^2.0.0",
20
+ "@nifrajs/client": "^2.2.0",
21
+ "@nifrajs/core": "^2.2.0",
22
+ "@nifrajs/schema": "^2.2.0",
23
+ "@nifrajs/deno": "^2.2.0",
24
+ "@nifrajs/node": "^2.2.0",
25
+ "@nifrajs/web": "^2.2.0",
26
+ "@nifrajs/web-solid": "^2.2.0",
27
27
  "solid-js": "^1.9.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@nifrajs/cli": "^2.0.0",
30
+ "@nifrajs/cli": "^2.2.0",
31
31
  "typescript": "^6.0.3",
32
32
  "vite": "^8.0.0",
33
33
  "vite-plugin-solid": "^2.10.0"
@@ -17,18 +17,18 @@
17
17
  "deploy:vercel": "vercel deploy --prebuilt"
18
18
  },
19
19
  "dependencies": {
20
- "@nifrajs/client": "^2.0.0",
21
- "@nifrajs/core": "^2.0.0",
22
- "@nifrajs/schema": "^2.0.0",
23
- "@nifrajs/deno": "^2.0.0",
24
- "@nifrajs/node": "^2.0.0",
25
- "@nifrajs/web": "^2.0.0",
26
- "@nifrajs/web-svelte": "^2.0.0",
20
+ "@nifrajs/client": "^2.2.0",
21
+ "@nifrajs/core": "^2.2.0",
22
+ "@nifrajs/schema": "^2.2.0",
23
+ "@nifrajs/deno": "^2.2.0",
24
+ "@nifrajs/node": "^2.2.0",
25
+ "@nifrajs/web": "^2.2.0",
26
+ "@nifrajs/web-svelte": "^2.2.0",
27
27
  "svelte": "^5.3.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@sveltejs/vite-plugin-svelte": "^5.0.0",
31
- "@nifrajs/cli": "^2.0.0",
31
+ "@nifrajs/cli": "^2.2.0",
32
32
  "typescript": "^6.0.3",
33
33
  "vite": "^8.0.0"
34
34
  }
@@ -17,17 +17,17 @@
17
17
  "deploy:vercel": "vercel deploy --prebuilt"
18
18
  },
19
19
  "dependencies": {
20
- "@nifrajs/client": "^2.0.0",
21
- "@nifrajs/core": "^2.0.0",
22
- "@nifrajs/schema": "^2.0.0",
23
- "@nifrajs/deno": "^2.0.0",
24
- "@nifrajs/node": "^2.0.0",
25
- "@nifrajs/web": "^2.0.0",
26
- "@nifrajs/web-vue": "^2.0.0",
20
+ "@nifrajs/client": "^2.2.0",
21
+ "@nifrajs/core": "^2.2.0",
22
+ "@nifrajs/schema": "^2.2.0",
23
+ "@nifrajs/deno": "^2.2.0",
24
+ "@nifrajs/node": "^2.2.0",
25
+ "@nifrajs/web": "^2.2.0",
26
+ "@nifrajs/web-vue": "^2.2.0",
27
27
  "vue": "^3.5.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@nifrajs/cli": "^2.0.0",
30
+ "@nifrajs/cli": "^2.2.0",
31
31
  "@vitejs/plugin-vue": "^5.2.0",
32
32
  "@vue/compiler-sfc": "^3.5.0",
33
33
  "typescript": "^6.0.3",