@dryui/cli 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1071 -506
- package/package.json +5 -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,
|
|
3247
|
-
const func =
|
|
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.
|
|
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.
|
|
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.
|
|
6557
|
+
version: "0.1.12",
|
|
6558
6558
|
package: "@dryui/ui",
|
|
6559
6559
|
themeImports: {
|
|
6560
6560
|
default: "@dryui/ui/themes/default.css",
|
|
@@ -17688,6 +17688,26 @@ var spec_default = {
|
|
|
17688
17688
|
type: "number",
|
|
17689
17689
|
required: false,
|
|
17690
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
|
|
17691
17711
|
}
|
|
17692
17712
|
},
|
|
17693
17713
|
forwardedProps: {
|
|
@@ -17701,25 +17721,23 @@ var spec_default = {
|
|
|
17701
17721
|
],
|
|
17702
17722
|
note: "Forwards <input> attributes via rest props."
|
|
17703
17723
|
},
|
|
17704
|
-
cssVars: {
|
|
17705
|
-
"--dry-time-input-bg": "Input Bg",
|
|
17706
|
-
"--dry-time-input-border": "Input Border",
|
|
17707
|
-
"--dry-time-input-color": "Input Color",
|
|
17708
|
-
"--dry-time-input-font-size": "Input Font Size",
|
|
17709
|
-
"--dry-time-input-padding-x": "Input Padding X",
|
|
17710
|
-
"--dry-time-input-padding-y": "Input Padding Y",
|
|
17711
|
-
"--dry-time-input-radius": "Input Radius"
|
|
17712
|
-
},
|
|
17724
|
+
cssVars: {},
|
|
17713
17725
|
dataAttributes: [
|
|
17714
17726
|
{
|
|
17715
17727
|
name: "data-disabled",
|
|
17716
17728
|
description: "Present when the component or part is disabled."
|
|
17717
17729
|
},
|
|
17718
17730
|
{
|
|
17719
|
-
name: "data-
|
|
17731
|
+
name: "data-placeholder"
|
|
17732
|
+
},
|
|
17733
|
+
{
|
|
17734
|
+
name: "data-time-display"
|
|
17720
17735
|
},
|
|
17721
17736
|
{
|
|
17722
17737
|
name: "data-time-input-wrapper"
|
|
17738
|
+
},
|
|
17739
|
+
{
|
|
17740
|
+
name: "data-time-separator"
|
|
17723
17741
|
}
|
|
17724
17742
|
],
|
|
17725
17743
|
example: "<TimeInput>Content</TimeInput>"
|
|
@@ -18587,14 +18605,18 @@ var spec_default = {
|
|
|
18587
18605
|
]
|
|
18588
18606
|
},
|
|
18589
18607
|
cssVars: {
|
|
18608
|
+
"--dry-alpha-rgb": "Rgb",
|
|
18590
18609
|
"--dry-color-picker-area-height": "Picker Area Height",
|
|
18591
18610
|
"--dry-color-picker-area-width": "Picker Area Width",
|
|
18592
18611
|
"--dry-color-picker-grid-dark": "Picker Grid Dark",
|
|
18593
18612
|
"--dry-color-picker-grid-light": "Picker Grid Light",
|
|
18594
18613
|
"--dry-color-picker-grid-size": "Picker Grid Size",
|
|
18595
18614
|
"--dry-color-picker-indicator-border": "Picker Indicator Border",
|
|
18615
|
+
"--dry-color-picker-indicator-color": "Picker Indicator Color",
|
|
18616
|
+
"--dry-color-picker-indicator-left": "Picker Indicator Left",
|
|
18596
18617
|
"--dry-color-picker-indicator-shadow": "Picker Indicator Shadow",
|
|
18597
18618
|
"--dry-color-picker-indicator-size": "Picker Indicator Size",
|
|
18619
|
+
"--dry-color-picker-indicator-top": "Picker Indicator Top",
|
|
18598
18620
|
"--dry-color-picker-slider-thumb-bg": "Picker Slider Thumb Bg",
|
|
18599
18621
|
"--dry-color-picker-slider-thumb-border": "Picker Slider Thumb Border",
|
|
18600
18622
|
"--dry-color-picker-slider-thumb-shadow": "Picker Slider Thumb Shadow",
|
|
@@ -18605,7 +18627,9 @@ var spec_default = {
|
|
|
18605
18627
|
"--dry-color-picker-swatch-size": "Picker Swatch Size",
|
|
18606
18628
|
"--dry-color-picker-width": "Picker Width",
|
|
18607
18629
|
"--dry-slider-thumb-size": "Thumb size",
|
|
18608
|
-
"--dry-slider-track-height": "Track height"
|
|
18630
|
+
"--dry-slider-track-height": "Track height",
|
|
18631
|
+
"--dry-swatch-color": "Text color",
|
|
18632
|
+
"--dry-swatch-opacity": "Opacity"
|
|
18609
18633
|
},
|
|
18610
18634
|
dataAttributes: [
|
|
18611
18635
|
{
|
|
@@ -19081,7 +19105,13 @@ var spec_default = {
|
|
|
19081
19105
|
],
|
|
19082
19106
|
note: "Forwards <div> attributes via rest props."
|
|
19083
19107
|
},
|
|
19084
|
-
cssVars: {
|
|
19108
|
+
cssVars: {
|
|
19109
|
+
"--dry-spotlight-color": "Text color",
|
|
19110
|
+
"--dry-spotlight-intensity": "Intensity",
|
|
19111
|
+
"--dry-spotlight-radius": "Border radius",
|
|
19112
|
+
"--dry-spotlight-x": "X",
|
|
19113
|
+
"--dry-spotlight-y": "Y"
|
|
19114
|
+
},
|
|
19085
19115
|
dataAttributes: [
|
|
19086
19116
|
{
|
|
19087
19117
|
name: "data-active",
|
|
@@ -19172,8 +19202,10 @@ var spec_default = {
|
|
|
19172
19202
|
"--dry-aurora-color-2": "Color 2",
|
|
19173
19203
|
"--dry-aurora-color-3": "Color 3",
|
|
19174
19204
|
"--dry-aurora-duration": "Duration",
|
|
19205
|
+
"--dry-aurora-intensity": "Intensity",
|
|
19175
19206
|
"--dry-aurora-shift": "Shift",
|
|
19176
|
-
"--dry-aurora-surface": "Surface"
|
|
19207
|
+
"--dry-aurora-surface": "Surface",
|
|
19208
|
+
"--dry-aurora-waviness": "Waviness"
|
|
19177
19209
|
},
|
|
19178
19210
|
dataAttributes: [
|
|
19179
19211
|
{
|
|
@@ -19835,6 +19867,9 @@ var spec_default = {
|
|
|
19835
19867
|
dataAttributes: [
|
|
19836
19868
|
{
|
|
19837
19869
|
name: "data-virtual-list"
|
|
19870
|
+
},
|
|
19871
|
+
{
|
|
19872
|
+
name: "data-virtual-list-inner"
|
|
19838
19873
|
}
|
|
19839
19874
|
],
|
|
19840
19875
|
example: "<VirtualList>Content</VirtualList>"
|
|
@@ -22137,6 +22172,7 @@ var spec_default = {
|
|
|
22137
22172
|
"--dry-image-comparison-handle-shadow": "Comparison Handle Shadow",
|
|
22138
22173
|
"--dry-image-comparison-handle-width": "Comparison Handle Width",
|
|
22139
22174
|
"--dry-image-comparison-handle-z-index": "Comparison Handle Z Index",
|
|
22175
|
+
"--dry-image-comparison-position": "Comparison Position",
|
|
22140
22176
|
"--dry-image-comparison-radius": "Comparison Radius"
|
|
22141
22177
|
},
|
|
22142
22178
|
dataAttributes: [
|
|
@@ -24452,7 +24488,13 @@ var spec_default = {
|
|
|
24452
24488
|
],
|
|
24453
24489
|
note: "Forwards <div> attributes via rest props."
|
|
24454
24490
|
},
|
|
24455
|
-
cssVars: {
|
|
24491
|
+
cssVars: {
|
|
24492
|
+
"--dry-beam-angle": "Angle",
|
|
24493
|
+
"--dry-beam-color": "Text color",
|
|
24494
|
+
"--dry-beam-intensity": "Intensity",
|
|
24495
|
+
"--dry-beam-speed": "Speed",
|
|
24496
|
+
"--dry-beam-width": "Width"
|
|
24497
|
+
},
|
|
24456
24498
|
dataAttributes: [
|
|
24457
24499
|
{
|
|
24458
24500
|
name: "data-beam"
|
|
@@ -24571,7 +24613,14 @@ var spec_default = {
|
|
|
24571
24613
|
],
|
|
24572
24614
|
note: "Forwards <div> attributes via rest props."
|
|
24573
24615
|
},
|
|
24574
|
-
cssVars: {
|
|
24616
|
+
cssVars: {
|
|
24617
|
+
"--dry-rays-blend": "Blend",
|
|
24618
|
+
"--dry-rays-color": "Text color",
|
|
24619
|
+
"--dry-rays-cx": "Cx",
|
|
24620
|
+
"--dry-rays-cy": "Cy",
|
|
24621
|
+
"--dry-rays-intensity": "Intensity",
|
|
24622
|
+
"--dry-rays-speed": "Speed"
|
|
24623
|
+
},
|
|
24575
24624
|
dataAttributes: [
|
|
24576
24625
|
{
|
|
24577
24626
|
name: "data-animated"
|
|
@@ -29925,21 +29974,10 @@ var spec_default = {
|
|
|
29925
29974
|
</body>
|
|
29926
29975
|
</html>
|
|
29927
29976
|
|
|
29928
|
-
<!-- 2. src/app.css —
|
|
29977
|
+
<!-- 2. src/app.css — import themes (resets are built in) -->
|
|
29929
29978
|
@import '@dryui/ui/themes/default.css';
|
|
29930
29979
|
@import '@dryui/ui/themes/dark.css';
|
|
29931
29980
|
|
|
29932
|
-
*, *::before, *::after { box-sizing: border-box; margin: 0; }
|
|
29933
|
-
|
|
29934
|
-
html {
|
|
29935
|
-
font-family: var(--dry-font-sans);
|
|
29936
|
-
color: var(--dry-color-text-strong);
|
|
29937
|
-
background: var(--dry-color-bg-base);
|
|
29938
|
-
-webkit-font-smoothing: antialiased;
|
|
29939
|
-
}
|
|
29940
|
-
|
|
29941
|
-
body { margin: 0; min-height: 100dvh; }
|
|
29942
|
-
|
|
29943
29981
|
<!-- 3. src/routes/+layout.svelte — root layout -->
|
|
29944
29982
|
<script>
|
|
29945
29983
|
import '../app.css';
|
|
@@ -31725,13 +31763,715 @@ body { margin: 0; min-height: 100dvh; }
|
|
|
31725
31763
|
]
|
|
31726
31764
|
};
|
|
31727
31765
|
|
|
31728
|
-
// ../mcp/src/
|
|
31766
|
+
// ../mcp/src/project-planner.ts
|
|
31767
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
31768
|
+
import { dirname, resolve } from "node:path";
|
|
31729
31769
|
var DIR_OVERRIDES = {
|
|
31730
31770
|
QRCode: "qr-code"
|
|
31731
31771
|
};
|
|
31732
31772
|
function componentDir(name) {
|
|
31733
31773
|
return DIR_OVERRIDES[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
31734
31774
|
}
|
|
31775
|
+
function resolveStart(inputPath) {
|
|
31776
|
+
const candidate = resolve(inputPath ?? process.cwd());
|
|
31777
|
+
return existsSync(candidate) && statSync(candidate).isFile() ? dirname(candidate) : candidate;
|
|
31778
|
+
}
|
|
31779
|
+
function findUp(start, fileName) {
|
|
31780
|
+
let current = start;
|
|
31781
|
+
while (true) {
|
|
31782
|
+
const candidate = resolve(current, fileName);
|
|
31783
|
+
if (existsSync(candidate))
|
|
31784
|
+
return candidate;
|
|
31785
|
+
const parent = dirname(current);
|
|
31786
|
+
if (parent === current)
|
|
31787
|
+
return null;
|
|
31788
|
+
current = parent;
|
|
31789
|
+
}
|
|
31790
|
+
}
|
|
31791
|
+
function findUpAny(start, fileNames) {
|
|
31792
|
+
let current = start;
|
|
31793
|
+
while (true) {
|
|
31794
|
+
for (const fileName of fileNames) {
|
|
31795
|
+
const candidate = resolve(current, fileName);
|
|
31796
|
+
if (existsSync(candidate))
|
|
31797
|
+
return candidate;
|
|
31798
|
+
}
|
|
31799
|
+
const parent = dirname(current);
|
|
31800
|
+
if (parent === current)
|
|
31801
|
+
return null;
|
|
31802
|
+
current = parent;
|
|
31803
|
+
}
|
|
31804
|
+
}
|
|
31805
|
+
function detectPackageManager(root) {
|
|
31806
|
+
if (!root)
|
|
31807
|
+
return "unknown";
|
|
31808
|
+
const lockfilePath = findUpAny(root, [
|
|
31809
|
+
"bun.lock",
|
|
31810
|
+
"bun.lockb",
|
|
31811
|
+
"pnpm-lock.yaml",
|
|
31812
|
+
"package-lock.json",
|
|
31813
|
+
"yarn.lock"
|
|
31814
|
+
]);
|
|
31815
|
+
if (!lockfilePath)
|
|
31816
|
+
return "unknown";
|
|
31817
|
+
if (lockfilePath.endsWith("bun.lock") || lockfilePath.endsWith("bun.lockb"))
|
|
31818
|
+
return "bun";
|
|
31819
|
+
if (lockfilePath.endsWith("pnpm-lock.yaml"))
|
|
31820
|
+
return "pnpm";
|
|
31821
|
+
if (lockfilePath.endsWith("package-lock.json"))
|
|
31822
|
+
return "npm";
|
|
31823
|
+
if (lockfilePath.endsWith("yarn.lock"))
|
|
31824
|
+
return "yarn";
|
|
31825
|
+
return "unknown";
|
|
31826
|
+
}
|
|
31827
|
+
function readPackageJson(packageJsonPath) {
|
|
31828
|
+
if (!packageJsonPath)
|
|
31829
|
+
return null;
|
|
31830
|
+
return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
31831
|
+
}
|
|
31832
|
+
function getDependencyNames(pkg) {
|
|
31833
|
+
return new Set([
|
|
31834
|
+
...Object.keys(pkg?.dependencies ?? {}),
|
|
31835
|
+
...Object.keys(pkg?.devDependencies ?? {})
|
|
31836
|
+
]);
|
|
31837
|
+
}
|
|
31838
|
+
function detectFramework(dependencyNames) {
|
|
31839
|
+
if (dependencyNames.has("@sveltejs/kit"))
|
|
31840
|
+
return "sveltekit";
|
|
31841
|
+
if (dependencyNames.has("svelte"))
|
|
31842
|
+
return "svelte";
|
|
31843
|
+
return "unknown";
|
|
31844
|
+
}
|
|
31845
|
+
function hasImport(filePath, importPath) {
|
|
31846
|
+
if (!filePath)
|
|
31847
|
+
return false;
|
|
31848
|
+
return readFileSync(filePath, "utf-8").includes(importPath);
|
|
31849
|
+
}
|
|
31850
|
+
function hasThemeAuto(appHtmlPath) {
|
|
31851
|
+
if (!appHtmlPath)
|
|
31852
|
+
return false;
|
|
31853
|
+
return readFileSync(appHtmlPath, "utf-8").includes("theme-auto");
|
|
31854
|
+
}
|
|
31855
|
+
function hasAnyImport(filePaths, importPath) {
|
|
31856
|
+
return filePaths.some((filePath) => hasImport(filePath, importPath));
|
|
31857
|
+
}
|
|
31858
|
+
function importsAppCss(rootLayoutPath) {
|
|
31859
|
+
if (!rootLayoutPath)
|
|
31860
|
+
return false;
|
|
31861
|
+
const content = readFileSync(rootLayoutPath, "utf-8");
|
|
31862
|
+
return content.includes("app.css");
|
|
31863
|
+
}
|
|
31864
|
+
function buildStatus(framework, hasPackageJson, stepsNeeded) {
|
|
31865
|
+
if (!hasPackageJson || framework === "unknown")
|
|
31866
|
+
return "unsupported";
|
|
31867
|
+
return stepsNeeded === 0 ? "ready" : "partial";
|
|
31868
|
+
}
|
|
31869
|
+
function installCommand(packageManager) {
|
|
31870
|
+
switch (packageManager) {
|
|
31871
|
+
case "bun":
|
|
31872
|
+
return "bun add @dryui/ui";
|
|
31873
|
+
case "pnpm":
|
|
31874
|
+
return "pnpm add @dryui/ui";
|
|
31875
|
+
case "yarn":
|
|
31876
|
+
return "yarn add @dryui/ui";
|
|
31877
|
+
default:
|
|
31878
|
+
return "npm install @dryui/ui";
|
|
31879
|
+
}
|
|
31880
|
+
}
|
|
31881
|
+
function buildThemeImportLines(spec) {
|
|
31882
|
+
return ` import '${spec.themeImports.default}';
|
|
31883
|
+
import '${spec.themeImports.dark}';`;
|
|
31884
|
+
}
|
|
31885
|
+
function buildThemeImportSnippet(spec) {
|
|
31886
|
+
return ["<script>", buildThemeImportLines(spec), "</script>"].join(`
|
|
31887
|
+
`);
|
|
31888
|
+
}
|
|
31889
|
+
function buildRootLayoutSnippet(spec) {
|
|
31890
|
+
return [
|
|
31891
|
+
"<script>",
|
|
31892
|
+
buildThemeImportLines(spec),
|
|
31893
|
+
"",
|
|
31894
|
+
" let { children } = $props();",
|
|
31895
|
+
"</script>",
|
|
31896
|
+
"",
|
|
31897
|
+
"{@render children()}"
|
|
31898
|
+
].join(`
|
|
31899
|
+
`);
|
|
31900
|
+
}
|
|
31901
|
+
function buildThemeImportCssSnippet(spec) {
|
|
31902
|
+
return [`@import '${spec.themeImports.default}';`, `@import '${spec.themeImports.dark}';`].join(`
|
|
31903
|
+
`);
|
|
31904
|
+
}
|
|
31905
|
+
function getSuggestedTarget(root, explicitTarget) {
|
|
31906
|
+
if (explicitTarget)
|
|
31907
|
+
return resolve(root ?? process.cwd(), explicitTarget);
|
|
31908
|
+
if (!root)
|
|
31909
|
+
return null;
|
|
31910
|
+
const rootPage = resolve(root, "src/routes/+page.svelte");
|
|
31911
|
+
return existsSync(rootPage) ? rootPage : null;
|
|
31912
|
+
}
|
|
31913
|
+
function getImportStatement(name, component, subpath = false) {
|
|
31914
|
+
if (subpath && component.import === "@dryui/ui") {
|
|
31915
|
+
return `import { ${name} } from '${component.import}/${componentDir(name)}';`;
|
|
31916
|
+
}
|
|
31917
|
+
return `import { ${name} } from '${component.import}';`;
|
|
31918
|
+
}
|
|
31919
|
+
function findComponent(spec, query) {
|
|
31920
|
+
const exact = spec.components[query];
|
|
31921
|
+
if (exact)
|
|
31922
|
+
return { name: query, def: exact };
|
|
31923
|
+
const lower = query.toLowerCase();
|
|
31924
|
+
for (const [name, def] of Object.entries(spec.components)) {
|
|
31925
|
+
if (name.toLowerCase() === lower)
|
|
31926
|
+
return { name, def };
|
|
31927
|
+
}
|
|
31928
|
+
return null;
|
|
31929
|
+
}
|
|
31930
|
+
function detectProject(spec, inputPath) {
|
|
31931
|
+
const start = resolveStart(inputPath);
|
|
31932
|
+
const packageJsonPath = findUp(start, "package.json");
|
|
31933
|
+
const root = packageJsonPath ? dirname(packageJsonPath) : null;
|
|
31934
|
+
const dependencyNames = getDependencyNames(readPackageJson(packageJsonPath));
|
|
31935
|
+
const framework = detectFramework(dependencyNames);
|
|
31936
|
+
const appHtmlPath = root ? resolve(root, "src/app.html") : null;
|
|
31937
|
+
const appCssPath = root ? resolve(root, "src/app.css") : null;
|
|
31938
|
+
const rootLayoutPath = root ? resolve(root, "src/routes/+layout.svelte") : null;
|
|
31939
|
+
const rootPagePath = root ? resolve(root, "src/routes/+page.svelte") : null;
|
|
31940
|
+
const appHtml = appHtmlPath && existsSync(appHtmlPath) ? appHtmlPath : null;
|
|
31941
|
+
const appCss = appCssPath && existsSync(appCssPath) ? appCssPath : null;
|
|
31942
|
+
const rootLayout = rootLayoutPath && existsSync(rootLayoutPath) ? rootLayoutPath : null;
|
|
31943
|
+
const rootPage = rootPagePath && existsSync(rootPagePath) ? rootPagePath : null;
|
|
31944
|
+
const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
|
|
31945
|
+
const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
|
|
31946
|
+
const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
|
|
31947
|
+
const themeAuto = appHtml ? hasThemeAuto(appHtml) : false;
|
|
31948
|
+
const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!themeAuto);
|
|
31949
|
+
const warnings = [];
|
|
31950
|
+
if (!packageJsonPath)
|
|
31951
|
+
warnings.push("No package.json found above the provided path.");
|
|
31952
|
+
if (framework === "unknown")
|
|
31953
|
+
warnings.push("DryUI planning currently targets Svelte and SvelteKit projects.");
|
|
31954
|
+
return {
|
|
31955
|
+
inputPath: start,
|
|
31956
|
+
root,
|
|
31957
|
+
packageJsonPath,
|
|
31958
|
+
framework,
|
|
31959
|
+
packageManager: detectPackageManager(root),
|
|
31960
|
+
status: buildStatus(framework, Boolean(packageJsonPath), stepsNeeded),
|
|
31961
|
+
dependencies: {
|
|
31962
|
+
ui: dependencyNames.has("@dryui/ui"),
|
|
31963
|
+
primitives: dependencyNames.has("@dryui/primitives")
|
|
31964
|
+
},
|
|
31965
|
+
files: {
|
|
31966
|
+
appHtml,
|
|
31967
|
+
appCss,
|
|
31968
|
+
rootLayout,
|
|
31969
|
+
rootPage
|
|
31970
|
+
},
|
|
31971
|
+
theme: {
|
|
31972
|
+
defaultImported,
|
|
31973
|
+
darkImported,
|
|
31974
|
+
themeAuto
|
|
31975
|
+
},
|
|
31976
|
+
warnings
|
|
31977
|
+
};
|
|
31978
|
+
}
|
|
31979
|
+
function planInstall(spec, inputPath) {
|
|
31980
|
+
const detection = detectProject(spec, inputPath);
|
|
31981
|
+
const steps = [];
|
|
31982
|
+
if (detection.status === "unsupported") {
|
|
31983
|
+
steps.push({
|
|
31984
|
+
kind: "blocked",
|
|
31985
|
+
status: "blocked",
|
|
31986
|
+
title: "Project detection incomplete",
|
|
31987
|
+
description: detection.warnings.join(" ") || "DryUI install planning requires a Svelte or SvelteKit project with a package.json."
|
|
31988
|
+
});
|
|
31989
|
+
return { detection, steps };
|
|
31990
|
+
}
|
|
31991
|
+
if (!detection.dependencies.ui) {
|
|
31992
|
+
steps.push({
|
|
31993
|
+
kind: "install-package",
|
|
31994
|
+
status: "pending",
|
|
31995
|
+
title: "Install @dryui/ui",
|
|
31996
|
+
description: "Add the styled DryUI package to the current project.",
|
|
31997
|
+
command: installCommand(detection.packageManager)
|
|
31998
|
+
});
|
|
31999
|
+
}
|
|
32000
|
+
if (!detection.theme.defaultImported || !detection.theme.darkImported) {
|
|
32001
|
+
if (detection.files.appCss && detection.files.rootLayout && importsAppCss(detection.files.rootLayout)) {
|
|
32002
|
+
steps.push({
|
|
32003
|
+
kind: "edit-file",
|
|
32004
|
+
status: "pending",
|
|
32005
|
+
title: "Add theme imports to app.css",
|
|
32006
|
+
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.",
|
|
32007
|
+
path: detection.files.appCss,
|
|
32008
|
+
snippet: buildThemeImportCssSnippet(spec)
|
|
32009
|
+
});
|
|
32010
|
+
} else if (!detection.files.rootLayout) {
|
|
32011
|
+
const path = detection.root ? resolve(detection.root, "src/routes/+layout.svelte") : null;
|
|
32012
|
+
steps.push({
|
|
32013
|
+
kind: "create-file",
|
|
32014
|
+
status: "pending",
|
|
32015
|
+
title: "Create root layout with theme imports",
|
|
32016
|
+
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.",
|
|
32017
|
+
...path ? { path } : {},
|
|
32018
|
+
snippet: buildRootLayoutSnippet(spec)
|
|
32019
|
+
});
|
|
32020
|
+
} else {
|
|
32021
|
+
steps.push({
|
|
32022
|
+
kind: "edit-file",
|
|
32023
|
+
status: "pending",
|
|
32024
|
+
title: "Add theme imports to the root layout",
|
|
32025
|
+
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.",
|
|
32026
|
+
path: detection.files.rootLayout,
|
|
32027
|
+
snippet: buildThemeImportSnippet(spec)
|
|
32028
|
+
});
|
|
32029
|
+
}
|
|
32030
|
+
}
|
|
32031
|
+
if (!detection.files.appHtml) {
|
|
32032
|
+
steps.push({
|
|
32033
|
+
kind: "blocked",
|
|
32034
|
+
status: "blocked",
|
|
32035
|
+
title: "app.html not found",
|
|
32036
|
+
description: "DryUI expects src/app.html so the document can default to theme-auto mode."
|
|
32037
|
+
});
|
|
32038
|
+
} else if (!detection.theme.themeAuto) {
|
|
32039
|
+
steps.push({
|
|
32040
|
+
kind: "edit-file",
|
|
32041
|
+
status: "pending",
|
|
32042
|
+
title: "Set html theme mode to auto",
|
|
32043
|
+
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.',
|
|
32044
|
+
path: detection.files.appHtml,
|
|
32045
|
+
snippet: 'class="theme-auto"'
|
|
32046
|
+
});
|
|
32047
|
+
}
|
|
32048
|
+
if (steps.length === 0) {
|
|
32049
|
+
steps.push({
|
|
32050
|
+
kind: "note",
|
|
32051
|
+
status: "done",
|
|
32052
|
+
title: "DryUI install plan is complete",
|
|
32053
|
+
description: "The project already has @dryui/ui, theme imports, and theme-auto configured."
|
|
32054
|
+
});
|
|
32055
|
+
}
|
|
32056
|
+
return { detection, steps };
|
|
32057
|
+
}
|
|
32058
|
+
function planAdd(spec, query, options = {}) {
|
|
32059
|
+
const installPlan = planInstall(spec, options.cwd);
|
|
32060
|
+
const component = findComponent(spec, query);
|
|
32061
|
+
if (component) {
|
|
32062
|
+
const target = getSuggestedTarget(installPlan.detection.root, options.target);
|
|
32063
|
+
const steps = [];
|
|
32064
|
+
const warnings = [...installPlan.detection.warnings];
|
|
32065
|
+
if (installPlan.steps.some((step) => step.status === "pending" || step.status === "blocked")) {
|
|
32066
|
+
steps.push({
|
|
32067
|
+
kind: "note",
|
|
32068
|
+
status: "info",
|
|
32069
|
+
title: "Complete install plan first",
|
|
32070
|
+
description: "Apply the install plan before inserting DryUI components into project files."
|
|
32071
|
+
});
|
|
32072
|
+
}
|
|
32073
|
+
steps.push(target ? {
|
|
32074
|
+
kind: "edit-file",
|
|
32075
|
+
status: "pending",
|
|
32076
|
+
title: "Insert component into the target file",
|
|
32077
|
+
description: "Add the import and snippet below to the chosen Svelte file.",
|
|
32078
|
+
path: target,
|
|
32079
|
+
snippet: `${getImportStatement(component.name, component.def, options.subpath)}
|
|
32080
|
+
|
|
32081
|
+
${component.def.example}`
|
|
32082
|
+
} : {
|
|
32083
|
+
kind: "note",
|
|
32084
|
+
status: "info",
|
|
32085
|
+
title: "Choose a target Svelte file",
|
|
32086
|
+
description: "No root page was found. Pick a target file and reuse the import and snippet in this plan."
|
|
32087
|
+
});
|
|
32088
|
+
return {
|
|
32089
|
+
detection: installPlan.detection,
|
|
32090
|
+
installPlan,
|
|
32091
|
+
targetType: "component",
|
|
32092
|
+
name: component.name,
|
|
32093
|
+
importStatement: getImportStatement(component.name, component.def, options.subpath),
|
|
32094
|
+
snippet: component.def.example,
|
|
32095
|
+
target,
|
|
32096
|
+
steps,
|
|
32097
|
+
warnings
|
|
32098
|
+
};
|
|
32099
|
+
}
|
|
32100
|
+
throw new Error(`Unknown component: "${query}"`);
|
|
32101
|
+
}
|
|
32102
|
+
|
|
32103
|
+
// ../mcp/dist/toon.js
|
|
32104
|
+
function esc(value) {
|
|
32105
|
+
if (value.includes(",") || value.includes(`
|
|
32106
|
+
`)) {
|
|
32107
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
32108
|
+
}
|
|
32109
|
+
return value;
|
|
32110
|
+
}
|
|
32111
|
+
function header(resource, count, fields) {
|
|
32112
|
+
return `${resource}[${count}]{${fields.join(",")}}:`;
|
|
32113
|
+
}
|
|
32114
|
+
function row(...values) {
|
|
32115
|
+
return " " + values.map((v) => esc(String(v))).join(",");
|
|
32116
|
+
}
|
|
32117
|
+
function truncate(text, maxLen, hint) {
|
|
32118
|
+
if (text.length <= maxLen)
|
|
32119
|
+
return text;
|
|
32120
|
+
return `(truncated, ${text.length} chars -- ${hint})`;
|
|
32121
|
+
}
|
|
32122
|
+
function buildContextualHelp(ctx) {
|
|
32123
|
+
const hints = [];
|
|
32124
|
+
switch (ctx.command) {
|
|
32125
|
+
case "info":
|
|
32126
|
+
if (ctx.componentName) {
|
|
32127
|
+
hints.push(`compose "${ctx.componentName.toLowerCase()}" -- see composition patterns`);
|
|
32128
|
+
hints.push(`add ${ctx.componentName} -- get starter snippet`);
|
|
32129
|
+
}
|
|
32130
|
+
break;
|
|
32131
|
+
case "list":
|
|
32132
|
+
hints.push("info <Component> -- see full API reference");
|
|
32133
|
+
break;
|
|
32134
|
+
case "compose":
|
|
32135
|
+
if (ctx.componentName) {
|
|
32136
|
+
hints.push(`info ${ctx.componentName} -- full API reference`);
|
|
32137
|
+
hints.push(`add ${ctx.componentName} -- starter snippet`);
|
|
32138
|
+
}
|
|
32139
|
+
break;
|
|
32140
|
+
case "review":
|
|
32141
|
+
if (ctx.hasErrors) {
|
|
32142
|
+
hints.push("info <Component> -- check API for errored component");
|
|
32143
|
+
hints.push("diagnose <file.css> -- check theme if theme warnings present");
|
|
32144
|
+
} else if (ctx.isEmpty) {
|
|
32145
|
+
hints.push("lint . -- check entire workspace");
|
|
32146
|
+
}
|
|
32147
|
+
break;
|
|
32148
|
+
case "diagnose":
|
|
32149
|
+
if (ctx.hasErrors) {
|
|
32150
|
+
hints.push('compose "app shell" -- get correct theme setup');
|
|
32151
|
+
} else if (ctx.isEmpty) {
|
|
32152
|
+
hints.push("review <file.svelte> -- validate component usage");
|
|
32153
|
+
}
|
|
32154
|
+
break;
|
|
32155
|
+
case "doctor":
|
|
32156
|
+
case "lint":
|
|
32157
|
+
if (ctx.hasFindings) {
|
|
32158
|
+
hints.push("lint --max-severity error -- focus on errors only");
|
|
32159
|
+
hints.push("review <file.svelte> -- check specific file");
|
|
32160
|
+
} else if (ctx.isEmpty) {
|
|
32161
|
+
hints.push("detect -- verify project setup");
|
|
32162
|
+
}
|
|
32163
|
+
break;
|
|
32164
|
+
case "detect":
|
|
32165
|
+
if (ctx.status === "partial" || ctx.status === "unsupported") {
|
|
32166
|
+
hints.push("install -- see step-by-step setup plan");
|
|
32167
|
+
} else if (ctx.status === "ready") {
|
|
32168
|
+
hints.push("list -- see available components");
|
|
32169
|
+
hints.push('compose "app shell" -- get root layout template');
|
|
32170
|
+
}
|
|
32171
|
+
break;
|
|
32172
|
+
case "install":
|
|
32173
|
+
hints.push("detect -- verify project after completing steps");
|
|
32174
|
+
break;
|
|
32175
|
+
}
|
|
32176
|
+
return hints;
|
|
32177
|
+
}
|
|
32178
|
+
function formatHelp(hints) {
|
|
32179
|
+
if (hints.length === 0)
|
|
32180
|
+
return "";
|
|
32181
|
+
const lines = [`next[${hints.length}]:`];
|
|
32182
|
+
for (const hint of hints) {
|
|
32183
|
+
lines.push(" " + hint);
|
|
32184
|
+
}
|
|
32185
|
+
return lines.join(`
|
|
32186
|
+
`);
|
|
32187
|
+
}
|
|
32188
|
+
function toonComponent(name, def, opts) {
|
|
32189
|
+
const full = opts?.full ?? false;
|
|
32190
|
+
const lines = [];
|
|
32191
|
+
lines.push(`${name} -- ${def.description}`, `category: ${def.category} | tags: ${def.tags.join(",")}`, `import: import { ${name} } from '${def.import}'`, `compound: ${def.compound}`);
|
|
32192
|
+
if (def.compound && def.structure?.tree.length) {
|
|
32193
|
+
lines.push("", header("structure", def.structure.tree.length, ["node"]));
|
|
32194
|
+
for (const node of def.structure.tree) {
|
|
32195
|
+
lines.push(" " + node);
|
|
32196
|
+
}
|
|
32197
|
+
if (def.structure.note)
|
|
32198
|
+
lines.push(` note: ${def.structure.note}`);
|
|
32199
|
+
}
|
|
32200
|
+
if (def.compound && def.parts) {
|
|
32201
|
+
const partEntries = Object.entries(def.parts);
|
|
32202
|
+
lines.push("", header("parts", partEntries.length, ["part"]));
|
|
32203
|
+
for (const [partName, partDef] of partEntries) {
|
|
32204
|
+
lines.push(` ${name}.${partName}`);
|
|
32205
|
+
const props = Object.entries(partDef.props ?? {});
|
|
32206
|
+
if (props.length > 0) {
|
|
32207
|
+
for (const [propName, propDef] of props) {
|
|
32208
|
+
const flags = [propDef.type];
|
|
32209
|
+
if (propDef.required)
|
|
32210
|
+
flags.push("required");
|
|
32211
|
+
if (propDef.default !== undefined)
|
|
32212
|
+
flags.push(`default:${propDef.default}`);
|
|
32213
|
+
if (propDef.acceptedValues?.length)
|
|
32214
|
+
flags.push(`values:${propDef.acceptedValues.join("|")}`);
|
|
32215
|
+
lines.push(` ${propName}: ${flags.join(" | ")}`);
|
|
32216
|
+
}
|
|
32217
|
+
}
|
|
32218
|
+
}
|
|
32219
|
+
} else if (def.props) {
|
|
32220
|
+
const propEntries = Object.entries(def.props);
|
|
32221
|
+
if (propEntries.length > 0) {
|
|
32222
|
+
lines.push("", header("props", propEntries.length, ["name", "type", "required", "default"]));
|
|
32223
|
+
for (const [propName, propDef] of propEntries) {
|
|
32224
|
+
lines.push(row(propName, propDef.type, propDef.required ? "yes" : "no", propDef.default ?? "-"));
|
|
32225
|
+
}
|
|
32226
|
+
}
|
|
32227
|
+
}
|
|
32228
|
+
const cssEntries = Object.entries(def.cssVars);
|
|
32229
|
+
if (cssEntries.length > 0) {
|
|
32230
|
+
lines.push("", header("css-vars", cssEntries.length, ["name", "description"]));
|
|
32231
|
+
for (const [varName, desc] of cssEntries) {
|
|
32232
|
+
lines.push(row(varName, desc));
|
|
32233
|
+
}
|
|
32234
|
+
}
|
|
32235
|
+
if (def.dataAttributes.length > 0) {
|
|
32236
|
+
lines.push("", header("data-attrs", def.dataAttributes.length, ["name", "values"]));
|
|
32237
|
+
for (const attr of def.dataAttributes) {
|
|
32238
|
+
lines.push(row(attr.name, attr.values?.join("|") ?? "-"));
|
|
32239
|
+
}
|
|
32240
|
+
}
|
|
32241
|
+
if (def.example) {
|
|
32242
|
+
const example = full ? def.example : truncate(def.example, 400, `use add ${name} for full snippet`);
|
|
32243
|
+
lines.push("", "example:", example);
|
|
32244
|
+
}
|
|
32245
|
+
const help = buildContextualHelp({ command: "info", componentName: name });
|
|
32246
|
+
if (help.length > 0) {
|
|
32247
|
+
lines.push("", formatHelp(help));
|
|
32248
|
+
}
|
|
32249
|
+
return lines.join(`
|
|
32250
|
+
`);
|
|
32251
|
+
}
|
|
32252
|
+
function toonComponentList(components, category) {
|
|
32253
|
+
const entries = Object.entries(components);
|
|
32254
|
+
const filtered = category ? entries.filter(([, def]) => def.category.toLowerCase() === category.toLowerCase()) : entries;
|
|
32255
|
+
if (filtered.length === 0) {
|
|
32256
|
+
const cats = [...new Set(entries.map(([, d]) => d.category))].sort();
|
|
32257
|
+
return `components[0]: no matches
|
|
32258
|
+
available categories: ${cats.join(", ")}`;
|
|
32259
|
+
}
|
|
32260
|
+
const groups = {};
|
|
32261
|
+
for (const entry of filtered) {
|
|
32262
|
+
const cat = entry[1].category;
|
|
32263
|
+
(groups[cat] ??= []).push(entry);
|
|
32264
|
+
}
|
|
32265
|
+
const lines = [header("components", filtered.length, ["name", "category", "compound", "description"])];
|
|
32266
|
+
const sortedCats = Object.keys(groups).sort();
|
|
32267
|
+
for (const cat of sortedCats) {
|
|
32268
|
+
const items = groups[cat] ?? [];
|
|
32269
|
+
for (const [name, def] of items) {
|
|
32270
|
+
lines.push(row(name, cat, def.compound, def.description));
|
|
32271
|
+
}
|
|
32272
|
+
}
|
|
32273
|
+
const help = buildContextualHelp({ command: "list" });
|
|
32274
|
+
if (help.length > 0) {
|
|
32275
|
+
lines.push("", formatHelp(help));
|
|
32276
|
+
}
|
|
32277
|
+
return lines.join(`
|
|
32278
|
+
`);
|
|
32279
|
+
}
|
|
32280
|
+
function toonComposition(results, opts) {
|
|
32281
|
+
const full = opts?.full ?? false;
|
|
32282
|
+
const lines = [];
|
|
32283
|
+
const totalMatches = results.componentMatches.length + results.recipeMatches.length;
|
|
32284
|
+
if (totalMatches === 0) {
|
|
32285
|
+
return "matches[0]: none";
|
|
32286
|
+
}
|
|
32287
|
+
for (const comp of results.componentMatches) {
|
|
32288
|
+
lines.push(`-- ${comp.component}: ${comp.useWhen}`);
|
|
32289
|
+
for (const alt of comp.alternatives) {
|
|
32290
|
+
const snippet = full ? alt.snippet : truncate(alt.snippet, 500, `use info ${alt.component} for full snippet`);
|
|
32291
|
+
lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
|
|
32292
|
+
lines.push(snippet.split(`
|
|
32293
|
+
`).map((l) => " " + l).join(`
|
|
32294
|
+
`));
|
|
32295
|
+
}
|
|
32296
|
+
for (const ap of comp.antiPatterns) {
|
|
32297
|
+
lines.push(` anti-pattern: ${ap.pattern}`);
|
|
32298
|
+
lines.push(` reason: ${ap.reason} | fix: ${ap.fix}`);
|
|
32299
|
+
}
|
|
32300
|
+
if (comp.combinesWith.length) {
|
|
32301
|
+
lines.push(` combines-with: ${comp.combinesWith.join(",")}`);
|
|
32302
|
+
}
|
|
32303
|
+
lines.push("");
|
|
32304
|
+
}
|
|
32305
|
+
for (const recipe of results.recipeMatches) {
|
|
32306
|
+
const snippet = full ? recipe.snippet : truncate(recipe.snippet, 500, `use compose "${recipe.name}" --full for complete code`);
|
|
32307
|
+
lines.push(`-- recipe: ${recipe.name}`);
|
|
32308
|
+
lines.push(` ${recipe.description}`);
|
|
32309
|
+
lines.push(` components: ${recipe.components.join(",")}`);
|
|
32310
|
+
lines.push(" code:");
|
|
32311
|
+
lines.push(snippet.split(`
|
|
32312
|
+
`).map((l) => " " + l).join(`
|
|
32313
|
+
`));
|
|
32314
|
+
lines.push("");
|
|
32315
|
+
}
|
|
32316
|
+
const firstComponent = results.componentMatches[0]?.alternatives[0]?.component ?? results.recipeMatches[0]?.components[0] ?? undefined;
|
|
32317
|
+
const ctx = { command: "compose" };
|
|
32318
|
+
if (firstComponent)
|
|
32319
|
+
ctx.componentName = firstComponent;
|
|
32320
|
+
const help = buildContextualHelp(ctx);
|
|
32321
|
+
if (help.length > 0) {
|
|
32322
|
+
lines.push(formatHelp(help));
|
|
32323
|
+
}
|
|
32324
|
+
return lines.join(`
|
|
32325
|
+
`).trimEnd();
|
|
32326
|
+
}
|
|
32327
|
+
function toonReviewResult(result) {
|
|
32328
|
+
const lines = [];
|
|
32329
|
+
const hasBlockers = result.issues.some((i) => i.severity === "error");
|
|
32330
|
+
const autoFixable = result.issues.filter((i) => i.fix !== null).length;
|
|
32331
|
+
if (result.issues.length === 0) {
|
|
32332
|
+
lines.push("issues[0]: clean");
|
|
32333
|
+
lines.push(`hasBlockers: false | autoFixable: 0`);
|
|
32334
|
+
} else {
|
|
32335
|
+
lines.push(header("issues", result.issues.length, ["severity", "line", "code", "message"]));
|
|
32336
|
+
for (const issue of result.issues) {
|
|
32337
|
+
lines.push(row(issue.severity, issue.line, issue.code, issue.message));
|
|
32338
|
+
if (issue.fix) {
|
|
32339
|
+
lines.push(` fix: ${issue.fix}`);
|
|
32340
|
+
}
|
|
32341
|
+
}
|
|
32342
|
+
lines.push(`hasBlockers: ${hasBlockers} | autoFixable: ${autoFixable}`);
|
|
32343
|
+
}
|
|
32344
|
+
lines.push(result.summary);
|
|
32345
|
+
const help = buildContextualHelp({
|
|
32346
|
+
command: "review",
|
|
32347
|
+
hasErrors: hasBlockers,
|
|
32348
|
+
isEmpty: result.issues.length === 0
|
|
32349
|
+
});
|
|
32350
|
+
if (help.length > 0) {
|
|
32351
|
+
lines.push("", formatHelp(help));
|
|
32352
|
+
}
|
|
32353
|
+
return lines.join(`
|
|
32354
|
+
`);
|
|
32355
|
+
}
|
|
32356
|
+
function toonDiagnoseResult(result) {
|
|
32357
|
+
const lines = [];
|
|
32358
|
+
const { variables } = result;
|
|
32359
|
+
const missingCount = result.issues.filter((i) => i.code === "missing-token").length;
|
|
32360
|
+
const totalRequired = variables.required + missingCount;
|
|
32361
|
+
const coverage = totalRequired > 0 ? Math.round(variables.required / totalRequired * 100) : 100;
|
|
32362
|
+
lines.push(`tokens: ${variables.found} found, ${variables.required} required, ${variables.extra} extra | coverage: ${coverage}%`);
|
|
32363
|
+
if (result.issues.length === 0) {
|
|
32364
|
+
lines.push("issues[0]: clean");
|
|
32365
|
+
} else {
|
|
32366
|
+
lines.push(header("issues", result.issues.length, ["severity", "code", "variable", "message"]));
|
|
32367
|
+
for (const issue of result.issues) {
|
|
32368
|
+
lines.push(row(issue.severity, issue.code, issue.variable, issue.message));
|
|
32369
|
+
if (issue.fix) {
|
|
32370
|
+
lines.push(` fix: ${issue.fix}`);
|
|
32371
|
+
}
|
|
32372
|
+
}
|
|
32373
|
+
}
|
|
32374
|
+
lines.push(result.summary);
|
|
32375
|
+
const hasErrors = result.issues.some((i) => i.severity === "error");
|
|
32376
|
+
const help = buildContextualHelp({
|
|
32377
|
+
command: "diagnose",
|
|
32378
|
+
hasErrors,
|
|
32379
|
+
isEmpty: result.issues.length === 0
|
|
32380
|
+
});
|
|
32381
|
+
if (help.length > 0) {
|
|
32382
|
+
lines.push("", formatHelp(help));
|
|
32383
|
+
}
|
|
32384
|
+
return lines.join(`
|
|
32385
|
+
`);
|
|
32386
|
+
}
|
|
32387
|
+
var MAX_FINDINGS_DEFAULT = 50;
|
|
32388
|
+
function toonWorkspaceReport(report, opts) {
|
|
32389
|
+
const full = opts?.full ?? false;
|
|
32390
|
+
const title = opts?.title ?? "workspace";
|
|
32391
|
+
const command = opts?.command ?? (title.includes("lint") ? "lint" : "doctor");
|
|
32392
|
+
const lines = [];
|
|
32393
|
+
lines.push(`${title} | root: ${report.root}`);
|
|
32394
|
+
lines.push(`scanned: ${report.scannedFiles} files | errors: ${report.summary.error} | warnings: ${report.summary.warning} | info: ${report.summary.info}`);
|
|
32395
|
+
if (report.summary.byRule && Object.keys(report.summary.byRule).length > 0) {
|
|
32396
|
+
const sorted = Object.entries(report.summary.byRule).sort(([, a], [, b]) => b - a);
|
|
32397
|
+
const topRule = sorted[0];
|
|
32398
|
+
if (topRule) {
|
|
32399
|
+
lines.push(`top-rule: ${topRule[0]} (${topRule[1]} occurrences)`);
|
|
32400
|
+
}
|
|
32401
|
+
}
|
|
32402
|
+
if (report.findings.length === 0) {
|
|
32403
|
+
lines.push("findings[0]: clean");
|
|
32404
|
+
} else {
|
|
32405
|
+
const findings = full ? report.findings : report.findings.slice(0, MAX_FINDINGS_DEFAULT);
|
|
32406
|
+
const truncated = !full && report.findings.length > MAX_FINDINGS_DEFAULT;
|
|
32407
|
+
lines.push(header("findings", findings.length, ["severity", "rule", "file", "line", "message"]));
|
|
32408
|
+
for (const f of findings) {
|
|
32409
|
+
lines.push(row(f.severity, f.ruleId, f.file, f.line ?? "-", f.message));
|
|
32410
|
+
if (f.suggestedFixes.length > 0) {
|
|
32411
|
+
for (const fix of f.suggestedFixes) {
|
|
32412
|
+
lines.push(` fix: ${fix.description}${fix.replacement ? ` -> ${fix.replacement}` : ""}`);
|
|
32413
|
+
}
|
|
32414
|
+
}
|
|
32415
|
+
}
|
|
32416
|
+
if (truncated) {
|
|
32417
|
+
lines.push(` (showing ${MAX_FINDINGS_DEFAULT} of ${report.findings.length} -- use --full to see all)`);
|
|
32418
|
+
}
|
|
32419
|
+
}
|
|
32420
|
+
if (report.warnings.length > 0) {
|
|
32421
|
+
lines.push("", header("warnings", report.warnings.length, ["message"]));
|
|
32422
|
+
for (const w of report.warnings) {
|
|
32423
|
+
lines.push(" " + w);
|
|
32424
|
+
}
|
|
32425
|
+
}
|
|
32426
|
+
const help = buildContextualHelp({
|
|
32427
|
+
command,
|
|
32428
|
+
hasFindings: report.findings.length > 0,
|
|
32429
|
+
isEmpty: report.findings.length === 0
|
|
32430
|
+
});
|
|
32431
|
+
if (help.length > 0) {
|
|
32432
|
+
lines.push("", formatHelp(help));
|
|
32433
|
+
}
|
|
32434
|
+
return lines.join(`
|
|
32435
|
+
`);
|
|
32436
|
+
}
|
|
32437
|
+
function toonProjectDetection(detection) {
|
|
32438
|
+
const lines = [];
|
|
32439
|
+
lines.push(`project: ${detection.status} | framework: ${detection.framework} | pkg-manager: ${detection.packageManager}`);
|
|
32440
|
+
lines.push(`root: ${detection.root ?? "(not found)"}`);
|
|
32441
|
+
lines.push(`deps: ui=${detection.dependencies.ui}, primitives=${detection.dependencies.primitives}`);
|
|
32442
|
+
lines.push(`theme: default=${detection.theme.defaultImported}, dark=${detection.theme.darkImported}, auto=${detection.theme.themeAuto}`);
|
|
32443
|
+
if (detection.warnings.length > 0) {
|
|
32444
|
+
lines.push(header("warnings", detection.warnings.length, ["message"]));
|
|
32445
|
+
for (const w of detection.warnings) {
|
|
32446
|
+
lines.push(" " + w);
|
|
32447
|
+
}
|
|
32448
|
+
}
|
|
32449
|
+
const help = buildContextualHelp({ command: "detect", status: detection.status });
|
|
32450
|
+
if (help.length > 0) {
|
|
32451
|
+
lines.push("", formatHelp(help));
|
|
32452
|
+
}
|
|
32453
|
+
return lines.join(`
|
|
32454
|
+
`);
|
|
32455
|
+
}
|
|
32456
|
+
function toonError(code, message, suggestions) {
|
|
32457
|
+
const lines = [`error[1]{code,message}: ${esc(code)},${esc(message)}`];
|
|
32458
|
+
if (suggestions?.length) {
|
|
32459
|
+
lines.push(header("suggestions", suggestions.length, ["value"]));
|
|
32460
|
+
for (const s of suggestions) {
|
|
32461
|
+
lines.push(" " + s);
|
|
32462
|
+
}
|
|
32463
|
+
}
|
|
32464
|
+
return lines.join(`
|
|
32465
|
+
`);
|
|
32466
|
+
}
|
|
32467
|
+
|
|
32468
|
+
// ../mcp/src/spec-formatters.ts
|
|
32469
|
+
var DIR_OVERRIDES2 = {
|
|
32470
|
+
QRCode: "qr-code"
|
|
32471
|
+
};
|
|
32472
|
+
function componentDir2(name) {
|
|
32473
|
+
return DIR_OVERRIDES2[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
32474
|
+
}
|
|
31735
32475
|
function pad(str, width) {
|
|
31736
32476
|
return str.length >= width ? str : str + " ".repeat(width - str.length);
|
|
31737
32477
|
}
|
|
@@ -31744,9 +32484,9 @@ function indent(text, spaces) {
|
|
|
31744
32484
|
function getSubpathImport(name, def) {
|
|
31745
32485
|
if (def.import !== "@dryui/ui")
|
|
31746
32486
|
return null;
|
|
31747
|
-
return `import { ${name} } from '${def.import}/${
|
|
32487
|
+
return `import { ${name} } from '${def.import}/${componentDir2(name)}'`;
|
|
31748
32488
|
}
|
|
31749
|
-
function
|
|
32489
|
+
function findComponent2(query, components) {
|
|
31750
32490
|
const exact = components[query];
|
|
31751
32491
|
if (exact)
|
|
31752
32492
|
return { name: query, def: exact };
|
|
@@ -31921,9 +32661,23 @@ function formatSimple(name, def) {
|
|
|
31921
32661
|
}
|
|
31922
32662
|
|
|
31923
32663
|
// src/run.ts
|
|
31924
|
-
|
|
32664
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
32665
|
+
function fileNotFound(filePath, toon) {
|
|
32666
|
+
if (existsSync2(filePath))
|
|
32667
|
+
return null;
|
|
32668
|
+
return {
|
|
32669
|
+
output: "",
|
|
32670
|
+
error: toon ? toonError("not-found", `File not found: ${filePath}`) : `File not found: ${filePath}`,
|
|
32671
|
+
exitCode: 1
|
|
32672
|
+
};
|
|
32673
|
+
}
|
|
32674
|
+
function runCommand(result, mode = "text") {
|
|
31925
32675
|
if (result.error) {
|
|
31926
|
-
|
|
32676
|
+
if (mode === "text") {
|
|
32677
|
+
console.error(result.error);
|
|
32678
|
+
} else {
|
|
32679
|
+
console.log(result.error);
|
|
32680
|
+
}
|
|
31927
32681
|
} else {
|
|
31928
32682
|
console.log(result.output);
|
|
31929
32683
|
}
|
|
@@ -31931,13 +32685,20 @@ function runCommand(result) {
|
|
|
31931
32685
|
}
|
|
31932
32686
|
|
|
31933
32687
|
// src/commands/info.ts
|
|
31934
|
-
function
|
|
31935
|
-
return
|
|
32688
|
+
function findComponent3(spec, query) {
|
|
32689
|
+
return findComponent2(query, spec.components);
|
|
31936
32690
|
}
|
|
31937
|
-
function getInfo(query, spec) {
|
|
31938
|
-
const result =
|
|
32691
|
+
function getInfo(query, spec, options) {
|
|
32692
|
+
const result = findComponent3(spec, query);
|
|
31939
32693
|
if (!result) {
|
|
31940
32694
|
const available = Object.keys(spec.components).sort();
|
|
32695
|
+
if (options?.toon) {
|
|
32696
|
+
return {
|
|
32697
|
+
output: "",
|
|
32698
|
+
error: toonError("not-found", `Unknown component: "${query}"`, available),
|
|
32699
|
+
exitCode: 1
|
|
32700
|
+
};
|
|
32701
|
+
}
|
|
31941
32702
|
const error = [
|
|
31942
32703
|
`Unknown component: "${query}"`,
|
|
31943
32704
|
"",
|
|
@@ -31948,27 +32709,37 @@ function getInfo(query, spec) {
|
|
|
31948
32709
|
return { output: "", error, exitCode: 1 };
|
|
31949
32710
|
}
|
|
31950
32711
|
const { name, def } = result;
|
|
32712
|
+
if (options?.toon) {
|
|
32713
|
+
return { output: toonComponent(name, def, { full: options?.full }), error: null, exitCode: 0 };
|
|
32714
|
+
}
|
|
31951
32715
|
const output = def.compound ? formatCompound(name, def) : formatSimple(name, def);
|
|
31952
32716
|
return { output, error: null, exitCode: 0 };
|
|
31953
32717
|
}
|
|
31954
32718
|
function runInfo(args, spec) {
|
|
31955
32719
|
if (args.length === 0 || args[0] === "--help") {
|
|
31956
|
-
console.log("Usage: dryui info <component>");
|
|
32720
|
+
console.log("Usage: dryui info <component> [--toon] [--full]");
|
|
31957
32721
|
console.log("");
|
|
31958
32722
|
console.log("Show API reference for a DryUI component or composed output.");
|
|
31959
32723
|
console.log("");
|
|
32724
|
+
console.log("Options:");
|
|
32725
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
32726
|
+
console.log(" --full Include full example code (disables truncation)");
|
|
32727
|
+
console.log("");
|
|
31960
32728
|
console.log("Examples:");
|
|
31961
32729
|
console.log(" dryui info Button");
|
|
31962
32730
|
console.log(" dryui info card # case-insensitive");
|
|
31963
|
-
console.log(
|
|
32731
|
+
console.log(" dryui info Button --toon");
|
|
31964
32732
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
31965
32733
|
}
|
|
31966
|
-
const
|
|
32734
|
+
const toon = args.includes("--toon");
|
|
32735
|
+
const full = args.includes("--full");
|
|
32736
|
+
const query = args.find((a) => !a.startsWith("--"));
|
|
31967
32737
|
if (!query) {
|
|
31968
32738
|
console.error("Error: missing component name");
|
|
31969
32739
|
process.exit(1);
|
|
31970
32740
|
}
|
|
31971
|
-
|
|
32741
|
+
const mode = toon ? "toon" : "text";
|
|
32742
|
+
runCommand(getInfo(query, spec, { toon, full }), mode);
|
|
31972
32743
|
}
|
|
31973
32744
|
|
|
31974
32745
|
// src/format.ts
|
|
@@ -32028,7 +32799,7 @@ function formatWorkspaceReport(report, options) {
|
|
|
32028
32799
|
// src/commands/project-planner.ts
|
|
32029
32800
|
function importStatement(name, def, subpath = false) {
|
|
32030
32801
|
if (subpath && def.import === "@dryui/ui") {
|
|
32031
|
-
return `import { ${name} } from '${def.import}/${
|
|
32802
|
+
return `import { ${name} } from '${def.import}/${componentDir2(name)}';`;
|
|
32032
32803
|
}
|
|
32033
32804
|
return `import { ${name} } from '${def.import}';`;
|
|
32034
32805
|
}
|
|
@@ -32150,7 +32921,7 @@ function formatAddPlan(plan) {
|
|
|
32150
32921
|
`);
|
|
32151
32922
|
}
|
|
32152
32923
|
function buildAddSnippet(query, spec, options = {}) {
|
|
32153
|
-
const component =
|
|
32924
|
+
const component = findComponent3(spec, query);
|
|
32154
32925
|
if (!component) {
|
|
32155
32926
|
return {
|
|
32156
32927
|
output: "",
|
|
@@ -32166,342 +32937,6 @@ function buildAddSnippet(query, spec, options = {}) {
|
|
|
32166
32937
|
};
|
|
32167
32938
|
}
|
|
32168
32939
|
|
|
32169
|
-
// ../mcp/src/project-planner.ts
|
|
32170
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
32171
|
-
import { dirname, resolve } from "node:path";
|
|
32172
|
-
var DIR_OVERRIDES2 = {
|
|
32173
|
-
QRCode: "qr-code"
|
|
32174
|
-
};
|
|
32175
|
-
function componentDir2(name) {
|
|
32176
|
-
return DIR_OVERRIDES2[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
32177
|
-
}
|
|
32178
|
-
function resolveStart(inputPath) {
|
|
32179
|
-
const candidate = resolve(inputPath ?? process.cwd());
|
|
32180
|
-
return existsSync(candidate) && statSync(candidate).isFile() ? dirname(candidate) : candidate;
|
|
32181
|
-
}
|
|
32182
|
-
function findUp(start, fileName) {
|
|
32183
|
-
let current = start;
|
|
32184
|
-
while (true) {
|
|
32185
|
-
const candidate = resolve(current, fileName);
|
|
32186
|
-
if (existsSync(candidate))
|
|
32187
|
-
return candidate;
|
|
32188
|
-
const parent = dirname(current);
|
|
32189
|
-
if (parent === current)
|
|
32190
|
-
return null;
|
|
32191
|
-
current = parent;
|
|
32192
|
-
}
|
|
32193
|
-
}
|
|
32194
|
-
function findUpAny(start, fileNames) {
|
|
32195
|
-
let current = start;
|
|
32196
|
-
while (true) {
|
|
32197
|
-
for (const fileName of fileNames) {
|
|
32198
|
-
const candidate = resolve(current, fileName);
|
|
32199
|
-
if (existsSync(candidate))
|
|
32200
|
-
return candidate;
|
|
32201
|
-
}
|
|
32202
|
-
const parent = dirname(current);
|
|
32203
|
-
if (parent === current)
|
|
32204
|
-
return null;
|
|
32205
|
-
current = parent;
|
|
32206
|
-
}
|
|
32207
|
-
}
|
|
32208
|
-
function detectPackageManager(root) {
|
|
32209
|
-
if (!root)
|
|
32210
|
-
return "unknown";
|
|
32211
|
-
const lockfilePath = findUpAny(root, [
|
|
32212
|
-
"bun.lock",
|
|
32213
|
-
"bun.lockb",
|
|
32214
|
-
"pnpm-lock.yaml",
|
|
32215
|
-
"package-lock.json",
|
|
32216
|
-
"yarn.lock"
|
|
32217
|
-
]);
|
|
32218
|
-
if (!lockfilePath)
|
|
32219
|
-
return "unknown";
|
|
32220
|
-
if (lockfilePath.endsWith("bun.lock") || lockfilePath.endsWith("bun.lockb"))
|
|
32221
|
-
return "bun";
|
|
32222
|
-
if (lockfilePath.endsWith("pnpm-lock.yaml"))
|
|
32223
|
-
return "pnpm";
|
|
32224
|
-
if (lockfilePath.endsWith("package-lock.json"))
|
|
32225
|
-
return "npm";
|
|
32226
|
-
if (lockfilePath.endsWith("yarn.lock"))
|
|
32227
|
-
return "yarn";
|
|
32228
|
-
return "unknown";
|
|
32229
|
-
}
|
|
32230
|
-
function readPackageJson(packageJsonPath) {
|
|
32231
|
-
if (!packageJsonPath)
|
|
32232
|
-
return null;
|
|
32233
|
-
return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
32234
|
-
}
|
|
32235
|
-
function getDependencyNames(pkg) {
|
|
32236
|
-
return new Set([
|
|
32237
|
-
...Object.keys(pkg?.dependencies ?? {}),
|
|
32238
|
-
...Object.keys(pkg?.devDependencies ?? {})
|
|
32239
|
-
]);
|
|
32240
|
-
}
|
|
32241
|
-
function detectFramework(dependencyNames) {
|
|
32242
|
-
if (dependencyNames.has("@sveltejs/kit"))
|
|
32243
|
-
return "sveltekit";
|
|
32244
|
-
if (dependencyNames.has("svelte"))
|
|
32245
|
-
return "svelte";
|
|
32246
|
-
return "unknown";
|
|
32247
|
-
}
|
|
32248
|
-
function hasImport(filePath, importPath) {
|
|
32249
|
-
if (!filePath)
|
|
32250
|
-
return false;
|
|
32251
|
-
return readFileSync(filePath, "utf-8").includes(importPath);
|
|
32252
|
-
}
|
|
32253
|
-
function hasThemeAuto(appHtmlPath) {
|
|
32254
|
-
if (!appHtmlPath)
|
|
32255
|
-
return false;
|
|
32256
|
-
return readFileSync(appHtmlPath, "utf-8").includes("theme-auto");
|
|
32257
|
-
}
|
|
32258
|
-
function hasAnyImport(filePaths, importPath) {
|
|
32259
|
-
return filePaths.some((filePath) => hasImport(filePath, importPath));
|
|
32260
|
-
}
|
|
32261
|
-
function importsAppCss(rootLayoutPath) {
|
|
32262
|
-
if (!rootLayoutPath)
|
|
32263
|
-
return false;
|
|
32264
|
-
const content = readFileSync(rootLayoutPath, "utf-8");
|
|
32265
|
-
return content.includes("app.css");
|
|
32266
|
-
}
|
|
32267
|
-
function buildStatus(framework, hasPackageJson, stepsNeeded) {
|
|
32268
|
-
if (!hasPackageJson || framework === "unknown")
|
|
32269
|
-
return "unsupported";
|
|
32270
|
-
return stepsNeeded === 0 ? "ready" : "partial";
|
|
32271
|
-
}
|
|
32272
|
-
function installCommand(packageManager) {
|
|
32273
|
-
switch (packageManager) {
|
|
32274
|
-
case "bun":
|
|
32275
|
-
return "bun add @dryui/ui";
|
|
32276
|
-
case "pnpm":
|
|
32277
|
-
return "pnpm add @dryui/ui";
|
|
32278
|
-
case "yarn":
|
|
32279
|
-
return "yarn add @dryui/ui";
|
|
32280
|
-
default:
|
|
32281
|
-
return "npm install @dryui/ui";
|
|
32282
|
-
}
|
|
32283
|
-
}
|
|
32284
|
-
function buildThemeImportLines(spec) {
|
|
32285
|
-
return ` import '${spec.themeImports.default}';
|
|
32286
|
-
import '${spec.themeImports.dark}';`;
|
|
32287
|
-
}
|
|
32288
|
-
function buildThemeImportSnippet(spec) {
|
|
32289
|
-
return ["<script>", buildThemeImportLines(spec), "</script>"].join(`
|
|
32290
|
-
`);
|
|
32291
|
-
}
|
|
32292
|
-
function buildRootLayoutSnippet(spec) {
|
|
32293
|
-
return [
|
|
32294
|
-
"<script>",
|
|
32295
|
-
buildThemeImportLines(spec),
|
|
32296
|
-
"",
|
|
32297
|
-
" let { children } = $props();",
|
|
32298
|
-
"</script>",
|
|
32299
|
-
"",
|
|
32300
|
-
"{@render children()}"
|
|
32301
|
-
].join(`
|
|
32302
|
-
`);
|
|
32303
|
-
}
|
|
32304
|
-
function buildThemeImportCssSnippet(spec) {
|
|
32305
|
-
return [`@import '${spec.themeImports.default}';`, `@import '${spec.themeImports.dark}';`].join(`
|
|
32306
|
-
`);
|
|
32307
|
-
}
|
|
32308
|
-
function getSuggestedTarget(root, explicitTarget) {
|
|
32309
|
-
if (explicitTarget)
|
|
32310
|
-
return resolve(root ?? process.cwd(), explicitTarget);
|
|
32311
|
-
if (!root)
|
|
32312
|
-
return null;
|
|
32313
|
-
const rootPage = resolve(root, "src/routes/+page.svelte");
|
|
32314
|
-
return existsSync(rootPage) ? rootPage : null;
|
|
32315
|
-
}
|
|
32316
|
-
function getImportStatement(name, component, subpath = false) {
|
|
32317
|
-
if (subpath && component.import === "@dryui/ui") {
|
|
32318
|
-
return `import { ${name} } from '${component.import}/${componentDir2(name)}';`;
|
|
32319
|
-
}
|
|
32320
|
-
return `import { ${name} } from '${component.import}';`;
|
|
32321
|
-
}
|
|
32322
|
-
function findComponent3(spec, query) {
|
|
32323
|
-
const exact = spec.components[query];
|
|
32324
|
-
if (exact)
|
|
32325
|
-
return { name: query, def: exact };
|
|
32326
|
-
const lower = query.toLowerCase();
|
|
32327
|
-
for (const [name, def] of Object.entries(spec.components)) {
|
|
32328
|
-
if (name.toLowerCase() === lower)
|
|
32329
|
-
return { name, def };
|
|
32330
|
-
}
|
|
32331
|
-
return null;
|
|
32332
|
-
}
|
|
32333
|
-
function detectProject(spec, inputPath) {
|
|
32334
|
-
const start = resolveStart(inputPath);
|
|
32335
|
-
const packageJsonPath = findUp(start, "package.json");
|
|
32336
|
-
const root = packageJsonPath ? dirname(packageJsonPath) : null;
|
|
32337
|
-
const dependencyNames = getDependencyNames(readPackageJson(packageJsonPath));
|
|
32338
|
-
const framework = detectFramework(dependencyNames);
|
|
32339
|
-
const appHtmlPath = root ? resolve(root, "src/app.html") : null;
|
|
32340
|
-
const appCssPath = root ? resolve(root, "src/app.css") : null;
|
|
32341
|
-
const rootLayoutPath = root ? resolve(root, "src/routes/+layout.svelte") : null;
|
|
32342
|
-
const rootPagePath = root ? resolve(root, "src/routes/+page.svelte") : null;
|
|
32343
|
-
const appHtml = appHtmlPath && existsSync(appHtmlPath) ? appHtmlPath : null;
|
|
32344
|
-
const appCss = appCssPath && existsSync(appCssPath) ? appCssPath : null;
|
|
32345
|
-
const rootLayout = rootLayoutPath && existsSync(rootLayoutPath) ? rootLayoutPath : null;
|
|
32346
|
-
const rootPage = rootPagePath && existsSync(rootPagePath) ? rootPagePath : null;
|
|
32347
|
-
const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
|
|
32348
|
-
const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
|
|
32349
|
-
const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
|
|
32350
|
-
const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!(appHtml && hasThemeAuto(appHtml)));
|
|
32351
|
-
const warnings = [];
|
|
32352
|
-
if (!packageJsonPath)
|
|
32353
|
-
warnings.push("No package.json found above the provided path.");
|
|
32354
|
-
if (framework === "unknown")
|
|
32355
|
-
warnings.push("DryUI planning currently targets Svelte and SvelteKit projects.");
|
|
32356
|
-
return {
|
|
32357
|
-
inputPath: start,
|
|
32358
|
-
root,
|
|
32359
|
-
packageJsonPath,
|
|
32360
|
-
framework,
|
|
32361
|
-
packageManager: detectPackageManager(root),
|
|
32362
|
-
status: buildStatus(framework, Boolean(packageJsonPath), stepsNeeded),
|
|
32363
|
-
dependencies: {
|
|
32364
|
-
ui: dependencyNames.has("@dryui/ui"),
|
|
32365
|
-
primitives: dependencyNames.has("@dryui/primitives")
|
|
32366
|
-
},
|
|
32367
|
-
files: {
|
|
32368
|
-
appHtml,
|
|
32369
|
-
appCss,
|
|
32370
|
-
rootLayout,
|
|
32371
|
-
rootPage
|
|
32372
|
-
},
|
|
32373
|
-
theme: {
|
|
32374
|
-
defaultImported,
|
|
32375
|
-
darkImported,
|
|
32376
|
-
themeAuto: hasThemeAuto(appHtml)
|
|
32377
|
-
},
|
|
32378
|
-
warnings
|
|
32379
|
-
};
|
|
32380
|
-
}
|
|
32381
|
-
function planInstall(spec, inputPath) {
|
|
32382
|
-
const detection = detectProject(spec, inputPath);
|
|
32383
|
-
const steps = [];
|
|
32384
|
-
if (detection.status === "unsupported") {
|
|
32385
|
-
steps.push({
|
|
32386
|
-
kind: "blocked",
|
|
32387
|
-
status: "blocked",
|
|
32388
|
-
title: "Project detection incomplete",
|
|
32389
|
-
description: detection.warnings.join(" ") || "DryUI install planning requires a Svelte or SvelteKit project with a package.json."
|
|
32390
|
-
});
|
|
32391
|
-
return { detection, steps };
|
|
32392
|
-
}
|
|
32393
|
-
if (!detection.dependencies.ui) {
|
|
32394
|
-
steps.push({
|
|
32395
|
-
kind: "install-package",
|
|
32396
|
-
status: "pending",
|
|
32397
|
-
title: "Install @dryui/ui",
|
|
32398
|
-
description: "Add the styled DryUI package to the current project.",
|
|
32399
|
-
command: installCommand(detection.packageManager)
|
|
32400
|
-
});
|
|
32401
|
-
}
|
|
32402
|
-
if (!detection.theme.defaultImported || !detection.theme.darkImported) {
|
|
32403
|
-
if (detection.files.appCss && detection.files.rootLayout && importsAppCss(detection.files.rootLayout)) {
|
|
32404
|
-
steps.push({
|
|
32405
|
-
kind: "edit-file",
|
|
32406
|
-
status: "pending",
|
|
32407
|
-
title: "Add theme imports to app.css",
|
|
32408
|
-
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.",
|
|
32409
|
-
path: detection.files.appCss,
|
|
32410
|
-
snippet: buildThemeImportCssSnippet(spec)
|
|
32411
|
-
});
|
|
32412
|
-
} else if (!detection.files.rootLayout) {
|
|
32413
|
-
const path = detection.root ? resolve(detection.root, "src/routes/+layout.svelte") : null;
|
|
32414
|
-
steps.push({
|
|
32415
|
-
kind: "create-file",
|
|
32416
|
-
status: "pending",
|
|
32417
|
-
title: "Create root layout with theme imports",
|
|
32418
|
-
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.",
|
|
32419
|
-
...path ? { path } : {},
|
|
32420
|
-
snippet: buildRootLayoutSnippet(spec)
|
|
32421
|
-
});
|
|
32422
|
-
} else {
|
|
32423
|
-
steps.push({
|
|
32424
|
-
kind: "edit-file",
|
|
32425
|
-
status: "pending",
|
|
32426
|
-
title: "Add theme imports to the root layout",
|
|
32427
|
-
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.",
|
|
32428
|
-
path: detection.files.rootLayout,
|
|
32429
|
-
snippet: buildThemeImportSnippet(spec)
|
|
32430
|
-
});
|
|
32431
|
-
}
|
|
32432
|
-
}
|
|
32433
|
-
if (!detection.files.appHtml) {
|
|
32434
|
-
steps.push({
|
|
32435
|
-
kind: "blocked",
|
|
32436
|
-
status: "blocked",
|
|
32437
|
-
title: "app.html not found",
|
|
32438
|
-
description: "DryUI expects src/app.html so the document can default to theme-auto mode."
|
|
32439
|
-
});
|
|
32440
|
-
} else if (!detection.theme.themeAuto) {
|
|
32441
|
-
steps.push({
|
|
32442
|
-
kind: "edit-file",
|
|
32443
|
-
status: "pending",
|
|
32444
|
-
title: "Set html theme mode to auto",
|
|
32445
|
-
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.',
|
|
32446
|
-
path: detection.files.appHtml,
|
|
32447
|
-
snippet: '<html lang="en" class="theme-auto">'
|
|
32448
|
-
});
|
|
32449
|
-
}
|
|
32450
|
-
if (steps.length === 0) {
|
|
32451
|
-
steps.push({
|
|
32452
|
-
kind: "note",
|
|
32453
|
-
status: "done",
|
|
32454
|
-
title: "DryUI install plan is complete",
|
|
32455
|
-
description: "The project already has @dryui/ui, theme imports, and theme-auto configured."
|
|
32456
|
-
});
|
|
32457
|
-
}
|
|
32458
|
-
return { detection, steps };
|
|
32459
|
-
}
|
|
32460
|
-
function planAdd(spec, query, options = {}) {
|
|
32461
|
-
const installPlan = planInstall(spec, options.cwd);
|
|
32462
|
-
const component = findComponent3(spec, query);
|
|
32463
|
-
if (component) {
|
|
32464
|
-
const target = getSuggestedTarget(installPlan.detection.root, options.target);
|
|
32465
|
-
const steps = [];
|
|
32466
|
-
const warnings = [...installPlan.detection.warnings];
|
|
32467
|
-
if (installPlan.steps.some((step) => step.status === "pending" || step.status === "blocked")) {
|
|
32468
|
-
steps.push({
|
|
32469
|
-
kind: "note",
|
|
32470
|
-
status: "info",
|
|
32471
|
-
title: "Complete install plan first",
|
|
32472
|
-
description: "Apply the install plan before inserting DryUI components into project files."
|
|
32473
|
-
});
|
|
32474
|
-
}
|
|
32475
|
-
steps.push(target ? {
|
|
32476
|
-
kind: "edit-file",
|
|
32477
|
-
status: "pending",
|
|
32478
|
-
title: "Insert component into the target file",
|
|
32479
|
-
description: "Add the import and snippet below to the chosen Svelte file.",
|
|
32480
|
-
path: target,
|
|
32481
|
-
snippet: `${getImportStatement(component.name, component.def, options.subpath)}
|
|
32482
|
-
|
|
32483
|
-
${component.def.example}`
|
|
32484
|
-
} : {
|
|
32485
|
-
kind: "note",
|
|
32486
|
-
status: "info",
|
|
32487
|
-
title: "Choose a target Svelte file",
|
|
32488
|
-
description: "No root page was found. Pick a target file and reuse the import and snippet in this plan."
|
|
32489
|
-
});
|
|
32490
|
-
return {
|
|
32491
|
-
detection: installPlan.detection,
|
|
32492
|
-
installPlan,
|
|
32493
|
-
targetType: "component",
|
|
32494
|
-
name: component.name,
|
|
32495
|
-
importStatement: getImportStatement(component.name, component.def, options.subpath),
|
|
32496
|
-
snippet: component.def.example,
|
|
32497
|
-
target,
|
|
32498
|
-
steps,
|
|
32499
|
-
warnings
|
|
32500
|
-
};
|
|
32501
|
-
}
|
|
32502
|
-
throw new Error(`Unknown component: "${query}"`);
|
|
32503
|
-
}
|
|
32504
|
-
|
|
32505
32940
|
// src/commands/add.ts
|
|
32506
32941
|
function parseProjectInput(positionals, spec) {
|
|
32507
32942
|
const first = positionals[0];
|
|
@@ -32513,8 +32948,8 @@ function parseProjectInput(positionals, spec) {
|
|
|
32513
32948
|
return { query: first };
|
|
32514
32949
|
}
|
|
32515
32950
|
if (positionals.length === 2) {
|
|
32516
|
-
const firstIsQuery = Boolean(
|
|
32517
|
-
const secondIsQuery = Boolean(
|
|
32951
|
+
const firstIsQuery = Boolean(findComponent3(spec, first));
|
|
32952
|
+
const secondIsQuery = Boolean(findComponent3(spec, second));
|
|
32518
32953
|
if (firstIsQuery && !secondIsQuery) {
|
|
32519
32954
|
return { query: first, cwd: second };
|
|
32520
32955
|
}
|
|
@@ -32641,6 +33076,7 @@ function runAdd(args, spec) {
|
|
|
32641
33076
|
// src/commands/detect.ts
|
|
32642
33077
|
function parseDetectArgs(args) {
|
|
32643
33078
|
let json = false;
|
|
33079
|
+
let toon = false;
|
|
32644
33080
|
let path;
|
|
32645
33081
|
for (let index = 0;index < args.length; index++) {
|
|
32646
33082
|
const arg = args[index];
|
|
@@ -32650,6 +33086,10 @@ function parseDetectArgs(args) {
|
|
|
32650
33086
|
json = true;
|
|
32651
33087
|
continue;
|
|
32652
33088
|
}
|
|
33089
|
+
if (arg === "--toon") {
|
|
33090
|
+
toon = true;
|
|
33091
|
+
continue;
|
|
33092
|
+
}
|
|
32653
33093
|
if (arg.startsWith("--")) {
|
|
32654
33094
|
continue;
|
|
32655
33095
|
}
|
|
@@ -32657,24 +33097,29 @@ function parseDetectArgs(args) {
|
|
|
32657
33097
|
path = arg;
|
|
32658
33098
|
}
|
|
32659
33099
|
}
|
|
32660
|
-
return { json, path };
|
|
33100
|
+
return { json, toon, path };
|
|
32661
33101
|
}
|
|
32662
33102
|
function getDetect(inputPath, spec, options = {}) {
|
|
32663
33103
|
const detection = detectProject(spec, inputPath);
|
|
33104
|
+
if (options.toon) {
|
|
33105
|
+
return { output: toonProjectDetection(detection), error: null, exitCode: 0 };
|
|
33106
|
+
}
|
|
32664
33107
|
return options.json ? { output: JSON.stringify(detection, null, 2), error: null, exitCode: 0 } : { output: formatProjectDetection(detection), error: null, exitCode: 0 };
|
|
32665
33108
|
}
|
|
32666
33109
|
function runDetect(args, spec) {
|
|
32667
33110
|
if (args[0] === "--help") {
|
|
32668
|
-
console.log("Usage: dryui detect [--json] [path]");
|
|
33111
|
+
console.log("Usage: dryui detect [--json] [--toon] [path]");
|
|
32669
33112
|
console.log("");
|
|
32670
33113
|
console.log("Detect the current DryUI project setup.");
|
|
32671
33114
|
console.log("");
|
|
32672
33115
|
console.log("Options:");
|
|
32673
33116
|
console.log(" --json Output raw JSON instead of formatted text");
|
|
33117
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
32674
33118
|
process.exit(0);
|
|
32675
33119
|
}
|
|
32676
|
-
const { json, path } = parseDetectArgs(args);
|
|
32677
|
-
|
|
33120
|
+
const { json, toon, path } = parseDetectArgs(args);
|
|
33121
|
+
const mode = toon ? "toon" : json ? "json" : "text";
|
|
33122
|
+
runCommand(getDetect(path, spec, { json, toon }), mode);
|
|
32678
33123
|
}
|
|
32679
33124
|
|
|
32680
33125
|
// src/commands/install.ts
|
|
@@ -32781,7 +33226,14 @@ function groupByCategory(spec) {
|
|
|
32781
33226
|
}
|
|
32782
33227
|
return groups;
|
|
32783
33228
|
}
|
|
32784
|
-
function getList(category, spec, options
|
|
33229
|
+
function getList(category, spec, options) {
|
|
33230
|
+
if (options?.toon) {
|
|
33231
|
+
return {
|
|
33232
|
+
output: toonComponentList(spec.components, category ?? undefined),
|
|
33233
|
+
error: null,
|
|
33234
|
+
exitCode: 0
|
|
33235
|
+
};
|
|
33236
|
+
}
|
|
32785
33237
|
const sections = [];
|
|
32786
33238
|
const validCategories = getCategories(spec);
|
|
32787
33239
|
if (category && !validCategories.includes(category)) {
|
|
@@ -32815,18 +33267,20 @@ function getList(category, spec, options = {}) {
|
|
|
32815
33267
|
}
|
|
32816
33268
|
function runList(args, spec) {
|
|
32817
33269
|
if (args[0] === "--help") {
|
|
32818
|
-
console.log("Usage: dryui list [--category <category>]");
|
|
33270
|
+
console.log("Usage: dryui list [--category <category>] [--toon]");
|
|
32819
33271
|
console.log("");
|
|
32820
33272
|
console.log("List DryUI components.");
|
|
32821
33273
|
console.log("");
|
|
32822
33274
|
console.log("Options:");
|
|
32823
33275
|
console.log(" --category <cat> Filter by category");
|
|
33276
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
32824
33277
|
console.log("");
|
|
32825
33278
|
console.log("Categories:");
|
|
32826
33279
|
const cats = getCategories(spec);
|
|
32827
33280
|
console.log(` ${cats.join(", ")}`);
|
|
32828
33281
|
process.exit(0);
|
|
32829
33282
|
}
|
|
33283
|
+
const toon = args.includes("--toon");
|
|
32830
33284
|
let filterCategory = null;
|
|
32831
33285
|
const catIdx = args.indexOf("--category");
|
|
32832
33286
|
if (catIdx !== -1) {
|
|
@@ -32837,11 +33291,12 @@ function runList(args, spec) {
|
|
|
32837
33291
|
}
|
|
32838
33292
|
filterCategory = catValue.toLowerCase();
|
|
32839
33293
|
}
|
|
32840
|
-
|
|
33294
|
+
const mode = toon ? "toon" : "text";
|
|
33295
|
+
runCommand(getList(filterCategory, spec, { toon }), mode);
|
|
32841
33296
|
}
|
|
32842
33297
|
|
|
32843
33298
|
// src/commands/review.ts
|
|
32844
|
-
import { readFileSync as readFileSync2
|
|
33299
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
32845
33300
|
|
|
32846
33301
|
// ../mcp/src/utils.ts
|
|
32847
33302
|
function buildLineOffsets(text) {
|
|
@@ -33542,12 +33997,19 @@ function reviewComponent(code, spec, filename) {
|
|
|
33542
33997
|
|
|
33543
33998
|
// src/commands/review.ts
|
|
33544
33999
|
function getReview(filePath, spec, options) {
|
|
33545
|
-
|
|
33546
|
-
|
|
33547
|
-
|
|
34000
|
+
const missing = fileNotFound(filePath, options?.toon);
|
|
34001
|
+
if (missing)
|
|
34002
|
+
return missing;
|
|
33548
34003
|
const code = readFileSync2(filePath, "utf-8");
|
|
33549
34004
|
const filename = filePath.split("/").pop();
|
|
33550
34005
|
const result = reviewComponent(code, spec, filename);
|
|
34006
|
+
if (options?.toon) {
|
|
34007
|
+
return {
|
|
34008
|
+
output: toonReviewResult(result),
|
|
34009
|
+
error: null,
|
|
34010
|
+
exitCode: result.issues.some((i) => i.severity === "error") ? 1 : 0
|
|
34011
|
+
};
|
|
34012
|
+
}
|
|
33551
34013
|
if (options?.json) {
|
|
33552
34014
|
return {
|
|
33553
34015
|
output: JSON.stringify(result, null, 2),
|
|
@@ -33574,7 +34036,7 @@ function getReview(filePath, spec, options) {
|
|
|
33574
34036
|
}
|
|
33575
34037
|
function runReview(args, spec) {
|
|
33576
34038
|
if (args.length === 0 || args[0] === "--help") {
|
|
33577
|
-
console.log("Usage: dryui review [--json] <file.svelte>");
|
|
34039
|
+
console.log("Usage: dryui review [--json] [--toon] <file.svelte>");
|
|
33578
34040
|
console.log("");
|
|
33579
34041
|
console.log("Validate a Svelte component against the DryUI spec.");
|
|
33580
34042
|
console.log("Checks for incorrect component usage, missing props,");
|
|
@@ -33582,20 +34044,23 @@ function runReview(args, spec) {
|
|
|
33582
34044
|
console.log("");
|
|
33583
34045
|
console.log("Options:");
|
|
33584
34046
|
console.log(" --json Output raw JSON instead of formatted text");
|
|
34047
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
33585
34048
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
33586
34049
|
}
|
|
33587
34050
|
const jsonMode = args.includes("--json");
|
|
33588
|
-
const
|
|
34051
|
+
const toon = args.includes("--toon");
|
|
34052
|
+
const fileArgs = args.filter((a) => !a.startsWith("--"));
|
|
33589
34053
|
const filePath = fileArgs[0];
|
|
33590
34054
|
if (!filePath) {
|
|
33591
34055
|
console.error("Error: missing file path");
|
|
33592
34056
|
process.exit(1);
|
|
33593
34057
|
}
|
|
33594
|
-
|
|
34058
|
+
const mode = toon ? "toon" : jsonMode ? "json" : "text";
|
|
34059
|
+
runCommand(getReview(filePath, spec, { json: jsonMode, toon }), mode);
|
|
33595
34060
|
}
|
|
33596
34061
|
|
|
33597
34062
|
// src/commands/diagnose.ts
|
|
33598
|
-
import { readFileSync as readFileSync3
|
|
34063
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
33599
34064
|
|
|
33600
34065
|
// ../mcp/src/theme-checker.ts
|
|
33601
34066
|
function capture(match, index) {
|
|
@@ -34351,11 +34816,18 @@ function diagnoseTheme(css, spec) {
|
|
|
34351
34816
|
|
|
34352
34817
|
// src/commands/diagnose.ts
|
|
34353
34818
|
function getDiagnose(filePath, spec, options) {
|
|
34354
|
-
|
|
34355
|
-
|
|
34356
|
-
|
|
34819
|
+
const missing = fileNotFound(filePath, options?.toon);
|
|
34820
|
+
if (missing)
|
|
34821
|
+
return missing;
|
|
34357
34822
|
const css = readFileSync3(filePath, "utf-8");
|
|
34358
34823
|
const result = diagnoseTheme(css, spec);
|
|
34824
|
+
if (options?.toon) {
|
|
34825
|
+
return {
|
|
34826
|
+
output: toonDiagnoseResult(result),
|
|
34827
|
+
error: null,
|
|
34828
|
+
exitCode: result.issues.some((i) => i.severity === "error") ? 1 : 0
|
|
34829
|
+
};
|
|
34830
|
+
}
|
|
34359
34831
|
if (options?.json) {
|
|
34360
34832
|
return {
|
|
34361
34833
|
output: JSON.stringify(result, null, 2),
|
|
@@ -34388,26 +34860,29 @@ function getDiagnose(filePath, spec, options) {
|
|
|
34388
34860
|
}
|
|
34389
34861
|
function runDiagnose(args, spec) {
|
|
34390
34862
|
if (args.length === 0 || args[0] === "--help") {
|
|
34391
|
-
console.log("Usage: dryui diagnose [--json] <file.css>");
|
|
34863
|
+
console.log("Usage: dryui diagnose [--json] [--toon] <file.css>");
|
|
34392
34864
|
console.log("");
|
|
34393
34865
|
console.log("Validate theme CSS for missing tokens, value errors,");
|
|
34394
34866
|
console.log("contrast issues, and component token problems.");
|
|
34395
34867
|
console.log("");
|
|
34396
34868
|
console.log("Options:");
|
|
34397
34869
|
console.log(" --json Output raw JSON instead of formatted text");
|
|
34870
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
34398
34871
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
34399
34872
|
}
|
|
34400
34873
|
const jsonMode = args.includes("--json");
|
|
34401
|
-
const
|
|
34874
|
+
const toon = args.includes("--toon");
|
|
34875
|
+
const fileArgs = args.filter((a) => !a.startsWith("--"));
|
|
34402
34876
|
const filePath = fileArgs[0];
|
|
34403
34877
|
if (!filePath) {
|
|
34404
34878
|
console.error("Error: missing file path");
|
|
34405
34879
|
process.exit(1);
|
|
34406
34880
|
}
|
|
34407
|
-
|
|
34881
|
+
const mode = toon ? "toon" : jsonMode ? "json" : "text";
|
|
34882
|
+
runCommand(getDiagnose(filePath, spec, { json: jsonMode, toon }), mode);
|
|
34408
34883
|
}
|
|
34409
34884
|
|
|
34410
|
-
//
|
|
34885
|
+
// ../mcp/dist/composition-search.js
|
|
34411
34886
|
var STOP_WORDS = new Set([
|
|
34412
34887
|
"a",
|
|
34413
34888
|
"an",
|
|
@@ -34473,81 +34948,58 @@ function scoreText(tokens, text) {
|
|
|
34473
34948
|
const lower = text.toLowerCase();
|
|
34474
34949
|
return tokens.reduce((score, t) => score + (lower.includes(t) ? 1 : 0), 0);
|
|
34475
34950
|
}
|
|
34476
|
-
function
|
|
34477
|
-
if (!spec.composition) {
|
|
34478
|
-
return {
|
|
34479
|
-
output: "",
|
|
34480
|
-
error: "No composition data available. Rebuild the MCP package.",
|
|
34481
|
-
exitCode: 1
|
|
34482
|
-
};
|
|
34483
|
-
}
|
|
34951
|
+
function searchComposition(composition, query) {
|
|
34484
34952
|
const q = query.toLowerCase();
|
|
34485
34953
|
const tokens = tokenize(query);
|
|
34486
34954
|
const exactComponentMatches = [];
|
|
34487
34955
|
const exactRecipeMatches = [];
|
|
34488
|
-
for (const comp of Object.values(
|
|
34956
|
+
for (const comp of Object.values(composition.components)) {
|
|
34489
34957
|
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))) {
|
|
34490
34958
|
exactComponentMatches.push(comp);
|
|
34491
34959
|
}
|
|
34492
34960
|
}
|
|
34493
|
-
for (const recipe of Object.values(
|
|
34961
|
+
for (const recipe of Object.values(composition.recipes)) {
|
|
34494
34962
|
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))) {
|
|
34495
34963
|
exactRecipeMatches.push(recipe);
|
|
34496
34964
|
}
|
|
34497
34965
|
}
|
|
34498
|
-
let componentMatches;
|
|
34499
|
-
let recipeMatches;
|
|
34500
34966
|
if (exactComponentMatches.length || exactRecipeMatches.length) {
|
|
34501
|
-
componentMatches
|
|
34502
|
-
|
|
34503
|
-
|
|
34504
|
-
|
|
34505
|
-
|
|
34506
|
-
|
|
34507
|
-
|
|
34508
|
-
|
|
34509
|
-
|
|
34510
|
-
|
|
34511
|
-
|
|
34512
|
-
|
|
34513
|
-
|
|
34514
|
-
|
|
34515
|
-
|
|
34516
|
-
|
|
34517
|
-
|
|
34518
|
-
|
|
34519
|
-
|
|
34520
|
-
|
|
34521
|
-
|
|
34522
|
-
|
|
34523
|
-
|
|
34524
|
-
|
|
34525
|
-
|
|
34526
|
-
|
|
34527
|
-
|
|
34528
|
-
|
|
34529
|
-
|
|
34530
|
-
componentMatches
|
|
34531
|
-
recipeMatches
|
|
34532
|
-
}
|
|
34533
|
-
|
|
34534
|
-
|
|
34535
|
-
output: "",
|
|
34536
|
-
error: `No composition guidance found for "${query}".
|
|
34537
|
-
|
|
34538
|
-
Try:
|
|
34539
|
-
- A component name (e.g. "DatePicker", "Avatar")
|
|
34540
|
-
- A UI concept (e.g. "date input", "image placeholder")
|
|
34541
|
-
- A pattern name (e.g. "search-form", "dashboard-page")`,
|
|
34542
|
-
exitCode: 1
|
|
34543
|
-
};
|
|
34544
|
-
}
|
|
34967
|
+
return { componentMatches: exactComponentMatches, recipeMatches: exactRecipeMatches };
|
|
34968
|
+
}
|
|
34969
|
+
if (!tokens.length) {
|
|
34970
|
+
return { componentMatches: [], recipeMatches: [] };
|
|
34971
|
+
}
|
|
34972
|
+
const scoredComponents = [];
|
|
34973
|
+
for (const comp of Object.values(composition.components)) {
|
|
34974
|
+
const corpus = [
|
|
34975
|
+
comp.component,
|
|
34976
|
+
comp.useWhen,
|
|
34977
|
+
...comp.alternatives.flatMap((a) => [a.component, a.useWhen]),
|
|
34978
|
+
...comp.antiPatterns.map((ap) => ap.pattern),
|
|
34979
|
+
...comp.combinesWith
|
|
34980
|
+
].join(" ");
|
|
34981
|
+
const score = scoreText(tokens, corpus);
|
|
34982
|
+
if (score > 0)
|
|
34983
|
+
scoredComponents.push({ comp, score });
|
|
34984
|
+
}
|
|
34985
|
+
const scoredRecipes = [];
|
|
34986
|
+
for (const recipe of Object.values(composition.recipes)) {
|
|
34987
|
+
const corpus = [recipe.name, recipe.description, ...recipe.tags, ...recipe.components].join(" ");
|
|
34988
|
+
const score = scoreText(tokens, corpus);
|
|
34989
|
+
if (score > 0)
|
|
34990
|
+
scoredRecipes.push({ recipe, score });
|
|
34991
|
+
}
|
|
34992
|
+
scoredComponents.sort((a, b) => b.score - a.score);
|
|
34993
|
+
scoredRecipes.sort((a, b) => b.score - a.score);
|
|
34994
|
+
const minScore = Math.max(1, Math.floor(tokens.length * 0.3));
|
|
34995
|
+
return {
|
|
34996
|
+
componentMatches: scoredComponents.filter((s) => s.score >= minScore).slice(0, 10).map((s) => s.comp),
|
|
34997
|
+
recipeMatches: scoredRecipes.filter((s) => s.score >= minScore).slice(0, 5).map((s) => s.recipe)
|
|
34998
|
+
};
|
|
34999
|
+
}
|
|
35000
|
+
function formatCompositionResult(results) {
|
|
34545
35001
|
const lines = [];
|
|
34546
|
-
|
|
34547
|
-
lines.push(' and dark.css. app.html needs <html class="theme-auto">.');
|
|
34548
|
-
lines.push(' Not set up? Run: dryui compose "app shell"');
|
|
34549
|
-
lines.push("");
|
|
34550
|
-
for (const comp of componentMatches) {
|
|
35002
|
+
for (const comp of results.componentMatches) {
|
|
34551
35003
|
lines.push(`── ${comp.component} ──────────────────────────────`);
|
|
34552
35004
|
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
34553
35005
|
lines.push(`Use: ${comp.component} — ${comp.useWhen}`);
|
|
@@ -34570,7 +35022,7 @@ Try:
|
|
|
34570
35022
|
}
|
|
34571
35023
|
lines.push("");
|
|
34572
35024
|
}
|
|
34573
|
-
for (const recipe of recipeMatches) {
|
|
35025
|
+
for (const recipe of results.recipeMatches) {
|
|
34574
35026
|
lines.push(`── Recipe: ${recipe.name} ──────────────────────────────`);
|
|
34575
35027
|
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
34576
35028
|
lines.push(recipe.description);
|
|
@@ -34580,29 +35032,88 @@ Try:
|
|
|
34580
35032
|
lines.push(recipe.snippet);
|
|
34581
35033
|
lines.push("");
|
|
34582
35034
|
}
|
|
34583
|
-
return
|
|
34584
|
-
`)
|
|
35035
|
+
return lines.join(`
|
|
35036
|
+
`);
|
|
35037
|
+
}
|
|
35038
|
+
|
|
35039
|
+
// src/commands/compose.ts
|
|
35040
|
+
var SETUP_PREAMBLE = [
|
|
35041
|
+
"⚠ SETUP: Root +layout.svelte must import '@dryui/ui/themes/default.css'",
|
|
35042
|
+
' and dark.css. app.html needs <html class="theme-auto">.',
|
|
35043
|
+
' Not set up? Run: dryui compose "app shell"',
|
|
35044
|
+
""
|
|
35045
|
+
].join(`
|
|
35046
|
+
`);
|
|
35047
|
+
function getCompose(query, spec, options) {
|
|
35048
|
+
if (!spec.composition) {
|
|
35049
|
+
return {
|
|
35050
|
+
output: "",
|
|
35051
|
+
error: "No composition data available. Rebuild the MCP package.",
|
|
35052
|
+
exitCode: 1
|
|
35053
|
+
};
|
|
35054
|
+
}
|
|
35055
|
+
const results = searchComposition(spec.composition, query);
|
|
35056
|
+
if (!results.componentMatches.length && !results.recipeMatches.length) {
|
|
35057
|
+
if (options?.toon) {
|
|
35058
|
+
return {
|
|
35059
|
+
output: "",
|
|
35060
|
+
error: toonError("no-results", `No composition guidance for "${query}"`, [
|
|
35061
|
+
"Try a component name (DatePicker, Avatar)",
|
|
35062
|
+
"Try a UI concept (date input, image placeholder)",
|
|
35063
|
+
"Try a pattern name (search-form, dashboard-page)"
|
|
35064
|
+
]),
|
|
35065
|
+
exitCode: 1
|
|
35066
|
+
};
|
|
35067
|
+
}
|
|
35068
|
+
return {
|
|
35069
|
+
output: "",
|
|
35070
|
+
error: `No composition guidance found for "${query}".
|
|
35071
|
+
|
|
35072
|
+
Try:
|
|
35073
|
+
- A component name (e.g. "DatePicker", "Avatar")
|
|
35074
|
+
- A UI concept (e.g. "date input", "image placeholder")
|
|
35075
|
+
- A pattern name (e.g. "search-form", "dashboard-page")`,
|
|
35076
|
+
exitCode: 1
|
|
35077
|
+
};
|
|
35078
|
+
}
|
|
35079
|
+
if (options?.toon) {
|
|
35080
|
+
return {
|
|
35081
|
+
output: toonComposition(results, { full: options?.full }),
|
|
35082
|
+
error: null,
|
|
35083
|
+
exitCode: 0
|
|
35084
|
+
};
|
|
35085
|
+
}
|
|
35086
|
+
const output = SETUP_PREAMBLE + `
|
|
35087
|
+
` + formatCompositionResult(results);
|
|
35088
|
+
return { output: output.trimEnd(), error: null, exitCode: 0 };
|
|
34585
35089
|
}
|
|
34586
35090
|
function runCompose(args, spec) {
|
|
34587
35091
|
if (args.length === 0 || args[0] === "--help") {
|
|
34588
|
-
console.log("Usage: dryui compose <query>");
|
|
35092
|
+
console.log("Usage: dryui compose <query> [--toon] [--full]");
|
|
34589
35093
|
console.log("");
|
|
34590
35094
|
console.log("Look up composition guidance for building UIs with DryUI.");
|
|
34591
35095
|
console.log("Returns ranked component alternatives, anti-patterns, and recipes.");
|
|
34592
35096
|
console.log("");
|
|
35097
|
+
console.log("Options:");
|
|
35098
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
35099
|
+
console.log(" --full Include full code snippets (disables truncation)");
|
|
35100
|
+
console.log("");
|
|
34593
35101
|
console.log("Examples:");
|
|
34594
35102
|
console.log(' dryui compose "search form"');
|
|
34595
35103
|
console.log(' dryui compose "hotel card"');
|
|
34596
|
-
console.log(' dryui compose "travel booking"');
|
|
35104
|
+
console.log(' dryui compose "travel booking" --toon');
|
|
34597
35105
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
34598
35106
|
}
|
|
34599
|
-
const
|
|
34600
|
-
|
|
35107
|
+
const toon = args.includes("--toon");
|
|
35108
|
+
const full = args.includes("--full");
|
|
35109
|
+
const query = args.filter((a) => !a.startsWith("--")).join(" ").trim();
|
|
35110
|
+
const mode = toon ? "toon" : "text";
|
|
35111
|
+
runCommand(getCompose(query, spec, { toon, full }), mode);
|
|
34601
35112
|
}
|
|
34602
35113
|
|
|
34603
35114
|
// ../mcp/src/workspace-audit.ts
|
|
34604
35115
|
import { execFileSync } from "node:child_process";
|
|
34605
|
-
import { existsSync as
|
|
35116
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync, realpathSync, statSync as statSync2 } from "node:fs";
|
|
34606
35117
|
import { dirname as dirname2, relative, resolve as resolve2 } from "node:path";
|
|
34607
35118
|
var DEFAULT_INCLUDE = [
|
|
34608
35119
|
"packages/ui",
|
|
@@ -34635,7 +35146,7 @@ function normalizePath(path) {
|
|
|
34635
35146
|
}
|
|
34636
35147
|
function resolveRoot(inputPath) {
|
|
34637
35148
|
const candidate = resolve2(inputPath ?? process.cwd());
|
|
34638
|
-
if (
|
|
35149
|
+
if (existsSync3(candidate) && statSync2(candidate).isFile()) {
|
|
34639
35150
|
return dirname2(candidate);
|
|
34640
35151
|
}
|
|
34641
35152
|
return candidate;
|
|
@@ -34706,7 +35217,7 @@ function collectChangedFiles(root) {
|
|
|
34706
35217
|
}
|
|
34707
35218
|
}
|
|
34708
35219
|
function defaultInclude(root) {
|
|
34709
|
-
const included = DEFAULT_INCLUDE.filter((entry) =>
|
|
35220
|
+
const included = DEFAULT_INCLUDE.filter((entry) => existsSync3(resolve2(root, entry)));
|
|
34710
35221
|
return included.length > 0 ? included : ["."];
|
|
34711
35222
|
}
|
|
34712
35223
|
function isDefaultExcluded(path) {
|
|
@@ -34880,6 +35391,8 @@ function parseWorkspaceArgs(args) {
|
|
|
34880
35391
|
const exclude = [];
|
|
34881
35392
|
let path;
|
|
34882
35393
|
let json = false;
|
|
35394
|
+
let toon = false;
|
|
35395
|
+
let full = false;
|
|
34883
35396
|
let changed = false;
|
|
34884
35397
|
let maxSeverity = "info";
|
|
34885
35398
|
for (let index = 0;index < args.length; index++) {
|
|
@@ -34890,6 +35403,14 @@ function parseWorkspaceArgs(args) {
|
|
|
34890
35403
|
json = true;
|
|
34891
35404
|
continue;
|
|
34892
35405
|
}
|
|
35406
|
+
if (arg === "--toon") {
|
|
35407
|
+
toon = true;
|
|
35408
|
+
continue;
|
|
35409
|
+
}
|
|
35410
|
+
if (arg === "--full") {
|
|
35411
|
+
full = true;
|
|
35412
|
+
continue;
|
|
35413
|
+
}
|
|
34893
35414
|
if (arg === "--changed") {
|
|
34894
35415
|
changed = true;
|
|
34895
35416
|
continue;
|
|
@@ -34918,7 +35439,7 @@ function parseWorkspaceArgs(args) {
|
|
|
34918
35439
|
if (!path)
|
|
34919
35440
|
path = arg;
|
|
34920
35441
|
}
|
|
34921
|
-
return { path, options: { json, include, exclude, maxSeverity, changed } };
|
|
35442
|
+
return { path, options: { json, toon, full, include, exclude, maxSeverity, changed } };
|
|
34922
35443
|
}
|
|
34923
35444
|
|
|
34924
35445
|
// src/commands/doctor.ts
|
|
@@ -34931,6 +35452,13 @@ function getDoctor(inputPath, spec, options = {}) {
|
|
|
34931
35452
|
...options.maxSeverity ? { maxSeverity: options.maxSeverity } : {},
|
|
34932
35453
|
...options.changed === undefined ? {} : { changed: options.changed }
|
|
34933
35454
|
});
|
|
35455
|
+
if (options.toon) {
|
|
35456
|
+
return {
|
|
35457
|
+
output: toonWorkspaceReport(report, { title: "doctor", command: "doctor", full: options.full }),
|
|
35458
|
+
error: null,
|
|
35459
|
+
exitCode: 0
|
|
35460
|
+
};
|
|
35461
|
+
}
|
|
34934
35462
|
return {
|
|
34935
35463
|
output: formatWorkspaceReport(report, {
|
|
34936
35464
|
title: "DryUI workspace doctor",
|
|
@@ -34950,13 +35478,18 @@ function getDoctor(inputPath, spec, options = {}) {
|
|
|
34950
35478
|
}
|
|
34951
35479
|
function runDoctor(args, spec) {
|
|
34952
35480
|
if (args[0] === "--help") {
|
|
34953
|
-
console.log("Usage: dryui doctor [path] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
35481
|
+
console.log("Usage: dryui doctor [path] [--toon] [--full] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
34954
35482
|
console.log("");
|
|
34955
35483
|
console.log("Inspect workspace health with human-readable findings.");
|
|
35484
|
+
console.log("");
|
|
35485
|
+
console.log("Options:");
|
|
35486
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
35487
|
+
console.log(" --full Show all findings (disables truncation at 50)");
|
|
34956
35488
|
process.exit(0);
|
|
34957
35489
|
}
|
|
34958
35490
|
const { path, options } = parseWorkspaceArgs(args);
|
|
34959
|
-
|
|
35491
|
+
const mode = options.toon ? "toon" : "text";
|
|
35492
|
+
runCommand(getDoctor(path, spec, options), mode);
|
|
34960
35493
|
}
|
|
34961
35494
|
|
|
34962
35495
|
// src/commands/lint.ts
|
|
@@ -34969,6 +35502,13 @@ function getLint(inputPath, spec, options = {}) {
|
|
|
34969
35502
|
...options.changed === undefined ? {} : { changed: options.changed },
|
|
34970
35503
|
...options.maxSeverity ? { maxSeverity: options.maxSeverity } : {}
|
|
34971
35504
|
});
|
|
35505
|
+
if (options.toon) {
|
|
35506
|
+
return {
|
|
35507
|
+
output: toonWorkspaceReport(report, { title: "lint", command: "lint", full: options.full }),
|
|
35508
|
+
error: null,
|
|
35509
|
+
exitCode: report.findings.length > 0 ? 1 : 0
|
|
35510
|
+
};
|
|
35511
|
+
}
|
|
34972
35512
|
if (options.json) {
|
|
34973
35513
|
return {
|
|
34974
35514
|
output: JSON.stringify(report, null, 2),
|
|
@@ -34991,23 +35531,28 @@ function getLint(inputPath, spec, options = {}) {
|
|
|
34991
35531
|
}
|
|
34992
35532
|
function runLint(args, spec) {
|
|
34993
35533
|
if (args[0] === "--help") {
|
|
34994
|
-
console.log("Usage: dryui lint [path] [--json] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
35534
|
+
console.log("Usage: dryui lint [path] [--json] [--toon] [--full] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
34995
35535
|
console.log("");
|
|
34996
35536
|
console.log("Print deterministic workspace findings.");
|
|
35537
|
+
console.log("");
|
|
35538
|
+
console.log("Options:");
|
|
35539
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
35540
|
+
console.log(" --full Show all findings (disables truncation at 50)");
|
|
34997
35541
|
process.exit(0);
|
|
34998
35542
|
}
|
|
34999
35543
|
const { path, options } = parseWorkspaceArgs(args);
|
|
35000
|
-
|
|
35544
|
+
const mode = options.toon ? "toon" : options.json ? "json" : "text";
|
|
35545
|
+
runCommand(getLint(path, spec, options), mode);
|
|
35001
35546
|
}
|
|
35002
35547
|
|
|
35003
35548
|
// src/commands/feedback.ts
|
|
35004
|
-
import { existsSync as
|
|
35549
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
35005
35550
|
import { spawnSync } from "node:child_process";
|
|
35006
35551
|
import { dirname as dirname4, resolve as resolve3 } from "node:path";
|
|
35007
35552
|
import { fileURLToPath } from "node:url";
|
|
35008
35553
|
|
|
35009
35554
|
// ../feedback-server/src/config.ts
|
|
35010
|
-
import { existsSync as
|
|
35555
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "node:fs";
|
|
35011
35556
|
import { homedir } from "node:os";
|
|
35012
35557
|
import { dirname as dirname3, join } from "node:path";
|
|
35013
35558
|
var DEFAULT_FEEDBACK_PORT = 4748;
|
|
@@ -35217,7 +35762,7 @@ __export(exports_util, {
|
|
|
35217
35762
|
finalizeIssue: () => finalizeIssue,
|
|
35218
35763
|
extend: () => extend,
|
|
35219
35764
|
escapeRegex: () => escapeRegex,
|
|
35220
|
-
esc: () =>
|
|
35765
|
+
esc: () => esc2,
|
|
35221
35766
|
defineLazy: () => defineLazy,
|
|
35222
35767
|
createTransparentProxy: () => createTransparentProxy,
|
|
35223
35768
|
cloneDef: () => cloneDef,
|
|
@@ -35368,7 +35913,7 @@ function randomString(length = 10) {
|
|
|
35368
35913
|
}
|
|
35369
35914
|
return str;
|
|
35370
35915
|
}
|
|
35371
|
-
function
|
|
35916
|
+
function esc2(str) {
|
|
35372
35917
|
return JSON.stringify(str);
|
|
35373
35918
|
}
|
|
35374
35919
|
function slugify(input) {
|
|
@@ -36853,10 +37398,10 @@ function isValidJWT(token, algorithm = null) {
|
|
|
36853
37398
|
const tokensParts = token.split(".");
|
|
36854
37399
|
if (tokensParts.length !== 3)
|
|
36855
37400
|
return false;
|
|
36856
|
-
const [
|
|
36857
|
-
if (!
|
|
37401
|
+
const [header2] = tokensParts;
|
|
37402
|
+
if (!header2)
|
|
36858
37403
|
return false;
|
|
36859
|
-
const parsedHeader = JSON.parse(atob(
|
|
37404
|
+
const parsedHeader = JSON.parse(atob(header2));
|
|
36860
37405
|
if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT")
|
|
36861
37406
|
return false;
|
|
36862
37407
|
if (!parsedHeader.alg)
|
|
@@ -37137,7 +37682,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
37137
37682
|
const doc = new Doc(["shape", "payload", "ctx"]);
|
|
37138
37683
|
const normalized = _normalized.value;
|
|
37139
37684
|
const parseStr = (key) => {
|
|
37140
|
-
const k =
|
|
37685
|
+
const k = esc2(key);
|
|
37141
37686
|
return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
37142
37687
|
};
|
|
37143
37688
|
doc.write(`const input = payload.value;`);
|
|
@@ -37149,7 +37694,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
37149
37694
|
doc.write(`const newResult = {};`);
|
|
37150
37695
|
for (const key of normalized.keys) {
|
|
37151
37696
|
const id = ids[key];
|
|
37152
|
-
const k =
|
|
37697
|
+
const k = esc2(key);
|
|
37153
37698
|
const schema = shape[key];
|
|
37154
37699
|
const isOptionalOut = schema?._zod?.optout === "optional";
|
|
37155
37700
|
doc.write(`const ${id} = ${parseStr(key)};`);
|
|
@@ -40733,7 +41278,7 @@ function usage() {
|
|
|
40733
41278
|
`);
|
|
40734
41279
|
}
|
|
40735
41280
|
function resolveServerEntry() {
|
|
40736
|
-
if (
|
|
41281
|
+
if (existsSync5(SERVER_DIST_PATH))
|
|
40737
41282
|
return SERVER_DIST_PATH;
|
|
40738
41283
|
return SERVER_SRC_PATH;
|
|
40739
41284
|
}
|
|
@@ -40831,32 +41376,52 @@ var USAGE = `Usage: dryui <command> [options]
|
|
|
40831
41376
|
|
|
40832
41377
|
Commands:
|
|
40833
41378
|
init Print setup snippets for a new DryUI app
|
|
40834
|
-
detect [--json] [path]
|
|
40835
|
-
|
|
41379
|
+
detect [--json] [--toon] [path]
|
|
41380
|
+
Detect DryUI project setup
|
|
41381
|
+
install [--json] [--toon] [path]
|
|
41382
|
+
Print a project install plan
|
|
40836
41383
|
add <component> Print a copyable starter snippet for a component
|
|
40837
|
-
info <component>
|
|
40838
|
-
list [--category <cat>]
|
|
40839
|
-
|
|
40840
|
-
|
|
40841
|
-
|
|
40842
|
-
|
|
41384
|
+
info <component> [--toon] Show component API reference
|
|
41385
|
+
list [--category <cat>] [--toon]
|
|
41386
|
+
List all components
|
|
41387
|
+
compose <query> [--toon] Look up composition guidance
|
|
41388
|
+
review [--json] [--toon] <file.svelte>
|
|
41389
|
+
Validate a Svelte file against DryUI spec
|
|
41390
|
+
diagnose [--json] [--toon] <file.css>
|
|
41391
|
+
Validate theme CSS
|
|
41392
|
+
doctor [path] [--toon] [--include <glob>] [--exclude <glob>] [--changed]
|
|
40843
41393
|
Inspect workspace health
|
|
40844
|
-
lint [path] [--json] [--
|
|
41394
|
+
lint [path] [--json] [--toon] [--include <glob>] [--exclude <glob>] [--changed]
|
|
40845
41395
|
Print deterministic workspace findings
|
|
40846
41396
|
feedback <subcommand> Start or inspect the feedback server
|
|
40847
41397
|
|
|
40848
41398
|
Options:
|
|
40849
41399
|
--help Show help for a command
|
|
40850
|
-
--version Show version
|
|
41400
|
+
--version Show version
|
|
41401
|
+
--toon Token-optimized output for AI agents (per-command)
|
|
41402
|
+
--full Disable truncation (use with --toon)`;
|
|
40851
41403
|
function main() {
|
|
40852
41404
|
const args = process.argv.slice(2);
|
|
40853
41405
|
const command = args[0];
|
|
40854
|
-
if (
|
|
41406
|
+
if (command === "--version" || command === "-v") {
|
|
41407
|
+
console.log(VERSION);
|
|
41408
|
+
process.exit(0);
|
|
41409
|
+
}
|
|
41410
|
+
if (command === "--help" || command === "-h") {
|
|
40855
41411
|
console.log(USAGE);
|
|
40856
41412
|
process.exit(0);
|
|
40857
41413
|
}
|
|
40858
|
-
if (command
|
|
40859
|
-
|
|
41414
|
+
if (!command) {
|
|
41415
|
+
try {
|
|
41416
|
+
const detection = detectProject(spec_default, undefined);
|
|
41417
|
+
if (detection.status === "ready" || detection.status === "partial") {
|
|
41418
|
+
console.log(`dryui v${VERSION}
|
|
41419
|
+
`);
|
|
41420
|
+
console.log(toonProjectDetection(detection));
|
|
41421
|
+
process.exit(0);
|
|
41422
|
+
}
|
|
41423
|
+
} catch {}
|
|
41424
|
+
console.log(USAGE);
|
|
40860
41425
|
process.exit(0);
|
|
40861
41426
|
}
|
|
40862
41427
|
const commandArgs = args.slice(1);
|