zz-meridian 0.6.0 → 0.7.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/README.md CHANGED
@@ -6,10 +6,15 @@ dashboard depends on it afterwards.
6
6
 
7
7
  ## The one sentence
8
8
 
9
- Give your coding agent (Codex, Claude Code, or any agent that can run a shell) this, from your frontend's folder:
9
+ Give your coding agent (Codex, Claude Code, or any agent that can run a shell) this, with what you want in your own
10
+ words at the end:
10
11
 
11
- > Run `npx zz-meridian@latest adopt` here, then follow the zz-meridian skill it installs: keep our data layer and
12
- > routes, restyle every page with Meridian's components and tokens, and run pnpm verify until it passes.
12
+ > Run `npx zz-meridian@latest skill --global`, then follow the zz-meridian skill it installs to: <what you want>.
13
+
14
+ The skill picks the command from what you said and what is in the folder, and names it before running anything:
15
+ `adopt` to change this Next.js App Router project in place ("make this admin panel look professional"), `create` for a
16
+ new dashboard in a new folder ("build an ops dashboard", "a new one based on this folder, in another folder"), and
17
+ `update` or `brand` for a project that already has Meridian.
13
18
 
14
19
  ## Commands
15
20
 
@@ -72,6 +77,14 @@ sends nothing anywhere; the only network access is your package manager's instal
72
77
  `--no-install`. Every release is built and published by GitHub Actions with npm provenance, so the registry shows the
73
78
  commit and workflow each version came from.
74
79
 
80
+ ## Privacy and feedback
81
+
82
+ The package collects nothing: no telemetry, no analytics, no account, and the template turns off Next.js's anonymous
83
+ telemetry. It talks to npm only, when `npx` fetches it and when `update` reads releases. Feedback reaches us only as a
84
+ [GitHub issue](https://github.com/zhixuan312/zz-meridian/issues/new/choose), a Bug or a Feature request; issues are
85
+ public, so leave out anything that identifies you, your organisation or your product, and describe the problem with
86
+ Meridian's own template or sample data.
87
+
75
88
  ## License
76
89
 
77
90
  MIT
package/dist/adopt.js CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
- import { PAYLOAD, VERSION, brandArgs, effectiveBrand, inside, installSkill, packageManager, payloadFiles, readJsonc, readPayload, relativeImports, run, runTool, sha256, writeIn, writeManifest, } from './files.js';
11
+ import { VERSION, brandArgs, effectiveBrand, inside, installSkill, packageManager, payloadBytes, payloadFiles, readJsonc, readPayload, relativeImports, run, runTool, sha256, writeIn, writeManifest, } from './files.js';
12
12
  import { BRIEF_TEMPLATE, managedBlock, upsertManagedBlock } from './context.js';
13
13
  import { FIXED, adoptSetOf, managedPaths } from './ownership.js';
14
14
  /** The template's dependencies a dashboard built from these files needs. */
@@ -161,7 +161,7 @@ https://github.com/zhixuan312/zz-meridian/blob/master/skills/zz-meridian/referen
161
161
  owned.push(rel);
162
162
  };
163
163
  for (const rel of set) {
164
- const raw = fs.readFileSync(path.join(PAYLOAD, rel));
164
+ const raw = payloadBytes(rel);
165
165
  own(rel, TS.test(rel) ? exactImports(rel, raw.toString('utf8'), known) : raw);
166
166
  }
167
167
  own('src/app.config.ts', exactImports('src/app.config.ts', appConfig(routes(root, dir)), known));
package/dist/create.js CHANGED
@@ -7,7 +7,7 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { managedPaths } from './ownership.js';
9
9
  import { BRIEF_TEMPLATE, managedBlock, upsertManagedBlock } from './context.js';
10
- import { PAYLOAD, VERSION, brandArgs, effectiveBrand, hasCommand, inside, installSkill, payloadFiles, run, sha256, writeIn, writeManifest } from './files.js';
10
+ import { VERSION, brandArgs, effectiveBrand, hasCommand, inside, installSkill, payloadBytes, payloadFiles, run, sha256, writeIn, writeManifest } from './files.js';
11
11
  export function create(o) {
12
12
  const root = path.resolve(o.dir);
13
13
  if (fs.existsSync(root) && fs.readdirSync(root).length)
@@ -22,7 +22,7 @@ export function create(o) {
22
22
  for (const rel of payloadFiles()) {
23
23
  if (rel.startsWith('skills/'))
24
24
  continue;
25
- writeIn(root, rel, fs.readFileSync(path.join(PAYLOAD, rel)));
25
+ writeIn(root, rel, payloadBytes(rel));
26
26
  }
27
27
  const name = brand.name ?? path.basename(root).replace(/[-_]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
28
28
  brand = effectiveBrand({ ...brand, name });
package/dist/files.js CHANGED
@@ -10,7 +10,16 @@ import { fileURLToPath } from 'node:url';
10
10
  /** The template snapshot this package carries, built from `git ls-files` at the release commit. */
11
11
  export const PAYLOAD = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'payload');
12
12
  export const VERSION = JSON.parse(fs.readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8')).version;
13
- /** Every file under a payload folder, as posix paths relative to the payload. Symbolic links are never followed. */
13
+ /**
14
+ * npm renames a `.gitignore` inside an installed package to `.npmignore`, so the payload carries the template's as
15
+ * `gitignore`. `shippedPath` is the name a project path ships under; `projectPath` reads one back, and also reads a
16
+ * `.gitignore` as itself, which is how releases before 0.6.1 carry it.
17
+ */
18
+ export const shippedPath = (rel) => (rel === '.gitignore' ? 'gitignore' : rel);
19
+ export const projectPath = (rel) => (rel === 'gitignore' ? '.gitignore' : rel);
20
+ /** A payload file's bytes, by its project path. */
21
+ export const payloadBytes = (rel) => fs.readFileSync(path.join(PAYLOAD, shippedPath(rel)));
22
+ /** Every file under a payload folder, as project paths. Symbolic links are never followed. */
14
23
  export function payloadFiles(dir = '') {
15
24
  const out = [];
16
25
  const walk = (rel) => {
@@ -21,14 +30,14 @@ export function payloadFiles(dir = '') {
21
30
  if (e.isDirectory())
22
31
  walk(p);
23
32
  else if (e.isFile())
24
- out.push(p);
33
+ out.push(projectPath(p));
25
34
  }
26
35
  };
27
36
  if (fs.existsSync(path.join(PAYLOAD, dir)))
28
37
  walk(dir);
29
38
  return out.sort();
30
39
  }
31
- export const readPayload = (rel) => fs.readFileSync(path.join(PAYLOAD, rel), 'utf8');
40
+ export const readPayload = (rel) => payloadBytes(rel).toString('utf8');
32
41
  /**
33
42
  * A path inside `root`; anything that would resolve outside it is refused: `..`, an absolute path, or a symbolic link
34
43
  * on the way (a linked folder or file could carry a write somewhere else).
@@ -132,7 +141,7 @@ export function installSkill(root, record) {
132
141
  for (const base of ['.agents/skills/zz-meridian', '.claude/skills/zz-meridian']) {
133
142
  for (const f of files) {
134
143
  const rel = `${base}/${f.slice('skills/zz-meridian/'.length)}`;
135
- const content = fs.readFileSync(path.join(PAYLOAD, f));
144
+ const content = payloadBytes(f);
136
145
  writeIn(root, rel, content);
137
146
  if (record)
138
147
  record[rel] = sha256(content);
package/dist/replay.js CHANGED
@@ -7,7 +7,7 @@ import { createHash } from 'node:crypto';
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
9
  import { fileURLToPath } from 'node:url';
10
- import { brandArgs, inside, run, sha256 } from './files.js';
10
+ import { brandArgs, inside, projectPath, run, sha256 } from './files.js';
11
11
  /** The package folder of the running CLI: the parent of its `dist/`. */
12
12
  export const runningRelease = () => path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
13
  /** A command's failure as one message: the cause, then everything it printed. */
@@ -165,7 +165,7 @@ function replayTree(pkgRoot, route, brand, scratch, moduleType) {
165
165
  export function replay(pkgRoot, route, brand, scratch, moduleType = false) {
166
166
  return replayTree(pkgRoot, route, brand, scratch, moduleType).hashes;
167
167
  }
168
- /** Every regular file under `<release>/payload` as posix paths, symbolic links never followed. */
168
+ /** Every regular file under `<release>/payload` as posix project paths, symbolic links never followed. */
169
169
  export function payloadList(release) {
170
170
  const base = path.join(release, 'payload');
171
171
  const out = [];
@@ -175,7 +175,7 @@ export function payloadList(release) {
175
175
  if (e.isDirectory())
176
176
  walk(p);
177
177
  else if (e.isFile())
178
- out.push(p);
178
+ out.push(projectPath(p));
179
179
  }
180
180
  };
181
181
  if (fs.existsSync(base))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-meridian",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Bring ZZ Meridian, a dashboard design system, into a Next.js project, or start a new dashboard on it. Copies the files in; nothing depends on this package at runtime.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -2,6 +2,22 @@
2
2
 
3
3
  Every release of ZZ Meridian, newest first. Versions follow semver: a removed or renamed token, prop or card is major; a new card, token or variant is minor; a corrected value is a patch. Each entry says what breaks and what to do instead.
4
4
 
5
+ ## [0.7.0] · 2026-10-06
6
+
7
+ ### Changed
8
+
9
+ - **One sentence for every route, and the skill chooses the command.** The README's sentence is `Run npx zz-meridian@latest skill --global, then follow the zz-meridian skill it installs to: <what you want>`. The skill opens with "Choose the route": a table from what the person said and what is in the folder to `adopt` (this Next.js App Router project, in place), `create` (a new folder, reading any folder named as the source and never writing it) or `update` and `brand`, and it says the route before the first command. The old sentence named `adopt`, which is wrong for a new dashboard.
10
+ - **Next.js telemetry is off.** The template's `next.config.ts` sets `NEXT_TELEMETRY_DISABLED` for `next dev` and `next build`, and Meridian's scripts set it for every `next` they run, an adopted project's included. Delete the line in `next.config.ts` to send it.
11
+ - **Feedback is an offer, and it identifies no one.** The skill's last step drafts a Bug or a Feature request issue only when the run found something about Meridian, removes every name, address, URL, record, schema and path of the person's own, shows the draft, and files nothing without a yes. The repository has Bug and Feature request issue forms that say the same, and no blank issues. The README and the npm page say what reaches the network (npm, the font download at build, what the team configures) and that an issue is the only way anything reaches Meridian.
12
+
13
+ ## [0.6.1] · 2026-10-06
14
+
15
+ ### Fixed
16
+
17
+ - **A created project has its `.gitignore`.** npm renames a `.gitignore` inside an installed package to `.npmignore`, so every project made with `create` since 0.3.0 had a `.npmignore` and no `.gitignore`, and `git add` took in `node_modules`, `.next` and `.env.local`. The package now carries the template's as `payload/gitignore`, and `create` writes it as `.gitignore`. In a project created before 0.6.1, run `git mv .npmignore .gitignore`, then `git rm -r --cached node_modules .next` for whatever was committed; `update` leaves the file alone, since it is the team's.
18
+ - **`update` runs from the registry.** It compares the running package with the published one, and npm's rename made the two differ, so `npx zz-meridian@<version> update` refused itself with "the running package differs from zz-meridian@<version> on the registry" since 0.5.0. 0.6.0 is on npm with this defect and has no tag or GitHub Release: update with 0.6.1.
19
+ - **A gate step whose tool is not installed says so.** `scripts/gate.ts` printed a `TypeError` in place of the missing command.
20
+
5
21
  ## [0.6.0] · 2026-10-06
6
22
 
7
23
  ### Changed
package/payload/README.md CHANGED
@@ -77,28 +77,46 @@ Dark is the default, written on `:root`; the light theme follows the operating s
77
77
  | `.github/workflows/release.yml` | The release, each check once: a timed default verify and the consumer path from the tarball (every published origin updated), then npm with provenance, the registry's bytes and provenance checked, then the tag (`.claude/commands/release.md`) |
78
78
  | `.github/workflows/weekly.yml` | Weekly, never at release: `verify --full --perf` (perf a report) and the consumer smoke's recovery and failure cases; nothing waits on it |
79
79
 
80
- ## Bring Meridian into your dashboard, in one sentence
80
+ ## Get a dashboard on Meridian, in one sentence
81
81
 
82
- Give your coding agent (Codex, Claude Code, or any agent that can run a shell) this, from your frontend's folder:
82
+ Give your coding agent (Codex, Claude Code, or any agent that can run a shell) this, with what you want in your own
83
+ words at the end:
83
84
 
84
- > Run `npx zz-meridian@latest adopt` here, then follow the zz-meridian skill it installs: keep our data layer and routes, restyle every page with Meridian's components and tokens, and run pnpm verify until it passes.
85
+ > Run `npx zz-meridian@latest skill --global`, then follow the zz-meridian skill it installs to: <what you want>.
85
86
 
86
- `adopt` does the settled part, the same way every time, and proves it type checks: it copies the components, tokens
87
- and gates in, merges the dependencies, brands it, adds Meridian's managed block to your `AGENTS.md` (your own text there is kept byte
88
- for byte) and an empty `docs/brief.md` for your product's own context, and installs the skill. The agent does the judgement: rebuilding each page on Meridian, and running
89
- `pnpm verify` until the project meets the standard (and `pnpm verify --full` after a change to shared components, the shell or
90
- data). It needs Node 22.18+, and Google Chrome for the browser checks. If your pages call a live API, say so in the sentence:
91
- `verify --full` presses every control, Delete included, so the agent builds a fake API first. For a new dashboard: `npx zz-meridian@latest create <dir>`. To move a project to a newer
92
- Meridian, run `npx zz-meridian@latest update --dry-run`, then follow the skill's `references/update.md`. The package
93
- (`cli/`, decision 0009) copies files and nothing depends on it afterwards.
87
+ You do not choose between the package's commands; the skill does, from what you said and what is in the folder, and
88
+ names the route before it runs anything:
94
89
 
95
- ## Install the skill once, for every project
96
-
97
- ```sh
98
- npx zz-meridian@latest skill --global
99
- ```
100
-
101
- That puts the skill in `~/.agents/skills/zz-meridian` (Codex) and `~/.claude/skills/zz-meridian` (Claude Code). Then tell your agent what you need, in your own words: "build an ops dashboard for our shipments", "make this admin panel look professional", "turn this schema into a dashboard". The skill reads what you already have, asks only what it cannot find out, creates the project (or brings Meridian into yours) with the package, builds the pages, and runs `pnpm verify` until the project meets the standard.
90
+ | You say | The skill |
91
+ |---|---|
92
+ | "Make this admin panel look professional", "change this product into our new dashboard" | `adopt` here, keeping your data layer and routes, when it is Next.js with the App Router; otherwise `create` next to it and port your pages |
93
+ | "Build an ops dashboard for our shipments", "turn this schema into a dashboard" | `create` in a new folder |
94
+ | "Based on this folder, make a new dashboard in another folder" | `create` in that folder, reading this one and leaving it untouched |
95
+ | "Update Meridian", "change our brand colour" | `update` or `brand`, in a project that already has Meridian |
96
+
97
+ `adopt` copies the components, tokens and gates in, merges the dependencies, brands it, adds Meridian's managed block to
98
+ your `AGENTS.md` (your own text there is kept byte for byte) and an empty `docs/brief.md` for your product's own
99
+ context, and installs the skill into the project. `create` writes a new, branded project with the same skill. The
100
+ agent then does the judgement: building each page on Meridian, and running `pnpm verify` until the project meets the
101
+ standard (`pnpm verify --full` after a change to shared components, the shell or data). It needs Node 22.18+, and
102
+ Google Chrome for the browser checks. If your pages call a live API, say so in the sentence: `verify --full` presses
103
+ every control, Delete included, so the agent builds a fake API first. The package (`cli/`, decision 0009) copies files
104
+ and nothing depends on it afterwards; its commands are in `cli/README.md`.
105
+
106
+ ## Privacy and feedback
107
+
108
+ Meridian takes nothing from you. The package, the template and the skill have no telemetry, no analytics and no
109
+ account, and the template turns off Next.js's own anonymous telemetry (`next.config.ts`; Meridian's scripts turn it off
110
+ for every `next` they run). What reaches the network is what you would expect: npm, when you run `npx` and when `update`
111
+ reads releases from it; Google Fonts, which `next/font` downloads the typefaces from at build time; and whatever you
112
+ configure yourself, such as the assistant's model provider.
113
+
114
+ The one way to tell us anything is a [GitHub issue](https://github.com/zhixuan312/zz-meridian/issues/new/choose): a
115
+ **Bug** (something in Meridian did the wrong thing) or a **Feature request** (something it should do, or do more
116
+ easily). Issues are public, so leave out anything that identifies you, your organisation or your product: names,
117
+ email addresses, URLs, your data and schemas, screenshots of your pages. Describe the problem with Meridian's own
118
+ template or sample data instead. At the end of a build the skill may offer to draft one with all of that removed; it
119
+ files nothing unless you say yes.
102
120
 
103
121
  ## Commands
104
122
 
@@ -4,12 +4,16 @@ Status: v1 (`create`, `adopt`, `skill`) shipped in 0.2.0 (decision 0009). v2 (`u
4
4
 
5
5
  ## The one sentence
6
6
 
7
- A team gives its coding agent this, from its frontend's folder:
7
+ A team gives its coding agent this, with what it wants in its own words at the end:
8
8
 
9
- > Run `npx zz-meridian@latest adopt` here, then follow the zz-meridian skill it installs: keep our data layer and
10
- > routes, restyle every page with Meridian's components and tokens, and run pnpm verify until it passes.
9
+ > Run `npx zz-meridian@latest skill --global`, then follow the zz-meridian skill it installs to: <what you want>.
11
10
 
12
- For a new dashboard: `npx zz-meridian@latest create <dir>`, then the same skill.
11
+ The sentence names no command, because people do not: "change this product into our dashboard", "a new one based on
12
+ this folder, in another folder" and "build an orders console" all ask for a dashboard on Meridian and differ only in
13
+ which folder holds it. The skill's "Choose the route" table maps what was said, and what is in the folder, to `adopt`
14
+ (this Next.js App Router project, in place), `create` (a new folder; a folder named as the source is read, never
15
+ written) or `update` and `brand` (a project that already has Meridian), and says the route before the first command.
16
+ An earlier sentence named `adopt`, the wrong command for every request for a new dashboard.
13
17
 
14
18
  The split follows what each part is good at. The package does the settled work, the same way every time, and proves it
15
19
  built: what to copy, which dependencies to merge, the alias, the stylesheet, the brand. The agent does the judgement:
@@ -219,8 +223,10 @@ Modelled on the release pipeline of the owner's earlier packages, one package in
219
223
  5. **Publish** the tarball with `npm` 11.5.1 or newer through trusted publishing (OIDC), with `--provenance`. `pnpm
220
224
  publish` does not perform the OIDC exchange.
221
225
  6. **The registry's package**: `npx zz-meridian@<version> --version` answers the version, the registry's tarball has the
222
- tested tarball's sha256, and it carries a provenance attestation. The bytes passed step 4 already, so nothing runs
223
- them twice. A failure here is an unsuccessful release, not a rollback: the version stays published and untagged
226
+ tested tarball's sha256, and it carries a provenance attestation. The bytes passed step 4 already; two paths still
227
+ change once the version is published, and run here, in about 10 seconds: `create` from the registry (npm renames a
228
+ packed `.gitignore` on install, so the project must have one), and `update` to the version in a project created
229
+ with the release before (it compares the running package with the registry's only once that one exists). A failure here is an unsuccessful release, not a rollback: the version stays published and untagged
224
230
  until a fix is released.
225
231
  7. **Tag `v<version>` last**, then the GitHub Release with the version's `CHANGELOG.md` section as its body.
226
232
 
@@ -1,5 +1,8 @@
1
1
  import type { NextConfig } from 'next';
2
2
 
3
+ // Next.js's anonymous usage telemetry is off for `next dev` and `next build`; delete this line to send it.
4
+ process.env.NEXT_TELEMETRY_DISABLED ??= '1';
5
+
3
6
  const config: NextConfig = {
4
7
  reactStrictMode: true,
5
8
  cacheComponents: true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-meridian-template",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "description": "ZZ Meridian: a dashboard design system and starter. DTCG tokens, five layers of React components and a Design Atlas, in a light and a dark theme.",
@@ -36,7 +36,8 @@ for (const [name, [cmd, ...args]] of STEPS) {
36
36
  const ok = r.status === 0;
37
37
  console.log(`${ok ? 'ok ' : 'FAIL'} ${name} (${((Date.now() - t) / 1000).toFixed(1)}s)`);
38
38
  if (!ok) {
39
- console.log((r.stdout + r.stderr).trim().split('\n').slice(-40).join('\n'));
39
+ // A tool that could not start (not installed) has no output, only the error.
40
+ console.log(`${r.stdout ?? ''}${r.stderr ?? ''}${r.error ? `${cmd}: ${r.error.message}` : ''}`.trim().split('\n').slice(-40).join('\n'));
40
41
  process.exit(1);
41
42
  }
42
43
  }
@@ -5,4 +5,8 @@ import path from 'node:path';
5
5
 
6
6
  const ROOT = path.resolve(import.meta.dirname, '../..');
7
7
 
8
+ // The gate, verify and the live check run `next` in the project: Meridian's own runs send no Next.js telemetry, whatever
9
+ // the project's next.config says.
10
+ process.env.NEXT_TELEMETRY_DISABLED ??= '1';
11
+
8
12
  export const bin = (name: string) => path.join(ROOT, 'node_modules', '.bin', process.platform === 'win32' ? `${name}.cmd` : name);
@@ -38,6 +38,35 @@ These hold in every project, however it started, and nothing below overrides the
38
38
  API or read a database names a `fakeApi` (or says `noLiveApi`) in `scripts/verify.config.ts` first
39
39
  (`references/existing-project.md`, step 5).
40
40
 
41
+ ## Choose the route before anything else
42
+
43
+ Whatever the words, the person wants one thing: a dashboard on Meridian. What you decide is **where its code lives**
44
+ and **which folder you may write to**, and that picks one of four commands. People rarely name the command, so read
45
+ the intent, not the verb: "change this product into our new dashboard", "redo this with Meridian", "make a new one
46
+ based on this folder in another folder" and "build me an orders console" are all requests for a dashboard; they
47
+ differ only in which folder ends up holding it.
48
+
49
+ | What is true | The route | The command |
50
+ |---|---|---|
51
+ | `optional:.meridian/manifest.json` exists in the folder they point at | Already on Meridian: update, rebrand or keep building | `update` or `brand` (the next section), or straight to step 5 |
52
+ | They want **this** project changed in place ("restyle this", "change this product into our dashboard", "make our admin look professional"), and it is Next.js with the App Router | Adopt, here | `npx zz-meridian@latest adopt` (`references/existing-project.md`, Route A) |
53
+ | They want this project changed, and it is another stack (Vite, CRA, Remix, Vue, a static page) | A new project next to it, ported from it | `create <sibling folder>` (`references/existing-project.md`, Routes A2, B, C) |
54
+ | They want a **new** dashboard: in another folder, "based on" or "from" this folder, a schema, a CSV, a spec or nothing | Create, elsewhere; what they pointed at is input you read, never a folder you write | `npx zz-meridian@latest create <new folder>` |
55
+
56
+ Rules that settle the hard cases:
57
+
58
+ - **The folder they name to read from is not the folder you write to unless they said "this", "here" or "in place".**
59
+ "Based on this folder", "use this as the source", "copy what this does" mean: read it, create next to it, leave it
60
+ untouched.
61
+ - **`create` writes only into a folder that does not exist or is empty**, and **`adopt` changes the project it runs in**.
62
+ Never run `adopt` in a folder they asked you to leave alone, and never `create` inside an existing project.
63
+ - **Two routes fit and nothing decides between them** (an existing Next.js app here and "build me a dashboard for
64
+ this"): ask once, with in place (`adopt`) as the recommended option when they said "this" or "our", and a new folder
65
+ (`create`) when they said "new" or "another". With no way to ask, take that recommendation and say so in the
66
+ hand-over.
67
+ - **Say the route in one sentence before the first command**: "This is a Next.js App Router project you want changed
68
+ in place, so I am running `adopt` here." A wrong route is cheap to stop before the command and costly after it.
69
+
41
70
  ## Updating or rebranding a project that already has Meridian
42
71
 
43
72
  When `optional:.meridian/manifest.json` exists and the person asks to update Meridian, or to change the brand, do not
@@ -70,8 +99,8 @@ Before asking anything, look at what you already have:
70
99
  Ask in a single round (AskUserQuestion when available), only the questions your homework could not answer, and offer
71
100
  your draft as the recommended option so the person can accept it in one click. Usually that is:
72
101
 
73
- 1. **New project or this one?** Only when a frontend already exists here: create a new Meridian project next to it, or
74
- bring Meridian into the existing one (`references/existing-project.md`).
102
+ 1. **New project or this one?** Only when "Choose the route" above left two routes open: create a new Meridian project
103
+ in another folder, or bring Meridian into the existing one (`references/existing-project.md`).
75
104
  2. **What it is and for whom.** The product name, who uses it, and the one question the home page must answer.
76
105
  3. **Pages.** Your drafted list, mapped to Meridian presets (Overview, list, detail, analytics, health, settings, sign-in).
77
106
  4. **Brand and surfaces.** A brand colour (a hex, or "no preference" for indigo), light or dark first (dark is the
@@ -230,49 +259,57 @@ Pages: <list, one line each, with what each answers>.
230
259
  Brand: <accent and how it was derived>. Surfaces: console, mobile<, MCP views: …>.
231
260
  Validation: pnpm verify passed; coverage line: <the line verify printed>.
232
261
  Next steps: replace the sample data in src/data/ with <their API>; run pnpm verify after every change.
233
- Feedback: <the issue URL from step 8, or "nothing to report">.
262
+ Feedback: <the issue URL from step 8, "nothing to report", or "declined">.
234
263
  ```
235
264
 
236
265
  Attach or show the screenshots of the main pages in both themes. Say plainly what is sample data and what is not. If
237
266
  the Atlas stays, say that `/system` is the live specification and that `node scripts/brand.ts --no-atlas` removes it
238
267
  before the product goes public.
239
268
 
240
- ## 8. Report back to Meridian
269
+ ## 8. Offer feedback to Meridian
241
270
 
242
- Every build teaches Meridian something. Before you finish, write down everything this run found about Meridian itself
243
- and file it as **one** GitHub issue on `zhixuan312/zz-meridian`, so the next person does not hit the same thing.
271
+ Meridian collects nothing from the people who use it: no telemetry, no analytics, no account. A GitHub issue the person
272
+ chooses to open is the only way anything reaches Meridian, so this step is an offer, never a requirement, and it
273
+ happens only when the run found something about Meridian itself.
244
274
 
245
- Collect, from the whole session (not only the last step):
275
+ Collect, from the whole session (not only the last step), and sort each finding into one of two kinds:
246
276
 
247
- - **Bugs**: a component, pattern, script, check or doc that did the wrong thing: a gate that failed on correct code, a
277
+ - **Bug**: a component, pattern, script, check or doc that did the wrong thing: a gate that failed on correct code, a
248
278
  check that passed broken code, a component that clipped, overflowed or did nothing, a doc that sent you the wrong way.
249
- - **Improvements**: anything that worked but cost you a workaround, a second try, or a guess, and how it could be easier.
250
- - **Not covered**: what the product needed that Meridian has no answer for: a missing component, pattern, state, page
251
- kind, surface rule, or a question this skill and the docs never answered.
279
+ - **Feature request**: anything that worked but cost a workaround, a second try or a guess, and the change that would
280
+ have saved it; and what the product needed that Meridian has no answer for (a component, pattern, state, page kind,
281
+ surface rule, or a question this skill and the docs never answered).
252
282
 
253
- Each item gets what someone needs to act on it without asking you: what happened, where (`file:line`, the route, the
254
- command), how to see it again, and what you did instead. Leave out what is the person's own (their product name, data,
255
- URLs, credentials, screenshots of their pages); describe the shape of the problem with Meridian's sample instead.
283
+ Each finding gets what someone needs to act on it without asking: what happened, where in Meridian (its `file:line`,
284
+ the Meridian command, the Meridian route), how to see it again in Meridian's own template or sample data, and what you
285
+ did instead.
256
286
 
257
- Draft it in this shape:
287
+ **Nothing in it may identify the person, their organisation or their product.** Before showing a draft, remove:
258
288
 
259
- ```
260
- Title: Field report: <one line on the most important finding>
289
+ - the product's, company's, team's and people's names, email addresses and accounts;
290
+ - their URLs, hostnames, IP addresses, API routes and environment variable values;
291
+ - their data, records, schemas, field names and screenshots of their pages;
292
+ - file paths outside Meridian's own files, and anything from `optional:docs/brief.md`.
293
+
294
+ Describe the shape of the problem with Meridian's sample instead ("a Data table with 40 columns", not their table). If
295
+ a finding cannot be told without one of these, leave it out.
261
296
 
262
- Meridian <commit or version> · Next <version> · <what was built, in generic terms: "an orders console, 6 pages">
297
+ Draft one issue per kind, in the shape of Meridian's two issue forms, Bug and Feature request:
298
+
299
+ ```
300
+ Title: <one line on the most important finding>
263
301
 
264
- ## Bugs
265
- - <what, where, how to reproduce, what you did instead>
302
+ Meridian <version from .meridian/manifest.json> · Next <version> · Route <adopt | create>
266
303
 
267
- ## Improvements
268
- - <what cost time, and the change that would have saved it>
304
+ ## What happened
305
+ - <finding: what, where in Meridian, how to see it again, what you did instead>
269
306
 
270
- ## Not covered
271
- - <what was needed, and how you filled the gap>
307
+ ## What would fix it
308
+ - <the change you would make to Meridian>
272
309
  ```
273
310
 
274
- Show the draft to the person and file it only when they agree: it is published under their account. File it with
275
- `gh issue create --repo zhixuan312/zz-meridian --title "<title>" --body-file <draft>`. Without `gh`, give them a link
276
- that opens the form filled in: `https://github.com/zhixuan312/zz-meridian/issues/new?title=<encoded title>&body=<encoded
277
- body>`. Put the issue's URL in the hand-over. If the run truly found nothing, say "nothing to report" rather than filing
278
- an empty issue.
311
+ Show the draft to the person and ask whether to open it: it is published under their account, in public. Only on a
312
+ yes, file it with `gh issue create --repo zhixuan312/zz-meridian --label bug|enhancement --title "<title>" --body-file
313
+ <draft>`, or, without `gh`, give them `https://github.com/zhixuan312/zz-meridian/issues/new/choose` and the draft to
314
+ paste. Put the issue's URL in the hand-over. When the run found nothing about Meridian, or they decline, say so in one
315
+ line and file nothing.
@@ -0,0 +1,15 @@
1
+ // @vitest-environment node
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ beforeEach(() => { vi.resetModules(); delete process.env.NEXT_TELEMETRY_DISABLED; });
5
+
6
+ describe('Next.js telemetry', () => {
7
+ it("is off in the template's next.config, which next dev and next build load before they send any", async () => {
8
+ await import('../next.config.ts');
9
+ expect(process.env.NEXT_TELEMETRY_DISABLED).toBe('1');
10
+ });
11
+ it("is off for every next the gate, verify and the live check run, whatever the project's own config says", async () => {
12
+ await import('../scripts/lib/bin.ts');
13
+ expect(process.env.NEXT_TELEMETRY_DISABLED).toBe('1');
14
+ });
15
+ });
File without changes