@dryui/cli 0.1.1 → 0.2.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 (2) hide show
  1. package/dist/index.js +1187 -585
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3243,8 +3243,8 @@ var require_utils = __commonJS((exports, module) => {
3243
3243
  }
3244
3244
  return output.join("");
3245
3245
  }
3246
- function normalizeComponentEncoding(component, esc2) {
3247
- const func = esc2 !== true ? escape : unescape;
3246
+ function normalizeComponentEncoding(component, esc3) {
3247
+ const func = esc3 !== true ? escape : unescape;
3248
3248
  if (component.scheme !== undefined) {
3249
3249
  component.scheme = func(component.scheme);
3250
3250
  }
@@ -6518,7 +6518,7 @@ var require_dist = __commonJS((exports, module) => {
6518
6518
  // package.json
6519
6519
  var package_default = {
6520
6520
  name: "@dryui/cli",
6521
- version: "0.1.1",
6521
+ version: "0.2.0",
6522
6522
  author: "Rob Balfre",
6523
6523
  license: "MIT",
6524
6524
  repository: {
@@ -6545,7 +6545,7 @@ var package_default = {
6545
6545
  },
6546
6546
  dependencies: {
6547
6547
  "@dryui/feedback-server": "^0.1.0",
6548
- "@dryui/mcp": "^0.1.0"
6548
+ "@dryui/mcp": "^0.2.0"
6549
6549
  },
6550
6550
  devDependencies: {
6551
6551
  "@types/node": "^25.5.0",
@@ -6554,7 +6554,7 @@ var package_default = {
6554
6554
  };
6555
6555
  // ../mcp/src/spec.json
6556
6556
  var spec_default = {
6557
- version: "0.1.1",
6557
+ version: "0.1.7",
6558
6558
  package: "@dryui/ui",
6559
6559
  themeImports: {
6560
6560
  default: "@dryui/ui/themes/default.css",
@@ -6728,6 +6728,12 @@ var spec_default = {
6728
6728
  name: "data-disabled",
6729
6729
  description: "Present when the component or part is disabled."
6730
6730
  },
6731
+ {
6732
+ name: "data-group-orientation"
6733
+ },
6734
+ {
6735
+ name: "data-in-group"
6736
+ },
6731
6737
  {
6732
6738
  name: "data-size"
6733
6739
  },
@@ -7453,6 +7459,13 @@ var spec_default = {
7453
7459
  ]
7454
7460
  },
7455
7461
  cssVars: {
7462
+ "--dry-btn-accent": "Accent",
7463
+ "--dry-btn-accent-active": "Accent Active",
7464
+ "--dry-btn-accent-fg": "Accent Fg",
7465
+ "--dry-btn-accent-hover": "Accent Hover",
7466
+ "--dry-btn-accent-stroke": "Accent Stroke",
7467
+ "--dry-btn-border": "Border color",
7468
+ "--dry-btn-on-accent": "On Accent",
7456
7469
  "--dry-dialog-bg": "Background color",
7457
7470
  "--dry-dialog-border": "Border color",
7458
7471
  "--dry-dialog-max-width": "Maximum width",
@@ -8536,6 +8549,9 @@ var spec_default = {
8536
8549
  name: "data-disabled",
8537
8550
  description: "Present when the component or part is disabled."
8538
8551
  },
8552
+ {
8553
+ name: "data-indicator"
8554
+ },
8539
8555
  {
8540
8556
  name: "data-orientation",
8541
8557
  description: "Reflects the current horizontal or vertical orientation.",
@@ -8671,6 +8687,9 @@ var spec_default = {
8671
8687
  name: "data-disabled",
8672
8688
  description: "Present when the component or part is disabled."
8673
8689
  },
8690
+ {
8691
+ name: "data-indicator"
8692
+ },
8674
8693
  {
8675
8694
  name: "data-state",
8676
8695
  description: "Reflects whether the collapsible content is expanded or collapsed.",
@@ -9074,8 +9093,7 @@ var spec_default = {
9074
9093
  ]
9075
9094
  },
9076
9095
  cssVars: {
9077
- "--dry-field-gap": "Gap spacing",
9078
- "--dry-field-label-order": "Label Order"
9096
+ "--dry-field-gap": "Gap spacing"
9079
9097
  },
9080
9098
  dataAttributes: [
9081
9099
  {
@@ -9848,6 +9866,9 @@ var spec_default = {
9848
9866
  name: "data-disabled",
9849
9867
  description: "Present when the component or part is disabled."
9850
9868
  },
9869
+ {
9870
+ name: "data-indicator"
9871
+ },
9851
9872
  {
9852
9873
  name: "data-invalid",
9853
9874
  description: "Present when the current field value is invalid."
@@ -10971,9 +10992,6 @@ var spec_default = {
10971
10992
  name: "data-disabled",
10972
10993
  description: "Present when the component or part is disabled."
10973
10994
  },
10974
- {
10975
- name: "data-option-swatch-group-content"
10976
- },
10977
10995
  {
10978
10996
  name: "data-option-swatch-group-item"
10979
10997
  },
@@ -11448,6 +11466,9 @@ var spec_default = {
11448
11466
  },
11449
11467
  {
11450
11468
  name: "data-tabs-trigger"
11469
+ },
11470
+ {
11471
+ name: "data-tabs-trigger-content"
11451
11472
  }
11452
11473
  ],
11453
11474
  example: `<Tabs.Root bind:value={activeTab}>
@@ -14626,11 +14647,11 @@ var spec_default = {
14626
14647
  tree: [
14627
14648
  "Timeline.Root",
14628
14649
  " Timeline.Item",
14629
- " Timeline.Icon",
14630
- " Timeline.Content",
14631
- " Timeline.Title",
14632
- " Timeline.Description",
14633
- " Timeline.Time"
14650
+ " Timeline.Icon",
14651
+ " Timeline.Content",
14652
+ " Timeline.Title",
14653
+ " Timeline.Description",
14654
+ " Timeline.Time"
14634
14655
  ]
14635
14656
  },
14636
14657
  cssVars: {
@@ -14668,12 +14689,14 @@ var spec_default = {
14668
14689
  }
14669
14690
  ],
14670
14691
  example: `<Timeline.Root>
14671
- <Timeline.Item>...</Timeline.Item>
14672
- <Timeline.Icon>...</Timeline.Icon>
14673
- <Timeline.Content>...</Timeline.Content>
14674
- <Timeline.Title>...</Timeline.Title>
14675
- <Timeline.Description>...</Timeline.Description>
14676
- <Timeline.Time>...</Timeline.Time>
14692
+ <Timeline.Item>
14693
+ <Timeline.Icon />
14694
+ <Timeline.Content>
14695
+ <Timeline.Title>Event title</Timeline.Title>
14696
+ <Timeline.Description>Event description</Timeline.Description>
14697
+ <Timeline.Time>2 hours ago</Timeline.Time>
14698
+ </Timeline.Content>
14699
+ </Timeline.Item>
14677
14700
  </Timeline.Root>`
14678
14701
  },
14679
14702
  Typography: {
@@ -16372,6 +16395,7 @@ var spec_default = {
16372
16395
  ]
16373
16396
  },
16374
16397
  cssVars: {
16398
+ "--dry-calendar-grid-gap": "Grid Gap",
16375
16399
  "--dry-dp-bg": "Background color",
16376
16400
  "--dry-dp-border": "Border color",
16377
16401
  "--dry-dp-font-size": "Font size",
@@ -16379,9 +16403,33 @@ var spec_default = {
16379
16403
  "--dry-dp-padding-y": "Vertical padding"
16380
16404
  },
16381
16405
  dataAttributes: [
16406
+ {
16407
+ name: "data-calendar-cell"
16408
+ },
16409
+ {
16410
+ name: "data-calendar-columnheader"
16411
+ },
16382
16412
  {
16383
16413
  name: "data-calendar-day"
16384
16414
  },
16415
+ {
16416
+ name: "data-calendar-day-button"
16417
+ },
16418
+ {
16419
+ name: "data-calendar-header"
16420
+ },
16421
+ {
16422
+ name: "data-calendar-heading"
16423
+ },
16424
+ {
16425
+ name: "data-calendar-nav"
16426
+ },
16427
+ {
16428
+ name: "data-calendar-panel"
16429
+ },
16430
+ {
16431
+ name: "data-calendar-row"
16432
+ },
16385
16433
  {
16386
16434
  name: "data-disabled",
16387
16435
  description: "Present when the component or part is disabled."
@@ -16395,6 +16443,9 @@ var spec_default = {
16395
16443
  {
16396
16444
  name: "data-dp-trigger"
16397
16445
  },
16446
+ {
16447
+ name: "data-indicator"
16448
+ },
16398
16449
  {
16399
16450
  name: "data-invalid",
16400
16451
  description: "Present when the current field value is invalid."
@@ -16858,9 +16909,33 @@ var spec_default = {
16858
16909
  },
16859
16910
  cssVars: {},
16860
16911
  dataAttributes: [
16912
+ {
16913
+ name: "data-calendar-cell"
16914
+ },
16915
+ {
16916
+ name: "data-calendar-columnheader"
16917
+ },
16861
16918
  {
16862
16919
  name: "data-calendar-day"
16863
16920
  },
16921
+ {
16922
+ name: "data-calendar-day-button"
16923
+ },
16924
+ {
16925
+ name: "data-calendar-header"
16926
+ },
16927
+ {
16928
+ name: "data-calendar-heading"
16929
+ },
16930
+ {
16931
+ name: "data-calendar-nav"
16932
+ },
16933
+ {
16934
+ name: "data-calendar-panel"
16935
+ },
16936
+ {
16937
+ name: "data-calendar-row"
16938
+ },
16864
16939
  {
16865
16940
  name: "data-disabled",
16866
16941
  description: "Present when the component or part is disabled."
@@ -16874,15 +16949,15 @@ var spec_default = {
16874
16949
  {
16875
16950
  name: "data-drp-preset"
16876
16951
  },
16877
- {
16878
- name: "data-drp-root"
16879
- },
16880
16952
  {
16881
16953
  name: "data-drp-trigger"
16882
16954
  },
16883
16955
  {
16884
16956
  name: "data-in-range"
16885
16957
  },
16958
+ {
16959
+ name: "data-indicator"
16960
+ },
16886
16961
  {
16887
16962
  name: "data-outside-month"
16888
16963
  },
@@ -17416,8 +17491,14 @@ var spec_default = {
17416
17491
  "--dry-range-calendar-day-hover-bg": "Calendar Day Hover Bg",
17417
17492
  "--dry-range-calendar-day-radius": "Calendar Day Radius",
17418
17493
  "--dry-range-calendar-day-size": "Calendar Day Size",
17494
+ "--dry-range-calendar-heading-color": "Calendar Heading Color",
17495
+ "--dry-range-calendar-nav-bg": "Calendar Nav Bg",
17496
+ "--dry-range-calendar-nav-border": "Calendar Nav Border",
17497
+ "--dry-range-calendar-nav-color": "Calendar Nav Color",
17419
17498
  "--dry-range-calendar-outside-color": "Calendar Outside Color",
17420
17499
  "--dry-range-calendar-padding": "Calendar Padding",
17500
+ "--dry-range-calendar-panel-bg": "Calendar Panel Bg",
17501
+ "--dry-range-calendar-panel-border": "Calendar Panel Border",
17421
17502
  "--dry-range-calendar-radius": "Calendar Radius",
17422
17503
  "--dry-range-calendar-range-bg": "Calendar Range Bg",
17423
17504
  "--dry-range-calendar-selected-bg": "Calendar Selected Bg",
@@ -17427,12 +17508,33 @@ var spec_default = {
17427
17508
  "--dry-range-calendar-today-color": "Calendar Today Color"
17428
17509
  },
17429
17510
  dataAttributes: [
17511
+ {
17512
+ name: "data-calendar-cell"
17513
+ },
17514
+ {
17515
+ name: "data-calendar-columnheader"
17516
+ },
17430
17517
  {
17431
17518
  name: "data-calendar-day"
17432
17519
  },
17520
+ {
17521
+ name: "data-calendar-day-button"
17522
+ },
17433
17523
  {
17434
17524
  name: "data-calendar-header"
17435
17525
  },
17526
+ {
17527
+ name: "data-calendar-heading"
17528
+ },
17529
+ {
17530
+ name: "data-calendar-nav"
17531
+ },
17532
+ {
17533
+ name: "data-calendar-panel"
17534
+ },
17535
+ {
17536
+ name: "data-calendar-row"
17537
+ },
17436
17538
  {
17437
17539
  name: "data-disabled",
17438
17540
  description: "Present when the component or part is disabled."
@@ -17455,10 +17557,6 @@ var spec_default = {
17455
17557
  {
17456
17558
  name: "data-range-start"
17457
17559
  },
17458
- {
17459
- name: "data-selected",
17460
- description: "Present when the current item is selected."
17461
- },
17462
17560
  {
17463
17561
  name: "data-today"
17464
17562
  }
@@ -17590,6 +17688,26 @@ var spec_default = {
17590
17688
  type: "number",
17591
17689
  required: false,
17592
17690
  description: "Step interval used when incrementing numeric values."
17691
+ },
17692
+ size: {
17693
+ type: "'sm' | 'md' | 'lg'",
17694
+ required: false,
17695
+ acceptedValues: [
17696
+ "sm",
17697
+ "md",
17698
+ "lg"
17699
+ ],
17700
+ description: "Size preset affecting density, spacing, or typography.",
17701
+ default: "'md'"
17702
+ },
17703
+ name: {
17704
+ type: "string",
17705
+ required: false,
17706
+ description: "Field name used during native form submission."
17707
+ },
17708
+ class: {
17709
+ type: "string",
17710
+ required: false
17593
17711
  }
17594
17712
  },
17595
17713
  forwardedProps: {
@@ -17603,25 +17721,23 @@ var spec_default = {
17603
17721
  ],
17604
17722
  note: "Forwards <input> attributes via rest props."
17605
17723
  },
17606
- cssVars: {
17607
- "--dry-time-input-bg": "Input Bg",
17608
- "--dry-time-input-border": "Input Border",
17609
- "--dry-time-input-color": "Input Color",
17610
- "--dry-time-input-font-size": "Input Font Size",
17611
- "--dry-time-input-padding-x": "Input Padding X",
17612
- "--dry-time-input-padding-y": "Input Padding Y",
17613
- "--dry-time-input-radius": "Input Radius"
17614
- },
17724
+ cssVars: {},
17615
17725
  dataAttributes: [
17616
17726
  {
17617
17727
  name: "data-disabled",
17618
17728
  description: "Present when the component or part is disabled."
17619
17729
  },
17620
17730
  {
17621
- name: "data-time-input"
17731
+ name: "data-placeholder"
17732
+ },
17733
+ {
17734
+ name: "data-time-display"
17622
17735
  },
17623
17736
  {
17624
17737
  name: "data-time-input-wrapper"
17738
+ },
17739
+ {
17740
+ name: "data-time-separator"
17625
17741
  }
17626
17742
  ],
17627
17743
  example: "<TimeInput>Content</TimeInput>"
@@ -17807,6 +17923,7 @@ var spec_default = {
17807
17923
  },
17808
17924
  cssVars: {
17809
17925
  "--dry-tags-input-font-size": "Input Font Size",
17926
+ "--dry-tags-input-gap": "Input Gap",
17810
17927
  "--dry-tags-input-tag-font-size": "Input Tag Font Size",
17811
17928
  "--dry-tags-input-tag-padding-x": "Input Tag Padding X"
17812
17929
  },
@@ -18078,9 +18195,6 @@ var spec_default = {
18078
18195
  {
18079
18196
  name: "data-dragging"
18080
18197
  },
18081
- {
18082
- name: "data-file-upload"
18083
- },
18084
18198
  {
18085
18199
  name: "data-fu-dropzone"
18086
18200
  },
@@ -19313,6 +19427,7 @@ var spec_default = {
19313
19427
  "--dry-fab-color": "Text color",
19314
19428
  "--dry-fab-gap": "Gap spacing",
19315
19429
  "--dry-fab-offset": "Offset",
19430
+ "--dry-fab-position": "Position",
19316
19431
  "--dry-fab-shadow": "Box shadow",
19317
19432
  "--dry-fab-z-index": "Z Index"
19318
19433
  },
@@ -19705,7 +19820,8 @@ var spec_default = {
19705
19820
  },
19706
19821
  overscan: {
19707
19822
  type: "number",
19708
- required: false
19823
+ required: false,
19824
+ default: "5"
19709
19825
  },
19710
19826
  children: {
19711
19827
  type: "Snippet<[{ item: T; index: number; style: string }]>",
@@ -20766,7 +20882,8 @@ var spec_default = {
20766
20882
  placeholder: {
20767
20883
  type: "string",
20768
20884
  required: false,
20769
- description: "Hint text shown when no value is selected or entered."
20885
+ description: "Hint text shown when no value is selected or entered.",
20886
+ default: "''"
20770
20887
  },
20771
20888
  readonly: {
20772
20889
  type: "boolean",
@@ -20852,8 +20969,20 @@ var spec_default = {
20852
20969
  {
20853
20970
  name: "data-part"
20854
20971
  },
20972
+ {
20973
+ name: "data-placeholder"
20974
+ },
20855
20975
  {
20856
20976
  name: "data-readonly"
20977
+ },
20978
+ {
20979
+ name: "data-rte-content"
20980
+ },
20981
+ {
20982
+ name: "data-rte-root"
20983
+ },
20984
+ {
20985
+ name: "data-rte-toolbar"
20857
20986
  }
20858
20987
  ],
20859
20988
  example: `<RichTextEditor.Root>
@@ -23746,18 +23875,6 @@ var spec_default = {
23746
23875
  "--dry-map-radius": "Border radius"
23747
23876
  },
23748
23877
  dataAttributes: [
23749
- {
23750
- name: "data-map-mapboxgl"
23751
- },
23752
- {
23753
- name: "data-map-maplibregl"
23754
- },
23755
- {
23756
- name: "data-map-marker"
23757
- },
23758
- {
23759
- name: "data-map-popup"
23760
- },
23761
23878
  {
23762
23879
  name: "data-map-root"
23763
23880
  },
@@ -23774,7 +23891,7 @@ var spec_default = {
23774
23891
  },
23775
23892
  SystemMap: {
23776
23893
  import: "@dryui/ui",
23777
- description: "Layered architecture diagram for DolphinGraph nodes, relationships, and audit signals",
23894
+ description: "Layered architecture diagram that visualizes component nodes, relationships, and dependency layers",
23778
23895
  category: "display",
23779
23896
  tags: [
23780
23897
  "diagram",
@@ -23845,92 +23962,40 @@ var spec_default = {
23845
23962
  ],
23846
23963
  note: "Forwards <div> attributes via rest props."
23847
23964
  },
23848
- cssVars: {
23849
- "--dry-system-map-bg": "Map Bg",
23850
- "--dry-system-map-border": "Map Border",
23851
- "--dry-system-map-shadow": "Map Shadow",
23852
- "--dry-system-map-surface": "Map Surface",
23853
- "--dry-system-map-text": "Map Text",
23854
- "--dry-system-map-text-muted": "Map Text Muted"
23855
- },
23965
+ cssVars: {},
23856
23966
  dataAttributes: [
23857
23967
  {
23858
- name: "data-canvas-shell"
23859
- },
23860
- {
23861
- name: "data-dashed"
23862
- },
23863
- {
23864
- name: "data-edge-label"
23968
+ name: "data-connector"
23865
23969
  },
23866
23970
  {
23867
- name: "data-empty-hint"
23971
+ name: "data-connector-label"
23868
23972
  },
23869
23973
  {
23870
- name: "data-empty-label"
23974
+ name: "data-connector-line"
23871
23975
  },
23872
23976
  {
23873
- name: "data-eyebrow"
23977
+ name: "data-edge-row"
23874
23978
  },
23875
23979
  {
23876
- name: "data-group-label"
23877
- },
23878
- {
23879
- name: "data-header"
23880
- },
23881
- {
23882
- name: "data-header-badges"
23883
- },
23884
- {
23885
- name: "data-header-text"
23886
- },
23887
- {
23888
- name: "data-lane-meta"
23889
- },
23890
- {
23891
- name: "data-lane-title"
23892
- },
23893
- {
23894
- name: "data-legend"
23895
- },
23896
- {
23897
- name: "data-legend-item"
23898
- },
23899
- {
23900
- name: "data-legend-items"
23901
- },
23902
- {
23903
- name: "data-legend-section"
23904
- },
23905
- {
23906
- name: "data-line-swatch"
23907
- },
23908
- {
23909
- name: "data-map-title"
23910
- },
23911
- {
23912
- name: "data-node-description"
23980
+ name: "data-empty"
23913
23981
  },
23914
23982
  {
23915
- name: "data-node-label"
23983
+ name: "data-focused"
23916
23984
  },
23917
23985
  {
23918
- name: "data-node-meta"
23986
+ name: "data-node"
23919
23987
  },
23920
23988
  {
23921
- name: "data-summary"
23989
+ name: "data-node-helper"
23922
23990
  },
23923
23991
  {
23924
- name: "data-svg"
23992
+ name: "data-node-name"
23925
23993
  },
23926
23994
  {
23927
- name: "data-swatch"
23995
+ name: "data-orphans"
23928
23996
  },
23929
23997
  {
23930
23998
  name: "data-system-map"
23931
- },
23932
- {
23933
- name: "data-thumbnail-object"
23934
23999
  }
23935
24000
  ],
23936
24001
  example: "<SystemMap>Content</SystemMap>"
@@ -24386,8 +24451,7 @@ var spec_default = {
24386
24451
  },
24387
24452
  blendMode: {
24388
24453
  type: "BlendMode",
24389
- required: false,
24390
- default: "'screen'"
24454
+ required: false
24391
24455
  },
24392
24456
  children: {
24393
24457
  type: "Snippet",
@@ -29995,7 +30059,7 @@ body { margin: 0; min-height: 100dvh; }
29995
30059
  },
29996
30060
  "simple-content-page": {
29997
30061
  name: "simple-content-page",
29998
- description: "Clean content page with constrained width and vertical rhythm. Use Container for max-width and Stack for vertical spacing. Suitable for blog posts, documentation, settings pages.",
30062
+ description: "Clean content page with constrained width and vertical rhythm. Use Container for max-width and CSS grid for vertical spacing. Suitable for blog posts, documentation, settings pages.",
29999
30063
  tags: [
30000
30064
  "page",
30001
30065
  "content",
@@ -31679,13 +31743,715 @@ body { margin: 0; min-height: 100dvh; }
31679
31743
  ]
31680
31744
  };
31681
31745
 
31682
- // ../mcp/src/spec-formatters.ts
31746
+ // ../mcp/src/project-planner.ts
31747
+ import { existsSync, readFileSync, statSync } from "node:fs";
31748
+ import { dirname, resolve } from "node:path";
31683
31749
  var DIR_OVERRIDES = {
31684
31750
  QRCode: "qr-code"
31685
31751
  };
31686
31752
  function componentDir(name) {
31687
31753
  return DIR_OVERRIDES[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
31688
31754
  }
31755
+ function resolveStart(inputPath) {
31756
+ const candidate = resolve(inputPath ?? process.cwd());
31757
+ return existsSync(candidate) && statSync(candidate).isFile() ? dirname(candidate) : candidate;
31758
+ }
31759
+ function findUp(start, fileName) {
31760
+ let current = start;
31761
+ while (true) {
31762
+ const candidate = resolve(current, fileName);
31763
+ if (existsSync(candidate))
31764
+ return candidate;
31765
+ const parent = dirname(current);
31766
+ if (parent === current)
31767
+ return null;
31768
+ current = parent;
31769
+ }
31770
+ }
31771
+ function findUpAny(start, fileNames) {
31772
+ let current = start;
31773
+ while (true) {
31774
+ for (const fileName of fileNames) {
31775
+ const candidate = resolve(current, fileName);
31776
+ if (existsSync(candidate))
31777
+ return candidate;
31778
+ }
31779
+ const parent = dirname(current);
31780
+ if (parent === current)
31781
+ return null;
31782
+ current = parent;
31783
+ }
31784
+ }
31785
+ function detectPackageManager(root) {
31786
+ if (!root)
31787
+ return "unknown";
31788
+ const lockfilePath = findUpAny(root, [
31789
+ "bun.lock",
31790
+ "bun.lockb",
31791
+ "pnpm-lock.yaml",
31792
+ "package-lock.json",
31793
+ "yarn.lock"
31794
+ ]);
31795
+ if (!lockfilePath)
31796
+ return "unknown";
31797
+ if (lockfilePath.endsWith("bun.lock") || lockfilePath.endsWith("bun.lockb"))
31798
+ return "bun";
31799
+ if (lockfilePath.endsWith("pnpm-lock.yaml"))
31800
+ return "pnpm";
31801
+ if (lockfilePath.endsWith("package-lock.json"))
31802
+ return "npm";
31803
+ if (lockfilePath.endsWith("yarn.lock"))
31804
+ return "yarn";
31805
+ return "unknown";
31806
+ }
31807
+ function readPackageJson(packageJsonPath) {
31808
+ if (!packageJsonPath)
31809
+ return null;
31810
+ return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
31811
+ }
31812
+ function getDependencyNames(pkg) {
31813
+ return new Set([
31814
+ ...Object.keys(pkg?.dependencies ?? {}),
31815
+ ...Object.keys(pkg?.devDependencies ?? {})
31816
+ ]);
31817
+ }
31818
+ function detectFramework(dependencyNames) {
31819
+ if (dependencyNames.has("@sveltejs/kit"))
31820
+ return "sveltekit";
31821
+ if (dependencyNames.has("svelte"))
31822
+ return "svelte";
31823
+ return "unknown";
31824
+ }
31825
+ function hasImport(filePath, importPath) {
31826
+ if (!filePath)
31827
+ return false;
31828
+ return readFileSync(filePath, "utf-8").includes(importPath);
31829
+ }
31830
+ function hasThemeAuto(appHtmlPath) {
31831
+ if (!appHtmlPath)
31832
+ return false;
31833
+ return readFileSync(appHtmlPath, "utf-8").includes("theme-auto");
31834
+ }
31835
+ function hasAnyImport(filePaths, importPath) {
31836
+ return filePaths.some((filePath) => hasImport(filePath, importPath));
31837
+ }
31838
+ function importsAppCss(rootLayoutPath) {
31839
+ if (!rootLayoutPath)
31840
+ return false;
31841
+ const content = readFileSync(rootLayoutPath, "utf-8");
31842
+ return content.includes("app.css");
31843
+ }
31844
+ function buildStatus(framework, hasPackageJson, stepsNeeded) {
31845
+ if (!hasPackageJson || framework === "unknown")
31846
+ return "unsupported";
31847
+ return stepsNeeded === 0 ? "ready" : "partial";
31848
+ }
31849
+ function installCommand(packageManager) {
31850
+ switch (packageManager) {
31851
+ case "bun":
31852
+ return "bun add @dryui/ui";
31853
+ case "pnpm":
31854
+ return "pnpm add @dryui/ui";
31855
+ case "yarn":
31856
+ return "yarn add @dryui/ui";
31857
+ default:
31858
+ return "npm install @dryui/ui";
31859
+ }
31860
+ }
31861
+ function buildThemeImportLines(spec) {
31862
+ return ` import '${spec.themeImports.default}';
31863
+ import '${spec.themeImports.dark}';`;
31864
+ }
31865
+ function buildThemeImportSnippet(spec) {
31866
+ return ["<script>", buildThemeImportLines(spec), "</script>"].join(`
31867
+ `);
31868
+ }
31869
+ function buildRootLayoutSnippet(spec) {
31870
+ return [
31871
+ "<script>",
31872
+ buildThemeImportLines(spec),
31873
+ "",
31874
+ " let { children } = $props();",
31875
+ "</script>",
31876
+ "",
31877
+ "{@render children()}"
31878
+ ].join(`
31879
+ `);
31880
+ }
31881
+ function buildThemeImportCssSnippet(spec) {
31882
+ return [`@import '${spec.themeImports.default}';`, `@import '${spec.themeImports.dark}';`].join(`
31883
+ `);
31884
+ }
31885
+ function getSuggestedTarget(root, explicitTarget) {
31886
+ if (explicitTarget)
31887
+ return resolve(root ?? process.cwd(), explicitTarget);
31888
+ if (!root)
31889
+ return null;
31890
+ const rootPage = resolve(root, "src/routes/+page.svelte");
31891
+ return existsSync(rootPage) ? rootPage : null;
31892
+ }
31893
+ function getImportStatement(name, component, subpath = false) {
31894
+ if (subpath && component.import === "@dryui/ui") {
31895
+ return `import { ${name} } from '${component.import}/${componentDir(name)}';`;
31896
+ }
31897
+ return `import { ${name} } from '${component.import}';`;
31898
+ }
31899
+ function findComponent(spec, query) {
31900
+ const exact = spec.components[query];
31901
+ if (exact)
31902
+ return { name: query, def: exact };
31903
+ const lower = query.toLowerCase();
31904
+ for (const [name, def] of Object.entries(spec.components)) {
31905
+ if (name.toLowerCase() === lower)
31906
+ return { name, def };
31907
+ }
31908
+ return null;
31909
+ }
31910
+ function detectProject(spec, inputPath) {
31911
+ const start = resolveStart(inputPath);
31912
+ const packageJsonPath = findUp(start, "package.json");
31913
+ const root = packageJsonPath ? dirname(packageJsonPath) : null;
31914
+ const dependencyNames = getDependencyNames(readPackageJson(packageJsonPath));
31915
+ const framework = detectFramework(dependencyNames);
31916
+ const appHtmlPath = root ? resolve(root, "src/app.html") : null;
31917
+ const appCssPath = root ? resolve(root, "src/app.css") : null;
31918
+ const rootLayoutPath = root ? resolve(root, "src/routes/+layout.svelte") : null;
31919
+ const rootPagePath = root ? resolve(root, "src/routes/+page.svelte") : null;
31920
+ const appHtml = appHtmlPath && existsSync(appHtmlPath) ? appHtmlPath : null;
31921
+ const appCss = appCssPath && existsSync(appCssPath) ? appCssPath : null;
31922
+ const rootLayout = rootLayoutPath && existsSync(rootLayoutPath) ? rootLayoutPath : null;
31923
+ const rootPage = rootPagePath && existsSync(rootPagePath) ? rootPagePath : null;
31924
+ const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
31925
+ const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
31926
+ const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
31927
+ const themeAuto = appHtml ? hasThemeAuto(appHtml) : false;
31928
+ const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!themeAuto);
31929
+ const warnings = [];
31930
+ if (!packageJsonPath)
31931
+ warnings.push("No package.json found above the provided path.");
31932
+ if (framework === "unknown")
31933
+ warnings.push("DryUI planning currently targets Svelte and SvelteKit projects.");
31934
+ return {
31935
+ inputPath: start,
31936
+ root,
31937
+ packageJsonPath,
31938
+ framework,
31939
+ packageManager: detectPackageManager(root),
31940
+ status: buildStatus(framework, Boolean(packageJsonPath), stepsNeeded),
31941
+ dependencies: {
31942
+ ui: dependencyNames.has("@dryui/ui"),
31943
+ primitives: dependencyNames.has("@dryui/primitives")
31944
+ },
31945
+ files: {
31946
+ appHtml,
31947
+ appCss,
31948
+ rootLayout,
31949
+ rootPage
31950
+ },
31951
+ theme: {
31952
+ defaultImported,
31953
+ darkImported,
31954
+ themeAuto
31955
+ },
31956
+ warnings
31957
+ };
31958
+ }
31959
+ function planInstall(spec, inputPath) {
31960
+ const detection = detectProject(spec, inputPath);
31961
+ const steps = [];
31962
+ if (detection.status === "unsupported") {
31963
+ steps.push({
31964
+ kind: "blocked",
31965
+ status: "blocked",
31966
+ title: "Project detection incomplete",
31967
+ description: detection.warnings.join(" ") || "DryUI install planning requires a Svelte or SvelteKit project with a package.json."
31968
+ });
31969
+ return { detection, steps };
31970
+ }
31971
+ if (!detection.dependencies.ui) {
31972
+ steps.push({
31973
+ kind: "install-package",
31974
+ status: "pending",
31975
+ title: "Install @dryui/ui",
31976
+ description: "Add the styled DryUI package to the current project.",
31977
+ command: installCommand(detection.packageManager)
31978
+ });
31979
+ }
31980
+ if (!detection.theme.defaultImported || !detection.theme.darkImported) {
31981
+ if (detection.files.appCss && detection.files.rootLayout && importsAppCss(detection.files.rootLayout)) {
31982
+ steps.push({
31983
+ kind: "edit-file",
31984
+ status: "pending",
31985
+ title: "Add theme imports to app.css",
31986
+ description: "Prepend the two @import lines from the snippet to the TOP of the existing src/app.css file, before any other CSS rules. Do not create a second file.",
31987
+ path: detection.files.appCss,
31988
+ snippet: buildThemeImportCssSnippet(spec)
31989
+ });
31990
+ } else if (!detection.files.rootLayout) {
31991
+ const path = detection.root ? resolve(detection.root, "src/routes/+layout.svelte") : null;
31992
+ steps.push({
31993
+ kind: "create-file",
31994
+ status: "pending",
31995
+ title: "Create root layout with theme imports",
31996
+ description: "Create the file at the path below with the snippet as its full content. The file must include {@render children()} or pages will not render.",
31997
+ ...path ? { path } : {},
31998
+ snippet: buildRootLayoutSnippet(spec)
31999
+ });
32000
+ } else {
32001
+ steps.push({
32002
+ kind: "edit-file",
32003
+ status: "pending",
32004
+ title: "Add theme imports to the root layout",
32005
+ description: "Add the two import lines from the snippet into the EXISTING <script> block in this file. Do not create a second <script> block. If no <script> block exists, add one at the top of the file.",
32006
+ path: detection.files.rootLayout,
32007
+ snippet: buildThemeImportSnippet(spec)
32008
+ });
32009
+ }
32010
+ }
32011
+ if (!detection.files.appHtml) {
32012
+ steps.push({
32013
+ kind: "blocked",
32014
+ status: "blocked",
32015
+ title: "app.html not found",
32016
+ description: "DryUI expects src/app.html so the document can default to theme-auto mode."
32017
+ });
32018
+ } else if (!detection.theme.themeAuto) {
32019
+ steps.push({
32020
+ kind: "edit-file",
32021
+ status: "pending",
32022
+ title: "Set html theme mode to auto",
32023
+ description: 'In src/app.html, find the opening <html> tag (e.g. <html lang="en">) and add class="theme-auto" to it, preserving any existing attributes. Result should be like <html lang="en" class="theme-auto">. Do NOT add a second <html> element.',
32024
+ path: detection.files.appHtml,
32025
+ snippet: 'class="theme-auto"'
32026
+ });
32027
+ }
32028
+ if (steps.length === 0) {
32029
+ steps.push({
32030
+ kind: "note",
32031
+ status: "done",
32032
+ title: "DryUI install plan is complete",
32033
+ description: "The project already has @dryui/ui, theme imports, and theme-auto configured."
32034
+ });
32035
+ }
32036
+ return { detection, steps };
32037
+ }
32038
+ function planAdd(spec, query, options = {}) {
32039
+ const installPlan = planInstall(spec, options.cwd);
32040
+ const component = findComponent(spec, query);
32041
+ if (component) {
32042
+ const target = getSuggestedTarget(installPlan.detection.root, options.target);
32043
+ const steps = [];
32044
+ const warnings = [...installPlan.detection.warnings];
32045
+ if (installPlan.steps.some((step) => step.status === "pending" || step.status === "blocked")) {
32046
+ steps.push({
32047
+ kind: "note",
32048
+ status: "info",
32049
+ title: "Complete install plan first",
32050
+ description: "Apply the install plan before inserting DryUI components into project files."
32051
+ });
32052
+ }
32053
+ steps.push(target ? {
32054
+ kind: "edit-file",
32055
+ status: "pending",
32056
+ title: "Insert component into the target file",
32057
+ description: "Add the import and snippet below to the chosen Svelte file.",
32058
+ path: target,
32059
+ snippet: `${getImportStatement(component.name, component.def, options.subpath)}
32060
+
32061
+ ${component.def.example}`
32062
+ } : {
32063
+ kind: "note",
32064
+ status: "info",
32065
+ title: "Choose a target Svelte file",
32066
+ description: "No root page was found. Pick a target file and reuse the import and snippet in this plan."
32067
+ });
32068
+ return {
32069
+ detection: installPlan.detection,
32070
+ installPlan,
32071
+ targetType: "component",
32072
+ name: component.name,
32073
+ importStatement: getImportStatement(component.name, component.def, options.subpath),
32074
+ snippet: component.def.example,
32075
+ target,
32076
+ steps,
32077
+ warnings
32078
+ };
32079
+ }
32080
+ throw new Error(`Unknown component: "${query}"`);
32081
+ }
32082
+
32083
+ // ../mcp/dist/toon.js
32084
+ function esc(value) {
32085
+ if (value.includes(",") || value.includes(`
32086
+ `)) {
32087
+ return `"${value.replace(/"/g, '""')}"`;
32088
+ }
32089
+ return value;
32090
+ }
32091
+ function header(resource, count, fields) {
32092
+ return `${resource}[${count}]{${fields.join(",")}}:`;
32093
+ }
32094
+ function row(...values) {
32095
+ return " " + values.map((v) => esc(String(v))).join(",");
32096
+ }
32097
+ function truncate(text, maxLen, hint) {
32098
+ if (text.length <= maxLen)
32099
+ return text;
32100
+ return `(truncated, ${text.length} chars -- ${hint})`;
32101
+ }
32102
+ function buildContextualHelp(ctx) {
32103
+ const hints = [];
32104
+ switch (ctx.command) {
32105
+ case "info":
32106
+ if (ctx.componentName) {
32107
+ hints.push(`compose "${ctx.componentName.toLowerCase()}" -- see composition patterns`);
32108
+ hints.push(`add ${ctx.componentName} -- get starter snippet`);
32109
+ }
32110
+ break;
32111
+ case "list":
32112
+ hints.push("info <Component> -- see full API reference");
32113
+ break;
32114
+ case "compose":
32115
+ if (ctx.componentName) {
32116
+ hints.push(`info ${ctx.componentName} -- full API reference`);
32117
+ hints.push(`add ${ctx.componentName} -- starter snippet`);
32118
+ }
32119
+ break;
32120
+ case "review":
32121
+ if (ctx.hasErrors) {
32122
+ hints.push("info <Component> -- check API for errored component");
32123
+ hints.push("diagnose <file.css> -- check theme if theme warnings present");
32124
+ } else if (ctx.isEmpty) {
32125
+ hints.push("lint . -- check entire workspace");
32126
+ }
32127
+ break;
32128
+ case "diagnose":
32129
+ if (ctx.hasErrors) {
32130
+ hints.push('compose "app shell" -- get correct theme setup');
32131
+ } else if (ctx.isEmpty) {
32132
+ hints.push("review <file.svelte> -- validate component usage");
32133
+ }
32134
+ break;
32135
+ case "doctor":
32136
+ case "lint":
32137
+ if (ctx.hasFindings) {
32138
+ hints.push("lint --max-severity error -- focus on errors only");
32139
+ hints.push("review <file.svelte> -- check specific file");
32140
+ } else if (ctx.isEmpty) {
32141
+ hints.push("detect -- verify project setup");
32142
+ }
32143
+ break;
32144
+ case "detect":
32145
+ if (ctx.status === "partial" || ctx.status === "unsupported") {
32146
+ hints.push("install -- see step-by-step setup plan");
32147
+ } else if (ctx.status === "ready") {
32148
+ hints.push("list -- see available components");
32149
+ hints.push('compose "app shell" -- get root layout template');
32150
+ }
32151
+ break;
32152
+ case "install":
32153
+ hints.push("detect -- verify project after completing steps");
32154
+ break;
32155
+ }
32156
+ return hints;
32157
+ }
32158
+ function formatHelp(hints) {
32159
+ if (hints.length === 0)
32160
+ return "";
32161
+ const lines = [`next[${hints.length}]:`];
32162
+ for (const hint of hints) {
32163
+ lines.push(" " + hint);
32164
+ }
32165
+ return lines.join(`
32166
+ `);
32167
+ }
32168
+ function toonComponent(name, def, opts) {
32169
+ const full = opts?.full ?? false;
32170
+ const lines = [];
32171
+ lines.push(`${name} -- ${def.description}`, `category: ${def.category} | tags: ${def.tags.join(",")}`, `import: import { ${name} } from '${def.import}'`, `compound: ${def.compound}`);
32172
+ if (def.compound && def.structure?.tree.length) {
32173
+ lines.push("", header("structure", def.structure.tree.length, ["node"]));
32174
+ for (const node of def.structure.tree) {
32175
+ lines.push(" " + node);
32176
+ }
32177
+ if (def.structure.note)
32178
+ lines.push(` note: ${def.structure.note}`);
32179
+ }
32180
+ if (def.compound && def.parts) {
32181
+ const partEntries = Object.entries(def.parts);
32182
+ lines.push("", header("parts", partEntries.length, ["part"]));
32183
+ for (const [partName, partDef] of partEntries) {
32184
+ lines.push(` ${name}.${partName}`);
32185
+ const props = Object.entries(partDef.props ?? {});
32186
+ if (props.length > 0) {
32187
+ for (const [propName, propDef] of props) {
32188
+ const flags = [propDef.type];
32189
+ if (propDef.required)
32190
+ flags.push("required");
32191
+ if (propDef.default !== undefined)
32192
+ flags.push(`default:${propDef.default}`);
32193
+ if (propDef.acceptedValues?.length)
32194
+ flags.push(`values:${propDef.acceptedValues.join("|")}`);
32195
+ lines.push(` ${propName}: ${flags.join(" | ")}`);
32196
+ }
32197
+ }
32198
+ }
32199
+ } else if (def.props) {
32200
+ const propEntries = Object.entries(def.props);
32201
+ if (propEntries.length > 0) {
32202
+ lines.push("", header("props", propEntries.length, ["name", "type", "required", "default"]));
32203
+ for (const [propName, propDef] of propEntries) {
32204
+ lines.push(row(propName, propDef.type, propDef.required ? "yes" : "no", propDef.default ?? "-"));
32205
+ }
32206
+ }
32207
+ }
32208
+ const cssEntries = Object.entries(def.cssVars);
32209
+ if (cssEntries.length > 0) {
32210
+ lines.push("", header("css-vars", cssEntries.length, ["name", "description"]));
32211
+ for (const [varName, desc] of cssEntries) {
32212
+ lines.push(row(varName, desc));
32213
+ }
32214
+ }
32215
+ if (def.dataAttributes.length > 0) {
32216
+ lines.push("", header("data-attrs", def.dataAttributes.length, ["name", "values"]));
32217
+ for (const attr of def.dataAttributes) {
32218
+ lines.push(row(attr.name, attr.values?.join("|") ?? "-"));
32219
+ }
32220
+ }
32221
+ if (def.example) {
32222
+ const example = full ? def.example : truncate(def.example, 400, `use add ${name} for full snippet`);
32223
+ lines.push("", "example:", example);
32224
+ }
32225
+ const help = buildContextualHelp({ command: "info", componentName: name });
32226
+ if (help.length > 0) {
32227
+ lines.push("", formatHelp(help));
32228
+ }
32229
+ return lines.join(`
32230
+ `);
32231
+ }
32232
+ function toonComponentList(components, category) {
32233
+ const entries = Object.entries(components);
32234
+ const filtered = category ? entries.filter(([, def]) => def.category.toLowerCase() === category.toLowerCase()) : entries;
32235
+ if (filtered.length === 0) {
32236
+ const cats = [...new Set(entries.map(([, d]) => d.category))].sort();
32237
+ return `components[0]: no matches
32238
+ available categories: ${cats.join(", ")}`;
32239
+ }
32240
+ const groups = {};
32241
+ for (const entry of filtered) {
32242
+ const cat = entry[1].category;
32243
+ (groups[cat] ??= []).push(entry);
32244
+ }
32245
+ const lines = [header("components", filtered.length, ["name", "category", "compound", "description"])];
32246
+ const sortedCats = Object.keys(groups).sort();
32247
+ for (const cat of sortedCats) {
32248
+ const items = groups[cat] ?? [];
32249
+ for (const [name, def] of items) {
32250
+ lines.push(row(name, cat, def.compound, def.description));
32251
+ }
32252
+ }
32253
+ const help = buildContextualHelp({ command: "list" });
32254
+ if (help.length > 0) {
32255
+ lines.push("", formatHelp(help));
32256
+ }
32257
+ return lines.join(`
32258
+ `);
32259
+ }
32260
+ function toonComposition(results, opts) {
32261
+ const full = opts?.full ?? false;
32262
+ const lines = [];
32263
+ const totalMatches = results.componentMatches.length + results.recipeMatches.length;
32264
+ if (totalMatches === 0) {
32265
+ return "matches[0]: none";
32266
+ }
32267
+ for (const comp of results.componentMatches) {
32268
+ lines.push(`-- ${comp.component}: ${comp.useWhen}`);
32269
+ for (const alt of comp.alternatives) {
32270
+ const snippet = full ? alt.snippet : truncate(alt.snippet, 500, `use info ${alt.component} for full snippet`);
32271
+ lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
32272
+ lines.push(snippet.split(`
32273
+ `).map((l) => " " + l).join(`
32274
+ `));
32275
+ }
32276
+ for (const ap of comp.antiPatterns) {
32277
+ lines.push(` anti-pattern: ${ap.pattern}`);
32278
+ lines.push(` reason: ${ap.reason} | fix: ${ap.fix}`);
32279
+ }
32280
+ if (comp.combinesWith.length) {
32281
+ lines.push(` combines-with: ${comp.combinesWith.join(",")}`);
32282
+ }
32283
+ lines.push("");
32284
+ }
32285
+ for (const recipe of results.recipeMatches) {
32286
+ const snippet = full ? recipe.snippet : truncate(recipe.snippet, 500, `use compose "${recipe.name}" --full for complete code`);
32287
+ lines.push(`-- recipe: ${recipe.name}`);
32288
+ lines.push(` ${recipe.description}`);
32289
+ lines.push(` components: ${recipe.components.join(",")}`);
32290
+ lines.push(" code:");
32291
+ lines.push(snippet.split(`
32292
+ `).map((l) => " " + l).join(`
32293
+ `));
32294
+ lines.push("");
32295
+ }
32296
+ const firstComponent = results.componentMatches[0]?.alternatives[0]?.component ?? results.recipeMatches[0]?.components[0] ?? undefined;
32297
+ const ctx = { command: "compose" };
32298
+ if (firstComponent)
32299
+ ctx.componentName = firstComponent;
32300
+ const help = buildContextualHelp(ctx);
32301
+ if (help.length > 0) {
32302
+ lines.push(formatHelp(help));
32303
+ }
32304
+ return lines.join(`
32305
+ `).trimEnd();
32306
+ }
32307
+ function toonReviewResult(result) {
32308
+ const lines = [];
32309
+ const hasBlockers = result.issues.some((i) => i.severity === "error");
32310
+ const autoFixable = result.issues.filter((i) => i.fix !== null).length;
32311
+ if (result.issues.length === 0) {
32312
+ lines.push("issues[0]: clean");
32313
+ lines.push(`hasBlockers: false | autoFixable: 0`);
32314
+ } else {
32315
+ lines.push(header("issues", result.issues.length, ["severity", "line", "code", "message"]));
32316
+ for (const issue of result.issues) {
32317
+ lines.push(row(issue.severity, issue.line, issue.code, issue.message));
32318
+ if (issue.fix) {
32319
+ lines.push(` fix: ${issue.fix}`);
32320
+ }
32321
+ }
32322
+ lines.push(`hasBlockers: ${hasBlockers} | autoFixable: ${autoFixable}`);
32323
+ }
32324
+ lines.push(result.summary);
32325
+ const help = buildContextualHelp({
32326
+ command: "review",
32327
+ hasErrors: hasBlockers,
32328
+ isEmpty: result.issues.length === 0
32329
+ });
32330
+ if (help.length > 0) {
32331
+ lines.push("", formatHelp(help));
32332
+ }
32333
+ return lines.join(`
32334
+ `);
32335
+ }
32336
+ function toonDiagnoseResult(result) {
32337
+ const lines = [];
32338
+ const { variables } = result;
32339
+ const missingCount = result.issues.filter((i) => i.code === "missing-token").length;
32340
+ const totalRequired = variables.required + missingCount;
32341
+ const coverage = totalRequired > 0 ? Math.round(variables.required / totalRequired * 100) : 100;
32342
+ lines.push(`tokens: ${variables.found} found, ${variables.required} required, ${variables.extra} extra | coverage: ${coverage}%`);
32343
+ if (result.issues.length === 0) {
32344
+ lines.push("issues[0]: clean");
32345
+ } else {
32346
+ lines.push(header("issues", result.issues.length, ["severity", "code", "variable", "message"]));
32347
+ for (const issue of result.issues) {
32348
+ lines.push(row(issue.severity, issue.code, issue.variable, issue.message));
32349
+ if (issue.fix) {
32350
+ lines.push(` fix: ${issue.fix}`);
32351
+ }
32352
+ }
32353
+ }
32354
+ lines.push(result.summary);
32355
+ const hasErrors = result.issues.some((i) => i.severity === "error");
32356
+ const help = buildContextualHelp({
32357
+ command: "diagnose",
32358
+ hasErrors,
32359
+ isEmpty: result.issues.length === 0
32360
+ });
32361
+ if (help.length > 0) {
32362
+ lines.push("", formatHelp(help));
32363
+ }
32364
+ return lines.join(`
32365
+ `);
32366
+ }
32367
+ var MAX_FINDINGS_DEFAULT = 50;
32368
+ function toonWorkspaceReport(report, opts) {
32369
+ const full = opts?.full ?? false;
32370
+ const title = opts?.title ?? "workspace";
32371
+ const command = opts?.command ?? (title.includes("lint") ? "lint" : "doctor");
32372
+ const lines = [];
32373
+ lines.push(`${title} | root: ${report.root}`);
32374
+ lines.push(`scanned: ${report.scannedFiles} files | errors: ${report.summary.error} | warnings: ${report.summary.warning} | info: ${report.summary.info}`);
32375
+ if (report.summary.byRule && Object.keys(report.summary.byRule).length > 0) {
32376
+ const sorted = Object.entries(report.summary.byRule).sort(([, a], [, b]) => b - a);
32377
+ const topRule = sorted[0];
32378
+ if (topRule) {
32379
+ lines.push(`top-rule: ${topRule[0]} (${topRule[1]} occurrences)`);
32380
+ }
32381
+ }
32382
+ if (report.findings.length === 0) {
32383
+ lines.push("findings[0]: clean");
32384
+ } else {
32385
+ const findings = full ? report.findings : report.findings.slice(0, MAX_FINDINGS_DEFAULT);
32386
+ const truncated = !full && report.findings.length > MAX_FINDINGS_DEFAULT;
32387
+ lines.push(header("findings", findings.length, ["severity", "rule", "file", "line", "message"]));
32388
+ for (const f of findings) {
32389
+ lines.push(row(f.severity, f.ruleId, f.file, f.line ?? "-", f.message));
32390
+ if (f.suggestedFixes.length > 0) {
32391
+ for (const fix of f.suggestedFixes) {
32392
+ lines.push(` fix: ${fix.description}${fix.replacement ? ` -> ${fix.replacement}` : ""}`);
32393
+ }
32394
+ }
32395
+ }
32396
+ if (truncated) {
32397
+ lines.push(` (showing ${MAX_FINDINGS_DEFAULT} of ${report.findings.length} -- use --full to see all)`);
32398
+ }
32399
+ }
32400
+ if (report.warnings.length > 0) {
32401
+ lines.push("", header("warnings", report.warnings.length, ["message"]));
32402
+ for (const w of report.warnings) {
32403
+ lines.push(" " + w);
32404
+ }
32405
+ }
32406
+ const help = buildContextualHelp({
32407
+ command,
32408
+ hasFindings: report.findings.length > 0,
32409
+ isEmpty: report.findings.length === 0
32410
+ });
32411
+ if (help.length > 0) {
32412
+ lines.push("", formatHelp(help));
32413
+ }
32414
+ return lines.join(`
32415
+ `);
32416
+ }
32417
+ function toonProjectDetection(detection) {
32418
+ const lines = [];
32419
+ lines.push(`project: ${detection.status} | framework: ${detection.framework} | pkg-manager: ${detection.packageManager}`);
32420
+ lines.push(`root: ${detection.root ?? "(not found)"}`);
32421
+ lines.push(`deps: ui=${detection.dependencies.ui}, primitives=${detection.dependencies.primitives}`);
32422
+ lines.push(`theme: default=${detection.theme.defaultImported}, dark=${detection.theme.darkImported}, auto=${detection.theme.themeAuto}`);
32423
+ if (detection.warnings.length > 0) {
32424
+ lines.push(header("warnings", detection.warnings.length, ["message"]));
32425
+ for (const w of detection.warnings) {
32426
+ lines.push(" " + w);
32427
+ }
32428
+ }
32429
+ const help = buildContextualHelp({ command: "detect", status: detection.status });
32430
+ if (help.length > 0) {
32431
+ lines.push("", formatHelp(help));
32432
+ }
32433
+ return lines.join(`
32434
+ `);
32435
+ }
32436
+ function toonError(code, message, suggestions) {
32437
+ const lines = [`error[1]{code,message}: ${esc(code)},${esc(message)}`];
32438
+ if (suggestions?.length) {
32439
+ lines.push(header("suggestions", suggestions.length, ["value"]));
32440
+ for (const s of suggestions) {
32441
+ lines.push(" " + s);
32442
+ }
32443
+ }
32444
+ return lines.join(`
32445
+ `);
32446
+ }
32447
+
32448
+ // ../mcp/src/spec-formatters.ts
32449
+ var DIR_OVERRIDES2 = {
32450
+ QRCode: "qr-code"
32451
+ };
32452
+ function componentDir2(name) {
32453
+ return DIR_OVERRIDES2[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
32454
+ }
31689
32455
  function pad(str, width) {
31690
32456
  return str.length >= width ? str : str + " ".repeat(width - str.length);
31691
32457
  }
@@ -31698,9 +32464,9 @@ function indent(text, spaces) {
31698
32464
  function getSubpathImport(name, def) {
31699
32465
  if (def.import !== "@dryui/ui")
31700
32466
  return null;
31701
- return `import { ${name} } from '${def.import}/${componentDir(name)}'`;
32467
+ return `import { ${name} } from '${def.import}/${componentDir2(name)}'`;
31702
32468
  }
31703
- function findComponent(query, components) {
32469
+ function findComponent2(query, components) {
31704
32470
  const exact = components[query];
31705
32471
  if (exact)
31706
32472
  return { name: query, def: exact };
@@ -31875,9 +32641,23 @@ function formatSimple(name, def) {
31875
32641
  }
31876
32642
 
31877
32643
  // src/run.ts
31878
- function runCommand(result) {
32644
+ import { existsSync as existsSync2 } from "node:fs";
32645
+ function fileNotFound(filePath, toon) {
32646
+ if (existsSync2(filePath))
32647
+ return null;
32648
+ return {
32649
+ output: "",
32650
+ error: toon ? toonError("not-found", `File not found: ${filePath}`) : `File not found: ${filePath}`,
32651
+ exitCode: 1
32652
+ };
32653
+ }
32654
+ function runCommand(result, mode = "text") {
31879
32655
  if (result.error) {
31880
- console.error(result.error);
32656
+ if (mode === "text") {
32657
+ console.error(result.error);
32658
+ } else {
32659
+ console.log(result.error);
32660
+ }
31881
32661
  } else {
31882
32662
  console.log(result.output);
31883
32663
  }
@@ -31885,13 +32665,20 @@ function runCommand(result) {
31885
32665
  }
31886
32666
 
31887
32667
  // src/commands/info.ts
31888
- function findComponent2(spec, query) {
31889
- return findComponent(query, spec.components);
32668
+ function findComponent3(spec, query) {
32669
+ return findComponent2(query, spec.components);
31890
32670
  }
31891
- function getInfo(query, spec) {
31892
- const result = findComponent2(spec, query);
32671
+ function getInfo(query, spec, options) {
32672
+ const result = findComponent3(spec, query);
31893
32673
  if (!result) {
31894
32674
  const available = Object.keys(spec.components).sort();
32675
+ if (options?.toon) {
32676
+ return {
32677
+ output: "",
32678
+ error: toonError("not-found", `Unknown component: "${query}"`, available),
32679
+ exitCode: 1
32680
+ };
32681
+ }
31895
32682
  const error = [
31896
32683
  `Unknown component: "${query}"`,
31897
32684
  "",
@@ -31902,27 +32689,37 @@ function getInfo(query, spec) {
31902
32689
  return { output: "", error, exitCode: 1 };
31903
32690
  }
31904
32691
  const { name, def } = result;
32692
+ if (options?.toon) {
32693
+ return { output: toonComponent(name, def, { full: options?.full }), error: null, exitCode: 0 };
32694
+ }
31905
32695
  const output = def.compound ? formatCompound(name, def) : formatSimple(name, def);
31906
32696
  return { output, error: null, exitCode: 0 };
31907
32697
  }
31908
32698
  function runInfo(args, spec) {
31909
32699
  if (args.length === 0 || args[0] === "--help") {
31910
- console.log("Usage: dryui info <component>");
32700
+ console.log("Usage: dryui info <component> [--toon] [--full]");
31911
32701
  console.log("");
31912
32702
  console.log("Show API reference for a DryUI component or composed output.");
31913
32703
  console.log("");
32704
+ console.log("Options:");
32705
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
32706
+ console.log(" --full Include full example code (disables truncation)");
32707
+ console.log("");
31914
32708
  console.log("Examples:");
31915
32709
  console.log(" dryui info Button");
31916
32710
  console.log(" dryui info card # case-insensitive");
31917
- console.log(' dryui info "hero sections"');
32711
+ console.log(" dryui info Button --toon");
31918
32712
  process.exit(args[0] === "--help" ? 0 : 1);
31919
32713
  }
31920
- const query = args[0];
32714
+ const toon = args.includes("--toon");
32715
+ const full = args.includes("--full");
32716
+ const query = args.find((a) => !a.startsWith("--"));
31921
32717
  if (!query) {
31922
32718
  console.error("Error: missing component name");
31923
32719
  process.exit(1);
31924
32720
  }
31925
- runCommand(getInfo(query, spec));
32721
+ const mode = toon ? "toon" : "text";
32722
+ runCommand(getInfo(query, spec, { toon, full }), mode);
31926
32723
  }
31927
32724
 
31928
32725
  // src/format.ts
@@ -31982,7 +32779,7 @@ function formatWorkspaceReport(report, options) {
31982
32779
  // src/commands/project-planner.ts
31983
32780
  function importStatement(name, def, subpath = false) {
31984
32781
  if (subpath && def.import === "@dryui/ui") {
31985
- return `import { ${name} } from '${def.import}/${componentDir(name)}';`;
32782
+ return `import { ${name} } from '${def.import}/${componentDir2(name)}';`;
31986
32783
  }
31987
32784
  return `import { ${name} } from '${def.import}';`;
31988
32785
  }
@@ -32104,7 +32901,7 @@ function formatAddPlan(plan) {
32104
32901
  `);
32105
32902
  }
32106
32903
  function buildAddSnippet(query, spec, options = {}) {
32107
- const component = findComponent2(spec, query);
32904
+ const component = findComponent3(spec, query);
32108
32905
  if (!component) {
32109
32906
  return {
32110
32907
  output: "",
@@ -32120,331 +32917,6 @@ function buildAddSnippet(query, spec, options = {}) {
32120
32917
  };
32121
32918
  }
32122
32919
 
32123
- // ../mcp/src/project-planner.ts
32124
- import { existsSync, readFileSync, statSync } from "node:fs";
32125
- import { dirname, resolve } from "node:path";
32126
- var DIR_OVERRIDES2 = {
32127
- QRCode: "qr-code"
32128
- };
32129
- function componentDir2(name) {
32130
- return DIR_OVERRIDES2[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
32131
- }
32132
- function resolveStart(inputPath) {
32133
- const candidate = resolve(inputPath ?? process.cwd());
32134
- return existsSync(candidate) && statSync(candidate).isFile() ? dirname(candidate) : candidate;
32135
- }
32136
- function findUp(start, fileName) {
32137
- let current = start;
32138
- while (true) {
32139
- const candidate = resolve(current, fileName);
32140
- if (existsSync(candidate))
32141
- return candidate;
32142
- const parent = dirname(current);
32143
- if (parent === current)
32144
- return null;
32145
- current = parent;
32146
- }
32147
- }
32148
- function findUpAny(start, fileNames) {
32149
- let current = start;
32150
- while (true) {
32151
- for (const fileName of fileNames) {
32152
- const candidate = resolve(current, fileName);
32153
- if (existsSync(candidate))
32154
- return candidate;
32155
- }
32156
- const parent = dirname(current);
32157
- if (parent === current)
32158
- return null;
32159
- current = parent;
32160
- }
32161
- }
32162
- function detectPackageManager(root) {
32163
- if (!root)
32164
- return "unknown";
32165
- const lockfilePath = findUpAny(root, [
32166
- "bun.lock",
32167
- "bun.lockb",
32168
- "pnpm-lock.yaml",
32169
- "package-lock.json",
32170
- "yarn.lock"
32171
- ]);
32172
- if (!lockfilePath)
32173
- return "unknown";
32174
- if (lockfilePath.endsWith("bun.lock") || lockfilePath.endsWith("bun.lockb"))
32175
- return "bun";
32176
- if (lockfilePath.endsWith("pnpm-lock.yaml"))
32177
- return "pnpm";
32178
- if (lockfilePath.endsWith("package-lock.json"))
32179
- return "npm";
32180
- if (lockfilePath.endsWith("yarn.lock"))
32181
- return "yarn";
32182
- return "unknown";
32183
- }
32184
- function readPackageJson(packageJsonPath) {
32185
- if (!packageJsonPath)
32186
- return null;
32187
- return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
32188
- }
32189
- function getDependencyNames(pkg) {
32190
- return new Set([
32191
- ...Object.keys(pkg?.dependencies ?? {}),
32192
- ...Object.keys(pkg?.devDependencies ?? {})
32193
- ]);
32194
- }
32195
- function detectFramework(dependencyNames) {
32196
- if (dependencyNames.has("@sveltejs/kit"))
32197
- return "sveltekit";
32198
- if (dependencyNames.has("svelte"))
32199
- return "svelte";
32200
- return "unknown";
32201
- }
32202
- function hasImport(filePath, importPath) {
32203
- if (!filePath)
32204
- return false;
32205
- return readFileSync(filePath, "utf-8").includes(importPath);
32206
- }
32207
- function hasThemeAuto(appHtmlPath) {
32208
- if (!appHtmlPath)
32209
- return false;
32210
- return readFileSync(appHtmlPath, "utf-8").includes("theme-auto");
32211
- }
32212
- function hasAnyImport(filePaths, importPath) {
32213
- return filePaths.some((filePath) => hasImport(filePath, importPath));
32214
- }
32215
- function importsAppCss(rootLayoutPath) {
32216
- if (!rootLayoutPath)
32217
- return false;
32218
- const content = readFileSync(rootLayoutPath, "utf-8");
32219
- return content.includes("app.css");
32220
- }
32221
- function buildStatus(framework, hasPackageJson, stepsNeeded) {
32222
- if (!hasPackageJson || framework === "unknown")
32223
- return "unsupported";
32224
- return stepsNeeded === 0 ? "ready" : "partial";
32225
- }
32226
- function installCommand(packageManager) {
32227
- switch (packageManager) {
32228
- case "bun":
32229
- return "bun add @dryui/ui";
32230
- case "pnpm":
32231
- return "pnpm add @dryui/ui";
32232
- case "yarn":
32233
- return "yarn add @dryui/ui";
32234
- default:
32235
- return "npm install @dryui/ui";
32236
- }
32237
- }
32238
- function buildThemeImportSnippet(spec) {
32239
- return [
32240
- '<script lang="ts">',
32241
- ` import '${spec.themeImports.default}';`,
32242
- ` import '${spec.themeImports.dark}';`,
32243
- "</script>"
32244
- ].join(`
32245
- `);
32246
- }
32247
- function buildThemeImportCssSnippet(spec) {
32248
- return [`@import '${spec.themeImports.default}';`, `@import '${spec.themeImports.dark}';`].join(`
32249
- `);
32250
- }
32251
- function getSuggestedTarget(root, explicitTarget) {
32252
- if (explicitTarget)
32253
- return resolve(root ?? process.cwd(), explicitTarget);
32254
- if (!root)
32255
- return null;
32256
- const rootPage = resolve(root, "src/routes/+page.svelte");
32257
- return existsSync(rootPage) ? rootPage : null;
32258
- }
32259
- function getImportStatement(name, component, subpath = false) {
32260
- if (subpath && component.import === "@dryui/ui") {
32261
- return `import { ${name} } from '${component.import}/${componentDir2(name)}';`;
32262
- }
32263
- return `import { ${name} } from '${component.import}';`;
32264
- }
32265
- function findComponent3(spec, query) {
32266
- const exact = spec.components[query];
32267
- if (exact)
32268
- return { name: query, def: exact };
32269
- const lower = query.toLowerCase();
32270
- for (const [name, def] of Object.entries(spec.components)) {
32271
- if (name.toLowerCase() === lower)
32272
- return { name, def };
32273
- }
32274
- return null;
32275
- }
32276
- function detectProject(spec, inputPath) {
32277
- const start = resolveStart(inputPath);
32278
- const packageJsonPath = findUp(start, "package.json");
32279
- const root = packageJsonPath ? dirname(packageJsonPath) : null;
32280
- const dependencyNames = getDependencyNames(readPackageJson(packageJsonPath));
32281
- const framework = detectFramework(dependencyNames);
32282
- const appHtmlPath = root ? resolve(root, "src/app.html") : null;
32283
- const appCssPath = root ? resolve(root, "src/app.css") : null;
32284
- const rootLayoutPath = root ? resolve(root, "src/routes/+layout.svelte") : null;
32285
- const rootPagePath = root ? resolve(root, "src/routes/+page.svelte") : null;
32286
- const appHtml = appHtmlPath && existsSync(appHtmlPath) ? appHtmlPath : null;
32287
- const appCss = appCssPath && existsSync(appCssPath) ? appCssPath : null;
32288
- const rootLayout = rootLayoutPath && existsSync(rootLayoutPath) ? rootLayoutPath : null;
32289
- const rootPage = rootPagePath && existsSync(rootPagePath) ? rootPagePath : null;
32290
- const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
32291
- const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
32292
- const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
32293
- const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!(appHtml && hasThemeAuto(appHtml)));
32294
- const warnings = [];
32295
- if (!packageJsonPath)
32296
- warnings.push("No package.json found above the provided path.");
32297
- if (framework === "unknown")
32298
- warnings.push("DryUI planning currently targets Svelte and SvelteKit projects.");
32299
- return {
32300
- inputPath: start,
32301
- root,
32302
- packageJsonPath,
32303
- framework,
32304
- packageManager: detectPackageManager(root),
32305
- status: buildStatus(framework, Boolean(packageJsonPath), stepsNeeded),
32306
- dependencies: {
32307
- ui: dependencyNames.has("@dryui/ui"),
32308
- primitives: dependencyNames.has("@dryui/primitives")
32309
- },
32310
- files: {
32311
- appHtml,
32312
- appCss,
32313
- rootLayout,
32314
- rootPage
32315
- },
32316
- theme: {
32317
- defaultImported,
32318
- darkImported,
32319
- themeAuto: hasThemeAuto(appHtml)
32320
- },
32321
- warnings
32322
- };
32323
- }
32324
- function planInstall(spec, inputPath) {
32325
- const detection = detectProject(spec, inputPath);
32326
- const steps = [];
32327
- if (detection.status === "unsupported") {
32328
- steps.push({
32329
- kind: "blocked",
32330
- status: "blocked",
32331
- title: "Project detection incomplete",
32332
- description: detection.warnings.join(" ") || "DryUI install planning requires a Svelte or SvelteKit project with a package.json."
32333
- });
32334
- return { detection, steps };
32335
- }
32336
- if (!detection.dependencies.ui) {
32337
- steps.push({
32338
- kind: "install-package",
32339
- status: "pending",
32340
- title: "Install @dryui/ui",
32341
- description: "Add the styled DryUI package to the current project.",
32342
- command: installCommand(detection.packageManager)
32343
- });
32344
- }
32345
- if (!detection.theme.defaultImported || !detection.theme.darkImported) {
32346
- if (detection.files.appCss && detection.files.rootLayout && importsAppCss(detection.files.rootLayout)) {
32347
- steps.push({
32348
- kind: "edit-file",
32349
- status: "pending",
32350
- title: "Add theme imports to app.css",
32351
- description: "Ensure the app-level stylesheet imports both default and dark DryUI themes.",
32352
- path: detection.files.appCss,
32353
- snippet: buildThemeImportCssSnippet(spec)
32354
- });
32355
- } else if (!detection.files.rootLayout) {
32356
- const path = detection.root ? resolve(detection.root, "src/routes/+layout.svelte") : null;
32357
- steps.push({
32358
- kind: "create-file",
32359
- status: "pending",
32360
- title: "Create root layout with theme imports",
32361
- description: "Create src/routes/+layout.svelte and add the required DryUI theme imports.",
32362
- ...path ? { path } : {},
32363
- snippet: buildThemeImportSnippet(spec)
32364
- });
32365
- } else {
32366
- steps.push({
32367
- kind: "edit-file",
32368
- status: "pending",
32369
- title: "Add theme imports to the root layout",
32370
- description: "Ensure the root layout imports both default and dark DryUI themes.",
32371
- path: detection.files.rootLayout,
32372
- snippet: buildThemeImportSnippet(spec)
32373
- });
32374
- }
32375
- }
32376
- if (!detection.files.appHtml) {
32377
- steps.push({
32378
- kind: "blocked",
32379
- status: "blocked",
32380
- title: "app.html not found",
32381
- description: "DryUI expects src/app.html so the document can default to theme-auto mode."
32382
- });
32383
- } else if (!detection.theme.themeAuto) {
32384
- steps.push({
32385
- kind: "edit-file",
32386
- status: "pending",
32387
- title: "Set html theme mode to auto",
32388
- description: 'Add class="theme-auto" to the html element in src/app.html.',
32389
- path: detection.files.appHtml,
32390
- snippet: '<html class="theme-auto">'
32391
- });
32392
- }
32393
- if (steps.length === 0) {
32394
- steps.push({
32395
- kind: "note",
32396
- status: "done",
32397
- title: "DryUI install plan is complete",
32398
- description: "The project already has @dryui/ui, theme imports, and theme-auto configured."
32399
- });
32400
- }
32401
- return { detection, steps };
32402
- }
32403
- function planAdd(spec, query, options = {}) {
32404
- const installPlan = planInstall(spec, options.cwd);
32405
- const component = findComponent3(spec, query);
32406
- if (component) {
32407
- const target = getSuggestedTarget(installPlan.detection.root, options.target);
32408
- const steps = [];
32409
- const warnings = [...installPlan.detection.warnings];
32410
- if (installPlan.steps.some((step) => step.status === "pending" || step.status === "blocked")) {
32411
- steps.push({
32412
- kind: "note",
32413
- status: "info",
32414
- title: "Complete install plan first",
32415
- description: "Apply the install plan before inserting DryUI components into project files."
32416
- });
32417
- }
32418
- steps.push(target ? {
32419
- kind: "edit-file",
32420
- status: "pending",
32421
- title: "Insert component into the target file",
32422
- description: "Add the import and snippet below to the chosen Svelte file.",
32423
- path: target,
32424
- snippet: `${getImportStatement(component.name, component.def, options.subpath)}
32425
-
32426
- ${component.def.example}`
32427
- } : {
32428
- kind: "note",
32429
- status: "info",
32430
- title: "Choose a target Svelte file",
32431
- description: "No root page was found. Pick a target file and reuse the import and snippet in this plan."
32432
- });
32433
- return {
32434
- detection: installPlan.detection,
32435
- installPlan,
32436
- targetType: "component",
32437
- name: component.name,
32438
- importStatement: getImportStatement(component.name, component.def, options.subpath),
32439
- snippet: component.def.example,
32440
- target,
32441
- steps,
32442
- warnings
32443
- };
32444
- }
32445
- throw new Error(`Unknown component: "${query}"`);
32446
- }
32447
-
32448
32920
  // src/commands/add.ts
32449
32921
  function parseProjectInput(positionals, spec) {
32450
32922
  const first = positionals[0];
@@ -32456,8 +32928,8 @@ function parseProjectInput(positionals, spec) {
32456
32928
  return { query: first };
32457
32929
  }
32458
32930
  if (positionals.length === 2) {
32459
- const firstIsQuery = Boolean(findComponent2(spec, first));
32460
- const secondIsQuery = Boolean(findComponent2(spec, second));
32931
+ const firstIsQuery = Boolean(findComponent3(spec, first));
32932
+ const secondIsQuery = Boolean(findComponent3(spec, second));
32461
32933
  if (firstIsQuery && !secondIsQuery) {
32462
32934
  return { query: first, cwd: second };
32463
32935
  }
@@ -32584,6 +33056,7 @@ function runAdd(args, spec) {
32584
33056
  // src/commands/detect.ts
32585
33057
  function parseDetectArgs(args) {
32586
33058
  let json = false;
33059
+ let toon = false;
32587
33060
  let path;
32588
33061
  for (let index = 0;index < args.length; index++) {
32589
33062
  const arg = args[index];
@@ -32593,6 +33066,10 @@ function parseDetectArgs(args) {
32593
33066
  json = true;
32594
33067
  continue;
32595
33068
  }
33069
+ if (arg === "--toon") {
33070
+ toon = true;
33071
+ continue;
33072
+ }
32596
33073
  if (arg.startsWith("--")) {
32597
33074
  continue;
32598
33075
  }
@@ -32600,24 +33077,29 @@ function parseDetectArgs(args) {
32600
33077
  path = arg;
32601
33078
  }
32602
33079
  }
32603
- return { json, path };
33080
+ return { json, toon, path };
32604
33081
  }
32605
33082
  function getDetect(inputPath, spec, options = {}) {
32606
33083
  const detection = detectProject(spec, inputPath);
33084
+ if (options.toon) {
33085
+ return { output: toonProjectDetection(detection), error: null, exitCode: 0 };
33086
+ }
32607
33087
  return options.json ? { output: JSON.stringify(detection, null, 2), error: null, exitCode: 0 } : { output: formatProjectDetection(detection), error: null, exitCode: 0 };
32608
33088
  }
32609
33089
  function runDetect(args, spec) {
32610
33090
  if (args[0] === "--help") {
32611
- console.log("Usage: dryui detect [--json] [path]");
33091
+ console.log("Usage: dryui detect [--json] [--toon] [path]");
32612
33092
  console.log("");
32613
33093
  console.log("Detect the current DryUI project setup.");
32614
33094
  console.log("");
32615
33095
  console.log("Options:");
32616
33096
  console.log(" --json Output raw JSON instead of formatted text");
33097
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
32617
33098
  process.exit(0);
32618
33099
  }
32619
- const { json, path } = parseDetectArgs(args);
32620
- runCommand(getDetect(path, spec, { json }));
33100
+ const { json, toon, path } = parseDetectArgs(args);
33101
+ const mode = toon ? "toon" : json ? "json" : "text";
33102
+ runCommand(getDetect(path, spec, { json, toon }), mode);
32621
33103
  }
32622
33104
 
32623
33105
  // src/commands/install.ts
@@ -32724,7 +33206,14 @@ function groupByCategory(spec) {
32724
33206
  }
32725
33207
  return groups;
32726
33208
  }
32727
- function getList(category, spec, options = {}) {
33209
+ function getList(category, spec, options) {
33210
+ if (options?.toon) {
33211
+ return {
33212
+ output: toonComponentList(spec.components, category ?? undefined),
33213
+ error: null,
33214
+ exitCode: 0
33215
+ };
33216
+ }
32728
33217
  const sections = [];
32729
33218
  const validCategories = getCategories(spec);
32730
33219
  if (category && !validCategories.includes(category)) {
@@ -32758,18 +33247,20 @@ function getList(category, spec, options = {}) {
32758
33247
  }
32759
33248
  function runList(args, spec) {
32760
33249
  if (args[0] === "--help") {
32761
- console.log("Usage: dryui list [--category <category>]");
33250
+ console.log("Usage: dryui list [--category <category>] [--toon]");
32762
33251
  console.log("");
32763
33252
  console.log("List DryUI components.");
32764
33253
  console.log("");
32765
33254
  console.log("Options:");
32766
33255
  console.log(" --category <cat> Filter by category");
33256
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
32767
33257
  console.log("");
32768
33258
  console.log("Categories:");
32769
33259
  const cats = getCategories(spec);
32770
33260
  console.log(` ${cats.join(", ")}`);
32771
33261
  process.exit(0);
32772
33262
  }
33263
+ const toon = args.includes("--toon");
32773
33264
  let filterCategory = null;
32774
33265
  const catIdx = args.indexOf("--category");
32775
33266
  if (catIdx !== -1) {
@@ -32780,11 +33271,12 @@ function runList(args, spec) {
32780
33271
  }
32781
33272
  filterCategory = catValue.toLowerCase();
32782
33273
  }
32783
- runCommand(getList(filterCategory, spec));
33274
+ const mode = toon ? "toon" : "text";
33275
+ runCommand(getList(filterCategory, spec, { toon }), mode);
32784
33276
  }
32785
33277
 
32786
33278
  // src/commands/review.ts
32787
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
33279
+ import { readFileSync as readFileSync2 } from "node:fs";
32788
33280
 
32789
33281
  // ../mcp/src/utils.ts
32790
33282
  function buildLineOffsets(text) {
@@ -33485,12 +33977,19 @@ function reviewComponent(code, spec, filename) {
33485
33977
 
33486
33978
  // src/commands/review.ts
33487
33979
  function getReview(filePath, spec, options) {
33488
- if (!existsSync2(filePath)) {
33489
- return { output: "", error: `File not found: ${filePath}`, exitCode: 1 };
33490
- }
33980
+ const missing = fileNotFound(filePath, options?.toon);
33981
+ if (missing)
33982
+ return missing;
33491
33983
  const code = readFileSync2(filePath, "utf-8");
33492
33984
  const filename = filePath.split("/").pop();
33493
33985
  const result = reviewComponent(code, spec, filename);
33986
+ if (options?.toon) {
33987
+ return {
33988
+ output: toonReviewResult(result),
33989
+ error: null,
33990
+ exitCode: result.issues.some((i) => i.severity === "error") ? 1 : 0
33991
+ };
33992
+ }
33494
33993
  if (options?.json) {
33495
33994
  return {
33496
33995
  output: JSON.stringify(result, null, 2),
@@ -33517,7 +34016,7 @@ function getReview(filePath, spec, options) {
33517
34016
  }
33518
34017
  function runReview(args, spec) {
33519
34018
  if (args.length === 0 || args[0] === "--help") {
33520
- console.log("Usage: dryui review [--json] <file.svelte>");
34019
+ console.log("Usage: dryui review [--json] [--toon] <file.svelte>");
33521
34020
  console.log("");
33522
34021
  console.log("Validate a Svelte component against the DryUI spec.");
33523
34022
  console.log("Checks for incorrect component usage, missing props,");
@@ -33525,20 +34024,23 @@ function runReview(args, spec) {
33525
34024
  console.log("");
33526
34025
  console.log("Options:");
33527
34026
  console.log(" --json Output raw JSON instead of formatted text");
34027
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
33528
34028
  process.exit(args[0] === "--help" ? 0 : 1);
33529
34029
  }
33530
34030
  const jsonMode = args.includes("--json");
33531
- const fileArgs = args.filter((a) => a !== "--json");
34031
+ const toon = args.includes("--toon");
34032
+ const fileArgs = args.filter((a) => !a.startsWith("--"));
33532
34033
  const filePath = fileArgs[0];
33533
34034
  if (!filePath) {
33534
34035
  console.error("Error: missing file path");
33535
34036
  process.exit(1);
33536
34037
  }
33537
- runCommand(getReview(filePath, spec, { json: jsonMode }));
34038
+ const mode = toon ? "toon" : jsonMode ? "json" : "text";
34039
+ runCommand(getReview(filePath, spec, { json: jsonMode, toon }), mode);
33538
34040
  }
33539
34041
 
33540
34042
  // src/commands/diagnose.ts
33541
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
34043
+ import { readFileSync as readFileSync3 } from "node:fs";
33542
34044
 
33543
34045
  // ../mcp/src/theme-checker.ts
33544
34046
  function capture(match, index) {
@@ -34294,11 +34796,18 @@ function diagnoseTheme(css, spec) {
34294
34796
 
34295
34797
  // src/commands/diagnose.ts
34296
34798
  function getDiagnose(filePath, spec, options) {
34297
- if (!existsSync3(filePath)) {
34298
- return { output: "", error: `File not found: ${filePath}`, exitCode: 1 };
34299
- }
34799
+ const missing = fileNotFound(filePath, options?.toon);
34800
+ if (missing)
34801
+ return missing;
34300
34802
  const css = readFileSync3(filePath, "utf-8");
34301
34803
  const result = diagnoseTheme(css, spec);
34804
+ if (options?.toon) {
34805
+ return {
34806
+ output: toonDiagnoseResult(result),
34807
+ error: null,
34808
+ exitCode: result.issues.some((i) => i.severity === "error") ? 1 : 0
34809
+ };
34810
+ }
34302
34811
  if (options?.json) {
34303
34812
  return {
34304
34813
  output: JSON.stringify(result, null, 2),
@@ -34331,26 +34840,29 @@ function getDiagnose(filePath, spec, options) {
34331
34840
  }
34332
34841
  function runDiagnose(args, spec) {
34333
34842
  if (args.length === 0 || args[0] === "--help") {
34334
- console.log("Usage: dryui diagnose [--json] <file.css>");
34843
+ console.log("Usage: dryui diagnose [--json] [--toon] <file.css>");
34335
34844
  console.log("");
34336
34845
  console.log("Validate theme CSS for missing tokens, value errors,");
34337
34846
  console.log("contrast issues, and component token problems.");
34338
34847
  console.log("");
34339
34848
  console.log("Options:");
34340
34849
  console.log(" --json Output raw JSON instead of formatted text");
34850
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
34341
34851
  process.exit(args[0] === "--help" ? 0 : 1);
34342
34852
  }
34343
34853
  const jsonMode = args.includes("--json");
34344
- const fileArgs = args.filter((a) => a !== "--json");
34854
+ const toon = args.includes("--toon");
34855
+ const fileArgs = args.filter((a) => !a.startsWith("--"));
34345
34856
  const filePath = fileArgs[0];
34346
34857
  if (!filePath) {
34347
34858
  console.error("Error: missing file path");
34348
34859
  process.exit(1);
34349
34860
  }
34350
- runCommand(getDiagnose(filePath, spec, { json: jsonMode }));
34861
+ const mode = toon ? "toon" : jsonMode ? "json" : "text";
34862
+ runCommand(getDiagnose(filePath, spec, { json: jsonMode, toon }), mode);
34351
34863
  }
34352
34864
 
34353
- // src/commands/compose.ts
34865
+ // ../mcp/dist/composition-search.js
34354
34866
  var STOP_WORDS = new Set([
34355
34867
  "a",
34356
34868
  "an",
@@ -34416,81 +34928,58 @@ function scoreText(tokens, text) {
34416
34928
  const lower = text.toLowerCase();
34417
34929
  return tokens.reduce((score, t) => score + (lower.includes(t) ? 1 : 0), 0);
34418
34930
  }
34419
- function getCompose(query, spec) {
34420
- if (!spec.composition) {
34421
- return {
34422
- output: "",
34423
- error: "No composition data available. Rebuild the MCP package.",
34424
- exitCode: 1
34425
- };
34426
- }
34931
+ function searchComposition(composition, query) {
34427
34932
  const q = query.toLowerCase();
34428
34933
  const tokens = tokenize(query);
34429
34934
  const exactComponentMatches = [];
34430
34935
  const exactRecipeMatches = [];
34431
- for (const comp of Object.values(spec.composition.components)) {
34936
+ for (const comp of Object.values(composition.components)) {
34432
34937
  if (comp.component.toLowerCase().includes(q) || comp.useWhen.toLowerCase().includes(q) || comp.alternatives.some((a) => a.component.toLowerCase().includes(q) || a.useWhen.toLowerCase().includes(q)) || comp.antiPatterns.some((ap) => ap.pattern.toLowerCase().includes(q))) {
34433
34938
  exactComponentMatches.push(comp);
34434
34939
  }
34435
34940
  }
34436
- for (const recipe of Object.values(spec.composition.recipes)) {
34941
+ for (const recipe of Object.values(composition.recipes)) {
34437
34942
  if (recipe.name.toLowerCase().includes(q) || recipe.description.toLowerCase().includes(q) || recipe.tags.some((t) => t.toLowerCase().includes(q)) || recipe.components.some((c) => c.toLowerCase().includes(q))) {
34438
34943
  exactRecipeMatches.push(recipe);
34439
34944
  }
34440
34945
  }
34441
- let componentMatches;
34442
- let recipeMatches;
34443
34946
  if (exactComponentMatches.length || exactRecipeMatches.length) {
34444
- componentMatches = exactComponentMatches;
34445
- recipeMatches = exactRecipeMatches;
34446
- } else if (tokens.length) {
34447
- const scoredComponents = [];
34448
- for (const comp of Object.values(spec.composition.components)) {
34449
- const corpus = [
34450
- comp.component,
34451
- comp.useWhen,
34452
- ...comp.alternatives.flatMap((a) => [a.component, a.useWhen]),
34453
- ...comp.antiPatterns.map((ap) => ap.pattern),
34454
- ...comp.combinesWith
34455
- ].join(" ");
34456
- const score = scoreText(tokens, corpus);
34457
- if (score > 0)
34458
- scoredComponents.push({ comp, score });
34459
- }
34460
- const scoredRecipes = [];
34461
- for (const recipe of Object.values(spec.composition.recipes)) {
34462
- const corpus = [recipe.name, recipe.description, ...recipe.tags, ...recipe.components].join(" ");
34463
- const score = scoreText(tokens, corpus);
34464
- if (score > 0)
34465
- scoredRecipes.push({ recipe, score });
34466
- }
34467
- scoredComponents.sort((a, b) => b.score - a.score);
34468
- scoredRecipes.sort((a, b) => b.score - a.score);
34469
- const minScore = Math.max(1, Math.floor(tokens.length * 0.3));
34470
- componentMatches = scoredComponents.filter((s) => s.score >= minScore).slice(0, 10).map((s) => s.comp);
34471
- recipeMatches = scoredRecipes.filter((s) => s.score >= minScore).slice(0, 5).map((s) => s.recipe);
34472
- } else {
34473
- componentMatches = [];
34474
- recipeMatches = [];
34475
- }
34476
- if (!componentMatches.length && !recipeMatches.length) {
34477
- return {
34478
- output: "",
34479
- error: `No composition guidance found for "${query}".
34480
-
34481
- Try:
34482
- - A component name (e.g. "DatePicker", "Avatar")
34483
- - A UI concept (e.g. "date input", "image placeholder")
34484
- - A pattern name (e.g. "search-form", "dashboard-page")`,
34485
- exitCode: 1
34486
- };
34487
- }
34947
+ return { componentMatches: exactComponentMatches, recipeMatches: exactRecipeMatches };
34948
+ }
34949
+ if (!tokens.length) {
34950
+ return { componentMatches: [], recipeMatches: [] };
34951
+ }
34952
+ const scoredComponents = [];
34953
+ for (const comp of Object.values(composition.components)) {
34954
+ const corpus = [
34955
+ comp.component,
34956
+ comp.useWhen,
34957
+ ...comp.alternatives.flatMap((a) => [a.component, a.useWhen]),
34958
+ ...comp.antiPatterns.map((ap) => ap.pattern),
34959
+ ...comp.combinesWith
34960
+ ].join(" ");
34961
+ const score = scoreText(tokens, corpus);
34962
+ if (score > 0)
34963
+ scoredComponents.push({ comp, score });
34964
+ }
34965
+ const scoredRecipes = [];
34966
+ for (const recipe of Object.values(composition.recipes)) {
34967
+ const corpus = [recipe.name, recipe.description, ...recipe.tags, ...recipe.components].join(" ");
34968
+ const score = scoreText(tokens, corpus);
34969
+ if (score > 0)
34970
+ scoredRecipes.push({ recipe, score });
34971
+ }
34972
+ scoredComponents.sort((a, b) => b.score - a.score);
34973
+ scoredRecipes.sort((a, b) => b.score - a.score);
34974
+ const minScore = Math.max(1, Math.floor(tokens.length * 0.3));
34975
+ return {
34976
+ componentMatches: scoredComponents.filter((s) => s.score >= minScore).slice(0, 10).map((s) => s.comp),
34977
+ recipeMatches: scoredRecipes.filter((s) => s.score >= minScore).slice(0, 5).map((s) => s.recipe)
34978
+ };
34979
+ }
34980
+ function formatCompositionResult(results) {
34488
34981
  const lines = [];
34489
- lines.push("⚠ SETUP: Root +layout.svelte must import '@dryui/ui/themes/default.css'");
34490
- lines.push(' and dark.css. app.html needs <html class="theme-auto">.');
34491
- lines.push(' Not set up? Run: dryui compose "app shell"');
34492
- lines.push("");
34493
- for (const comp of componentMatches) {
34982
+ for (const comp of results.componentMatches) {
34494
34983
  lines.push(`── ${comp.component} ──────────────────────────────`);
34495
34984
  lines.push(`[DEV GUIDANCE — do not render as page content]`);
34496
34985
  lines.push(`Use: ${comp.component} — ${comp.useWhen}`);
@@ -34513,7 +35002,7 @@ Try:
34513
35002
  }
34514
35003
  lines.push("");
34515
35004
  }
34516
- for (const recipe of recipeMatches) {
35005
+ for (const recipe of results.recipeMatches) {
34517
35006
  lines.push(`── Recipe: ${recipe.name} ──────────────────────────────`);
34518
35007
  lines.push(`[DEV GUIDANCE — do not render as page content]`);
34519
35008
  lines.push(recipe.description);
@@ -34523,29 +35012,88 @@ Try:
34523
35012
  lines.push(recipe.snippet);
34524
35013
  lines.push("");
34525
35014
  }
34526
- return { output: lines.join(`
34527
- `).trimEnd(), error: null, exitCode: 0 };
35015
+ return lines.join(`
35016
+ `);
35017
+ }
35018
+
35019
+ // src/commands/compose.ts
35020
+ var SETUP_PREAMBLE = [
35021
+ "⚠ SETUP: Root +layout.svelte must import '@dryui/ui/themes/default.css'",
35022
+ ' and dark.css. app.html needs <html class="theme-auto">.',
35023
+ ' Not set up? Run: dryui compose "app shell"',
35024
+ ""
35025
+ ].join(`
35026
+ `);
35027
+ function getCompose(query, spec, options) {
35028
+ if (!spec.composition) {
35029
+ return {
35030
+ output: "",
35031
+ error: "No composition data available. Rebuild the MCP package.",
35032
+ exitCode: 1
35033
+ };
35034
+ }
35035
+ const results = searchComposition(spec.composition, query);
35036
+ if (!results.componentMatches.length && !results.recipeMatches.length) {
35037
+ if (options?.toon) {
35038
+ return {
35039
+ output: "",
35040
+ error: toonError("no-results", `No composition guidance for "${query}"`, [
35041
+ "Try a component name (DatePicker, Avatar)",
35042
+ "Try a UI concept (date input, image placeholder)",
35043
+ "Try a pattern name (search-form, dashboard-page)"
35044
+ ]),
35045
+ exitCode: 1
35046
+ };
35047
+ }
35048
+ return {
35049
+ output: "",
35050
+ error: `No composition guidance found for "${query}".
35051
+
35052
+ Try:
35053
+ - A component name (e.g. "DatePicker", "Avatar")
35054
+ - A UI concept (e.g. "date input", "image placeholder")
35055
+ - A pattern name (e.g. "search-form", "dashboard-page")`,
35056
+ exitCode: 1
35057
+ };
35058
+ }
35059
+ if (options?.toon) {
35060
+ return {
35061
+ output: toonComposition(results, { full: options?.full }),
35062
+ error: null,
35063
+ exitCode: 0
35064
+ };
35065
+ }
35066
+ const output = SETUP_PREAMBLE + `
35067
+ ` + formatCompositionResult(results);
35068
+ return { output: output.trimEnd(), error: null, exitCode: 0 };
34528
35069
  }
34529
35070
  function runCompose(args, spec) {
34530
35071
  if (args.length === 0 || args[0] === "--help") {
34531
- console.log("Usage: dryui compose <query>");
35072
+ console.log("Usage: dryui compose <query> [--toon] [--full]");
34532
35073
  console.log("");
34533
35074
  console.log("Look up composition guidance for building UIs with DryUI.");
34534
35075
  console.log("Returns ranked component alternatives, anti-patterns, and recipes.");
34535
35076
  console.log("");
35077
+ console.log("Options:");
35078
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
35079
+ console.log(" --full Include full code snippets (disables truncation)");
35080
+ console.log("");
34536
35081
  console.log("Examples:");
34537
35082
  console.log(' dryui compose "search form"');
34538
35083
  console.log(' dryui compose "hotel card"');
34539
- console.log(' dryui compose "travel booking"');
35084
+ console.log(' dryui compose "travel booking" --toon');
34540
35085
  process.exit(args[0] === "--help" ? 0 : 1);
34541
35086
  }
34542
- const query = args.join(" ").trim();
34543
- runCommand(getCompose(query, spec));
35087
+ const toon = args.includes("--toon");
35088
+ const full = args.includes("--full");
35089
+ const query = args.filter((a) => !a.startsWith("--")).join(" ").trim();
35090
+ const mode = toon ? "toon" : "text";
35091
+ runCommand(getCompose(query, spec, { toon, full }), mode);
34544
35092
  }
34545
35093
 
34546
35094
  // ../mcp/src/workspace-audit.ts
34547
35095
  import { execFileSync } from "node:child_process";
34548
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, realpathSync, statSync as statSync2 } from "node:fs";
35096
+ import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync, realpathSync, statSync as statSync2 } from "node:fs";
34549
35097
  import { dirname as dirname2, relative, resolve as resolve2 } from "node:path";
34550
35098
  var DEFAULT_INCLUDE = [
34551
35099
  "packages/ui",
@@ -34578,7 +35126,7 @@ function normalizePath(path) {
34578
35126
  }
34579
35127
  function resolveRoot(inputPath) {
34580
35128
  const candidate = resolve2(inputPath ?? process.cwd());
34581
- if (existsSync4(candidate) && statSync2(candidate).isFile()) {
35129
+ if (existsSync3(candidate) && statSync2(candidate).isFile()) {
34582
35130
  return dirname2(candidate);
34583
35131
  }
34584
35132
  return candidate;
@@ -34649,7 +35197,7 @@ function collectChangedFiles(root) {
34649
35197
  }
34650
35198
  }
34651
35199
  function defaultInclude(root) {
34652
- const included = DEFAULT_INCLUDE.filter((entry) => existsSync4(resolve2(root, entry)));
35200
+ const included = DEFAULT_INCLUDE.filter((entry) => existsSync3(resolve2(root, entry)));
34653
35201
  return included.length > 0 ? included : ["."];
34654
35202
  }
34655
35203
  function isDefaultExcluded(path) {
@@ -34823,6 +35371,8 @@ function parseWorkspaceArgs(args) {
34823
35371
  const exclude = [];
34824
35372
  let path;
34825
35373
  let json = false;
35374
+ let toon = false;
35375
+ let full = false;
34826
35376
  let changed = false;
34827
35377
  let maxSeverity = "info";
34828
35378
  for (let index = 0;index < args.length; index++) {
@@ -34833,6 +35383,14 @@ function parseWorkspaceArgs(args) {
34833
35383
  json = true;
34834
35384
  continue;
34835
35385
  }
35386
+ if (arg === "--toon") {
35387
+ toon = true;
35388
+ continue;
35389
+ }
35390
+ if (arg === "--full") {
35391
+ full = true;
35392
+ continue;
35393
+ }
34836
35394
  if (arg === "--changed") {
34837
35395
  changed = true;
34838
35396
  continue;
@@ -34861,7 +35419,7 @@ function parseWorkspaceArgs(args) {
34861
35419
  if (!path)
34862
35420
  path = arg;
34863
35421
  }
34864
- return { path, options: { json, include, exclude, maxSeverity, changed } };
35422
+ return { path, options: { json, toon, full, include, exclude, maxSeverity, changed } };
34865
35423
  }
34866
35424
 
34867
35425
  // src/commands/doctor.ts
@@ -34874,6 +35432,13 @@ function getDoctor(inputPath, spec, options = {}) {
34874
35432
  ...options.maxSeverity ? { maxSeverity: options.maxSeverity } : {},
34875
35433
  ...options.changed === undefined ? {} : { changed: options.changed }
34876
35434
  });
35435
+ if (options.toon) {
35436
+ return {
35437
+ output: toonWorkspaceReport(report, { title: "doctor", command: "doctor", full: options.full }),
35438
+ error: null,
35439
+ exitCode: 0
35440
+ };
35441
+ }
34877
35442
  return {
34878
35443
  output: formatWorkspaceReport(report, {
34879
35444
  title: "DryUI workspace doctor",
@@ -34893,13 +35458,18 @@ function getDoctor(inputPath, spec, options = {}) {
34893
35458
  }
34894
35459
  function runDoctor(args, spec) {
34895
35460
  if (args[0] === "--help") {
34896
- console.log("Usage: dryui doctor [path] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
35461
+ console.log("Usage: dryui doctor [path] [--toon] [--full] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
34897
35462
  console.log("");
34898
35463
  console.log("Inspect workspace health with human-readable findings.");
35464
+ console.log("");
35465
+ console.log("Options:");
35466
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
35467
+ console.log(" --full Show all findings (disables truncation at 50)");
34899
35468
  process.exit(0);
34900
35469
  }
34901
35470
  const { path, options } = parseWorkspaceArgs(args);
34902
- runCommand(getDoctor(path, spec, options));
35471
+ const mode = options.toon ? "toon" : "text";
35472
+ runCommand(getDoctor(path, spec, options), mode);
34903
35473
  }
34904
35474
 
34905
35475
  // src/commands/lint.ts
@@ -34912,6 +35482,13 @@ function getLint(inputPath, spec, options = {}) {
34912
35482
  ...options.changed === undefined ? {} : { changed: options.changed },
34913
35483
  ...options.maxSeverity ? { maxSeverity: options.maxSeverity } : {}
34914
35484
  });
35485
+ if (options.toon) {
35486
+ return {
35487
+ output: toonWorkspaceReport(report, { title: "lint", command: "lint", full: options.full }),
35488
+ error: null,
35489
+ exitCode: report.findings.length > 0 ? 1 : 0
35490
+ };
35491
+ }
34915
35492
  if (options.json) {
34916
35493
  return {
34917
35494
  output: JSON.stringify(report, null, 2),
@@ -34934,23 +35511,28 @@ function getLint(inputPath, spec, options = {}) {
34934
35511
  }
34935
35512
  function runLint(args, spec) {
34936
35513
  if (args[0] === "--help") {
34937
- console.log("Usage: dryui lint [path] [--json] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
35514
+ console.log("Usage: dryui lint [path] [--json] [--toon] [--full] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
34938
35515
  console.log("");
34939
35516
  console.log("Print deterministic workspace findings.");
35517
+ console.log("");
35518
+ console.log("Options:");
35519
+ console.log(" --toon Output in TOON format (token-optimized for agents)");
35520
+ console.log(" --full Show all findings (disables truncation at 50)");
34940
35521
  process.exit(0);
34941
35522
  }
34942
35523
  const { path, options } = parseWorkspaceArgs(args);
34943
- runCommand(getLint(path, spec, options));
35524
+ const mode = options.toon ? "toon" : options.json ? "json" : "text";
35525
+ runCommand(getLint(path, spec, options), mode);
34944
35526
  }
34945
35527
 
34946
35528
  // src/commands/feedback.ts
34947
- import { existsSync as existsSync6 } from "node:fs";
35529
+ import { existsSync as existsSync5 } from "node:fs";
34948
35530
  import { spawnSync } from "node:child_process";
34949
35531
  import { dirname as dirname4, resolve as resolve3 } from "node:path";
34950
35532
  import { fileURLToPath } from "node:url";
34951
35533
 
34952
35534
  // ../feedback-server/src/config.ts
34953
- import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "node:fs";
35535
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "node:fs";
34954
35536
  import { homedir } from "node:os";
34955
35537
  import { dirname as dirname3, join } from "node:path";
34956
35538
  var DEFAULT_FEEDBACK_PORT = 4748;
@@ -35160,7 +35742,7 @@ __export(exports_util, {
35160
35742
  finalizeIssue: () => finalizeIssue,
35161
35743
  extend: () => extend,
35162
35744
  escapeRegex: () => escapeRegex,
35163
- esc: () => esc,
35745
+ esc: () => esc2,
35164
35746
  defineLazy: () => defineLazy,
35165
35747
  createTransparentProxy: () => createTransparentProxy,
35166
35748
  cloneDef: () => cloneDef,
@@ -35311,7 +35893,7 @@ function randomString(length = 10) {
35311
35893
  }
35312
35894
  return str;
35313
35895
  }
35314
- function esc(str) {
35896
+ function esc2(str) {
35315
35897
  return JSON.stringify(str);
35316
35898
  }
35317
35899
  function slugify(input) {
@@ -36796,10 +37378,10 @@ function isValidJWT(token, algorithm = null) {
36796
37378
  const tokensParts = token.split(".");
36797
37379
  if (tokensParts.length !== 3)
36798
37380
  return false;
36799
- const [header] = tokensParts;
36800
- if (!header)
37381
+ const [header2] = tokensParts;
37382
+ if (!header2)
36801
37383
  return false;
36802
- const parsedHeader = JSON.parse(atob(header));
37384
+ const parsedHeader = JSON.parse(atob(header2));
36803
37385
  if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT")
36804
37386
  return false;
36805
37387
  if (!parsedHeader.alg)
@@ -37080,7 +37662,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
37080
37662
  const doc = new Doc(["shape", "payload", "ctx"]);
37081
37663
  const normalized = _normalized.value;
37082
37664
  const parseStr = (key) => {
37083
- const k = esc(key);
37665
+ const k = esc2(key);
37084
37666
  return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
37085
37667
  };
37086
37668
  doc.write(`const input = payload.value;`);
@@ -37092,7 +37674,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
37092
37674
  doc.write(`const newResult = {};`);
37093
37675
  for (const key of normalized.keys) {
37094
37676
  const id = ids[key];
37095
- const k = esc(key);
37677
+ const k = esc2(key);
37096
37678
  const schema = shape[key];
37097
37679
  const isOptionalOut = schema?._zod?.optout === "optional";
37098
37680
  doc.write(`const ${id} = ${parseStr(key)};`);
@@ -40676,7 +41258,7 @@ function usage() {
40676
41258
  `);
40677
41259
  }
40678
41260
  function resolveServerEntry() {
40679
- if (existsSync6(SERVER_DIST_PATH))
41261
+ if (existsSync5(SERVER_DIST_PATH))
40680
41262
  return SERVER_DIST_PATH;
40681
41263
  return SERVER_SRC_PATH;
40682
41264
  }
@@ -40774,32 +41356,52 @@ var USAGE = `Usage: dryui <command> [options]
40774
41356
 
40775
41357
  Commands:
40776
41358
  init Print setup snippets for a new DryUI app
40777
- detect [--json] [path] Detect DryUI project setup
40778
- install [--json] [path] Print a project install plan
41359
+ detect [--json] [--toon] [path]
41360
+ Detect DryUI project setup
41361
+ install [--json] [--toon] [path]
41362
+ Print a project install plan
40779
41363
  add <component> Print a copyable starter snippet for a component
40780
- info <component> Show component API reference
40781
- list [--category <cat>] List all components
40782
- compose <query> Look up composition guidance
40783
- review [--json] <file.svelte> Validate a Svelte file against DryUI spec
40784
- diagnose [--json] <file.css> Validate theme CSS
40785
- doctor [path] [--include <glob>] [--exclude <glob>] [--changed]
41364
+ info <component> [--toon] Show component API reference
41365
+ list [--category <cat>] [--toon]
41366
+ List all components
41367
+ compose <query> [--toon] Look up composition guidance
41368
+ review [--json] [--toon] <file.svelte>
41369
+ Validate a Svelte file against DryUI spec
41370
+ diagnose [--json] [--toon] <file.css>
41371
+ Validate theme CSS
41372
+ doctor [path] [--toon] [--include <glob>] [--exclude <glob>] [--changed]
40786
41373
  Inspect workspace health
40787
- lint [path] [--json] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]
41374
+ lint [path] [--json] [--toon] [--include <glob>] [--exclude <glob>] [--changed]
40788
41375
  Print deterministic workspace findings
40789
41376
  feedback <subcommand> Start or inspect the feedback server
40790
41377
 
40791
41378
  Options:
40792
41379
  --help Show help for a command
40793
- --version Show version`;
41380
+ --version Show version
41381
+ --toon Token-optimized output for AI agents (per-command)
41382
+ --full Disable truncation (use with --toon)`;
40794
41383
  function main() {
40795
41384
  const args = process.argv.slice(2);
40796
41385
  const command = args[0];
40797
- if (!command || command === "--help" || command === "-h") {
41386
+ if (command === "--version" || command === "-v") {
41387
+ console.log(VERSION);
41388
+ process.exit(0);
41389
+ }
41390
+ if (command === "--help" || command === "-h") {
40798
41391
  console.log(USAGE);
40799
41392
  process.exit(0);
40800
41393
  }
40801
- if (command === "--version" || command === "-v") {
40802
- console.log(VERSION);
41394
+ if (!command) {
41395
+ try {
41396
+ const detection = detectProject(spec_default, undefined);
41397
+ if (detection.status === "ready" || detection.status === "partial") {
41398
+ console.log(`dryui v${VERSION}
41399
+ `);
41400
+ console.log(toonProjectDetection(detection));
41401
+ process.exit(0);
41402
+ }
41403
+ } catch {}
41404
+ console.log(USAGE);
40803
41405
  process.exit(0);
40804
41406
  }
40805
41407
  const commandArgs = args.slice(1);