@madebywild/wvk 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  Wireframe kit — accessible React components, 183 icons, and design tokens that map 1:1 to the wild Wireframe Kit Figma library.
4
4
 
5
+ ## Quick start (new project)
6
+
7
+ Scaffold a fresh Next.js 16 / React 19 / Tailwind v4 app that ships with `@madebywild/wvk` already wired up — `ThemeProvider`, design tokens, the package stylesheet, and a small `Header` composition:
8
+
9
+ ```bash
10
+ npx @madebywild/wvk create-next-app my-wvk-app
11
+ cd my-wvk-app
12
+ npm install
13
+ npm run dev
14
+ ```
15
+
16
+ Then open [http://localhost:3000](http://localhost:3000). The scaffold also writes an `AGENTS.md` so your agent harness (Claude Code, Cursor, Copilot, etc.) picks up the wvk authoring rules — pass `--no-agents` to skip that, or `--force` to scaffold into a non-empty directory.
17
+
5
18
  ## Install
6
19
 
7
20
  ```bash
package/bin/wvk.mjs CHANGED
@@ -1,19 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import { createHash } from "node:crypto";
3
3
  import {
4
+ cpSync,
4
5
  existsSync,
5
6
  mkdirSync,
6
7
  readFileSync,
8
+ readdirSync,
9
+ renameSync,
7
10
  statSync,
11
+ unlinkSync,
8
12
  writeFileSync,
9
13
  } from "node:fs";
10
- import { dirname, relative, resolve } from "node:path";
14
+ import { basename, dirname, join, relative, resolve } from "node:path";
11
15
  import { fileURLToPath } from "node:url";
12
16
 
13
17
  const PKG_NAME = "@madebywild/wvk";
14
18
  const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
15
19
  const PKG_ROOT = resolve(SCRIPT_DIR, "..");
16
20
  const SOURCE = resolve(PKG_ROOT, "AGENTS.md");
21
+ const TEMPLATES_ROOT = resolve(PKG_ROOT, "templates");
17
22
  const PKG_JSON = JSON.parse(
18
23
  readFileSync(resolve(PKG_ROOT, "package.json"), "utf8"),
19
24
  );
@@ -25,6 +30,9 @@ const BEGIN_RE = new RegExp(
25
30
  );
26
31
  const END_MARK = `<!-- END ${PKG_NAME} AGENTS.md -->`;
27
32
 
33
+ // Valid npm package name per https://github.com/npm/validate-npm-package-name
34
+ const NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
35
+
28
36
  function escapeRegex(s) {
29
37
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
30
38
  }
@@ -33,19 +41,28 @@ function usage() {
33
41
  return `wvk — ${PKG_NAME} CLI
34
42
 
35
43
  Usage:
44
+ wvk create-next-app <name> [--force] [--no-agents]
36
45
  wvk copy-harness [--force] [--append] [--out <path>]
37
46
  wvk --help
38
47
  wvk --version
39
48
 
40
49
  Commands:
41
- copy-harness Copy this package's AGENTS.md into your project so your
42
- agent harness picks up the wvk authoring rules.
50
+ create-next-app Scaffold a new Next.js 16 / React 19 app preconfigured with
51
+ ${PKG_NAME}, Tailwind v4, ThemeProvider, and a Header. Drops
52
+ the package's AGENTS.md into the new project so a consumer's
53
+ agent harness picks up the wvk authoring rules.
54
+ copy-harness Copy this package's AGENTS.md into your project so your
55
+ agent harness picks up the wvk authoring rules.
43
56
 
44
57
  Options:
45
- --out <path> Destination path (default: ./AGENTS.md). Parent dirs are created.
46
- --force Overwrite the destination if it exists. Mutually exclusive with --append.
47
- --append Merge the rules into the destination as a delimited, idempotent block.
48
- Re-running is safe: the block is replaced in place, never duplicated.
58
+ --out <path> (copy-harness) Destination path (default: ./AGENTS.md). Parent dirs are created.
59
+ --force Overwrite an existing destination. For create-next-app, allow
60
+ scaffolding into an existing non-empty directory.
61
+ Mutually exclusive with --append on copy-harness.
62
+ --append (copy-harness) Merge the rules into the destination as a
63
+ delimited, idempotent block. Re-running is safe: the block is
64
+ replaced in place, never duplicated.
65
+ --no-agents (create-next-app) Skip writing AGENTS.md into the new project.
49
66
  `;
50
67
  }
51
68
 
@@ -55,11 +72,12 @@ function fail(msg, code = 1) {
55
72
  }
56
73
 
57
74
  function parseArgs(argv) {
58
- const args = { _: [], force: false, append: false, out: null };
75
+ const args = { _: [], force: false, append: false, out: null, noAgents: false };
59
76
  for (let i = 0; i < argv.length; i++) {
60
77
  const a = argv[i];
61
78
  if (a === "--force") args.force = true;
62
79
  else if (a === "--append") args.append = true;
80
+ else if (a === "--no-agents") args.noAgents = true;
63
81
  else if (a === "--out") {
64
82
  const v = argv[++i];
65
83
  if (!v) fail("--out requires a path");
@@ -172,6 +190,110 @@ function report(destRel, mode) {
172
190
  }
173
191
  }
174
192
 
193
+ function isDirEmpty(dir) {
194
+ try {
195
+ return readdirSync(dir).length === 0;
196
+ } catch {
197
+ return true;
198
+ }
199
+ }
200
+
201
+ function createNextApp(args) {
202
+ if (args.append) {
203
+ fail("--append is not supported for create-next-app");
204
+ }
205
+ const name = args._[1];
206
+ if (!name) {
207
+ fail("create-next-app requires a project name. Example: wvk create-next-app my-app");
208
+ }
209
+ if (!NAME_RE.test(name) && name !== ".") {
210
+ fail(
211
+ `invalid project name: ${name}\n` +
212
+ `wvk: must be a valid npm package name (lowercase, no spaces, may start with @scope/).`,
213
+ );
214
+ }
215
+ const templateRoot = resolve(TEMPLATES_ROOT, "next-app");
216
+ if (!existsSync(templateRoot)) {
217
+ fail(`scaffolding template missing in package at ${templateRoot}`);
218
+ }
219
+
220
+ const targetDir =
221
+ name === "." ? process.cwd() : resolve(process.cwd(), name);
222
+ const targetRel = relative(process.cwd(), targetDir) || ".";
223
+ const projectName = name === "." ? basename(targetDir) : name;
224
+ if (!NAME_RE.test(projectName)) {
225
+ fail(
226
+ `derived project name "${projectName}" is not a valid npm package name. ` +
227
+ `Pass an explicit name: wvk create-next-app <name>`,
228
+ );
229
+ }
230
+
231
+ if (existsSync(targetDir)) {
232
+ if (!statSync(targetDir).isDirectory()) {
233
+ fail(`${targetRel} exists and is not a directory`);
234
+ }
235
+ if (!isDirEmpty(targetDir) && !args.force) {
236
+ fail(
237
+ `${targetRel} is not empty. Re-run with --force to scaffold into it anyway ` +
238
+ `(existing files with the same name will be overwritten).`,
239
+ );
240
+ }
241
+ } else {
242
+ mkdirSync(targetDir, { recursive: true });
243
+ }
244
+
245
+ // Copy every file from the template tree. cpSync with recursive copies
246
+ // directories without descending into anything that doesn't exist.
247
+ cpSync(templateRoot, targetDir, { recursive: true, force: true });
248
+
249
+ // npm strips a literal `.gitignore` from packed tarballs (it's renamed to
250
+ // `.npmignore`). The template ships it as `gitignore`; rename on copy. If
251
+ // the caller already had a `.gitignore` (only possible with --force), keep
252
+ // theirs and drop ours so we don't leave an orphan `gitignore` lying around.
253
+ const giSrc = join(targetDir, "gitignore");
254
+ const giDst = join(targetDir, ".gitignore");
255
+ if (existsSync(giSrc)) {
256
+ if (existsSync(giDst)) {
257
+ unlinkSync(giSrc);
258
+ } else {
259
+ renameSync(giSrc, giDst);
260
+ }
261
+ }
262
+
263
+ // Set the `name` field in the new app's package.json to the requested name.
264
+ const targetPkgPath = join(targetDir, "package.json");
265
+ if (existsSync(targetPkgPath)) {
266
+ const pkg = JSON.parse(readFileSync(targetPkgPath, "utf8"));
267
+ pkg.name = projectName;
268
+ writeFileSync(targetPkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
269
+ }
270
+
271
+ // Drop the package's AGENTS.md into the project so the consumer's agent
272
+ // harness picks up the wvk authoring rules. Sourced from PKG_ROOT (same
273
+ // file `wvk copy-harness` writes), not from the template, so the two
274
+ // never drift. Opt out with --no-agents.
275
+ if (!args.noAgents) {
276
+ if (!existsSync(SOURCE)) {
277
+ fail(`source AGENTS.md missing in package at ${SOURCE}`);
278
+ }
279
+ const agentsBody = readFileSync(SOURCE, "utf8");
280
+ writeFileSync(join(targetDir, "AGENTS.md"), agentsBody);
281
+ }
282
+
283
+ process.stdout.write(`wvk: scaffolded ${PKG_NAME} Next.js app in ${targetRel}\n`);
284
+ if (!args.noAgents) {
285
+ process.stdout.write(
286
+ `wvk: wrote AGENTS.md so your agent harness picks up the wvk authoring rules\n`,
287
+ );
288
+ }
289
+ process.stdout.write(`wvk: next steps:\n`);
290
+ if (targetRel !== ".") {
291
+ process.stdout.write(` cd ${targetRel}\n`);
292
+ }
293
+ process.stdout.write(` npm install # or pnpm install\n`);
294
+ process.stdout.write(` npm run dev # then open http://localhost:3000\n`);
295
+ }
296
+
175
297
  function main() {
176
298
  const args = parseArgs(process.argv.slice(2));
177
299
  if (args.help) {
@@ -191,6 +313,10 @@ function main() {
191
313
  copyHarness(args);
192
314
  return;
193
315
  }
316
+ if (cmd === "create-next-app") {
317
+ createNextApp(args);
318
+ return;
319
+ }
194
320
  process.stderr.write(`wvk: unknown command: ${cmd}\n\n${usage()}`);
195
321
  process.exit(1);
196
322
  }
package/dist/index.cjs CHANGED
@@ -7311,6 +7311,9 @@ var TextInput = React24.forwardRef(
7311
7311
  "data-wvk-input": "shell",
7312
7312
  style: { cursor: disabled ? "not-allowed" : "var(--wvk-cursor-text)" },
7313
7313
  className: cn(textInputShellVariants({ inputStyle, size }), className),
7314
+ onClick: () => {
7315
+ if (!disabled) innerRef.current?.focus();
7316
+ },
7314
7317
  children: [
7315
7318
  leadingIcon ? /* @__PURE__ */ (0, import_jsx_runtime208.jsx)(
7316
7319
  "span",