nerdsystem 0.0.9 → 0.0.10
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/commands/check.js +4 -2
- package/dist/commands/deploy.js +8 -3
- package/dist/commands/init.js +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/verify.d.ts +2 -0
- package/dist/commands/verify.js +11 -0
- package/dist/gates/code.js +7 -4
- package/dist/gates/smoke.js +4 -2
- package/dist/util.d.ts +8 -8
- package/dist/util.js +3 -2
- package/package.json +1 -1
- package/template/.claude/skills/nerdsystem/SKILL.md +2 -2
- package/template/.cursor/rules/nerdsystem.mdc +3 -3
- package/template/.github/copilot-instructions.md +3 -3
- package/template/AGENTS.md +3 -3
- package/template/GEMINI.md +3 -3
package/dist/commands/check.js
CHANGED
|
@@ -121,7 +121,7 @@ export async function runChecks(root, opts) {
|
|
|
121
121
|
}
|
|
122
122
|
if (opts.typecheck && problems.every((p) => p.endsWith("(warning)") || p.startsWith("Agent instruction files"))) {
|
|
123
123
|
log.step("Typechecking");
|
|
124
|
-
const code = await run("npx", ["tsc", "-b", "--pretty"], { cwd: root });
|
|
124
|
+
const code = await run("npx", ["tsc", "-b", "--pretty"], { cwd: root, stdoutToStderr: true });
|
|
125
125
|
if (code !== 0)
|
|
126
126
|
problems.push("Typecheck failed (see errors above).");
|
|
127
127
|
}
|
|
@@ -144,7 +144,9 @@ export async function check(opts) {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
// Loaded here, not at the top: the gates import staticChecks from this file.
|
|
147
|
-
const { runVerify } = await import("./verify.js");
|
|
147
|
+
const { runVerify, assertRuleHasGate } = await import("./verify.js");
|
|
148
|
+
if (opts.rule)
|
|
149
|
+
assertRuleHasGate(opts.rule);
|
|
148
150
|
const out = await runVerify(root, { withBuild: !opts["no-build"], stopOnFail: false, quiet: opts.json, onlyRules: opts.rule ? [opts.rule] : undefined });
|
|
149
151
|
const failed = out.results.filter((r) => r.status === "fail").length;
|
|
150
152
|
if (opts.json)
|
package/dist/commands/deploy.js
CHANGED
|
@@ -65,7 +65,10 @@ async function probeAfterDeploy(deployment, url, skip) {
|
|
|
65
65
|
return skippedProbe("--skip-probe was given");
|
|
66
66
|
if (deployment.status !== "live")
|
|
67
67
|
return skippedProbe(`the deployment status is ${deployment.status}, not live`);
|
|
68
|
-
|
|
68
|
+
// Until the dispatcher knows the new script it answers its own 404 page, which has none of the app's
|
|
69
|
+
// headers or routes; probing that would report false auth failures. Wait, and say so when it never came.
|
|
70
|
+
if (!(await waitForDeployment(url)))
|
|
71
|
+
return skippedProbe("the app had not started answering within 30 seconds; open the URL to check, or run nerd deploy again to probe it");
|
|
69
72
|
return probeDeployment(url);
|
|
70
73
|
}
|
|
71
74
|
function printProbe(url, probes) {
|
|
@@ -99,7 +102,7 @@ export async function deploy(opts) {
|
|
|
99
102
|
if (!opts["skip-build"]) {
|
|
100
103
|
log.step("Building with vite");
|
|
101
104
|
ensureDeps(root);
|
|
102
|
-
await runOrThrow("npx", ["vite", "build"], { cwd: root });
|
|
105
|
+
await runOrThrow("npx", ["vite", "build"], { cwd: root, stdoutToStderr: true });
|
|
103
106
|
}
|
|
104
107
|
const build = readBuildOutput(root);
|
|
105
108
|
const built = await runGates(BUILD_GATES, { root, build, knownSecrets, log: (l) => log.info(l) }, { stopOnFail: true });
|
|
@@ -127,10 +130,12 @@ export async function deploy(opts) {
|
|
|
127
130
|
}
|
|
128
131
|
const { deployment } = await api.post(projectPath(nerd, "/deployments"), form);
|
|
129
132
|
const { project } = await api.get(projectPath(nerd));
|
|
133
|
+
log.ok(`Deployed ${deployment.id} (${deployment.status}) → ${project.url}`);
|
|
134
|
+
if (!opts["skip-probe"] && deployment.status === "live")
|
|
135
|
+
log.step(`Probing ${project.url}`);
|
|
130
136
|
const probes = await probeAfterDeploy(deployment, project.url, Boolean(opts["skip-probe"]));
|
|
131
137
|
if (opts.json)
|
|
132
138
|
return printJson({ deployment, url: project.url, migrations: mig, probes });
|
|
133
|
-
log.ok(`Deployed ${deployment.id} (${deployment.status}) → ${project.url}`);
|
|
134
139
|
printProbe(project.url, probes);
|
|
135
140
|
log.info(`First time? Sign up on your app at ${project.url}, then make yourself admin in the dashboard's Auth tab: ${dashboardUrl(api.apiUrl)}/app/${nerd.org}/projects/${nerd.project}/auth`);
|
|
136
141
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -174,4 +174,4 @@ export async function init(dir, args) {
|
|
|
174
174
|
log.ok(`Ready. Next:\n cd ${dir}\n pnpm install\n nerd dev\n\nThen open the folder in your coding agent and ask it to build the first feature; the nerdsystem skill walks it through.\nDeploys go to ${project.url}`);
|
|
175
175
|
}
|
|
176
176
|
// Kept in sync with packages/sdk/package.json by the release script.
|
|
177
|
-
const SDK_VERSION = "0.0.
|
|
177
|
+
const SDK_VERSION = "0.0.10";
|
package/dist/commands/login.js
CHANGED
|
@@ -28,7 +28,7 @@ export async function login(args) {
|
|
|
28
28
|
else {
|
|
29
29
|
log.info(`Open this link in your browser and approve code ${start.userCode}:\n\n ${start.verificationUrl}\n`);
|
|
30
30
|
if (args.email)
|
|
31
|
-
log.info(`An email went to ${args.email}:
|
|
31
|
+
log.info(`An email went to ${args.email}: the link signs you in (creating your account if you are new) and opens a page where you press Approve.`);
|
|
32
32
|
log.info("Waiting for approval…");
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -17,6 +17,8 @@ export declare function runVerify(root: string, opts: {
|
|
|
17
17
|
onlyRules?: string[] | undefined;
|
|
18
18
|
trackAttempts?: boolean;
|
|
19
19
|
}): Promise<VerifyOutput>;
|
|
20
|
+
/** `--rule smoke/x` would select no gate and report "All 0 gates passed"; say which prefixes a gate owns instead. */
|
|
21
|
+
export declare function assertRuleHasGate(rule: string): void;
|
|
20
22
|
export declare function verify(opts: {
|
|
21
23
|
json: boolean;
|
|
22
24
|
"no-build": boolean;
|
package/dist/commands/verify.js
CHANGED
|
@@ -35,8 +35,19 @@ export async function runVerify(root, opts) {
|
|
|
35
35
|
}
|
|
36
36
|
return { ok: results.every((r) => r.status !== "fail"), failures, stop, results };
|
|
37
37
|
}
|
|
38
|
+
/** `--rule smoke/x` would select no gate and report "All 0 gates passed"; say which prefixes a gate owns instead. */
|
|
39
|
+
export function assertRuleHasGate(rule) {
|
|
40
|
+
const prefixes = [...new Set([...SOURCE_GATES, ...BUILD_GATES].flatMap((g) => g.rulePrefixes))];
|
|
41
|
+
if (prefixes.some((p) => rule.startsWith(p)))
|
|
42
|
+
return;
|
|
43
|
+
if (rule.startsWith("smoke/"))
|
|
44
|
+
throw new CliError(`No gate runs ${rule} on the source: the live URL is probed by nerd deploy. Deploy again to re-check it.`, 2);
|
|
45
|
+
throw new CliError(`No gate runs ${rule}. Rule ids start with one of: ${prefixes.join(", ")}`, 2);
|
|
46
|
+
}
|
|
38
47
|
export async function verify(opts) {
|
|
39
48
|
const root = findProjectRoot();
|
|
49
|
+
if (opts.rule)
|
|
50
|
+
assertRuleHasGate(opts.rule);
|
|
40
51
|
const out = await runVerify(root, { withBuild: !opts["no-build"], stopOnFail: false, quiet: opts.json, onlyRules: opts.rule ? [opts.rule] : undefined });
|
|
41
52
|
if (opts.json)
|
|
42
53
|
process.stdout.write(`${JSON.stringify({ ok: out.ok, failures: out.failures, stop: out.stop, skipped: skippedGates(out.results) }, null, 2)}\n`);
|
package/dist/gates/code.js
CHANGED
|
@@ -73,14 +73,16 @@ export function depsRuleFor(severity) {
|
|
|
73
73
|
}
|
|
74
74
|
/** Known vulnerabilities in dependencies. Critical and high block; moderate warns. Package names come from the registry, not the repo. */
|
|
75
75
|
/**
|
|
76
|
-
* The lockfile for the project: in its root, or in an ancestor
|
|
77
|
-
*
|
|
76
|
+
* The lockfile for the project: in its root, or in an ancestor that is itself a package or workspace root
|
|
77
|
+
* (it holds a package.json or pnpm-workspace.yaml), searched up to the repository root (the directory holding
|
|
78
|
+
* .git). A workspace member such as the template inside this monorepo shares the lockfile committed at the top;
|
|
79
|
+
* a stray lockfile in an unrelated ancestor such as the home folder is never adopted.
|
|
78
80
|
*/
|
|
79
81
|
function findLockfile(start) {
|
|
80
82
|
let dir = start;
|
|
81
83
|
for (;;) {
|
|
82
84
|
const hit = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock"].find((f) => existsSync(path.join(dir, f)));
|
|
83
|
-
if (hit)
|
|
85
|
+
if (hit && (dir === start || existsSync(path.join(dir, "package.json")) || existsSync(path.join(dir, "pnpm-workspace.yaml"))))
|
|
84
86
|
return path.join(dir, hit);
|
|
85
87
|
const parent = path.dirname(dir);
|
|
86
88
|
if (parent === dir || existsSync(path.join(dir, ".git")))
|
|
@@ -101,7 +103,8 @@ export const depsGate = {
|
|
|
101
103
|
if (!lock)
|
|
102
104
|
findings.push(finding("deps/lockfile-required", {}));
|
|
103
105
|
if (await hasTool("osv-scanner")) {
|
|
104
|
-
|
|
106
|
+
// Run from the lockfile's own folder with a bare name: on Windows the shell would split an absolute path with a space.
|
|
107
|
+
const r = await exec("osv-scanner", ["scan", "source", "--format", "json", "--lockfile", lock ?? "pnpm-lock.yaml"], lockPath ? path.dirname(lockPath) : ctx.root);
|
|
105
108
|
try {
|
|
106
109
|
const json = JSON.parse(r.stdout);
|
|
107
110
|
for (const res of json.results ?? [])
|
package/dist/gates/smoke.js
CHANGED
|
@@ -14,8 +14,10 @@ function withTimeout(init) {
|
|
|
14
14
|
const signal = typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(PROBE_TIMEOUT_MS) : undefined;
|
|
15
15
|
return { redirect: "manual", ...(signal ? { signal } : {}), ...init };
|
|
16
16
|
}
|
|
17
|
+
/** No gate owns the smoke/ prefix, so `nerd verify --rule smoke/...` would run nothing; the way to re-check is another deploy. */
|
|
18
|
+
const VERIFY = "nerd deploy (the live URL is probed after every deploy)";
|
|
17
19
|
function smokeFinding(ruleId, risk, key) {
|
|
18
|
-
return finding(ruleId, { risk, fingerprint: fingerprint(ruleId, key) });
|
|
20
|
+
return { ...finding(ruleId, { risk, fingerprint: fingerprint(ruleId, key) }), verify: VERIFY };
|
|
19
21
|
}
|
|
20
22
|
function probeResult(findings, startedAt) {
|
|
21
23
|
const status = verdict(findings);
|
|
@@ -28,7 +30,7 @@ function probeResult(findings, startedAt) {
|
|
|
28
30
|
* 404 for a script it does not know yet). Returns true as soon as it does; false after the last attempt.
|
|
29
31
|
*/
|
|
30
32
|
export async function waitForDeployment(url, opts = {}) {
|
|
31
|
-
const attempts = opts.attempts ??
|
|
33
|
+
const attempts = opts.attempts ?? 8;
|
|
32
34
|
const delayMs = opts.delayMs ?? 2000;
|
|
33
35
|
const fetchImpl = opts.fetchImpl ?? defaultFetch;
|
|
34
36
|
for (let i = 0; i < attempts; i++) {
|
package/dist/util.d.ts
CHANGED
|
@@ -10,22 +10,22 @@ export declare class CliError extends Error {
|
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
12
|
/** The one sentence every unauthenticated command prints, with both ways in. */
|
|
13
|
-
export declare const NOT_LOGGED_IN = "Not logged in. Run `nerd login --email you@example.com` (
|
|
13
|
+
export declare const NOT_LOGGED_IN = "Not logged in. Run `nerd login --email you@example.com` (the link in the email signs you in, creating your account the first time, and opens a page where you press Approve), or set NERD_TOKEN to an API token for scripts.";
|
|
14
14
|
export declare const log: {
|
|
15
15
|
info: (msg: string) => void;
|
|
16
16
|
step: (msg: string) => void;
|
|
17
17
|
ok: (msg: string) => void;
|
|
18
18
|
warn: (msg: string) => void;
|
|
19
19
|
};
|
|
20
|
-
|
|
21
|
-
export declare function run(cmd: string, args: string[], opts?: {
|
|
22
|
-
cwd?: string;
|
|
23
|
-
env?: NodeJS.ProcessEnv;
|
|
24
|
-
}): Promise<number>;
|
|
25
|
-
export declare function runOrThrow(cmd: string, args: string[], opts?: {
|
|
20
|
+
export interface RunOptions {
|
|
26
21
|
cwd?: string;
|
|
27
22
|
env?: NodeJS.ProcessEnv;
|
|
28
|
-
|
|
23
|
+
/** Send the child's stdout to our stderr, so stdout stays reserved for `--json` output. */
|
|
24
|
+
stdoutToStderr?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Runs a command, streaming output. Resolves with the exit code. */
|
|
27
|
+
export declare function run(cmd: string, args: string[], opts?: RunOptions): Promise<number>;
|
|
28
|
+
export declare function runOrThrow(cmd: string, args: string[], opts?: RunOptions): Promise<void>;
|
|
29
29
|
export declare function printJson(value: unknown): void;
|
|
30
30
|
export declare function table(rows: Record<string, unknown>[], columns: string[]): void;
|
|
31
31
|
/** The dashboard that goes with an API origin (https://api.nerdsystem.com -> https://nerdsystem.com). */
|
package/dist/util.js
CHANGED
|
@@ -15,7 +15,7 @@ export class CliError extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
/** The one sentence every unauthenticated command prints, with both ways in. */
|
|
18
|
-
export const NOT_LOGGED_IN = "Not logged in. Run `nerd login --email you@example.com` (
|
|
18
|
+
export const NOT_LOGGED_IN = "Not logged in. Run `nerd login --email you@example.com` (the link in the email signs you in, creating your account the first time, and opens a page where you press Approve), or set NERD_TOKEN to an API token for scripts.";
|
|
19
19
|
export const log = {
|
|
20
20
|
info: (msg) => console.error(`${msg}`),
|
|
21
21
|
step: (msg) => console.error(`\x1b[36m›\x1b[0m ${msg}`),
|
|
@@ -25,7 +25,8 @@ export const log = {
|
|
|
25
25
|
/** Runs a command, streaming output. Resolves with the exit code. */
|
|
26
26
|
export function run(cmd, args, opts = {}) {
|
|
27
27
|
return new Promise((resolve, reject) => {
|
|
28
|
-
const child = spawn(cmd, args, { cwd: opts.cwd, env: { ...process.env, ...opts.env }, stdio: "inherit", shell: process.platform === "win32" });
|
|
28
|
+
const child = spawn(cmd, args, { cwd: opts.cwd, env: { ...process.env, ...opts.env }, stdio: ["inherit", opts.stdoutToStderr ? "pipe" : "inherit", "inherit"], shell: process.platform === "win32" });
|
|
29
|
+
child.stdout?.pipe(process.stderr);
|
|
29
30
|
child.on("error", reject);
|
|
30
31
|
child.on("exit", (code) => resolve(code ?? 1));
|
|
31
32
|
});
|
package/package.json
CHANGED
|
@@ -36,14 +36,14 @@ Tell the person plainly what does not carry. Their users sign up again on the ne
|
|
|
36
36
|
1. Make the change.
|
|
37
37
|
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
38
38
|
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
39
|
-
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and
|
|
39
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, prints the URL, and probes the live URL (headers and the sign-in routes). With `--json` the output carries `probes`, the same shape as a check result, with `status: "skip"` and the reason when it did not run; probe findings never fail the deploy.
|
|
40
40
|
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
41
41
|
|
|
42
42
|
Run `nerd check --json` when you want a machine-readable list of problems. Its summary says how many gates ran and which were skipped (`skipped` in the JSON): semgrep and gitleaks only run locally when installed; git builds always run them in the builder.
|
|
43
43
|
|
|
44
44
|
## Deploying from GitHub
|
|
45
45
|
|
|
46
|
-
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically
|
|
46
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Every preview gets its own database and bucket, and pending migrations are applied to it on each preview build, so a PR that changes the schema can be tried end to end before it merges.
|
|
47
47
|
|
|
48
48
|
## Where things go
|
|
49
49
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: How to build, run, and deploy this app on nerdsystem. Applies to server, database, auth, and deploy work.
|
|
3
3
|
alwaysApply: true
|
|
4
4
|
---
|
|
5
|
-
<!-- generated from .claude/skills/nerdsystem/SKILL.md (
|
|
5
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (10dcd79a44bd); edit the skill, then run: nerd agents sync -->
|
|
6
6
|
|
|
7
7
|
# Building on nerdsystem
|
|
8
8
|
|
|
@@ -37,14 +37,14 @@ Tell the person plainly what does not carry. Their users sign up again on the ne
|
|
|
37
37
|
1. Make the change.
|
|
38
38
|
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
39
39
|
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
40
|
-
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and
|
|
40
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, prints the URL, and probes the live URL (headers and the sign-in routes). With `--json` the output carries `probes`, the same shape as a check result, with `status: "skip"` and the reason when it did not run; probe findings never fail the deploy.
|
|
41
41
|
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
42
42
|
|
|
43
43
|
Run `nerd check --json` when you want a machine-readable list of problems. Its summary says how many gates ran and which were skipped (`skipped` in the JSON): semgrep and gitleaks only run locally when installed; git builds always run them in the builder.
|
|
44
44
|
|
|
45
45
|
## Deploying from GitHub
|
|
46
46
|
|
|
47
|
-
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically
|
|
47
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Every preview gets its own database and bucket, and pending migrations are applied to it on each preview build, so a PR that changes the schema can be tried end to end before it merges.
|
|
48
48
|
|
|
49
49
|
## Where things go
|
|
50
50
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- generated from .claude/skills/nerdsystem/SKILL.md (
|
|
1
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (10dcd79a44bd); edit the skill, then run: nerd agents sync -->
|
|
2
2
|
|
|
3
3
|
# Building on nerdsystem
|
|
4
4
|
|
|
@@ -33,14 +33,14 @@ Tell the person plainly what does not carry. Their users sign up again on the ne
|
|
|
33
33
|
1. Make the change.
|
|
34
34
|
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
35
35
|
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
36
|
-
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and
|
|
36
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, prints the URL, and probes the live URL (headers and the sign-in routes). With `--json` the output carries `probes`, the same shape as a check result, with `status: "skip"` and the reason when it did not run; probe findings never fail the deploy.
|
|
37
37
|
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
38
38
|
|
|
39
39
|
Run `nerd check --json` when you want a machine-readable list of problems. Its summary says how many gates ran and which were skipped (`skipped` in the JSON): semgrep and gitleaks only run locally when installed; git builds always run them in the builder.
|
|
40
40
|
|
|
41
41
|
## Deploying from GitHub
|
|
42
42
|
|
|
43
|
-
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically
|
|
43
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Every preview gets its own database and bucket, and pending migrations are applied to it on each preview build, so a PR that changes the schema can be tried end to end before it merges.
|
|
44
44
|
|
|
45
45
|
## Where things go
|
|
46
46
|
|
package/template/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- generated from .claude/skills/nerdsystem/SKILL.md (
|
|
1
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (10dcd79a44bd); edit the skill, then run: nerd agents sync -->
|
|
2
2
|
|
|
3
3
|
# Building on nerdsystem
|
|
4
4
|
|
|
@@ -33,14 +33,14 @@ Tell the person plainly what does not carry. Their users sign up again on the ne
|
|
|
33
33
|
1. Make the change.
|
|
34
34
|
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
35
35
|
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
36
|
-
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and
|
|
36
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, prints the URL, and probes the live URL (headers and the sign-in routes). With `--json` the output carries `probes`, the same shape as a check result, with `status: "skip"` and the reason when it did not run; probe findings never fail the deploy.
|
|
37
37
|
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
38
38
|
|
|
39
39
|
Run `nerd check --json` when you want a machine-readable list of problems. Its summary says how many gates ran and which were skipped (`skipped` in the JSON): semgrep and gitleaks only run locally when installed; git builds always run them in the builder.
|
|
40
40
|
|
|
41
41
|
## Deploying from GitHub
|
|
42
42
|
|
|
43
|
-
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically
|
|
43
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Every preview gets its own database and bucket, and pending migrations are applied to it on each preview build, so a PR that changes the schema can be tried end to end before it merges.
|
|
44
44
|
|
|
45
45
|
## Where things go
|
|
46
46
|
|
package/template/GEMINI.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- generated from .claude/skills/nerdsystem/SKILL.md (
|
|
1
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (10dcd79a44bd); edit the skill, then run: nerd agents sync -->
|
|
2
2
|
|
|
3
3
|
# Building on nerdsystem
|
|
4
4
|
|
|
@@ -33,14 +33,14 @@ Tell the person plainly what does not carry. Their users sign up again on the ne
|
|
|
33
33
|
1. Make the change.
|
|
34
34
|
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
35
35
|
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
36
|
-
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and
|
|
36
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, prints the URL, and probes the live URL (headers and the sign-in routes). With `--json` the output carries `probes`, the same shape as a check result, with `status: "skip"` and the reason when it did not run; probe findings never fail the deploy.
|
|
37
37
|
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
38
38
|
|
|
39
39
|
Run `nerd check --json` when you want a machine-readable list of problems. Its summary says how many gates ran and which were skipped (`skipped` in the JSON): semgrep and gitleaks only run locally when installed; git builds always run them in the builder.
|
|
40
40
|
|
|
41
41
|
## Deploying from GitHub
|
|
42
42
|
|
|
43
|
-
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically
|
|
43
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Every preview gets its own database and bucket, and pending migrations are applied to it on each preview build, so a PR that changes the schema can be tried end to end before it merges.
|
|
44
44
|
|
|
45
45
|
## Where things go
|
|
46
46
|
|