@ansh-ui/cli 0.3.0 → 0.4.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/index.js CHANGED
@@ -1,8 +1,22 @@
1
- import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
2
- import { join, dirname, resolve } from 'path';
1
+ import 'readline';
2
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { join, dirname, relative, resolve } from 'path';
3
4
  import { effects, behaviours, parseColor, rgbToOklch, createScale, checkContrast, toOklchString, toHex, harmony, generatePalette, createGradient } from '@ansh-ui/core';
4
5
 
5
- // src/commands/add.ts
6
+ // src/commands/mcp.ts
7
+ var CONFIG_FILE = "ansh.config.json";
8
+ function readConfig(root) {
9
+ const path = join(root, CONFIG_FILE);
10
+ if (!existsSync(path)) return null;
11
+ try {
12
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
13
+ return parsed && typeof parsed === "object" ? parsed : null;
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+
19
+ // src/project.ts
6
20
  function findRoot(cwd) {
7
21
  let current = resolve(cwd);
8
22
  for (let depth = 0; depth < 10; depth++) {
@@ -30,11 +44,12 @@ function detectProject(cwd = process.cwd()) {
30
44
  const framework = deps.nuxt ? "nuxt" : deps.next ? "next" : deps.vue ? "vue" : deps.react ? "react" : "unknown";
31
45
  const typescript = Boolean(deps.typescript) || existsSync(join(root, "tsconfig.json"));
32
46
  const packageManager = existsSync(join(root, "pnpm-lock.yaml")) ? "pnpm" : existsSync(join(root, "bun.lockb")) || existsSync(join(root, "bun.lock")) ? "bun" : existsSync(join(root, "yarn.lock")) ? "yarn" : "npm";
47
+ const config = readConfig(root);
33
48
  return {
34
49
  root,
35
50
  framework,
36
51
  typescript,
37
- componentsDir: detectComponentsDir(root, framework),
52
+ componentsDir: config?.componentsDir ?? detectComponentsDir(root, framework),
38
53
  alias: detectAlias(root),
39
54
  packageManager,
40
55
  installed: Boolean(
@@ -64,6 +79,29 @@ function detectAlias(root) {
64
79
  }
65
80
  return null;
66
81
  }
82
+ function packagesFor(framework) {
83
+ switch (framework) {
84
+ case "nuxt":
85
+ return ["@ansh-ui/nuxt"];
86
+ case "vue":
87
+ return ["@ansh-ui/vue", "@ansh-ui/tokens"];
88
+ default:
89
+ return ["@ansh-ui/react", "@ansh-ui/tokens"];
90
+ }
91
+ }
92
+ function installCommand(info, packages) {
93
+ const list = packages.join(" ");
94
+ switch (info.packageManager) {
95
+ case "pnpm":
96
+ return `pnpm add ${list}`;
97
+ case "yarn":
98
+ return `yarn add ${list}`;
99
+ case "bun":
100
+ return `bun add ${list}`;
101
+ default:
102
+ return `npm install ${list}`;
103
+ }
104
+ }
67
105
  var COMPONENT_NAMES = {
68
106
  aurora: "AuroraBackground",
69
107
  "mesh-gradient": "MeshGradient",
@@ -157,15 +195,7 @@ function addComponent(options) {
157
195
  return { path, contents, written: true, missingDependencies };
158
196
  }
159
197
  function missingFor(project) {
160
- if (project.installed) return [];
161
- switch (project.framework) {
162
- case "nuxt":
163
- return ["@ansh-ui/nuxt"];
164
- case "vue":
165
- return ["@ansh-ui/vue", "@ansh-ui/tokens"];
166
- default:
167
- return ["@ansh-ui/react", "@ansh-ui/tokens"];
168
- }
198
+ return project.installed ? [] : packagesFor(project.framework);
169
199
  }
170
200
  function literal(value) {
171
201
  if (typeof value === "string") return JSON.stringify(value);
@@ -345,6 +375,229 @@ ${accentLines}
345
375
  }
346
376
  return { css, unsafeOnLight, unsafeOnDark, nearestStep };
347
377
  }
378
+
379
+ // src/commands/init.ts
380
+ function initProject(options = {}) {
381
+ const project = detectProject(options.cwd);
382
+ if (project.framework === "unknown") {
383
+ throw new Error(
384
+ "Could not detect a React, Next, Vue or Nuxt project. Run this from your project root, or install the framework first."
385
+ );
386
+ }
387
+ const configPath = join(project.root, CONFIG_FILE);
388
+ if (existsSync(configPath) && !options.force) {
389
+ throw new Error(`${CONFIG_FILE} already exists. Pass --force to overwrite it.`);
390
+ }
391
+ const themeRelative = options.brand ? join(stylesDir(project), "ansh-theme.css") : null;
392
+ const themePath = themeRelative ? join(project.root, themeRelative) : null;
393
+ if (themePath && existsSync(themePath) && !options.force) {
394
+ throw new Error(`${themePath} already exists. Pass --force to overwrite it.`);
395
+ }
396
+ const config = {
397
+ framework: project.framework,
398
+ typescript: project.typescript,
399
+ componentsDir: options.componentsDir ?? project.componentsDir,
400
+ alias: project.alias,
401
+ theme: themeRelative
402
+ };
403
+ const written = [];
404
+ if (!options.dryRun) {
405
+ if (options.brand && themePath) {
406
+ generateTheme({ color: options.brand, out: themePath });
407
+ written.push(themePath);
408
+ }
409
+ writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
410
+ `, "utf8");
411
+ written.push(configPath);
412
+ } else if (options.brand) {
413
+ generateTheme({ color: options.brand });
414
+ }
415
+ return {
416
+ project,
417
+ config,
418
+ written,
419
+ install: project.installed ? null : installCommand(project, packagesFor(project.framework)),
420
+ setup: setupFor(project, themeRelative)
421
+ };
422
+ }
423
+ function stylesDir(project) {
424
+ if (project.framework === "nuxt") return "assets/css";
425
+ return existsSync(join(project.root, "src")) ? "src/styles" : "styles";
426
+ }
427
+ function setupFor(project, theme) {
428
+ if (project.framework === "nuxt") {
429
+ const css = ["'@ansh-ui/vue/styles.css'", ...theme ? [`'~/${theme}'`] : []].join(", ");
430
+ return {
431
+ file: "nuxt.config.ts",
432
+ code: `export default defineNuxtConfig({
433
+ modules: ['@ansh-ui/nuxt'],
434
+ css: [${css}],
435
+ })`
436
+ };
437
+ }
438
+ const binding = project.framework === "vue" ? "vue" : "react";
439
+ const entry = project.framework === "next" ? existsSync(join(project.root, "src/app")) ? "src/app/layout.tsx" : "app/layout.tsx" : project.framework === "vue" ? `src/main.${project.typescript ? "ts" : "js"}` : `src/main.${project.typescript ? "tsx" : "jsx"}`;
440
+ const themeImport = theme ? `
441
+ import '${themeImportPath(project, entry, theme)}'` : "";
442
+ return {
443
+ file: entry,
444
+ code: `import '@ansh-ui/tokens/ansh.css'
445
+ import '@ansh-ui/${binding}/styles.css'${themeImport}`
446
+ };
447
+ }
448
+ function themeImportPath(project, entry, theme) {
449
+ const path = relative(join(project.root, entry, ".."), join(project.root, theme));
450
+ return path.startsWith(".") ? path : `./${path}`;
451
+ }
452
+
453
+ // src/commands/mcp.ts
454
+ var PROTOCOL_VERSION = "2025-06-18";
455
+ var str = (value) => typeof value === "string" ? value : void 0;
456
+ var TOOLS = [
457
+ {
458
+ name: "ansh_list",
459
+ description: "List Ansh canvas effects and DOM behaviours with their component names and option counts.",
460
+ inputSchema: {
461
+ type: "object",
462
+ properties: { kind: { type: "string", enum: ["effect", "behaviour"] } }
463
+ },
464
+ run: (args) => {
465
+ const kind = args.kind === "effect" || args.kind === "behaviour" ? args.kind : void 0;
466
+ const entries = listCatalogue(kind).map(({ name, kind: kind2, component, options }) => ({
467
+ name,
468
+ kind: kind2,
469
+ component,
470
+ options: options.length
471
+ }));
472
+ return JSON.stringify(entries, null, 2);
473
+ }
474
+ },
475
+ {
476
+ name: "ansh_info",
477
+ description: "Every option of one Ansh effect or behaviour, with its default value. Accepts the catalogue name (aurora) or component name (AuroraBackground).",
478
+ inputSchema: {
479
+ type: "object",
480
+ properties: { name: { type: "string" } },
481
+ required: ["name"]
482
+ },
483
+ run: (args) => {
484
+ const entry = findEntry(str(args.name) ?? "");
485
+ if (!entry) throw new Error(`Unknown component "${String(args.name)}". Call ansh_list.`);
486
+ return JSON.stringify(entry, null, 2);
487
+ }
488
+ },
489
+ {
490
+ name: "ansh_setup",
491
+ description: "How to set up Ansh in the current project: detected framework, install command, and the imports or nuxt.config lines to add. Pass write: true to also create ansh.config.json (and a theme file when brand is given).",
492
+ inputSchema: {
493
+ type: "object",
494
+ properties: {
495
+ brand: { type: "string", description: "Brand colour for a generated theme, e.g. #0275f4" },
496
+ write: { type: "boolean", default: false }
497
+ }
498
+ },
499
+ run: (args, cwd) => {
500
+ const result = initProject({ cwd, brand: str(args.brand), dryRun: args.write !== true });
501
+ return JSON.stringify(
502
+ {
503
+ framework: result.config.framework,
504
+ install: result.install,
505
+ setupFile: result.setup.file,
506
+ setupCode: result.setup.code,
507
+ config: result.config,
508
+ written: result.written
509
+ },
510
+ null,
511
+ 2
512
+ );
513
+ }
514
+ },
515
+ {
516
+ name: "ansh_add",
517
+ description: "Generate a typed, project-local wrapper component around an Ansh effect or behaviour, with the project\u2019s defaults. Returns the file path and contents; pass write: true to write the file.",
518
+ inputSchema: {
519
+ type: "object",
520
+ properties: {
521
+ name: { type: "string" },
522
+ as: { type: "string", description: "Local component name, e.g. HeroBackground" },
523
+ write: { type: "boolean", default: false }
524
+ },
525
+ required: ["name"]
526
+ },
527
+ run: (args, cwd) => {
528
+ const result = addComponent({
529
+ cwd,
530
+ name: str(args.name) ?? "",
531
+ as: str(args.as),
532
+ dryRun: args.write !== true
533
+ });
534
+ return JSON.stringify(result, null, 2);
535
+ }
536
+ },
537
+ {
538
+ name: "ansh_theme",
539
+ description: "Generate an Ansh token override stylesheet from a brand colour, with WCAG contrast notes for each step.",
540
+ inputSchema: {
541
+ type: "object",
542
+ properties: { color: { type: "string" }, withAccent: { type: "boolean", default: false } },
543
+ required: ["color"]
544
+ },
545
+ run: (args) => {
546
+ const report = generateTheme({
547
+ color: str(args.color) ?? "",
548
+ withAccent: args.withAccent === true
549
+ });
550
+ return JSON.stringify(report, null, 2);
551
+ }
552
+ }
553
+ ];
554
+ function callTool(params, cwd) {
555
+ const tool = TOOLS.find((t) => t.name === params?.name);
556
+ if (!tool)
557
+ return {
558
+ content: [{ type: "text", text: `Unknown tool "${String(params?.name)}".` }],
559
+ isError: true
560
+ };
561
+ const args = params?.arguments ?? {};
562
+ try {
563
+ return { content: [{ type: "text", text: tool.run(args, cwd) }] };
564
+ } catch (error) {
565
+ const text = error instanceof Error ? error.message : String(error);
566
+ return { content: [{ type: "text", text }], isError: true };
567
+ }
568
+ }
569
+ function handleMessage(message, options) {
570
+ const cwd = options.cwd ?? process.cwd();
571
+ const reply = (result) => ({ jsonrpc: "2.0", id: message.id ?? null, result });
572
+ if (message.id === void 0) return null;
573
+ switch (message.method) {
574
+ case "initialize":
575
+ return reply({
576
+ protocolVersion: str(message.params?.protocolVersion) ?? PROTOCOL_VERSION,
577
+ capabilities: { tools: {} },
578
+ serverInfo: { name: "ansh", version: options.version },
579
+ instructions: "Ansh is a UI and animation library for React, Next.js, Vue and Nuxt. Call ansh_setup before writing Ansh code in a project, and ansh_info for an effect\u2019s exact options."
580
+ });
581
+ case "ping":
582
+ return reply({});
583
+ case "tools/list":
584
+ return reply({
585
+ tools: TOOLS.map(({ name, description, inputSchema }) => ({
586
+ name,
587
+ description,
588
+ inputSchema
589
+ }))
590
+ });
591
+ case "tools/call":
592
+ return reply(callTool(message.params, cwd));
593
+ default:
594
+ return {
595
+ jsonrpc: "2.0",
596
+ id: message.id,
597
+ error: { code: -32601, message: `Method not found: ${message.method}` }
598
+ };
599
+ }
600
+ }
348
601
  function generatePaletteReport(options = {}) {
349
602
  const parsed = options.colors && options.colors.length > 0 ? options.colors.map((color) => parseColor(color)) : generatePalette({
350
603
  count: options.count ?? 5,
@@ -368,6 +621,6 @@ function generatePaletteReport(options = {}) {
368
621
  return { colors, gradientSamples, snippet };
369
622
  }
370
623
 
371
- export { addComponent, detectProject, generatePaletteReport, generateTheme, listCatalogue };
624
+ export { CONFIG_FILE, TOOLS as MCP_TOOLS, addComponent, detectProject, generatePaletteReport, generateTheme, handleMessage as handleMcpMessage, initProject, listCatalogue, readConfig };
372
625
  //# sourceMappingURL=index.js.map
373
626
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/project.ts","../src/commands/list.ts","../src/commands/add.ts","../src/commands/theme.ts","../src/commands/palette.ts"],"names":["join","existsSync","mkdirSync","dirname","writeFileSync","parseColor","rgbToOklch","toHex","toOklchString"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\n\n/**\n * Project detection.\n *\n * The CLI needs to know which framework it is generating for, whether the project uses TypeScript,\n * and where components live. Asking the user all of that on every invocation is friction for\n * information that is sitting in `package.json` and on disk — so we detect it, and only prompt for\n * what we genuinely cannot infer.\n */\n\nexport type Framework = 'react' | 'vue' | 'nuxt' | 'next' | 'unknown'\n\nexport interface ProjectInfo {\n root: string\n framework: Framework\n typescript: boolean\n /** Where generated components should go, relative to `root`. */\n componentsDir: string\n /** Detected import alias for the source root, e.g. `'@/'`. `null` when none is configured. */\n alias: string | null\n packageManager: 'pnpm' | 'yarn' | 'npm' | 'bun'\n /** `true` when the relevant `@ansh-ui/*` package is already a dependency. */\n installed: boolean\n}\n\n/** Walk up from `cwd` looking for a `package.json`, so the CLI works from a subdirectory. */\nfunction findRoot(cwd: string): string {\n let current = resolve(cwd)\n for (let depth = 0; depth < 10; depth++) {\n if (existsSync(join(current, 'package.json'))) return current\n const parent = resolve(current, '..')\n if (parent === current) break\n current = parent\n }\n return resolve(cwd)\n}\n\nfunction readJson(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\nexport function detectProject(cwd = process.cwd()): ProjectInfo {\n const root = findRoot(cwd)\n const pkg = readJson(join(root, 'package.json')) ?? {}\n\n const deps: Record<string, string> = {\n ...((pkg.dependencies as Record<string, string>) ?? {}),\n ...((pkg.devDependencies as Record<string, string>) ?? {}),\n }\n\n // Order matters: Nuxt projects also depend on Vue, and Next projects on React. Checking the\n // meta-framework first gives the more specific — and more useful — answer.\n const framework: Framework = deps.nuxt\n ? 'nuxt'\n : deps.next\n ? 'next'\n : deps.vue\n ? 'vue'\n : deps.react\n ? 'react'\n : 'unknown'\n\n const typescript = Boolean(deps.typescript) || existsSync(join(root, 'tsconfig.json'))\n\n const packageManager: ProjectInfo['packageManager'] = existsSync(join(root, 'pnpm-lock.yaml'))\n ? 'pnpm'\n : existsSync(join(root, 'bun.lockb')) || existsSync(join(root, 'bun.lock'))\n ? 'bun'\n : existsSync(join(root, 'yarn.lock'))\n ? 'yarn'\n : 'npm'\n\n return {\n root,\n framework,\n typescript,\n componentsDir: detectComponentsDir(root, framework),\n alias: detectAlias(root),\n packageManager,\n installed: Boolean(\n deps['@ansh-ui/react'] ||\n deps['@ansh-ui/vue'] ||\n deps['@ansh-ui/nuxt'] ||\n deps['@ansh-ui/core'],\n ),\n }\n}\n\n/**\n * Find where components already live, rather than imposing a convention.\n *\n * Dropping a file into `src/components` in a project that uses `app/ui` is the kind of small\n * disrespect that makes a tool feel foreign. Candidates are ordered by how strong a signal they are.\n */\nfunction detectComponentsDir(root: string, framework: Framework): string {\n const candidates =\n framework === 'nuxt'\n ? ['components', 'app/components', 'src/components']\n : ['src/components', 'components', 'app/components', 'src/lib/components', 'app/ui', 'src/ui']\n\n for (const candidate of candidates) {\n if (existsSync(join(root, candidate))) return candidate\n }\n\n // Nothing exists yet: pick the convention that matches the project's existing shape.\n if (existsSync(join(root, 'src'))) return 'src/components'\n if (framework === 'nuxt') return 'components'\n return 'components'\n}\n\n/**\n * Read the source alias from `tsconfig.json` so generated imports match the project's style.\n *\n * A generated file that writes `../../lib/utils` in a project where everything else says\n * `@/lib/utils` is immediately identifiable as machine-written.\n */\nfunction detectAlias(root: string): string | null {\n for (const file of ['tsconfig.json', 'jsconfig.json']) {\n const config = readJson(join(root, file))\n if (!config) continue\n\n const compilerOptions = config.compilerOptions as Record<string, unknown> | undefined\n const paths = compilerOptions?.paths as Record<string, string[]> | undefined\n if (!paths) continue\n\n for (const pattern of Object.keys(paths)) {\n // Match a plain wildcard alias like `\"@/*\"` or `\"~/*\"` — the overwhelmingly common shape.\n if (pattern.endsWith('/*')) return pattern.slice(0, -1)\n }\n }\n return null\n}\n\nexport interface KnownInconsistency {\n title: string\n detail: string\n}\n\n/**\n * Maintainer-facing issues logged in `docs/STATUS.md` §7 — restated here so a contributor\n * discovers them from `ansh doctor` output, not by reading a markdown file they may not know\n * exists. Kept in sync with STATUS.md by hand: this list should shrink exactly when that section\n * does, and for the same reason (the item was resolved), not because someone forgot one file.\n */\nconst KNOWN_INCONSISTENCIES: KnownInconsistency[] = [\n {\n title: 'License key prefix is still `aur_`',\n detail:\n 'A leftover from the Aurelis → Ansh rename (packages/license/src/public-key.ts). Changing it ' +\n 'without a dual-prefix verification path would invalidate already-issued keys — needs a ' +\n 'maintainer decision, not a drive-by fix. See STATUS.md §7.',\n },\n]\n\n/**\n * `ansh doctor` runs against whatever project it's invoked in — almost always a consumer's app.\n * The one exception worth detecting is the Ansh monorepo itself: a contributor running the CLI\n * from within its own source tree should see the maintainer-facing known-inconsistencies list\n * (above), which is meaningless noise for every other project `doctor` will ever run in.\n */\nexport function isAnshRepoRoot(root: string): boolean {\n const pkg = readJson(join(root, 'package.json'))\n return pkg?.name === 'ansh' && existsSync(join(root, 'packages/license/src/index.ts'))\n}\n\nexport function detectKnownInconsistencies(root: string): KnownInconsistency[] {\n return isAnshRepoRoot(root) ? KNOWN_INCONSISTENCIES : []\n}\n\n/** The install command for the detected package manager. */\nexport function installCommand(info: ProjectInfo, packages: string[]): string {\n const list = packages.join(' ')\n switch (info.packageManager) {\n case 'pnpm':\n return `pnpm add ${list}`\n case 'yarn':\n return `yarn add ${list}`\n case 'bun':\n return `bun add ${list}`\n default:\n return `npm install ${list}`\n }\n}\n","import { behaviours, effects } from '@ansh-ui/core'\n\n/**\n * Catalogue enumeration.\n *\n * Reads the registry rather than a hardcoded list, so a new effect appears in `ansh list` the\n * moment it is defined — no second place to update, and no possibility of the CLI advertising\n * something that doesn't exist or omitting something that does.\n */\n\nexport interface CatalogueEntry {\n name: string\n kind: 'effect' | 'behaviour'\n /** The component name in `@ansh-ui/react` and `@ansh-ui/vue`. */\n component: string\n /** Every option, with its default value, read from the definition. */\n options: Array<{ name: string; default: unknown }>\n}\n\n/**\n * Effect and behaviour names map to component names that read better than a mechanical\n * transformation would produce — `meteors` → `MeteorShower`, `blob` → `LiquidBlob`. Where a\n * definition isn't listed here, the name is PascalCased.\n */\nconst COMPONENT_NAMES: Record<string, string> = {\n aurora: 'AuroraBackground',\n 'mesh-gradient': 'MeshGradient',\n 'animated-gradient': 'AnimatedGradient',\n 'particle-field': 'ParticleField',\n constellation: 'Constellation',\n meteors: 'MeteorShower',\n rain: 'Rain',\n snow: 'Snow',\n fireflies: 'Fireflies',\n waves: 'Waves',\n blob: 'LiquidBlob',\n 'noise-layer': 'NoiseLayer',\n spotlight: 'Spotlight',\n magnetic: 'MagneticButton',\n tilt: 'TiltCard',\n ripple: 'Ripple',\n marquee: 'Marquee',\n 'scroll-reveal': 'ScrollReveal',\n parallax: 'Parallax',\n 'text-scramble': 'ScrambleText',\n}\n\n/**\n * Behaviours with no component in either binding. `split-text` is a building block (`AnimatedText`\n * does its own split so the markup server-renders), so `ansh add split-text` would generate an\n * import of a component that doesn't exist. Leaving it out of the catalogue is the honest answer.\n */\nconst NO_COMPONENT = new Set(['split-text'])\n\nexport function componentNameFor(name: string): string {\n return (\n COMPONENT_NAMES[name] ??\n name.replace(/(^|-)([a-z])/g, (_, __, letter: string) => letter.toUpperCase())\n )\n}\n\nexport function listCatalogue(filter?: 'effect' | 'behaviour'): CatalogueEntry[] {\n const entries: CatalogueEntry[] = []\n\n if (filter !== 'behaviour') {\n for (const [name, definition] of Object.entries(effects)) {\n entries.push({\n name,\n kind: 'effect',\n component: componentNameFor(name),\n options: describeOptions(definition.defaults as Record<string, unknown>),\n })\n }\n }\n\n if (filter !== 'effect') {\n for (const [name, definition] of Object.entries(behaviours)) {\n if (NO_COMPONENT.has(name)) continue\n entries.push({\n name,\n kind: 'behaviour',\n component: componentNameFor(name),\n options: describeOptions(definition.defaults as Record<string, unknown>),\n })\n }\n }\n\n return entries.sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/**\n * Turn a `defaults` object into a documented option list.\n *\n * This is why {@link import('@ansh-ui/core').EffectDefinition}'s `defaults` is typed\n * `Required<Options>`: it makes \"every option has a documented default\" a compile-time guarantee,\n * which in turn makes this function complete by construction rather than by diligence.\n */\nfunction describeOptions(defaults: Record<string, unknown>): CatalogueEntry['options'] {\n return Object.entries(defaults)\n .map(([name, value]) => ({ name, default: value }))\n .sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/** Look up a single entry by effect/behaviour name or by component name, case-insensitively. */\nexport function findEntry(query: string): CatalogueEntry | undefined {\n const needle = query.toLowerCase()\n return listCatalogue().find(\n (entry) => entry.name.toLowerCase() === needle || entry.component.toLowerCase() === needle,\n )\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { type ProjectInfo, detectProject } from '../project'\nimport { findEntry } from './list'\n\n/**\n * `ansh add <name>` — generate a local, editable wrapper for a component.\n *\n * ## Why a wrapper rather than a source copy\n *\n * shadcn/ui popularised copying component source into the project, and for *unstyled primitives*\n * that is exactly right: the value is in the markup and the styling, and you want to own both.\n *\n * Ansh is a different shape. The value is in the engine — an 800-line curl-noise particle system,\n * a spring solver, a colour pipeline — and copying that into an application would be actively\n * harmful: it would never receive a bug fix, and it would bloat the repository with code nobody on\n * the team intends to maintain.\n *\n * What developers actually want to own is the **configuration**: which colours, what intensity, what\n * it is called in their design system. So that is what this generates — a thin, fully-typed local\n * component with the project's own defaults baked in, importing the engine from the package.\n *\n * The result: `<HeroBackground />` in application code, one file to edit when the brand palette\n * changes, and engine fixes arrive through a normal dependency upgrade.\n */\n\nexport interface AddOptions {\n /** Effect or behaviour name, or component name. Case-insensitive. */\n name: string\n /** Local component name. Defaults to the catalogue's component name. */\n as?: string\n /** Target directory, relative to the project root. Defaults to the detected components dir. */\n dir?: string\n /** Overwrite an existing file. Default `false`. */\n force?: boolean\n /** Print the file instead of writing it. Default `false`. */\n dryRun?: boolean\n cwd?: string\n}\n\nexport interface AddResult {\n path: string\n contents: string\n written: boolean\n /** Packages the project needs but doesn't have. */\n missingDependencies: string[]\n}\n\nexport function addComponent(options: AddOptions): AddResult {\n const project = detectProject(options.cwd)\n const entry = findEntry(options.name)\n\n if (!entry) {\n throw new Error(\n `Unknown component \"${options.name}\". Run \\`ansh list\\` to see what's available.`,\n )\n }\n\n if (project.framework === 'unknown') {\n throw new Error(\n 'Could not detect a React, Next, Vue or Nuxt project. ' +\n 'Run this from your project root, or install the framework first.',\n )\n }\n\n const isVue = project.framework === 'vue' || project.framework === 'nuxt'\n const componentName = options.as ?? entry.component\n const extension = isVue ? 'vue' : project.typescript ? 'tsx' : 'jsx'\n\n const directory = options.dir ?? project.componentsDir\n const path = join(project.root, directory, `${componentName}.${extension}`)\n\n const contents = isVue\n ? renderVue(componentName, entry.component, entry.options, project)\n : renderReact(componentName, entry.component, entry.options, project)\n\n const missingDependencies = missingFor(project)\n\n if (options.dryRun) {\n return { path, contents, written: false, missingDependencies }\n }\n\n if (existsSync(path) && !options.force) {\n throw new Error(`${path} already exists. Pass --force to overwrite.`)\n }\n\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, contents, 'utf8')\n\n return { path, contents, written: true, missingDependencies }\n}\n\nfunction missingFor(project: ProjectInfo): string[] {\n if (project.installed) return []\n switch (project.framework) {\n case 'nuxt':\n return ['@ansh-ui/nuxt']\n case 'vue':\n return ['@ansh-ui/vue', '@ansh-ui/tokens']\n default:\n return ['@ansh-ui/react', '@ansh-ui/tokens']\n }\n}\n\n/**\n * Serialise a default value as source.\n *\n * Only the options worth surfacing are emitted — see {@link INTERESTING_OPTIONS}. Emitting all\n * twenty would produce a wall of noise that obscures the three an author will actually change.\n */\nfunction literal(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value)\n if (Array.isArray(value)) return `[${value.map(literal).join(', ')}]`\n if (value === null) return 'null'\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\n/**\n * Options included in generated source.\n *\n * These are the ones that carry design intent — the knobs a designer asks to change. Structural and\n * performance options are omitted from the generated defaults but remain available as props, since\n * the wrapper spreads everything through.\n */\nconst INTERESTING_OPTIONS = new Set([\n 'colors',\n 'speed',\n 'intensity',\n 'opacity',\n 'blur',\n 'seed',\n 'strength',\n 'radius',\n 'max',\n 'distance',\n 'amount',\n 'count',\n])\n\nfunction pickDefaults(options: Array<{ name: string; default: unknown }>) {\n return options.filter((option) => INTERESTING_OPTIONS.has(option.name))\n}\n\nfunction renderReact(\n componentName: string,\n sourceComponent: string,\n options: Array<{ name: string; default: unknown }>,\n project: ProjectInfo,\n): string {\n const ts = project.typescript\n const defaults = pickDefaults(options)\n\n const defaultLines = defaults\n .map((option) => ` ${option.name}: ${literal(option.default)},`)\n .join('\\n')\n\n const propsType = ts\n ? `\\ntype ${componentName}Props = ComponentProps<typeof ${sourceComponent}>\\n`\n : ''\n\n const signature = ts\n ? `export function ${componentName}({ children, ...props }: ${componentName}Props) {`\n : `export function ${componentName}({ children, ...props }) {`\n\n const typeImport = ts ? `import type { ComponentProps } from 'react'\\n` : ''\n\n return `${typeImport}import { ${sourceComponent} } from '@ansh-ui/react'\n\n/**\n * ${componentName} — a project-local wrapper around Ansh's ${sourceComponent}.\n *\n * Edit the defaults below to match your design system. Every option remains overridable per\n * instance, because props spread after the defaults.\n *\n * Generated by \\`ansh add\\`. Safe to edit — it will not be regenerated unless you ask.\n */\nconst defaults = {\n${defaultLines}\n}\n\n${propsType}\n${signature}\n return (\n <${sourceComponent} {...defaults} {...props}>\n {children}\n </${sourceComponent}>\n )\n}\n`\n}\n\nfunction renderVue(\n componentName: string,\n sourceComponent: string,\n options: Array<{ name: string; default: unknown }>,\n project: ProjectInfo,\n): string {\n const defaults = pickDefaults(options)\n const defaultLines = defaults\n .map((option) => ` ${option.name}: ${literal(option.default)},`)\n .join('\\n')\n\n const lang = project.typescript ? ' lang=\"ts\"' : ''\n\n // Nuxt auto-imports Ansh components, so an explicit import there would be redundant — and\n // would shadow the auto-import in a way that breaks if the module's prefix option changes.\n const importLine =\n project.framework === 'nuxt'\n ? `// ${sourceComponent} is auto-imported by @ansh-ui/nuxt.`\n : `import { ${sourceComponent} } from '@ansh-ui/vue'`\n\n return `<script setup${lang}>\n${importLine}\n\n/**\n * ${componentName} — a project-local wrapper around Ansh's ${sourceComponent}.\n *\n * Edit the defaults below to match your design system. Every option remains overridable per\n * instance, because \\`$attrs\\` is bound after the defaults.\n *\n * Generated by \\`ansh add\\`. Safe to edit — it will not be regenerated unless you ask.\n */\nconst defaults = {\n${defaultLines}\n}\n</script>\n\n<template>\n <${sourceComponent} v-bind=\"{ ...defaults, ...$attrs }\">\n <slot />\n </${sourceComponent}>\n</template>\n`\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport {\n checkContrast,\n createScale,\n harmony,\n parseColor,\n rgbToOklch,\n toHex,\n toOklchString,\n} from '@ansh-ui/core'\n\n/**\n * `ansh theme <color>` — generate a token override stylesheet from a brand colour.\n *\n * The job this does that hand-authoring doesn't: it **checks the result**. An 11-step scale\n * generated from a brand colour will, for certain hues, produce steps that fail contrast against\n * white or against each other — a saturated yellow at step 600 is a classic offender. Generating the\n * scale is easy; noticing that step 600 can't legally hold body text is the part people skip.\n *\n * So the emitted stylesheet carries contrast annotations as comments, and the report flags any step\n * that cannot be used for text.\n */\n\nexport interface ThemeOptions {\n /** Brand colour in any CSS syntax. */\n color: string\n /** Where to write the stylesheet. Prints to stdout when omitted. */\n out?: string\n /** Hue drift across the scale, in degrees. `0` keeps a single hue. Default `0`. */\n hueShift?: number\n /** Chroma falloff at the extremes, in `[0, 1]`. Default `0.35`. */\n chromaFalloff?: number\n /** Also emit a harmonious accent scale. Default `false`. */\n withAccent?: boolean\n}\n\nexport interface ThemeReport {\n css: string\n /** Steps whose contrast against white is below 4.5:1 — unusable for body text on light surfaces. */\n unsafeOnLight: string[]\n /** Steps whose contrast against near-black is below 4.5:1. */\n unsafeOnDark: string[]\n /** The step closest in lightness to the input colour. */\n nearestStep: string\n}\n\nconst TAILWIND_KEYS = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950']\n\nexport function generateTheme(options: ThemeOptions): ThemeReport {\n const base = parseColor(options.color)\n const baseLightness = rgbToOklch(base).l\n\n const scale = createScale(base, {\n steps: 11,\n hueShift: options.hueShift ?? 0,\n chromaFalloff: options.chromaFalloff ?? 0.35,\n })\n\n const unsafeOnLight: string[] = []\n const unsafeOnDark: string[] = []\n\n const lines: string[] = []\n let nearestStep = '500'\n let nearestDistance = Infinity\n\n scale.colors.forEach((color, index) => {\n const key = TAILWIND_KEYS[index]\n const onLight = checkContrast(color, '#ffffff')\n const onDark = checkContrast(color, '#0b0b0f')\n\n if (!onLight.passes) unsafeOnLight.push(key)\n if (!onDark.passes) unsafeOnDark.push(key)\n\n const distance = Math.abs(rgbToOklch(color).l - baseLightness)\n if (distance < nearestDistance) {\n nearestDistance = distance\n nearestStep = key\n }\n\n // The comment carries the numbers, so a designer reviewing the file can see at a glance which\n // steps are safe for text without opening a contrast checker.\n lines.push(\n ` --ansh-brand-${key}: ${toOklchString(color)};` +\n ` /* ${toHex(color)} on-white ${onLight.ratio.toFixed(2)}:1` +\n ` on-black ${onDark.ratio.toFixed(2)}:1 */`,\n )\n })\n\n let accentLines = ''\n if (options.withAccent) {\n // A split-complementary partner: far enough from the brand hue to be clearly a different\n // colour, close enough to still look related. The straight complement is usually too harsh for\n // UI, which is why it isn't the default choice here.\n const [, accent] = harmony(base, 'splitComplementary')\n const accentScale = createScale(accent, { steps: 11 })\n accentLines =\n '\\n /* Accent — split-complementary partner to the brand hue. */\\n' +\n accentScale.colors\n .map((color, index) => ` --ansh-accent-${TAILWIND_KEYS[index]}: ${toOklchString(color)};`)\n .join('\\n') +\n '\\n'\n }\n\n const css = `/**\n * Ansh theme override.\n *\n * Generated by \\`ansh theme ${options.color}\\`.\n * Import this *after* \\`@ansh-ui/tokens/ansh.css\\` so it wins in the cascade.\n *\n * Contrast ratios are annotated per step. Steps marked unsafe cannot legally hold body text on the\n * corresponding surface — use them for fills, borders and large display type instead.\n */\n\n:root {\n${lines.join('\\n')}\n${accentLines}\n /* Wire the semantic accent tokens to the generated scale. */\n --ansh-accent-subtle: var(--ansh-brand-50);\n --ansh-accent-muted: var(--ansh-brand-100);\n --ansh-accent-default: var(--ansh-brand-600);\n --ansh-accent-hover: var(--ansh-brand-700);\n --ansh-accent-active: var(--ansh-brand-800);\n --ansh-accent-text: var(--ansh-brand-700);\n --ansh-border-focus: var(--ansh-brand-600);\n}\n\n/**\n * Dark theme.\n *\n * Not an inversion. Accents step *lighter* rather than mirroring their light-mode index, because\n * saturated colour reads as less vivid against a dark background (Helmholtz–Kohlrausch).\n */\n[data-theme='dark'] {\n --ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);\n --ansh-accent-muted: var(--ansh-brand-950);\n --ansh-accent-default: var(--ansh-brand-500);\n --ansh-accent-hover: var(--ansh-brand-400);\n --ansh-accent-active: var(--ansh-brand-300);\n --ansh-accent-text: var(--ansh-brand-300);\n --ansh-border-focus: var(--ansh-brand-400);\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n --ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);\n --ansh-accent-muted: var(--ansh-brand-950);\n --ansh-accent-default: var(--ansh-brand-500);\n --ansh-accent-hover: var(--ansh-brand-400);\n --ansh-accent-active: var(--ansh-brand-300);\n --ansh-accent-text: var(--ansh-brand-300);\n --ansh-border-focus: var(--ansh-brand-400);\n }\n}\n`\n\n if (options.out) {\n mkdirSync(dirname(options.out), { recursive: true })\n writeFileSync(options.out, css, 'utf8')\n }\n\n return { css, unsafeOnLight, unsafeOnDark, nearestStep }\n}\n","import {\n createGradient,\n generatePalette,\n parseColor,\n perceivedLightness,\n rgbToOklch,\n toHex,\n toOklchString,\n} from '@ansh-ui/core'\n\n/**\n * `ansh palette` — generate and inspect palettes.\n *\n * Two modes:\n *\n * - **Generate** — a seeded palette from scratch. The same seed always produces the same palette,\n * so a designer who likes seed 4821 can pin it and get that palette back next week.\n * - **Inspect** — report a supplied palette's OKLCH decomposition and the gradient it produces.\n * The useful part is seeing the *interpolated midpoints*: a palette can look fine as swatches and\n * produce a muddy gradient, and this shows that before it ships.\n */\n\nexport interface PaletteReportOptions {\n /** Colours to inspect. Generates a new palette when omitted. */\n colors?: string[]\n /** Number of colours to generate. Default `5`. */\n count?: number\n seed?: number | string\n /** Overall lightness in `[0, 1]`. Default `0.65`. */\n lightness?: number\n /** Chroma as a fraction of the maximum available. Default `0.85`. */\n saturation?: number\n /** Hue span in degrees. Default `120`. */\n spread?: number\n /** Also print interpolated gradient midpoints. Default `true`. */\n gradient?: boolean\n}\n\nexport interface PaletteReport {\n colors: Array<{ hex: string; oklch: string; lightness: number; chroma: number; hue: number }>\n /** Interpolated samples across the palette, showing what a gradient through it looks like. */\n gradientSamples: string[]\n /** Copy-pasteable literal for a `colors` prop. */\n snippet: string\n}\n\nexport function generatePaletteReport(options: PaletteReportOptions = {}): PaletteReport {\n const parsed =\n options.colors && options.colors.length > 0\n ? options.colors.map((color) => parseColor(color))\n : generatePalette({\n count: options.count ?? 5,\n seed: options.seed ?? 1,\n lightness: options.lightness ?? 0.65,\n saturation: options.saturation ?? 0.85,\n spread: options.spread ?? 120,\n })\n\n const colors = parsed.map((color) => {\n const lch = rgbToOklch(color)\n return {\n hex: toHex(color),\n oklch: toOklchString(color),\n lightness: Number(lch.l.toFixed(4)),\n chroma: Number(lch.c.toFixed(4)),\n hue: Number(lch.h.toFixed(2)),\n }\n })\n\n // Sampled in OKLCH rather than OKLab: for a gradient *through a palette* the goal is maximum\n // vividness across the transitions, which is what travelling the hue circle gives.\n const gradientSamples =\n options.gradient === false || parsed.length < 2\n ? []\n : createGradient(parsed, { space: 'oklch' })\n .sample(9)\n .map((color) => toHex(color))\n\n const snippet = `colors={[${colors.map((c) => `'${c.hex}'`).join(', ')}]}`\n\n return { colors, gradientSamples, snippet }\n}\n\n/**\n * Sort a palette by perceived lightness and report the ordering.\n *\n * Useful for layered effects, where the draw order should generally run dark to light so brighter\n * colours land on top and read as nearer.\n */\nexport function orderByLightness(colors: string[]): Array<{ hex: string; lightness: number }> {\n return colors\n .map((color) => ({ hex: toHex(parseColor(color)), lightness: perceivedLightness(color) }))\n .sort((a, b) => a.lightness - b.lightness)\n}\n"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/project.ts","../src/commands/list.ts","../src/commands/add.ts","../src/commands/theme.ts","../src/commands/init.ts","../src/commands/mcp.ts","../src/commands/palette.ts"],"names":["existsSync","join","readFileSync","mkdirSync","dirname","writeFileSync","kind","parseColor","rgbToOklch","toHex","toOklchString"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Framework } from './project'\n\n/**\n * `ansh.config.json` — the project-level answers `ansh init` records.\n *\n * Detection (see `project.ts`) is good, but it is a guess made fresh on every run. Once a developer\n * has confirmed where components go and which theme file is theirs, later commands should use that\n * answer rather than guessing again — especially in a monorepo where two candidate directories both\n * exist. The file is plain JSON so it can be read by any other tool and edited by hand.\n */\n\nexport const CONFIG_FILE = 'ansh.config.json'\n\nexport interface AnshConfig {\n framework: Exclude<Framework, 'unknown'>\n typescript: boolean\n /** Where `ansh add` writes components, relative to the project root. */\n componentsDir: string\n /** Import alias for the source root, e.g. `'@/'`. */\n alias: string | null\n /** The generated theme stylesheet, relative to the project root. `null` when none was made. */\n theme: string | null\n}\n\n/** Read `ansh.config.json` from `root`. Returns `null` when it is missing or not valid JSON. */\nexport function readConfig(root: string): Partial<AnshConfig> | null {\n const path = join(root, CONFIG_FILE)\n if (!existsSync(path)) return null\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n return parsed && typeof parsed === 'object' ? (parsed as Partial<AnshConfig>) : null\n } catch {\n return null\n }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { readConfig } from './config'\n\n/**\n * Project detection.\n *\n * The CLI needs to know which framework it is generating for, whether the project uses TypeScript,\n * and where components live. Asking the user all of that on every invocation is friction for\n * information that is sitting in `package.json` and on disk — so we detect it, and only prompt for\n * what we genuinely cannot infer.\n */\n\nexport type Framework = 'react' | 'vue' | 'nuxt' | 'next' | 'unknown'\n\nexport interface ProjectInfo {\n root: string\n framework: Framework\n typescript: boolean\n /** Where generated components should go, relative to `root`. */\n componentsDir: string\n /** Detected import alias for the source root, e.g. `'@/'`. `null` when none is configured. */\n alias: string | null\n packageManager: 'pnpm' | 'yarn' | 'npm' | 'bun'\n /** `true` when the relevant `@ansh-ui/*` package is already a dependency. */\n installed: boolean\n}\n\n/** Walk up from `cwd` looking for a `package.json`, so the CLI works from a subdirectory. */\nfunction findRoot(cwd: string): string {\n let current = resolve(cwd)\n for (let depth = 0; depth < 10; depth++) {\n if (existsSync(join(current, 'package.json'))) return current\n const parent = resolve(current, '..')\n if (parent === current) break\n current = parent\n }\n return resolve(cwd)\n}\n\nfunction readJson(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\nexport function detectProject(cwd = process.cwd()): ProjectInfo {\n const root = findRoot(cwd)\n const pkg = readJson(join(root, 'package.json')) ?? {}\n\n const deps: Record<string, string> = {\n ...((pkg.dependencies as Record<string, string>) ?? {}),\n ...((pkg.devDependencies as Record<string, string>) ?? {}),\n }\n\n // Order matters: Nuxt projects also depend on Vue, and Next projects on React. Checking the\n // meta-framework first gives the more specific — and more useful — answer.\n const framework: Framework = deps.nuxt\n ? 'nuxt'\n : deps.next\n ? 'next'\n : deps.vue\n ? 'vue'\n : deps.react\n ? 'react'\n : 'unknown'\n\n const typescript = Boolean(deps.typescript) || existsSync(join(root, 'tsconfig.json'))\n\n const packageManager: ProjectInfo['packageManager'] = existsSync(join(root, 'pnpm-lock.yaml'))\n ? 'pnpm'\n : existsSync(join(root, 'bun.lockb')) || existsSync(join(root, 'bun.lock'))\n ? 'bun'\n : existsSync(join(root, 'yarn.lock'))\n ? 'yarn'\n : 'npm'\n\n // A recorded answer from `ansh init` beats a fresh guess.\n const config = readConfig(root)\n\n return {\n root,\n framework,\n typescript,\n componentsDir: config?.componentsDir ?? detectComponentsDir(root, framework),\n alias: detectAlias(root),\n packageManager,\n installed: Boolean(\n deps['@ansh-ui/react'] ||\n deps['@ansh-ui/vue'] ||\n deps['@ansh-ui/nuxt'] ||\n deps['@ansh-ui/core'],\n ),\n }\n}\n\n/**\n * Find where components already live, rather than imposing a convention.\n *\n * Dropping a file into `src/components` in a project that uses `app/ui` is the kind of small\n * disrespect that makes a tool feel foreign. Candidates are ordered by how strong a signal they are.\n */\nfunction detectComponentsDir(root: string, framework: Framework): string {\n const candidates =\n framework === 'nuxt'\n ? ['components', 'app/components', 'src/components']\n : ['src/components', 'components', 'app/components', 'src/lib/components', 'app/ui', 'src/ui']\n\n for (const candidate of candidates) {\n if (existsSync(join(root, candidate))) return candidate\n }\n\n // Nothing exists yet: pick the convention that matches the project's existing shape.\n if (existsSync(join(root, 'src'))) return 'src/components'\n if (framework === 'nuxt') return 'components'\n return 'components'\n}\n\n/**\n * Read the source alias from `tsconfig.json` so generated imports match the project's style.\n *\n * A generated file that writes `../../lib/utils` in a project where everything else says\n * `@/lib/utils` is immediately identifiable as machine-written.\n */\nfunction detectAlias(root: string): string | null {\n for (const file of ['tsconfig.json', 'jsconfig.json']) {\n const config = readJson(join(root, file))\n if (!config) continue\n\n const compilerOptions = config.compilerOptions as Record<string, unknown> | undefined\n const paths = compilerOptions?.paths as Record<string, string[]> | undefined\n if (!paths) continue\n\n for (const pattern of Object.keys(paths)) {\n // Match a plain wildcard alias like `\"@/*\"` or `\"~/*\"` — the overwhelmingly common shape.\n if (pattern.endsWith('/*')) return pattern.slice(0, -1)\n }\n }\n return null\n}\n\nexport interface KnownInconsistency {\n title: string\n detail: string\n}\n\n/**\n * Maintainer-facing issues logged in `docs/STATUS.md` §7 — restated here so a contributor\n * discovers them from `ansh doctor` output, not by reading a markdown file they may not know\n * exists. Kept in sync with STATUS.md by hand: this list should shrink exactly when that section\n * does, and for the same reason (the item was resolved), not because someone forgot one file.\n */\nexport const KNOWN_INCONSISTENCIES: KnownInconsistency[] = [\n // Empty since the licence key prefix moved to `ansh_` (with `aur_` still accepted). Add an entry\n // here when STATUS.md §7 gains one.\n]\n\n/**\n * `ansh doctor` runs against whatever project it's invoked in — almost always a consumer's app.\n * The one exception worth detecting is the Ansh monorepo itself: a contributor running the CLI\n * from within its own source tree should see the maintainer-facing known-inconsistencies list\n * (above), which is meaningless noise for every other project `doctor` will ever run in.\n */\nexport function isAnshRepoRoot(root: string): boolean {\n const pkg = readJson(join(root, 'package.json'))\n return pkg?.name === 'ansh' && existsSync(join(root, 'packages/license/src/index.ts'))\n}\n\nexport function detectKnownInconsistencies(root: string): KnownInconsistency[] {\n return isAnshRepoRoot(root) ? KNOWN_INCONSISTENCIES : []\n}\n\n/** The `@ansh-ui/*` packages a project of this framework installs. */\nexport function packagesFor(framework: Framework): string[] {\n switch (framework) {\n case 'nuxt':\n return ['@ansh-ui/nuxt']\n case 'vue':\n return ['@ansh-ui/vue', '@ansh-ui/tokens']\n default:\n return ['@ansh-ui/react', '@ansh-ui/tokens']\n }\n}\n\n/** The install command for the detected package manager. */\nexport function installCommand(info: ProjectInfo, packages: string[]): string {\n const list = packages.join(' ')\n switch (info.packageManager) {\n case 'pnpm':\n return `pnpm add ${list}`\n case 'yarn':\n return `yarn add ${list}`\n case 'bun':\n return `bun add ${list}`\n default:\n return `npm install ${list}`\n }\n}\n","import { behaviours, effects } from '@ansh-ui/core'\n\n/**\n * Catalogue enumeration.\n *\n * Reads the registry rather than a hardcoded list, so a new effect appears in `ansh list` the\n * moment it is defined — no second place to update, and no possibility of the CLI advertising\n * something that doesn't exist or omitting something that does.\n */\n\nexport interface CatalogueEntry {\n name: string\n kind: 'effect' | 'behaviour'\n /** The component name in `@ansh-ui/react` and `@ansh-ui/vue`. */\n component: string\n /** Every option, with its default value, read from the definition. */\n options: Array<{ name: string; default: unknown }>\n}\n\n/**\n * Effect and behaviour names map to component names that read better than a mechanical\n * transformation would produce — `meteors` → `MeteorShower`, `blob` → `LiquidBlob`. Where a\n * definition isn't listed here, the name is PascalCased.\n */\nconst COMPONENT_NAMES: Record<string, string> = {\n aurora: 'AuroraBackground',\n 'mesh-gradient': 'MeshGradient',\n 'animated-gradient': 'AnimatedGradient',\n 'particle-field': 'ParticleField',\n constellation: 'Constellation',\n meteors: 'MeteorShower',\n rain: 'Rain',\n snow: 'Snow',\n fireflies: 'Fireflies',\n waves: 'Waves',\n blob: 'LiquidBlob',\n 'noise-layer': 'NoiseLayer',\n spotlight: 'Spotlight',\n magnetic: 'MagneticButton',\n tilt: 'TiltCard',\n ripple: 'Ripple',\n marquee: 'Marquee',\n 'scroll-reveal': 'ScrollReveal',\n parallax: 'Parallax',\n 'text-scramble': 'ScrambleText',\n}\n\n/**\n * Behaviours with no component in either binding. `split-text` is a building block (`AnimatedText`\n * does its own split so the markup server-renders), so `ansh add split-text` would generate an\n * import of a component that doesn't exist. Leaving it out of the catalogue is the honest answer.\n */\nconst NO_COMPONENT = new Set(['split-text'])\n\nexport function componentNameFor(name: string): string {\n return (\n COMPONENT_NAMES[name] ??\n name.replace(/(^|-)([a-z])/g, (_, __, letter: string) => letter.toUpperCase())\n )\n}\n\nexport function listCatalogue(filter?: 'effect' | 'behaviour'): CatalogueEntry[] {\n const entries: CatalogueEntry[] = []\n\n if (filter !== 'behaviour') {\n for (const [name, definition] of Object.entries(effects)) {\n entries.push({\n name,\n kind: 'effect',\n component: componentNameFor(name),\n options: describeOptions(definition.defaults as Record<string, unknown>),\n })\n }\n }\n\n if (filter !== 'effect') {\n for (const [name, definition] of Object.entries(behaviours)) {\n if (NO_COMPONENT.has(name)) continue\n entries.push({\n name,\n kind: 'behaviour',\n component: componentNameFor(name),\n options: describeOptions(definition.defaults as Record<string, unknown>),\n })\n }\n }\n\n return entries.sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/**\n * Turn a `defaults` object into a documented option list.\n *\n * This is why {@link import('@ansh-ui/core').EffectDefinition}'s `defaults` is typed\n * `Required<Options>`: it makes \"every option has a documented default\" a compile-time guarantee,\n * which in turn makes this function complete by construction rather than by diligence.\n */\nfunction describeOptions(defaults: Record<string, unknown>): CatalogueEntry['options'] {\n return Object.entries(defaults)\n .map(([name, value]) => ({ name, default: value }))\n .sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/** Look up a single entry by effect/behaviour name or by component name, case-insensitively. */\nexport function findEntry(query: string): CatalogueEntry | undefined {\n const needle = query.toLowerCase()\n return listCatalogue().find(\n (entry) => entry.name.toLowerCase() === needle || entry.component.toLowerCase() === needle,\n )\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { type ProjectInfo, detectProject, packagesFor } from '../project'\nimport { findEntry } from './list'\n\n/**\n * `ansh add <name>` — generate a local, editable wrapper for a component.\n *\n * ## Why a wrapper rather than a source copy\n *\n * shadcn/ui popularised copying component source into the project, and for *unstyled primitives*\n * that is exactly right: the value is in the markup and the styling, and you want to own both.\n *\n * Ansh is a different shape. The value is in the engine — an 800-line curl-noise particle system,\n * a spring solver, a colour pipeline — and copying that into an application would be actively\n * harmful: it would never receive a bug fix, and it would bloat the repository with code nobody on\n * the team intends to maintain.\n *\n * What developers actually want to own is the **configuration**: which colours, what intensity, what\n * it is called in their design system. So that is what this generates — a thin, fully-typed local\n * component with the project's own defaults baked in, importing the engine from the package.\n *\n * The result: `<HeroBackground />` in application code, one file to edit when the brand palette\n * changes, and engine fixes arrive through a normal dependency upgrade.\n */\n\nexport interface AddOptions {\n /** Effect or behaviour name, or component name. Case-insensitive. */\n name: string\n /** Local component name. Defaults to the catalogue's component name. */\n as?: string\n /** Target directory, relative to the project root. Defaults to the detected components dir. */\n dir?: string\n /** Overwrite an existing file. Default `false`. */\n force?: boolean\n /** Print the file instead of writing it. Default `false`. */\n dryRun?: boolean\n cwd?: string\n}\n\nexport interface AddResult {\n path: string\n contents: string\n written: boolean\n /** Packages the project needs but doesn't have. */\n missingDependencies: string[]\n}\n\nexport function addComponent(options: AddOptions): AddResult {\n const project = detectProject(options.cwd)\n const entry = findEntry(options.name)\n\n if (!entry) {\n throw new Error(\n `Unknown component \"${options.name}\". Run \\`ansh list\\` to see what's available.`,\n )\n }\n\n if (project.framework === 'unknown') {\n throw new Error(\n 'Could not detect a React, Next, Vue or Nuxt project. ' +\n 'Run this from your project root, or install the framework first.',\n )\n }\n\n const isVue = project.framework === 'vue' || project.framework === 'nuxt'\n const componentName = options.as ?? entry.component\n const extension = isVue ? 'vue' : project.typescript ? 'tsx' : 'jsx'\n\n const directory = options.dir ?? project.componentsDir\n const path = join(project.root, directory, `${componentName}.${extension}`)\n\n const contents = isVue\n ? renderVue(componentName, entry.component, entry.options, project)\n : renderReact(componentName, entry.component, entry.options, project)\n\n const missingDependencies = missingFor(project)\n\n if (options.dryRun) {\n return { path, contents, written: false, missingDependencies }\n }\n\n if (existsSync(path) && !options.force) {\n throw new Error(`${path} already exists. Pass --force to overwrite.`)\n }\n\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, contents, 'utf8')\n\n return { path, contents, written: true, missingDependencies }\n}\n\nfunction missingFor(project: ProjectInfo): string[] {\n return project.installed ? [] : packagesFor(project.framework)\n}\n\n/**\n * Serialise a default value as source.\n *\n * Only the options worth surfacing are emitted — see {@link INTERESTING_OPTIONS}. Emitting all\n * twenty would produce a wall of noise that obscures the three an author will actually change.\n */\nfunction literal(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value)\n if (Array.isArray(value)) return `[${value.map(literal).join(', ')}]`\n if (value === null) return 'null'\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\n/**\n * Options included in generated source.\n *\n * These are the ones that carry design intent — the knobs a designer asks to change. Structural and\n * performance options are omitted from the generated defaults but remain available as props, since\n * the wrapper spreads everything through.\n */\nconst INTERESTING_OPTIONS = new Set([\n 'colors',\n 'speed',\n 'intensity',\n 'opacity',\n 'blur',\n 'seed',\n 'strength',\n 'radius',\n 'max',\n 'distance',\n 'amount',\n 'count',\n])\n\nfunction pickDefaults(options: Array<{ name: string; default: unknown }>) {\n return options.filter((option) => INTERESTING_OPTIONS.has(option.name))\n}\n\nfunction renderReact(\n componentName: string,\n sourceComponent: string,\n options: Array<{ name: string; default: unknown }>,\n project: ProjectInfo,\n): string {\n const ts = project.typescript\n const defaults = pickDefaults(options)\n\n const defaultLines = defaults\n .map((option) => ` ${option.name}: ${literal(option.default)},`)\n .join('\\n')\n\n const propsType = ts\n ? `\\ntype ${componentName}Props = ComponentProps<typeof ${sourceComponent}>\\n`\n : ''\n\n const signature = ts\n ? `export function ${componentName}({ children, ...props }: ${componentName}Props) {`\n : `export function ${componentName}({ children, ...props }) {`\n\n const typeImport = ts ? `import type { ComponentProps } from 'react'\\n` : ''\n\n return `${typeImport}import { ${sourceComponent} } from '@ansh-ui/react'\n\n/**\n * ${componentName} — a project-local wrapper around Ansh's ${sourceComponent}.\n *\n * Edit the defaults below to match your design system. Every option remains overridable per\n * instance, because props spread after the defaults.\n *\n * Generated by \\`ansh add\\`. Safe to edit — it will not be regenerated unless you ask.\n */\nconst defaults = {\n${defaultLines}\n}\n\n${propsType}\n${signature}\n return (\n <${sourceComponent} {...defaults} {...props}>\n {children}\n </${sourceComponent}>\n )\n}\n`\n}\n\nfunction renderVue(\n componentName: string,\n sourceComponent: string,\n options: Array<{ name: string; default: unknown }>,\n project: ProjectInfo,\n): string {\n const defaults = pickDefaults(options)\n const defaultLines = defaults\n .map((option) => ` ${option.name}: ${literal(option.default)},`)\n .join('\\n')\n\n const lang = project.typescript ? ' lang=\"ts\"' : ''\n\n // Nuxt auto-imports Ansh components, so an explicit import there would be redundant — and\n // would shadow the auto-import in a way that breaks if the module's prefix option changes.\n const importLine =\n project.framework === 'nuxt'\n ? `// ${sourceComponent} is auto-imported by @ansh-ui/nuxt.`\n : `import { ${sourceComponent} } from '@ansh-ui/vue'`\n\n return `<script setup${lang}>\n${importLine}\n\n/**\n * ${componentName} — a project-local wrapper around Ansh's ${sourceComponent}.\n *\n * Edit the defaults below to match your design system. Every option remains overridable per\n * instance, because \\`$attrs\\` is bound after the defaults.\n *\n * Generated by \\`ansh add\\`. Safe to edit — it will not be regenerated unless you ask.\n */\nconst defaults = {\n${defaultLines}\n}\n</script>\n\n<template>\n <${sourceComponent} v-bind=\"{ ...defaults, ...$attrs }\">\n <slot />\n </${sourceComponent}>\n</template>\n`\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport {\n checkContrast,\n createScale,\n harmony,\n parseColor,\n rgbToOklch,\n toHex,\n toOklchString,\n} from '@ansh-ui/core'\n\n/**\n * `ansh theme <color>` — generate a token override stylesheet from a brand colour.\n *\n * The job this does that hand-authoring doesn't: it **checks the result**. An 11-step scale\n * generated from a brand colour will, for certain hues, produce steps that fail contrast against\n * white or against each other — a saturated yellow at step 600 is a classic offender. Generating the\n * scale is easy; noticing that step 600 can't legally hold body text is the part people skip.\n *\n * So the emitted stylesheet carries contrast annotations as comments, and the report flags any step\n * that cannot be used for text.\n */\n\nexport interface ThemeOptions {\n /** Brand colour in any CSS syntax. */\n color: string\n /** Where to write the stylesheet. Prints to stdout when omitted. */\n out?: string\n /** Hue drift across the scale, in degrees. `0` keeps a single hue. Default `0`. */\n hueShift?: number\n /** Chroma falloff at the extremes, in `[0, 1]`. Default `0.35`. */\n chromaFalloff?: number\n /** Also emit a harmonious accent scale. Default `false`. */\n withAccent?: boolean\n}\n\nexport interface ThemeReport {\n css: string\n /** Steps whose contrast against white is below 4.5:1 — unusable for body text on light surfaces. */\n unsafeOnLight: string[]\n /** Steps whose contrast against near-black is below 4.5:1. */\n unsafeOnDark: string[]\n /** The step closest in lightness to the input colour. */\n nearestStep: string\n}\n\nconst TAILWIND_KEYS = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950']\n\nexport function generateTheme(options: ThemeOptions): ThemeReport {\n const base = parseColor(options.color)\n const baseLightness = rgbToOklch(base).l\n\n const scale = createScale(base, {\n steps: 11,\n hueShift: options.hueShift ?? 0,\n chromaFalloff: options.chromaFalloff ?? 0.35,\n })\n\n const unsafeOnLight: string[] = []\n const unsafeOnDark: string[] = []\n\n const lines: string[] = []\n let nearestStep = '500'\n let nearestDistance = Infinity\n\n scale.colors.forEach((color, index) => {\n const key = TAILWIND_KEYS[index]\n const onLight = checkContrast(color, '#ffffff')\n const onDark = checkContrast(color, '#0b0b0f')\n\n if (!onLight.passes) unsafeOnLight.push(key)\n if (!onDark.passes) unsafeOnDark.push(key)\n\n const distance = Math.abs(rgbToOklch(color).l - baseLightness)\n if (distance < nearestDistance) {\n nearestDistance = distance\n nearestStep = key\n }\n\n // The comment carries the numbers, so a designer reviewing the file can see at a glance which\n // steps are safe for text without opening a contrast checker.\n lines.push(\n ` --ansh-brand-${key}: ${toOklchString(color)};` +\n ` /* ${toHex(color)} on-white ${onLight.ratio.toFixed(2)}:1` +\n ` on-black ${onDark.ratio.toFixed(2)}:1 */`,\n )\n })\n\n let accentLines = ''\n if (options.withAccent) {\n // A split-complementary partner: far enough from the brand hue to be clearly a different\n // colour, close enough to still look related. The straight complement is usually too harsh for\n // UI, which is why it isn't the default choice here.\n const [, accent] = harmony(base, 'splitComplementary')\n const accentScale = createScale(accent, { steps: 11 })\n accentLines =\n '\\n /* Accent — split-complementary partner to the brand hue. */\\n' +\n accentScale.colors\n .map((color, index) => ` --ansh-accent-${TAILWIND_KEYS[index]}: ${toOklchString(color)};`)\n .join('\\n') +\n '\\n'\n }\n\n const css = `/**\n * Ansh theme override.\n *\n * Generated by \\`ansh theme ${options.color}\\`.\n * Import this *after* \\`@ansh-ui/tokens/ansh.css\\` so it wins in the cascade.\n *\n * Contrast ratios are annotated per step. Steps marked unsafe cannot legally hold body text on the\n * corresponding surface — use them for fills, borders and large display type instead.\n */\n\n:root {\n${lines.join('\\n')}\n${accentLines}\n /* Wire the semantic accent tokens to the generated scale. */\n --ansh-accent-subtle: var(--ansh-brand-50);\n --ansh-accent-muted: var(--ansh-brand-100);\n --ansh-accent-default: var(--ansh-brand-600);\n --ansh-accent-hover: var(--ansh-brand-700);\n --ansh-accent-active: var(--ansh-brand-800);\n --ansh-accent-text: var(--ansh-brand-700);\n --ansh-border-focus: var(--ansh-brand-600);\n}\n\n/**\n * Dark theme.\n *\n * Not an inversion. Accents step *lighter* rather than mirroring their light-mode index, because\n * saturated colour reads as less vivid against a dark background (Helmholtz–Kohlrausch).\n */\n[data-theme='dark'] {\n --ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);\n --ansh-accent-muted: var(--ansh-brand-950);\n --ansh-accent-default: var(--ansh-brand-500);\n --ansh-accent-hover: var(--ansh-brand-400);\n --ansh-accent-active: var(--ansh-brand-300);\n --ansh-accent-text: var(--ansh-brand-300);\n --ansh-border-focus: var(--ansh-brand-400);\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n --ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);\n --ansh-accent-muted: var(--ansh-brand-950);\n --ansh-accent-default: var(--ansh-brand-500);\n --ansh-accent-hover: var(--ansh-brand-400);\n --ansh-accent-active: var(--ansh-brand-300);\n --ansh-accent-text: var(--ansh-brand-300);\n --ansh-border-focus: var(--ansh-brand-400);\n }\n}\n`\n\n if (options.out) {\n mkdirSync(dirname(options.out), { recursive: true })\n writeFileSync(options.out, css, 'utf8')\n }\n\n return { css, unsafeOnLight, unsafeOnDark, nearestStep }\n}\n","import { existsSync, writeFileSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { type AnshConfig, CONFIG_FILE } from '../config'\nimport { type ProjectInfo, detectProject, installCommand, packagesFor } from '../project'\nimport { generateTheme } from './theme'\n\n/**\n * `ansh init` — set a project up for Ansh in one step.\n *\n * Before this, getting started meant reading the README to learn which package matches your\n * framework, which two stylesheets to import and in what order, and that `ansh theme` exists. All of\n * that is derivable from the project itself, so this command derives it and records the answers in\n * `ansh.config.json` for `ansh add` to reuse.\n *\n * It deliberately does **not** run the package manager or edit the app's entry file. Installing is\n * one command the developer can copy, and rewriting someone's `main.ts` is the kind of change a\n * scaffolder gets wrong in exactly the projects it was not tested against. It prints both instead.\n */\n\nexport interface InitOptions {\n /** Brand colour for a generated theme stylesheet. No theme is generated when omitted. */\n brand?: string\n /** Override the detected components directory, relative to the project root. */\n componentsDir?: string\n /** Overwrite an existing `ansh.config.json` or theme file. Default `false`. */\n force?: boolean\n /** Compute everything but write nothing. Default `false`. */\n dryRun?: boolean\n cwd?: string\n}\n\nexport interface InitResult {\n project: ProjectInfo\n config: AnshConfig\n /** Absolute paths of files written (empty on a dry run). */\n written: string[]\n /** The install command, or `null` when the packages are already installed. */\n install: string | null\n /** The file the developer should edit next, and the lines to add to it. */\n setup: { file: string; code: string }\n}\n\nexport function initProject(options: InitOptions = {}): InitResult {\n const project = detectProject(options.cwd)\n\n if (project.framework === 'unknown') {\n throw new Error(\n 'Could not detect a React, Next, Vue or Nuxt project. ' +\n 'Run this from your project root, or install the framework first.',\n )\n }\n\n const configPath = join(project.root, CONFIG_FILE)\n if (existsSync(configPath) && !options.force) {\n throw new Error(`${CONFIG_FILE} already exists. Pass --force to overwrite it.`)\n }\n\n const themeRelative = options.brand ? join(stylesDir(project), 'ansh-theme.css') : null\n const themePath = themeRelative ? join(project.root, themeRelative) : null\n if (themePath && existsSync(themePath) && !options.force) {\n throw new Error(`${themePath} already exists. Pass --force to overwrite it.`)\n }\n\n const config: AnshConfig = {\n framework: project.framework,\n typescript: project.typescript,\n componentsDir: options.componentsDir ?? project.componentsDir,\n alias: project.alias,\n theme: themeRelative,\n }\n\n const written: string[] = []\n if (!options.dryRun) {\n // Generate the theme first: a bad colour should fail before any file is written.\n if (options.brand && themePath) {\n generateTheme({ color: options.brand, out: themePath })\n written.push(themePath)\n }\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, 'utf8')\n written.push(configPath)\n } else if (options.brand) {\n // Still validate the colour on a dry run, so it reports the same error a real run would.\n generateTheme({ color: options.brand })\n }\n\n return {\n project,\n config,\n written,\n install: project.installed ? null : installCommand(project, packagesFor(project.framework)),\n setup: setupFor(project, themeRelative),\n }\n}\n\n/** Where a generated stylesheet should live, following the project's existing shape. */\nfunction stylesDir(project: ProjectInfo): string {\n if (project.framework === 'nuxt') return 'assets/css'\n return existsSync(join(project.root, 'src')) ? 'src/styles' : 'styles'\n}\n\n/** The lines to add to the app's entry point, per framework. */\nfunction setupFor(project: ProjectInfo, theme: string | null): InitResult['setup'] {\n if (project.framework === 'nuxt') {\n const css = [\"'@ansh-ui/vue/styles.css'\", ...(theme ? [`'~/${theme}'`] : [])].join(', ')\n return {\n file: 'nuxt.config.ts',\n code: `export default defineNuxtConfig({\\n modules: ['@ansh-ui/nuxt'],\\n css: [${css}],\\n})`,\n }\n }\n\n const binding = project.framework === 'vue' ? 'vue' : 'react'\n const entry =\n project.framework === 'next'\n ? existsSync(join(project.root, 'src/app'))\n ? 'src/app/layout.tsx'\n : 'app/layout.tsx'\n : project.framework === 'vue'\n ? `src/main.${project.typescript ? 'ts' : 'js'}`\n : `src/main.${project.typescript ? 'tsx' : 'jsx'}`\n\n // The theme is imported last so its overrides win in the cascade.\n const themeImport = theme ? `\\nimport '${themeImportPath(project, entry, theme)}'` : ''\n return {\n file: entry,\n code: `import '@ansh-ui/tokens/ansh.css'\\nimport '@ansh-ui/${binding}/styles.css'${themeImport}`,\n }\n}\n\n/**\n * A relative path from the entry file to the theme. Not the import alias: `detectAlias` knows the\n * alias prefix but not what it maps to, and `@/` means `src/` in some projects and the root in others.\n */\nfunction themeImportPath(project: ProjectInfo, entry: string, theme: string): string {\n const path = relative(join(project.root, entry, '..'), join(project.root, theme))\n return path.startsWith('.') ? path : `./${path}`\n}\n","import { createInterface } from 'node:readline'\nimport { addComponent } from './add'\nimport { initProject } from './init'\nimport { findEntry, listCatalogue } from './list'\nimport { generateTheme } from './theme'\n\n/**\n * `ansh mcp` — a Model Context Protocol server over stdio, so AI coding agents (Claude Code,\n * Cursor, and others) can discover Ansh and use the CLI's own commands as tools.\n *\n * claude mcp add ansh -- npx -y @ansh-ui/cli mcp\n *\n * Hand-written rather than built on the MCP SDK: the CLI's rule is no runtime dependencies, and the\n * subset a tools-only server needs is small — newline-delimited JSON-RPC 2.0 with `initialize`,\n * `tools/list`, `tools/call` and `ping`. Every tool reuses the function behind the matching CLI\n * command, so the agent and the terminal can never disagree.\n *\n * Tools that would write files default to returning the contents instead (`write: false`), so an\n * agent shows the change through its own editing flow unless it asks to write directly.\n */\n\nconst PROTOCOL_VERSION = '2025-06-18'\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0'\n id?: string | number | null\n method: string\n params?: Record<string, unknown>\n}\n\ntype ToolResult = { content: Array<{ type: 'text'; text: string }>; isError?: boolean }\n\ninterface Tool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n run: (args: Record<string, unknown>, cwd: string) => string\n}\n\nconst str = (value: unknown): string | undefined => (typeof value === 'string' ? value : undefined)\n\nexport const TOOLS: Tool[] = [\n {\n name: 'ansh_list',\n description:\n 'List Ansh canvas effects and DOM behaviours with their component names and option counts.',\n inputSchema: {\n type: 'object',\n properties: { kind: { type: 'string', enum: ['effect', 'behaviour'] } },\n },\n run: (args) => {\n const kind = args.kind === 'effect' || args.kind === 'behaviour' ? args.kind : undefined\n const entries = listCatalogue(kind).map(({ name, kind, component, options }) => ({\n name,\n kind,\n component,\n options: options.length,\n }))\n return JSON.stringify(entries, null, 2)\n },\n },\n {\n name: 'ansh_info',\n description:\n 'Every option of one Ansh effect or behaviour, with its default value. Accepts the catalogue name (aurora) or component name (AuroraBackground).',\n inputSchema: {\n type: 'object',\n properties: { name: { type: 'string' } },\n required: ['name'],\n },\n run: (args) => {\n const entry = findEntry(str(args.name) ?? '')\n if (!entry) throw new Error(`Unknown component \"${String(args.name)}\". Call ansh_list.`)\n return JSON.stringify(entry, null, 2)\n },\n },\n {\n name: 'ansh_setup',\n description:\n 'How to set up Ansh in the current project: detected framework, install command, and the imports or nuxt.config lines to add. Pass write: true to also create ansh.config.json (and a theme file when brand is given).',\n inputSchema: {\n type: 'object',\n properties: {\n brand: { type: 'string', description: 'Brand colour for a generated theme, e.g. #0275f4' },\n write: { type: 'boolean', default: false },\n },\n },\n run: (args, cwd) => {\n const result = initProject({ cwd, brand: str(args.brand), dryRun: args.write !== true })\n return JSON.stringify(\n {\n framework: result.config.framework,\n install: result.install,\n setupFile: result.setup.file,\n setupCode: result.setup.code,\n config: result.config,\n written: result.written,\n },\n null,\n 2,\n )\n },\n },\n {\n name: 'ansh_add',\n description:\n 'Generate a typed, project-local wrapper component around an Ansh effect or behaviour, with the project’s defaults. Returns the file path and contents; pass write: true to write the file.',\n inputSchema: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n as: { type: 'string', description: 'Local component name, e.g. HeroBackground' },\n write: { type: 'boolean', default: false },\n },\n required: ['name'],\n },\n run: (args, cwd) => {\n const result = addComponent({\n cwd,\n name: str(args.name) ?? '',\n as: str(args.as),\n dryRun: args.write !== true,\n })\n return JSON.stringify(result, null, 2)\n },\n },\n {\n name: 'ansh_theme',\n description:\n 'Generate an Ansh token override stylesheet from a brand colour, with WCAG contrast notes for each step.',\n inputSchema: {\n type: 'object',\n properties: { color: { type: 'string' }, withAccent: { type: 'boolean', default: false } },\n required: ['color'],\n },\n run: (args) => {\n const report = generateTheme({\n color: str(args.color) ?? '',\n withAccent: args.withAccent === true,\n })\n return JSON.stringify(report, null, 2)\n },\n },\n]\n\nfunction callTool(params: Record<string, unknown> | undefined, cwd: string): ToolResult {\n const tool = TOOLS.find((t) => t.name === params?.name)\n if (!tool)\n return {\n content: [{ type: 'text', text: `Unknown tool \"${String(params?.name)}\".` }],\n isError: true,\n }\n const args = (params?.arguments ?? {}) as Record<string, unknown>\n try {\n return { content: [{ type: 'text', text: tool.run(args, cwd) }] }\n } catch (error) {\n // Tool failures are results, not protocol errors, so the agent can read and correct them.\n const text = error instanceof Error ? error.message : String(error)\n return { content: [{ type: 'text', text }], isError: true }\n }\n}\n\n/**\n * Handle one JSON-RPC message. Returns the response, or `null` for notifications (no `id`),\n * which get no reply.\n */\nexport function handleMessage(\n message: JsonRpcRequest,\n options: { version: string; cwd?: string },\n): Record<string, unknown> | null {\n const cwd = options.cwd ?? process.cwd()\n const reply = (result: unknown) => ({ jsonrpc: '2.0', id: message.id ?? null, result })\n\n if (message.id === undefined) return null\n\n switch (message.method) {\n case 'initialize':\n return reply({\n protocolVersion: str(message.params?.protocolVersion) ?? PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: { name: 'ansh', version: options.version },\n instructions:\n 'Ansh is a UI and animation library for React, Next.js, Vue and Nuxt. Call ansh_setup before writing Ansh code in a project, and ansh_info for an effect’s exact options.',\n })\n case 'ping':\n return reply({})\n case 'tools/list':\n return reply({\n tools: TOOLS.map(({ name, description, inputSchema }) => ({\n name,\n description,\n inputSchema,\n })),\n })\n case 'tools/call':\n return reply(callTool(message.params, cwd))\n default:\n return {\n jsonrpc: '2.0',\n id: message.id,\n error: { code: -32601, message: `Method not found: ${message.method}` },\n }\n }\n}\n\n/** Run the server until `input` closes. Logs go to stderr, never to the protocol stream. */\nexport function serveMcp(\n version: string,\n input: NodeJS.ReadableStream = process.stdin,\n output: NodeJS.WritableStream = process.stdout,\n): void {\n const send = (payload: unknown) => output.write(`${JSON.stringify(payload)}\\n`)\n createInterface({ input }).on('line', (line) => {\n if (!line.trim()) return\n let message: JsonRpcRequest\n try {\n message = JSON.parse(line) as JsonRpcRequest\n } catch {\n send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } })\n return\n }\n const response = handleMessage(message, { version })\n if (response) send(response)\n })\n console.error(`ansh mcp v${version} ready on stdio`)\n}\n","import {\n createGradient,\n generatePalette,\n parseColor,\n perceivedLightness,\n rgbToOklch,\n toHex,\n toOklchString,\n} from '@ansh-ui/core'\n\n/**\n * `ansh palette` — generate and inspect palettes.\n *\n * Two modes:\n *\n * - **Generate** — a seeded palette from scratch. The same seed always produces the same palette,\n * so a designer who likes seed 4821 can pin it and get that palette back next week.\n * - **Inspect** — report a supplied palette's OKLCH decomposition and the gradient it produces.\n * The useful part is seeing the *interpolated midpoints*: a palette can look fine as swatches and\n * produce a muddy gradient, and this shows that before it ships.\n */\n\nexport interface PaletteReportOptions {\n /** Colours to inspect. Generates a new palette when omitted. */\n colors?: string[]\n /** Number of colours to generate. Default `5`. */\n count?: number\n seed?: number | string\n /** Overall lightness in `[0, 1]`. Default `0.65`. */\n lightness?: number\n /** Chroma as a fraction of the maximum available. Default `0.85`. */\n saturation?: number\n /** Hue span in degrees. Default `120`. */\n spread?: number\n /** Also print interpolated gradient midpoints. Default `true`. */\n gradient?: boolean\n}\n\nexport interface PaletteReport {\n colors: Array<{ hex: string; oklch: string; lightness: number; chroma: number; hue: number }>\n /** Interpolated samples across the palette, showing what a gradient through it looks like. */\n gradientSamples: string[]\n /** Copy-pasteable literal for a `colors` prop. */\n snippet: string\n}\n\nexport function generatePaletteReport(options: PaletteReportOptions = {}): PaletteReport {\n const parsed =\n options.colors && options.colors.length > 0\n ? options.colors.map((color) => parseColor(color))\n : generatePalette({\n count: options.count ?? 5,\n seed: options.seed ?? 1,\n lightness: options.lightness ?? 0.65,\n saturation: options.saturation ?? 0.85,\n spread: options.spread ?? 120,\n })\n\n const colors = parsed.map((color) => {\n const lch = rgbToOklch(color)\n return {\n hex: toHex(color),\n oklch: toOklchString(color),\n lightness: Number(lch.l.toFixed(4)),\n chroma: Number(lch.c.toFixed(4)),\n hue: Number(lch.h.toFixed(2)),\n }\n })\n\n // Sampled in OKLCH rather than OKLab: for a gradient *through a palette* the goal is maximum\n // vividness across the transitions, which is what travelling the hue circle gives.\n const gradientSamples =\n options.gradient === false || parsed.length < 2\n ? []\n : createGradient(parsed, { space: 'oklch' })\n .sample(9)\n .map((color) => toHex(color))\n\n const snippet = `colors={[${colors.map((c) => `'${c.hex}'`).join(', ')}]}`\n\n return { colors, gradientSamples, snippet }\n}\n\n/**\n * Sort a palette by perceived lightness and report the ordering.\n *\n * Useful for layered effects, where the draw order should generally run dark to light so brighter\n * colours land on top and read as nearer.\n */\nexport function orderByLightness(colors: string[]): Array<{ hex: string; lightness: number }> {\n return colors\n .map((color) => ({ hex: toHex(parseColor(color)), lightness: perceivedLightness(color) }))\n .sort((a, b) => a.lightness - b.lightness)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ansh-ui/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "The Ansh CLI — scaffold components, generate themes and palettes, and inspect the catalogue.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "types": "./dist/index.d.ts",
33
33
  "dependencies": {
34
- "@ansh-ui/core": "0.3.0",
35
- "@ansh-ui/tokens": "0.3.0"
34
+ "@ansh-ui/core": "0.4.0",
35
+ "@ansh-ui/tokens": "0.4.0"
36
36
  },
37
37
  "engines": {
38
38
  "node": ">=18.18.0"