@1agh/maude 0.17.2 → 0.18.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.
Files changed (24) hide show
  1. package/cli/commands/design.mjs +92 -29
  2. package/package.json +8 -8
  3. package/plugins/design/dev-server/bin/screenshot.sh +12 -0
  4. package/plugins/design/dev-server/boot-self-heal.ts +90 -0
  5. package/plugins/design/dev-server/build.ts +18 -2
  6. package/plugins/design/dev-server/config.schema.json +12 -0
  7. package/plugins/design/dev-server/dist/client.bundle.js +3 -3
  8. package/plugins/design/dev-server/runtime-bundle.ts +29 -1
  9. package/plugins/design/dev-server/server.ts +6 -0
  10. package/plugins/design/dev-server/test/boot-self-heal.test.ts +112 -0
  11. package/plugins/design/dev-server/test/runtime-bundle-error-mapping.test.ts +43 -0
  12. package/plugins/design/templates/canvas.tsx.template +7 -7
  13. package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html +1 -1
  14. package/plugins/design/templates/design-system-inspiration/core/README.philosophy.md.tpl +11 -7
  15. package/plugins/design/templates/design-system-inspiration/core/SKILL.md.tpl +4 -3
  16. package/plugins/design/templates/design-system-inspiration/core/colors_and_type.css.tpl +61 -57
  17. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +2 -0
  18. package/plugins/design/templates/design-system-inspiration/core/preview/colors-accent.html +4 -4
  19. package/plugins/design/templates/design-system-inspiration/meta/presence-multiplayer.html +1 -1
  20. package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-showcase.html +3 -0
  21. package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-showcase.html +1 -1
  22. package/plugins/design/templates/design-system-inspiration/theme-both/colors-themes-side-by-side.html +6 -6
  23. package/plugins/design/templates/design-system-inspiration/universal/components-dialogs.html +3 -2
  24. package/plugins/design/templates/design-system-inspiration/universal/logo.html +2 -1
@@ -508,6 +508,13 @@ function escapeReg(s) {
508
508
  }
509
509
 
510
510
  function defaultPayload({ projectName, dsName }) {
511
+ // `--no-discovery` mode emits a deliberately NEUTRAL skeleton:
512
+ // achromatic grayscale, system fonts only, equal spacing/radii steps,
513
+ // single accent set to neutral graphite. The intent is for the project
514
+ // to immediately overwrite these with discovery-driven values via
515
+ // `/design:setup-ds`. These are NOT a "recommended starter palette" —
516
+ // they're an explicit placeholder that should look unfinished, so the
517
+ // designer is nudged toward actual decisions (DDR-026, 2026-05-25).
511
518
  return {
512
519
  project_name: projectName,
513
520
  project_label: titleCase(projectName),
@@ -515,7 +522,7 @@ function defaultPayload({ projectName, dsName }) {
515
522
  ds_skill_name: `${dsName}-design`,
516
523
  ds_description: `Default design system for ${projectName}.`,
517
524
  root_class: 'app',
518
- theme_default: 'dark',
525
+ theme_default: 'light',
519
526
  theme_extra: '',
520
527
  handoff_targets: '[]',
521
528
  active_families: JSON.stringify(['accent']),
@@ -528,38 +535,94 @@ function defaultPayload({ projectName, dsName }) {
528
535
  platform_hard_rules: '',
529
536
  content_tone: 'direct-terse',
530
537
  mood_references_block: '_(not specified — extend during the next /design:setup-ds run)_',
531
- type_scale_summary: '8-step ladder, 12–36px.',
532
- voice_tone_block: 'Direct, terse. Action verbs. No marketing puffery.',
533
- voice_tone_summary: 'direct-terse',
534
- iconography_summary: 'Lucide, 1.5px stroke.',
538
+ type_scale_summary: '8-step ladder, populated by discovery.',
539
+ type_scale_summary_compact: '8-step (modular ratio)',
540
+ voice_tone_block: 'Voice not declared yet — capture during /design:setup-ds.',
541
+ voice_tone_summary: 'not-declared',
542
+ iconography_summary: 'Icon family not declared yet — capture during /design:setup-ds.',
535
543
  hard_rules_from_system_readme: '_(see system/<ds>/README.md Hard rules section)_',
536
544
  hard_rules_block:
537
- '- WCAG 2.1 AA contrast\n- No off-token colors / radii / spacings\n- Real product strings only — no placeholder copy',
545
+ '- WCAG 2.1 AA contrast\n- No off-token values in canvases\n- Real product strings only — no placeholder copy',
538
546
  iso_timestamp: new Date().toISOString(),
539
- // Token defaults (dark theme, neutral indigo accent)
540
- bg_0_oklch: 'oklch(16% 0.012 245)',
541
- bg_1_oklch: 'oklch(20% 0.014 245)',
542
- bg_2_oklch: 'oklch(24% 0.014 245)',
543
- bg_3_oklch: 'oklch(28% 0.013 245)',
544
- bg_4_oklch: 'oklch(33% 0.013 245)',
545
- fg_0_oklch: 'oklch(96% 0.008 245)',
546
- fg_1_oklch: 'oklch(78% 0.012 245)',
547
- fg_2_oklch: 'oklch(60% 0.012 245)',
548
- fg_3_oklch: 'oklch(42% 0.010 245)',
549
- accent_oklch: 'oklch(64% 0.18 264)',
550
- accent_fg_oklch: 'oklch(98% 0.008 264)',
551
- radius_xs: '4px',
552
- radius_sm: '6px',
553
- radius_md: '8px',
554
- radius_lg: '12px',
555
- radius_xl: '16px',
556
- font_display: '"Inter", system-ui, sans-serif',
557
- font_body: '"Inter", system-ui, sans-serif',
558
- font_mono: '"JetBrains Mono", ui-monospace, monospace',
559
- dur_flip: '140ms',
560
- dur_panel: '220ms',
561
- dur_route: '260ms',
547
+ // ─── Neutral-skeleton tokens (light theme grayscale, no chroma) ───
548
+ bg_0: 'oklch(100% 0 0)',
549
+ bg_1: 'oklch(97% 0 0)',
550
+ bg_2: 'oklch(94% 0 0)',
551
+ bg_3: 'oklch(90% 0 0)',
552
+ bg_4: 'oklch(85% 0 0)',
553
+ border_subtle: 'oklch(92% 0 0)',
554
+ border_default: 'oklch(85% 0 0)',
555
+ border_strong: 'oklch(70% 0 0)',
556
+ border_strategy: 'fixed-token',
557
+ fg_0: 'oklch(15% 0 0)',
558
+ fg_1: 'oklch(35% 0 0)',
559
+ fg_2: 'oklch(55% 0 0)',
560
+ fg_3: 'oklch(75% 0 0)',
561
+ accent_strategy: 'single',
562
+ accent_strategy_summary: 'single accent — discovery has not been run',
563
+ accent_block:
564
+ ' --accent: oklch(35% 0 0);\n --accent-hover: oklch(30% 0 0);\n --accent-active: oklch(25% 0 0);\n --accent-fg: oklch(98% 0 0);',
565
+ accent_rules_block:
566
+ 'Single accent family (`--accent`, `--accent-hover`, `--accent-active`, `--accent-fg`). Project has not run discovery yet — the accent is set to neutral graphite as a placeholder. Run `/design:setup-ds` to pick a real palette.',
567
+ accent_rules_summary: 'Single family until discovery runs.',
568
+ color_space: 'oklch',
569
+ color_space_summary: 'OKLCH (default)',
570
+ color_space_block:
571
+ '**Color space:** OKLCH (default). Override via `config.colorSpace` if your project requires HSL, hex, or LAB.',
572
+ status_success: 'oklch(55% 0 0)',
573
+ status_warn: 'oklch(55% 0 0)',
574
+ status_error: 'oklch(55% 0 0)',
575
+ status_info: 'oklch(55% 0 0)',
576
+ presence_online: 'var(--status-success)',
577
+ presence_away: 'var(--status-warn)',
578
+ presence_offline: 'var(--fg-3)',
579
+ shadow_sm: 'none',
580
+ shadow_md: 'none',
581
+ shadow_lg: 'none',
582
+ radius_xs: '0',
583
+ radius_sm: '0',
584
+ radius_md: '0',
585
+ radius_lg: '0',
586
+ radius_xl: '0',
587
+ radius_pill: '999px',
588
+ space_0: '0',
589
+ space_1: '4px',
590
+ space_2: '8px',
591
+ space_3: '12px',
592
+ space_4: '16px',
593
+ space_5: '24px',
594
+ space_6: '32px',
595
+ space_7: '48px',
596
+ space_8: '64px',
597
+ font_display: 'system-ui, sans-serif',
598
+ font_body: 'system-ui, sans-serif',
599
+ font_mono: 'ui-monospace, monospace',
600
+ type_xs: '12px',
601
+ lh_xs: '16px',
602
+ type_sm: '13px',
603
+ lh_sm: '18px',
604
+ type_base: '14px',
605
+ lh_base: '20px',
606
+ type_md: '16px',
607
+ lh_md: '22px',
608
+ type_lg: '18px',
609
+ lh_lg: '26px',
610
+ type_xl: '22px',
611
+ lh_xl: '30px',
612
+ type_2xl: '28px',
613
+ lh_2xl: '36px',
614
+ type_3xl: '36px',
615
+ lh_3xl: '44px',
616
+ dur_flip: '120ms',
617
+ dur_panel: '200ms',
618
+ dur_route: '240ms',
562
619
  dur_soft: '320ms',
620
+ ease_out_curve: 'ease-out',
621
+ ease_in_out_curve: 'ease-in-out',
622
+ layout_max_w: '1200px',
623
+ layout_gutter: 'var(--space-4)',
624
+ touch_target_rule:
625
+ '44×44 minimum on touch surfaces (iOS HIG); 48×48 if the primary platform is Android (Material). N/A on desktop-only systems.',
563
626
  // Empty-state template fields
564
627
  empty_subject: 'items',
565
628
  empty_supporting: "When you create your first one, it'll show up here.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agh/maude",
3
- "version": "0.17.2",
3
+ "version": "0.18.0",
4
4
  "description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -41,13 +41,13 @@
41
41
  "prepublishOnly": "bash scripts/check-version-parity.sh"
42
42
  },
43
43
  "optionalDependencies": {
44
- "@1agh/maude-darwin-arm64": "0.17.2",
45
- "@1agh/maude-darwin-x64": "0.17.2",
46
- "@1agh/maude-linux-arm64": "0.17.2",
47
- "@1agh/maude-linux-arm64-musl": "0.17.2",
48
- "@1agh/maude-linux-x64": "0.17.2",
49
- "@1agh/maude-linux-x64-musl": "0.17.2",
50
- "@1agh/maude-win32-x64": "0.17.2"
44
+ "@1agh/maude-darwin-arm64": "0.18.0",
45
+ "@1agh/maude-darwin-x64": "0.18.0",
46
+ "@1agh/maude-linux-arm64": "0.18.0",
47
+ "@1agh/maude-linux-arm64-musl": "0.18.0",
48
+ "@1agh/maude-linux-x64": "0.18.0",
49
+ "@1agh/maude-linux-x64-musl": "0.18.0",
50
+ "@1agh/maude-win32-x64": "0.18.0"
51
51
  },
52
52
  "files": [
53
53
  "cli",
@@ -64,6 +64,18 @@ else
64
64
  [ -z "$OUT" ] && { echo "screenshot.sh: --out required for single-shot modes" >&2; exit 2; }
65
65
  fi
66
66
 
67
+ # TSX specimens cannot be opened via file:// — the browser would see raw JSX.
68
+ # They must go through the dev-server route (http://localhost:PORT/<rel>),
69
+ # which transpiles via _canvas-shell.html?canvas=<rel>. Phase 19 / DDR-044.
70
+ case "$URL" in
71
+ file://*.tsx)
72
+ echo "screenshot.sh: TSX specimens cannot be screenshot via file:// (the browser sees raw JSX)." >&2
73
+ echo " Use --port instead — the dev-server compiles TSX through _canvas-shell.html?canvas=<rel>." >&2
74
+ echo " Example: screenshot.sh --port 4399 --full --out shot.png (set the active canvas in the browser first)" >&2
75
+ exit 2
76
+ ;;
77
+ esac
78
+
67
79
  # ---------- url resolution ----------
68
80
  if [ -z "$URL" ]; then
69
81
  REPO="${ROOT:-${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
@@ -0,0 +1,90 @@
1
+ // Boot-time self-heal — covers the gap between marketplace-cache installs
2
+ // (git clone, honors .gitignore, no `npm install`) and what server.ts needs
3
+ // at runtime. Two artifacts can be missing on a fresh clone:
4
+ //
5
+ // - dist/client.bundle.js → 404 on /_client/*
6
+ // - node_modules/react → 500 on /_canvas-runtime/*
7
+ //
8
+ // Per DDR-044 we commit the bundle + styles, so dist/ should be present.
9
+ // For node_modules/ we self-heal: detect missing react, run `bun install
10
+ // --production`. Opt out with MAUDE_NO_AUTOBUILD=1 for read-only-filesystem
11
+ // deployments (e.g. immutable infra).
12
+ //
13
+ // Phase 19. DDR-044.
14
+
15
+ import { existsSync } from 'node:fs';
16
+ import { dirname, join } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ export interface SelfHealOptions {
20
+ /** Plugin install directory (the dev-server root). Defaults to this file's dir. */
21
+ here?: string;
22
+ /** Defaults to process.env.MAUDE_NO_AUTOBUILD === '1'. */
23
+ optOut?: boolean;
24
+ /** Defaults to Bun.spawn; tests override. */
25
+ spawn?: (cmd: readonly string[], cwd: string) => Promise<{ code: number }>;
26
+ /** Defaults to console.error; tests override to capture. */
27
+ log?: (msg: string) => void;
28
+ /** Defaults to process.exit; tests override to assert. */
29
+ exit?: (code: number) => never;
30
+ }
31
+
32
+ export interface SelfHealResult {
33
+ ran: ('install' | 'build')[];
34
+ skipped: 'all-present' | null;
35
+ }
36
+
37
+ export async function bootSelfHeal(opts: SelfHealOptions = {}): Promise<SelfHealResult> {
38
+ const here = opts.here ?? dirname(fileURLToPath(import.meta.url));
39
+ const optOut = opts.optOut ?? process.env.MAUDE_NO_AUTOBUILD === '1';
40
+ const log = opts.log ?? ((m) => console.error(m));
41
+ const exit =
42
+ opts.exit ??
43
+ ((code: number) => {
44
+ process.exit(code);
45
+ });
46
+ const spawn = opts.spawn ?? defaultSpawn;
47
+
48
+ const distMissing = !existsSync(join(here, 'dist', 'client.bundle.js'));
49
+ const depsMissing = !existsSync(join(here, 'node_modules', 'react', 'package.json'));
50
+
51
+ if (!distMissing && !depsMissing) return { ran: [], skipped: 'all-present' };
52
+
53
+ if (optOut) {
54
+ const missing = [
55
+ distMissing ? 'dist/client.bundle.js (run `bun run build.ts`)' : null,
56
+ depsMissing ? 'node_modules/react (run `bun install --production`)' : null,
57
+ ]
58
+ .filter(Boolean)
59
+ .join('\n - ');
60
+ log(`\n ⚠ first-boot artifacts missing and MAUDE_NO_AUTOBUILD=1 is set:\n - ${missing}\n`);
61
+ exit(1);
62
+ }
63
+
64
+ const ran: ('install' | 'build')[] = [];
65
+ if (depsMissing) {
66
+ log(' ⚠ first-boot: installing runtime deps (one-time, ~15s)…');
67
+ const { code } = await spawn(['bun', 'install', '--production'], here);
68
+ if (code !== 0) {
69
+ log(` ⚠ \`bun install --production\` exited ${code}. Set MAUDE_NO_AUTOBUILD=1 and run manually.`);
70
+ exit(1);
71
+ }
72
+ ran.push('install');
73
+ }
74
+ if (distMissing) {
75
+ log(' ⚠ first-boot: building client assets (one-time, ~2s)…');
76
+ const { code } = await spawn(['bun', 'run', 'build.ts'], here);
77
+ if (code !== 0) {
78
+ log(` ⚠ \`bun run build.ts\` exited ${code}. Set MAUDE_NO_AUTOBUILD=1 and run manually.`);
79
+ exit(1);
80
+ }
81
+ ran.push('build');
82
+ }
83
+ return { ran, skipped: null };
84
+ }
85
+
86
+ async function defaultSpawn(cmd: readonly string[], cwd: string): Promise<{ code: number }> {
87
+ const proc = Bun.spawn([...cmd], { cwd, stdout: 'inherit', stderr: 'inherit' });
88
+ const code = await proc.exited;
89
+ return { code };
90
+ }
@@ -65,13 +65,29 @@ function ensureDist() {
65
65
  if (!existsSync(DIST)) mkdirSync(DIST, { recursive: true });
66
66
  }
67
67
 
68
+ async function readPluginVersion(): Promise<{ version: string }> {
69
+ // plugins/design/dev-server/ → plugins/design/.claude-plugin/plugin.json
70
+ const manifest = join(ROOT, '..', '.claude-plugin', 'plugin.json');
71
+ try {
72
+ const parsed = JSON.parse(await Bun.file(manifest).text()) as { version?: unknown };
73
+ if (typeof parsed.version === 'string') return { version: parsed.version };
74
+ } catch {
75
+ /* fall through to dev default */
76
+ }
77
+ return { version: 'dev' };
78
+ }
79
+
68
80
  // ---------- (a) Client JSX bundle ----------
69
81
 
70
82
  async function buildClient(): Promise<{ outBytes: number; outPath: string }> {
71
83
  ensureDist();
72
84
  const outPath = join(DIST, 'client.bundle.js');
73
- // Read package.json version at build time for the wordmark sub-line.
74
- const pkg = JSON.parse(await Bun.file(join(ROOT, '..', '..', '..', 'package.json')).text());
85
+ // Wordmark sub-line version. Read from plugins/design/.claude-plugin/plugin.json —
86
+ // that file ships in both npm installs AND marketplace-cache clones (it's the
87
+ // plugin manifest, always present). The old `../../../package.json` hop
88
+ // resolved to the repo root in dev but ENOENT'd in marketplace caches where
89
+ // the maude bundle isn't installed as a package. DDR-044, Phase 19.
90
+ const pkg = await readPluginVersion();
75
91
  const result = await Bun.build({
76
92
  entrypoints: [join(ROOT, 'client/app.jsx')],
77
93
  outdir: DIST,
@@ -116,6 +116,18 @@
116
116
  },
117
117
  "default": ["accent"]
118
118
  },
119
+ "accentStrategy": {
120
+ "type": "string",
121
+ "description": "How many --accent* families exist. The completeness-critic C7 gates the actual family count against this declaration. Default: 'single' (backwards-compatible). 'chromatic-N' is parameterized — write the literal N (e.g. 'chromatic-5'). Per DDR-043.",
122
+ "pattern": "^(single|paired|chromatic-[1-9][0-9]?)$",
123
+ "default": "single"
124
+ },
125
+ "colorSpace": {
126
+ "type": "string",
127
+ "enum": ["oklch", "hsl", "hex", "lab"],
128
+ "description": "Color space the project uses in tokens CSS. The completeness-critic V2 gates the actual token format against this declaration. Default: 'oklch' (backwards-compatible). Per DDR-043.",
129
+ "default": "oklch"
130
+ },
119
131
  "designSystems": {
120
132
  "type": "array",
121
133
  "description": "Design systems available in this project. Single-DS projects have one entry; multi-DS projects (marketing vs. admin vs. mobile) list each here. Each canvas's .meta.json declares which DS it uses via the 'designSystem' field.",