@dryui/cli 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1034 -489
- 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.7",
|
|
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>"
|
|
@@ -31725,13 +31743,715 @@ body { margin: 0; min-height: 100dvh; }
|
|
|
31725
31743
|
]
|
|
31726
31744
|
};
|
|
31727
31745
|
|
|
31728
|
-
// ../mcp/src/
|
|
31746
|
+
// ../mcp/src/project-planner.ts
|
|
31747
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
31748
|
+
import { dirname, resolve } from "node:path";
|
|
31729
31749
|
var DIR_OVERRIDES = {
|
|
31730
31750
|
QRCode: "qr-code"
|
|
31731
31751
|
};
|
|
31732
31752
|
function componentDir(name) {
|
|
31733
31753
|
return DIR_OVERRIDES[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
31734
31754
|
}
|
|
31755
|
+
function resolveStart(inputPath) {
|
|
31756
|
+
const candidate = resolve(inputPath ?? process.cwd());
|
|
31757
|
+
return existsSync(candidate) && statSync(candidate).isFile() ? dirname(candidate) : candidate;
|
|
31758
|
+
}
|
|
31759
|
+
function findUp(start, fileName) {
|
|
31760
|
+
let current = start;
|
|
31761
|
+
while (true) {
|
|
31762
|
+
const candidate = resolve(current, fileName);
|
|
31763
|
+
if (existsSync(candidate))
|
|
31764
|
+
return candidate;
|
|
31765
|
+
const parent = dirname(current);
|
|
31766
|
+
if (parent === current)
|
|
31767
|
+
return null;
|
|
31768
|
+
current = parent;
|
|
31769
|
+
}
|
|
31770
|
+
}
|
|
31771
|
+
function findUpAny(start, fileNames) {
|
|
31772
|
+
let current = start;
|
|
31773
|
+
while (true) {
|
|
31774
|
+
for (const fileName of fileNames) {
|
|
31775
|
+
const candidate = resolve(current, fileName);
|
|
31776
|
+
if (existsSync(candidate))
|
|
31777
|
+
return candidate;
|
|
31778
|
+
}
|
|
31779
|
+
const parent = dirname(current);
|
|
31780
|
+
if (parent === current)
|
|
31781
|
+
return null;
|
|
31782
|
+
current = parent;
|
|
31783
|
+
}
|
|
31784
|
+
}
|
|
31785
|
+
function detectPackageManager(root) {
|
|
31786
|
+
if (!root)
|
|
31787
|
+
return "unknown";
|
|
31788
|
+
const lockfilePath = findUpAny(root, [
|
|
31789
|
+
"bun.lock",
|
|
31790
|
+
"bun.lockb",
|
|
31791
|
+
"pnpm-lock.yaml",
|
|
31792
|
+
"package-lock.json",
|
|
31793
|
+
"yarn.lock"
|
|
31794
|
+
]);
|
|
31795
|
+
if (!lockfilePath)
|
|
31796
|
+
return "unknown";
|
|
31797
|
+
if (lockfilePath.endsWith("bun.lock") || lockfilePath.endsWith("bun.lockb"))
|
|
31798
|
+
return "bun";
|
|
31799
|
+
if (lockfilePath.endsWith("pnpm-lock.yaml"))
|
|
31800
|
+
return "pnpm";
|
|
31801
|
+
if (lockfilePath.endsWith("package-lock.json"))
|
|
31802
|
+
return "npm";
|
|
31803
|
+
if (lockfilePath.endsWith("yarn.lock"))
|
|
31804
|
+
return "yarn";
|
|
31805
|
+
return "unknown";
|
|
31806
|
+
}
|
|
31807
|
+
function readPackageJson(packageJsonPath) {
|
|
31808
|
+
if (!packageJsonPath)
|
|
31809
|
+
return null;
|
|
31810
|
+
return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
31811
|
+
}
|
|
31812
|
+
function getDependencyNames(pkg) {
|
|
31813
|
+
return new Set([
|
|
31814
|
+
...Object.keys(pkg?.dependencies ?? {}),
|
|
31815
|
+
...Object.keys(pkg?.devDependencies ?? {})
|
|
31816
|
+
]);
|
|
31817
|
+
}
|
|
31818
|
+
function detectFramework(dependencyNames) {
|
|
31819
|
+
if (dependencyNames.has("@sveltejs/kit"))
|
|
31820
|
+
return "sveltekit";
|
|
31821
|
+
if (dependencyNames.has("svelte"))
|
|
31822
|
+
return "svelte";
|
|
31823
|
+
return "unknown";
|
|
31824
|
+
}
|
|
31825
|
+
function hasImport(filePath, importPath) {
|
|
31826
|
+
if (!filePath)
|
|
31827
|
+
return false;
|
|
31828
|
+
return readFileSync(filePath, "utf-8").includes(importPath);
|
|
31829
|
+
}
|
|
31830
|
+
function hasThemeAuto(appHtmlPath) {
|
|
31831
|
+
if (!appHtmlPath)
|
|
31832
|
+
return false;
|
|
31833
|
+
return readFileSync(appHtmlPath, "utf-8").includes("theme-auto");
|
|
31834
|
+
}
|
|
31835
|
+
function hasAnyImport(filePaths, importPath) {
|
|
31836
|
+
return filePaths.some((filePath) => hasImport(filePath, importPath));
|
|
31837
|
+
}
|
|
31838
|
+
function importsAppCss(rootLayoutPath) {
|
|
31839
|
+
if (!rootLayoutPath)
|
|
31840
|
+
return false;
|
|
31841
|
+
const content = readFileSync(rootLayoutPath, "utf-8");
|
|
31842
|
+
return content.includes("app.css");
|
|
31843
|
+
}
|
|
31844
|
+
function buildStatus(framework, hasPackageJson, stepsNeeded) {
|
|
31845
|
+
if (!hasPackageJson || framework === "unknown")
|
|
31846
|
+
return "unsupported";
|
|
31847
|
+
return stepsNeeded === 0 ? "ready" : "partial";
|
|
31848
|
+
}
|
|
31849
|
+
function installCommand(packageManager) {
|
|
31850
|
+
switch (packageManager) {
|
|
31851
|
+
case "bun":
|
|
31852
|
+
return "bun add @dryui/ui";
|
|
31853
|
+
case "pnpm":
|
|
31854
|
+
return "pnpm add @dryui/ui";
|
|
31855
|
+
case "yarn":
|
|
31856
|
+
return "yarn add @dryui/ui";
|
|
31857
|
+
default:
|
|
31858
|
+
return "npm install @dryui/ui";
|
|
31859
|
+
}
|
|
31860
|
+
}
|
|
31861
|
+
function buildThemeImportLines(spec) {
|
|
31862
|
+
return ` import '${spec.themeImports.default}';
|
|
31863
|
+
import '${spec.themeImports.dark}';`;
|
|
31864
|
+
}
|
|
31865
|
+
function buildThemeImportSnippet(spec) {
|
|
31866
|
+
return ["<script>", buildThemeImportLines(spec), "</script>"].join(`
|
|
31867
|
+
`);
|
|
31868
|
+
}
|
|
31869
|
+
function buildRootLayoutSnippet(spec) {
|
|
31870
|
+
return [
|
|
31871
|
+
"<script>",
|
|
31872
|
+
buildThemeImportLines(spec),
|
|
31873
|
+
"",
|
|
31874
|
+
" let { children } = $props();",
|
|
31875
|
+
"</script>",
|
|
31876
|
+
"",
|
|
31877
|
+
"{@render children()}"
|
|
31878
|
+
].join(`
|
|
31879
|
+
`);
|
|
31880
|
+
}
|
|
31881
|
+
function buildThemeImportCssSnippet(spec) {
|
|
31882
|
+
return [`@import '${spec.themeImports.default}';`, `@import '${spec.themeImports.dark}';`].join(`
|
|
31883
|
+
`);
|
|
31884
|
+
}
|
|
31885
|
+
function getSuggestedTarget(root, explicitTarget) {
|
|
31886
|
+
if (explicitTarget)
|
|
31887
|
+
return resolve(root ?? process.cwd(), explicitTarget);
|
|
31888
|
+
if (!root)
|
|
31889
|
+
return null;
|
|
31890
|
+
const rootPage = resolve(root, "src/routes/+page.svelte");
|
|
31891
|
+
return existsSync(rootPage) ? rootPage : null;
|
|
31892
|
+
}
|
|
31893
|
+
function getImportStatement(name, component, subpath = false) {
|
|
31894
|
+
if (subpath && component.import === "@dryui/ui") {
|
|
31895
|
+
return `import { ${name} } from '${component.import}/${componentDir(name)}';`;
|
|
31896
|
+
}
|
|
31897
|
+
return `import { ${name} } from '${component.import}';`;
|
|
31898
|
+
}
|
|
31899
|
+
function findComponent(spec, query) {
|
|
31900
|
+
const exact = spec.components[query];
|
|
31901
|
+
if (exact)
|
|
31902
|
+
return { name: query, def: exact };
|
|
31903
|
+
const lower = query.toLowerCase();
|
|
31904
|
+
for (const [name, def] of Object.entries(spec.components)) {
|
|
31905
|
+
if (name.toLowerCase() === lower)
|
|
31906
|
+
return { name, def };
|
|
31907
|
+
}
|
|
31908
|
+
return null;
|
|
31909
|
+
}
|
|
31910
|
+
function detectProject(spec, inputPath) {
|
|
31911
|
+
const start = resolveStart(inputPath);
|
|
31912
|
+
const packageJsonPath = findUp(start, "package.json");
|
|
31913
|
+
const root = packageJsonPath ? dirname(packageJsonPath) : null;
|
|
31914
|
+
const dependencyNames = getDependencyNames(readPackageJson(packageJsonPath));
|
|
31915
|
+
const framework = detectFramework(dependencyNames);
|
|
31916
|
+
const appHtmlPath = root ? resolve(root, "src/app.html") : null;
|
|
31917
|
+
const appCssPath = root ? resolve(root, "src/app.css") : null;
|
|
31918
|
+
const rootLayoutPath = root ? resolve(root, "src/routes/+layout.svelte") : null;
|
|
31919
|
+
const rootPagePath = root ? resolve(root, "src/routes/+page.svelte") : null;
|
|
31920
|
+
const appHtml = appHtmlPath && existsSync(appHtmlPath) ? appHtmlPath : null;
|
|
31921
|
+
const appCss = appCssPath && existsSync(appCssPath) ? appCssPath : null;
|
|
31922
|
+
const rootLayout = rootLayoutPath && existsSync(rootLayoutPath) ? rootLayoutPath : null;
|
|
31923
|
+
const rootPage = rootPagePath && existsSync(rootPagePath) ? rootPagePath : null;
|
|
31924
|
+
const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
|
|
31925
|
+
const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
|
|
31926
|
+
const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
|
|
31927
|
+
const themeAuto = appHtml ? hasThemeAuto(appHtml) : false;
|
|
31928
|
+
const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!themeAuto);
|
|
31929
|
+
const warnings = [];
|
|
31930
|
+
if (!packageJsonPath)
|
|
31931
|
+
warnings.push("No package.json found above the provided path.");
|
|
31932
|
+
if (framework === "unknown")
|
|
31933
|
+
warnings.push("DryUI planning currently targets Svelte and SvelteKit projects.");
|
|
31934
|
+
return {
|
|
31935
|
+
inputPath: start,
|
|
31936
|
+
root,
|
|
31937
|
+
packageJsonPath,
|
|
31938
|
+
framework,
|
|
31939
|
+
packageManager: detectPackageManager(root),
|
|
31940
|
+
status: buildStatus(framework, Boolean(packageJsonPath), stepsNeeded),
|
|
31941
|
+
dependencies: {
|
|
31942
|
+
ui: dependencyNames.has("@dryui/ui"),
|
|
31943
|
+
primitives: dependencyNames.has("@dryui/primitives")
|
|
31944
|
+
},
|
|
31945
|
+
files: {
|
|
31946
|
+
appHtml,
|
|
31947
|
+
appCss,
|
|
31948
|
+
rootLayout,
|
|
31949
|
+
rootPage
|
|
31950
|
+
},
|
|
31951
|
+
theme: {
|
|
31952
|
+
defaultImported,
|
|
31953
|
+
darkImported,
|
|
31954
|
+
themeAuto
|
|
31955
|
+
},
|
|
31956
|
+
warnings
|
|
31957
|
+
};
|
|
31958
|
+
}
|
|
31959
|
+
function planInstall(spec, inputPath) {
|
|
31960
|
+
const detection = detectProject(spec, inputPath);
|
|
31961
|
+
const steps = [];
|
|
31962
|
+
if (detection.status === "unsupported") {
|
|
31963
|
+
steps.push({
|
|
31964
|
+
kind: "blocked",
|
|
31965
|
+
status: "blocked",
|
|
31966
|
+
title: "Project detection incomplete",
|
|
31967
|
+
description: detection.warnings.join(" ") || "DryUI install planning requires a Svelte or SvelteKit project with a package.json."
|
|
31968
|
+
});
|
|
31969
|
+
return { detection, steps };
|
|
31970
|
+
}
|
|
31971
|
+
if (!detection.dependencies.ui) {
|
|
31972
|
+
steps.push({
|
|
31973
|
+
kind: "install-package",
|
|
31974
|
+
status: "pending",
|
|
31975
|
+
title: "Install @dryui/ui",
|
|
31976
|
+
description: "Add the styled DryUI package to the current project.",
|
|
31977
|
+
command: installCommand(detection.packageManager)
|
|
31978
|
+
});
|
|
31979
|
+
}
|
|
31980
|
+
if (!detection.theme.defaultImported || !detection.theme.darkImported) {
|
|
31981
|
+
if (detection.files.appCss && detection.files.rootLayout && importsAppCss(detection.files.rootLayout)) {
|
|
31982
|
+
steps.push({
|
|
31983
|
+
kind: "edit-file",
|
|
31984
|
+
status: "pending",
|
|
31985
|
+
title: "Add theme imports to app.css",
|
|
31986
|
+
description: "Prepend the two @import lines from the snippet to the TOP of the existing src/app.css file, before any other CSS rules. Do not create a second file.",
|
|
31987
|
+
path: detection.files.appCss,
|
|
31988
|
+
snippet: buildThemeImportCssSnippet(spec)
|
|
31989
|
+
});
|
|
31990
|
+
} else if (!detection.files.rootLayout) {
|
|
31991
|
+
const path = detection.root ? resolve(detection.root, "src/routes/+layout.svelte") : null;
|
|
31992
|
+
steps.push({
|
|
31993
|
+
kind: "create-file",
|
|
31994
|
+
status: "pending",
|
|
31995
|
+
title: "Create root layout with theme imports",
|
|
31996
|
+
description: "Create the file at the path below with the snippet as its full content. The file must include {@render children()} or pages will not render.",
|
|
31997
|
+
...path ? { path } : {},
|
|
31998
|
+
snippet: buildRootLayoutSnippet(spec)
|
|
31999
|
+
});
|
|
32000
|
+
} else {
|
|
32001
|
+
steps.push({
|
|
32002
|
+
kind: "edit-file",
|
|
32003
|
+
status: "pending",
|
|
32004
|
+
title: "Add theme imports to the root layout",
|
|
32005
|
+
description: "Add the two import lines from the snippet into the EXISTING <script> block in this file. Do not create a second <script> block. If no <script> block exists, add one at the top of the file.",
|
|
32006
|
+
path: detection.files.rootLayout,
|
|
32007
|
+
snippet: buildThemeImportSnippet(spec)
|
|
32008
|
+
});
|
|
32009
|
+
}
|
|
32010
|
+
}
|
|
32011
|
+
if (!detection.files.appHtml) {
|
|
32012
|
+
steps.push({
|
|
32013
|
+
kind: "blocked",
|
|
32014
|
+
status: "blocked",
|
|
32015
|
+
title: "app.html not found",
|
|
32016
|
+
description: "DryUI expects src/app.html so the document can default to theme-auto mode."
|
|
32017
|
+
});
|
|
32018
|
+
} else if (!detection.theme.themeAuto) {
|
|
32019
|
+
steps.push({
|
|
32020
|
+
kind: "edit-file",
|
|
32021
|
+
status: "pending",
|
|
32022
|
+
title: "Set html theme mode to auto",
|
|
32023
|
+
description: 'In src/app.html, find the opening <html> tag (e.g. <html lang="en">) and add class="theme-auto" to it, preserving any existing attributes. Result should be like <html lang="en" class="theme-auto">. Do NOT add a second <html> element.',
|
|
32024
|
+
path: detection.files.appHtml,
|
|
32025
|
+
snippet: 'class="theme-auto"'
|
|
32026
|
+
});
|
|
32027
|
+
}
|
|
32028
|
+
if (steps.length === 0) {
|
|
32029
|
+
steps.push({
|
|
32030
|
+
kind: "note",
|
|
32031
|
+
status: "done",
|
|
32032
|
+
title: "DryUI install plan is complete",
|
|
32033
|
+
description: "The project already has @dryui/ui, theme imports, and theme-auto configured."
|
|
32034
|
+
});
|
|
32035
|
+
}
|
|
32036
|
+
return { detection, steps };
|
|
32037
|
+
}
|
|
32038
|
+
function planAdd(spec, query, options = {}) {
|
|
32039
|
+
const installPlan = planInstall(spec, options.cwd);
|
|
32040
|
+
const component = findComponent(spec, query);
|
|
32041
|
+
if (component) {
|
|
32042
|
+
const target = getSuggestedTarget(installPlan.detection.root, options.target);
|
|
32043
|
+
const steps = [];
|
|
32044
|
+
const warnings = [...installPlan.detection.warnings];
|
|
32045
|
+
if (installPlan.steps.some((step) => step.status === "pending" || step.status === "blocked")) {
|
|
32046
|
+
steps.push({
|
|
32047
|
+
kind: "note",
|
|
32048
|
+
status: "info",
|
|
32049
|
+
title: "Complete install plan first",
|
|
32050
|
+
description: "Apply the install plan before inserting DryUI components into project files."
|
|
32051
|
+
});
|
|
32052
|
+
}
|
|
32053
|
+
steps.push(target ? {
|
|
32054
|
+
kind: "edit-file",
|
|
32055
|
+
status: "pending",
|
|
32056
|
+
title: "Insert component into the target file",
|
|
32057
|
+
description: "Add the import and snippet below to the chosen Svelte file.",
|
|
32058
|
+
path: target,
|
|
32059
|
+
snippet: `${getImportStatement(component.name, component.def, options.subpath)}
|
|
32060
|
+
|
|
32061
|
+
${component.def.example}`
|
|
32062
|
+
} : {
|
|
32063
|
+
kind: "note",
|
|
32064
|
+
status: "info",
|
|
32065
|
+
title: "Choose a target Svelte file",
|
|
32066
|
+
description: "No root page was found. Pick a target file and reuse the import and snippet in this plan."
|
|
32067
|
+
});
|
|
32068
|
+
return {
|
|
32069
|
+
detection: installPlan.detection,
|
|
32070
|
+
installPlan,
|
|
32071
|
+
targetType: "component",
|
|
32072
|
+
name: component.name,
|
|
32073
|
+
importStatement: getImportStatement(component.name, component.def, options.subpath),
|
|
32074
|
+
snippet: component.def.example,
|
|
32075
|
+
target,
|
|
32076
|
+
steps,
|
|
32077
|
+
warnings
|
|
32078
|
+
};
|
|
32079
|
+
}
|
|
32080
|
+
throw new Error(`Unknown component: "${query}"`);
|
|
32081
|
+
}
|
|
32082
|
+
|
|
32083
|
+
// ../mcp/dist/toon.js
|
|
32084
|
+
function esc(value) {
|
|
32085
|
+
if (value.includes(",") || value.includes(`
|
|
32086
|
+
`)) {
|
|
32087
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
32088
|
+
}
|
|
32089
|
+
return value;
|
|
32090
|
+
}
|
|
32091
|
+
function header(resource, count, fields) {
|
|
32092
|
+
return `${resource}[${count}]{${fields.join(",")}}:`;
|
|
32093
|
+
}
|
|
32094
|
+
function row(...values) {
|
|
32095
|
+
return " " + values.map((v) => esc(String(v))).join(",");
|
|
32096
|
+
}
|
|
32097
|
+
function truncate(text, maxLen, hint) {
|
|
32098
|
+
if (text.length <= maxLen)
|
|
32099
|
+
return text;
|
|
32100
|
+
return `(truncated, ${text.length} chars -- ${hint})`;
|
|
32101
|
+
}
|
|
32102
|
+
function buildContextualHelp(ctx) {
|
|
32103
|
+
const hints = [];
|
|
32104
|
+
switch (ctx.command) {
|
|
32105
|
+
case "info":
|
|
32106
|
+
if (ctx.componentName) {
|
|
32107
|
+
hints.push(`compose "${ctx.componentName.toLowerCase()}" -- see composition patterns`);
|
|
32108
|
+
hints.push(`add ${ctx.componentName} -- get starter snippet`);
|
|
32109
|
+
}
|
|
32110
|
+
break;
|
|
32111
|
+
case "list":
|
|
32112
|
+
hints.push("info <Component> -- see full API reference");
|
|
32113
|
+
break;
|
|
32114
|
+
case "compose":
|
|
32115
|
+
if (ctx.componentName) {
|
|
32116
|
+
hints.push(`info ${ctx.componentName} -- full API reference`);
|
|
32117
|
+
hints.push(`add ${ctx.componentName} -- starter snippet`);
|
|
32118
|
+
}
|
|
32119
|
+
break;
|
|
32120
|
+
case "review":
|
|
32121
|
+
if (ctx.hasErrors) {
|
|
32122
|
+
hints.push("info <Component> -- check API for errored component");
|
|
32123
|
+
hints.push("diagnose <file.css> -- check theme if theme warnings present");
|
|
32124
|
+
} else if (ctx.isEmpty) {
|
|
32125
|
+
hints.push("lint . -- check entire workspace");
|
|
32126
|
+
}
|
|
32127
|
+
break;
|
|
32128
|
+
case "diagnose":
|
|
32129
|
+
if (ctx.hasErrors) {
|
|
32130
|
+
hints.push('compose "app shell" -- get correct theme setup');
|
|
32131
|
+
} else if (ctx.isEmpty) {
|
|
32132
|
+
hints.push("review <file.svelte> -- validate component usage");
|
|
32133
|
+
}
|
|
32134
|
+
break;
|
|
32135
|
+
case "doctor":
|
|
32136
|
+
case "lint":
|
|
32137
|
+
if (ctx.hasFindings) {
|
|
32138
|
+
hints.push("lint --max-severity error -- focus on errors only");
|
|
32139
|
+
hints.push("review <file.svelte> -- check specific file");
|
|
32140
|
+
} else if (ctx.isEmpty) {
|
|
32141
|
+
hints.push("detect -- verify project setup");
|
|
32142
|
+
}
|
|
32143
|
+
break;
|
|
32144
|
+
case "detect":
|
|
32145
|
+
if (ctx.status === "partial" || ctx.status === "unsupported") {
|
|
32146
|
+
hints.push("install -- see step-by-step setup plan");
|
|
32147
|
+
} else if (ctx.status === "ready") {
|
|
32148
|
+
hints.push("list -- see available components");
|
|
32149
|
+
hints.push('compose "app shell" -- get root layout template');
|
|
32150
|
+
}
|
|
32151
|
+
break;
|
|
32152
|
+
case "install":
|
|
32153
|
+
hints.push("detect -- verify project after completing steps");
|
|
32154
|
+
break;
|
|
32155
|
+
}
|
|
32156
|
+
return hints;
|
|
32157
|
+
}
|
|
32158
|
+
function formatHelp(hints) {
|
|
32159
|
+
if (hints.length === 0)
|
|
32160
|
+
return "";
|
|
32161
|
+
const lines = [`next[${hints.length}]:`];
|
|
32162
|
+
for (const hint of hints) {
|
|
32163
|
+
lines.push(" " + hint);
|
|
32164
|
+
}
|
|
32165
|
+
return lines.join(`
|
|
32166
|
+
`);
|
|
32167
|
+
}
|
|
32168
|
+
function toonComponent(name, def, opts) {
|
|
32169
|
+
const full = opts?.full ?? false;
|
|
32170
|
+
const lines = [];
|
|
32171
|
+
lines.push(`${name} -- ${def.description}`, `category: ${def.category} | tags: ${def.tags.join(",")}`, `import: import { ${name} } from '${def.import}'`, `compound: ${def.compound}`);
|
|
32172
|
+
if (def.compound && def.structure?.tree.length) {
|
|
32173
|
+
lines.push("", header("structure", def.structure.tree.length, ["node"]));
|
|
32174
|
+
for (const node of def.structure.tree) {
|
|
32175
|
+
lines.push(" " + node);
|
|
32176
|
+
}
|
|
32177
|
+
if (def.structure.note)
|
|
32178
|
+
lines.push(` note: ${def.structure.note}`);
|
|
32179
|
+
}
|
|
32180
|
+
if (def.compound && def.parts) {
|
|
32181
|
+
const partEntries = Object.entries(def.parts);
|
|
32182
|
+
lines.push("", header("parts", partEntries.length, ["part"]));
|
|
32183
|
+
for (const [partName, partDef] of partEntries) {
|
|
32184
|
+
lines.push(` ${name}.${partName}`);
|
|
32185
|
+
const props = Object.entries(partDef.props ?? {});
|
|
32186
|
+
if (props.length > 0) {
|
|
32187
|
+
for (const [propName, propDef] of props) {
|
|
32188
|
+
const flags = [propDef.type];
|
|
32189
|
+
if (propDef.required)
|
|
32190
|
+
flags.push("required");
|
|
32191
|
+
if (propDef.default !== undefined)
|
|
32192
|
+
flags.push(`default:${propDef.default}`);
|
|
32193
|
+
if (propDef.acceptedValues?.length)
|
|
32194
|
+
flags.push(`values:${propDef.acceptedValues.join("|")}`);
|
|
32195
|
+
lines.push(` ${propName}: ${flags.join(" | ")}`);
|
|
32196
|
+
}
|
|
32197
|
+
}
|
|
32198
|
+
}
|
|
32199
|
+
} else if (def.props) {
|
|
32200
|
+
const propEntries = Object.entries(def.props);
|
|
32201
|
+
if (propEntries.length > 0) {
|
|
32202
|
+
lines.push("", header("props", propEntries.length, ["name", "type", "required", "default"]));
|
|
32203
|
+
for (const [propName, propDef] of propEntries) {
|
|
32204
|
+
lines.push(row(propName, propDef.type, propDef.required ? "yes" : "no", propDef.default ?? "-"));
|
|
32205
|
+
}
|
|
32206
|
+
}
|
|
32207
|
+
}
|
|
32208
|
+
const cssEntries = Object.entries(def.cssVars);
|
|
32209
|
+
if (cssEntries.length > 0) {
|
|
32210
|
+
lines.push("", header("css-vars", cssEntries.length, ["name", "description"]));
|
|
32211
|
+
for (const [varName, desc] of cssEntries) {
|
|
32212
|
+
lines.push(row(varName, desc));
|
|
32213
|
+
}
|
|
32214
|
+
}
|
|
32215
|
+
if (def.dataAttributes.length > 0) {
|
|
32216
|
+
lines.push("", header("data-attrs", def.dataAttributes.length, ["name", "values"]));
|
|
32217
|
+
for (const attr of def.dataAttributes) {
|
|
32218
|
+
lines.push(row(attr.name, attr.values?.join("|") ?? "-"));
|
|
32219
|
+
}
|
|
32220
|
+
}
|
|
32221
|
+
if (def.example) {
|
|
32222
|
+
const example = full ? def.example : truncate(def.example, 400, `use add ${name} for full snippet`);
|
|
32223
|
+
lines.push("", "example:", example);
|
|
32224
|
+
}
|
|
32225
|
+
const help = buildContextualHelp({ command: "info", componentName: name });
|
|
32226
|
+
if (help.length > 0) {
|
|
32227
|
+
lines.push("", formatHelp(help));
|
|
32228
|
+
}
|
|
32229
|
+
return lines.join(`
|
|
32230
|
+
`);
|
|
32231
|
+
}
|
|
32232
|
+
function toonComponentList(components, category) {
|
|
32233
|
+
const entries = Object.entries(components);
|
|
32234
|
+
const filtered = category ? entries.filter(([, def]) => def.category.toLowerCase() === category.toLowerCase()) : entries;
|
|
32235
|
+
if (filtered.length === 0) {
|
|
32236
|
+
const cats = [...new Set(entries.map(([, d]) => d.category))].sort();
|
|
32237
|
+
return `components[0]: no matches
|
|
32238
|
+
available categories: ${cats.join(", ")}`;
|
|
32239
|
+
}
|
|
32240
|
+
const groups = {};
|
|
32241
|
+
for (const entry of filtered) {
|
|
32242
|
+
const cat = entry[1].category;
|
|
32243
|
+
(groups[cat] ??= []).push(entry);
|
|
32244
|
+
}
|
|
32245
|
+
const lines = [header("components", filtered.length, ["name", "category", "compound", "description"])];
|
|
32246
|
+
const sortedCats = Object.keys(groups).sort();
|
|
32247
|
+
for (const cat of sortedCats) {
|
|
32248
|
+
const items = groups[cat] ?? [];
|
|
32249
|
+
for (const [name, def] of items) {
|
|
32250
|
+
lines.push(row(name, cat, def.compound, def.description));
|
|
32251
|
+
}
|
|
32252
|
+
}
|
|
32253
|
+
const help = buildContextualHelp({ command: "list" });
|
|
32254
|
+
if (help.length > 0) {
|
|
32255
|
+
lines.push("", formatHelp(help));
|
|
32256
|
+
}
|
|
32257
|
+
return lines.join(`
|
|
32258
|
+
`);
|
|
32259
|
+
}
|
|
32260
|
+
function toonComposition(results, opts) {
|
|
32261
|
+
const full = opts?.full ?? false;
|
|
32262
|
+
const lines = [];
|
|
32263
|
+
const totalMatches = results.componentMatches.length + results.recipeMatches.length;
|
|
32264
|
+
if (totalMatches === 0) {
|
|
32265
|
+
return "matches[0]: none";
|
|
32266
|
+
}
|
|
32267
|
+
for (const comp of results.componentMatches) {
|
|
32268
|
+
lines.push(`-- ${comp.component}: ${comp.useWhen}`);
|
|
32269
|
+
for (const alt of comp.alternatives) {
|
|
32270
|
+
const snippet = full ? alt.snippet : truncate(alt.snippet, 500, `use info ${alt.component} for full snippet`);
|
|
32271
|
+
lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
|
|
32272
|
+
lines.push(snippet.split(`
|
|
32273
|
+
`).map((l) => " " + l).join(`
|
|
32274
|
+
`));
|
|
32275
|
+
}
|
|
32276
|
+
for (const ap of comp.antiPatterns) {
|
|
32277
|
+
lines.push(` anti-pattern: ${ap.pattern}`);
|
|
32278
|
+
lines.push(` reason: ${ap.reason} | fix: ${ap.fix}`);
|
|
32279
|
+
}
|
|
32280
|
+
if (comp.combinesWith.length) {
|
|
32281
|
+
lines.push(` combines-with: ${comp.combinesWith.join(",")}`);
|
|
32282
|
+
}
|
|
32283
|
+
lines.push("");
|
|
32284
|
+
}
|
|
32285
|
+
for (const recipe of results.recipeMatches) {
|
|
32286
|
+
const snippet = full ? recipe.snippet : truncate(recipe.snippet, 500, `use compose "${recipe.name}" --full for complete code`);
|
|
32287
|
+
lines.push(`-- recipe: ${recipe.name}`);
|
|
32288
|
+
lines.push(` ${recipe.description}`);
|
|
32289
|
+
lines.push(` components: ${recipe.components.join(",")}`);
|
|
32290
|
+
lines.push(" code:");
|
|
32291
|
+
lines.push(snippet.split(`
|
|
32292
|
+
`).map((l) => " " + l).join(`
|
|
32293
|
+
`));
|
|
32294
|
+
lines.push("");
|
|
32295
|
+
}
|
|
32296
|
+
const firstComponent = results.componentMatches[0]?.alternatives[0]?.component ?? results.recipeMatches[0]?.components[0] ?? undefined;
|
|
32297
|
+
const ctx = { command: "compose" };
|
|
32298
|
+
if (firstComponent)
|
|
32299
|
+
ctx.componentName = firstComponent;
|
|
32300
|
+
const help = buildContextualHelp(ctx);
|
|
32301
|
+
if (help.length > 0) {
|
|
32302
|
+
lines.push(formatHelp(help));
|
|
32303
|
+
}
|
|
32304
|
+
return lines.join(`
|
|
32305
|
+
`).trimEnd();
|
|
32306
|
+
}
|
|
32307
|
+
function toonReviewResult(result) {
|
|
32308
|
+
const lines = [];
|
|
32309
|
+
const hasBlockers = result.issues.some((i) => i.severity === "error");
|
|
32310
|
+
const autoFixable = result.issues.filter((i) => i.fix !== null).length;
|
|
32311
|
+
if (result.issues.length === 0) {
|
|
32312
|
+
lines.push("issues[0]: clean");
|
|
32313
|
+
lines.push(`hasBlockers: false | autoFixable: 0`);
|
|
32314
|
+
} else {
|
|
32315
|
+
lines.push(header("issues", result.issues.length, ["severity", "line", "code", "message"]));
|
|
32316
|
+
for (const issue of result.issues) {
|
|
32317
|
+
lines.push(row(issue.severity, issue.line, issue.code, issue.message));
|
|
32318
|
+
if (issue.fix) {
|
|
32319
|
+
lines.push(` fix: ${issue.fix}`);
|
|
32320
|
+
}
|
|
32321
|
+
}
|
|
32322
|
+
lines.push(`hasBlockers: ${hasBlockers} | autoFixable: ${autoFixable}`);
|
|
32323
|
+
}
|
|
32324
|
+
lines.push(result.summary);
|
|
32325
|
+
const help = buildContextualHelp({
|
|
32326
|
+
command: "review",
|
|
32327
|
+
hasErrors: hasBlockers,
|
|
32328
|
+
isEmpty: result.issues.length === 0
|
|
32329
|
+
});
|
|
32330
|
+
if (help.length > 0) {
|
|
32331
|
+
lines.push("", formatHelp(help));
|
|
32332
|
+
}
|
|
32333
|
+
return lines.join(`
|
|
32334
|
+
`);
|
|
32335
|
+
}
|
|
32336
|
+
function toonDiagnoseResult(result) {
|
|
32337
|
+
const lines = [];
|
|
32338
|
+
const { variables } = result;
|
|
32339
|
+
const missingCount = result.issues.filter((i) => i.code === "missing-token").length;
|
|
32340
|
+
const totalRequired = variables.required + missingCount;
|
|
32341
|
+
const coverage = totalRequired > 0 ? Math.round(variables.required / totalRequired * 100) : 100;
|
|
32342
|
+
lines.push(`tokens: ${variables.found} found, ${variables.required} required, ${variables.extra} extra | coverage: ${coverage}%`);
|
|
32343
|
+
if (result.issues.length === 0) {
|
|
32344
|
+
lines.push("issues[0]: clean");
|
|
32345
|
+
} else {
|
|
32346
|
+
lines.push(header("issues", result.issues.length, ["severity", "code", "variable", "message"]));
|
|
32347
|
+
for (const issue of result.issues) {
|
|
32348
|
+
lines.push(row(issue.severity, issue.code, issue.variable, issue.message));
|
|
32349
|
+
if (issue.fix) {
|
|
32350
|
+
lines.push(` fix: ${issue.fix}`);
|
|
32351
|
+
}
|
|
32352
|
+
}
|
|
32353
|
+
}
|
|
32354
|
+
lines.push(result.summary);
|
|
32355
|
+
const hasErrors = result.issues.some((i) => i.severity === "error");
|
|
32356
|
+
const help = buildContextualHelp({
|
|
32357
|
+
command: "diagnose",
|
|
32358
|
+
hasErrors,
|
|
32359
|
+
isEmpty: result.issues.length === 0
|
|
32360
|
+
});
|
|
32361
|
+
if (help.length > 0) {
|
|
32362
|
+
lines.push("", formatHelp(help));
|
|
32363
|
+
}
|
|
32364
|
+
return lines.join(`
|
|
32365
|
+
`);
|
|
32366
|
+
}
|
|
32367
|
+
var MAX_FINDINGS_DEFAULT = 50;
|
|
32368
|
+
function toonWorkspaceReport(report, opts) {
|
|
32369
|
+
const full = opts?.full ?? false;
|
|
32370
|
+
const title = opts?.title ?? "workspace";
|
|
32371
|
+
const command = opts?.command ?? (title.includes("lint") ? "lint" : "doctor");
|
|
32372
|
+
const lines = [];
|
|
32373
|
+
lines.push(`${title} | root: ${report.root}`);
|
|
32374
|
+
lines.push(`scanned: ${report.scannedFiles} files | errors: ${report.summary.error} | warnings: ${report.summary.warning} | info: ${report.summary.info}`);
|
|
32375
|
+
if (report.summary.byRule && Object.keys(report.summary.byRule).length > 0) {
|
|
32376
|
+
const sorted = Object.entries(report.summary.byRule).sort(([, a], [, b]) => b - a);
|
|
32377
|
+
const topRule = sorted[0];
|
|
32378
|
+
if (topRule) {
|
|
32379
|
+
lines.push(`top-rule: ${topRule[0]} (${topRule[1]} occurrences)`);
|
|
32380
|
+
}
|
|
32381
|
+
}
|
|
32382
|
+
if (report.findings.length === 0) {
|
|
32383
|
+
lines.push("findings[0]: clean");
|
|
32384
|
+
} else {
|
|
32385
|
+
const findings = full ? report.findings : report.findings.slice(0, MAX_FINDINGS_DEFAULT);
|
|
32386
|
+
const truncated = !full && report.findings.length > MAX_FINDINGS_DEFAULT;
|
|
32387
|
+
lines.push(header("findings", findings.length, ["severity", "rule", "file", "line", "message"]));
|
|
32388
|
+
for (const f of findings) {
|
|
32389
|
+
lines.push(row(f.severity, f.ruleId, f.file, f.line ?? "-", f.message));
|
|
32390
|
+
if (f.suggestedFixes.length > 0) {
|
|
32391
|
+
for (const fix of f.suggestedFixes) {
|
|
32392
|
+
lines.push(` fix: ${fix.description}${fix.replacement ? ` -> ${fix.replacement}` : ""}`);
|
|
32393
|
+
}
|
|
32394
|
+
}
|
|
32395
|
+
}
|
|
32396
|
+
if (truncated) {
|
|
32397
|
+
lines.push(` (showing ${MAX_FINDINGS_DEFAULT} of ${report.findings.length} -- use --full to see all)`);
|
|
32398
|
+
}
|
|
32399
|
+
}
|
|
32400
|
+
if (report.warnings.length > 0) {
|
|
32401
|
+
lines.push("", header("warnings", report.warnings.length, ["message"]));
|
|
32402
|
+
for (const w of report.warnings) {
|
|
32403
|
+
lines.push(" " + w);
|
|
32404
|
+
}
|
|
32405
|
+
}
|
|
32406
|
+
const help = buildContextualHelp({
|
|
32407
|
+
command,
|
|
32408
|
+
hasFindings: report.findings.length > 0,
|
|
32409
|
+
isEmpty: report.findings.length === 0
|
|
32410
|
+
});
|
|
32411
|
+
if (help.length > 0) {
|
|
32412
|
+
lines.push("", formatHelp(help));
|
|
32413
|
+
}
|
|
32414
|
+
return lines.join(`
|
|
32415
|
+
`);
|
|
32416
|
+
}
|
|
32417
|
+
function toonProjectDetection(detection) {
|
|
32418
|
+
const lines = [];
|
|
32419
|
+
lines.push(`project: ${detection.status} | framework: ${detection.framework} | pkg-manager: ${detection.packageManager}`);
|
|
32420
|
+
lines.push(`root: ${detection.root ?? "(not found)"}`);
|
|
32421
|
+
lines.push(`deps: ui=${detection.dependencies.ui}, primitives=${detection.dependencies.primitives}`);
|
|
32422
|
+
lines.push(`theme: default=${detection.theme.defaultImported}, dark=${detection.theme.darkImported}, auto=${detection.theme.themeAuto}`);
|
|
32423
|
+
if (detection.warnings.length > 0) {
|
|
32424
|
+
lines.push(header("warnings", detection.warnings.length, ["message"]));
|
|
32425
|
+
for (const w of detection.warnings) {
|
|
32426
|
+
lines.push(" " + w);
|
|
32427
|
+
}
|
|
32428
|
+
}
|
|
32429
|
+
const help = buildContextualHelp({ command: "detect", status: detection.status });
|
|
32430
|
+
if (help.length > 0) {
|
|
32431
|
+
lines.push("", formatHelp(help));
|
|
32432
|
+
}
|
|
32433
|
+
return lines.join(`
|
|
32434
|
+
`);
|
|
32435
|
+
}
|
|
32436
|
+
function toonError(code, message, suggestions) {
|
|
32437
|
+
const lines = [`error[1]{code,message}: ${esc(code)},${esc(message)}`];
|
|
32438
|
+
if (suggestions?.length) {
|
|
32439
|
+
lines.push(header("suggestions", suggestions.length, ["value"]));
|
|
32440
|
+
for (const s of suggestions) {
|
|
32441
|
+
lines.push(" " + s);
|
|
32442
|
+
}
|
|
32443
|
+
}
|
|
32444
|
+
return lines.join(`
|
|
32445
|
+
`);
|
|
32446
|
+
}
|
|
32447
|
+
|
|
32448
|
+
// ../mcp/src/spec-formatters.ts
|
|
32449
|
+
var DIR_OVERRIDES2 = {
|
|
32450
|
+
QRCode: "qr-code"
|
|
32451
|
+
};
|
|
32452
|
+
function componentDir2(name) {
|
|
32453
|
+
return DIR_OVERRIDES2[name] ?? name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
32454
|
+
}
|
|
31735
32455
|
function pad(str, width) {
|
|
31736
32456
|
return str.length >= width ? str : str + " ".repeat(width - str.length);
|
|
31737
32457
|
}
|
|
@@ -31744,9 +32464,9 @@ function indent(text, spaces) {
|
|
|
31744
32464
|
function getSubpathImport(name, def) {
|
|
31745
32465
|
if (def.import !== "@dryui/ui")
|
|
31746
32466
|
return null;
|
|
31747
|
-
return `import { ${name} } from '${def.import}/${
|
|
32467
|
+
return `import { ${name} } from '${def.import}/${componentDir2(name)}'`;
|
|
31748
32468
|
}
|
|
31749
|
-
function
|
|
32469
|
+
function findComponent2(query, components) {
|
|
31750
32470
|
const exact = components[query];
|
|
31751
32471
|
if (exact)
|
|
31752
32472
|
return { name: query, def: exact };
|
|
@@ -31921,9 +32641,23 @@ function formatSimple(name, def) {
|
|
|
31921
32641
|
}
|
|
31922
32642
|
|
|
31923
32643
|
// src/run.ts
|
|
31924
|
-
|
|
32644
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
32645
|
+
function fileNotFound(filePath, toon) {
|
|
32646
|
+
if (existsSync2(filePath))
|
|
32647
|
+
return null;
|
|
32648
|
+
return {
|
|
32649
|
+
output: "",
|
|
32650
|
+
error: toon ? toonError("not-found", `File not found: ${filePath}`) : `File not found: ${filePath}`,
|
|
32651
|
+
exitCode: 1
|
|
32652
|
+
};
|
|
32653
|
+
}
|
|
32654
|
+
function runCommand(result, mode = "text") {
|
|
31925
32655
|
if (result.error) {
|
|
31926
|
-
|
|
32656
|
+
if (mode === "text") {
|
|
32657
|
+
console.error(result.error);
|
|
32658
|
+
} else {
|
|
32659
|
+
console.log(result.error);
|
|
32660
|
+
}
|
|
31927
32661
|
} else {
|
|
31928
32662
|
console.log(result.output);
|
|
31929
32663
|
}
|
|
@@ -31931,13 +32665,20 @@ function runCommand(result) {
|
|
|
31931
32665
|
}
|
|
31932
32666
|
|
|
31933
32667
|
// src/commands/info.ts
|
|
31934
|
-
function
|
|
31935
|
-
return
|
|
32668
|
+
function findComponent3(spec, query) {
|
|
32669
|
+
return findComponent2(query, spec.components);
|
|
31936
32670
|
}
|
|
31937
|
-
function getInfo(query, spec) {
|
|
31938
|
-
const result =
|
|
32671
|
+
function getInfo(query, spec, options) {
|
|
32672
|
+
const result = findComponent3(spec, query);
|
|
31939
32673
|
if (!result) {
|
|
31940
32674
|
const available = Object.keys(spec.components).sort();
|
|
32675
|
+
if (options?.toon) {
|
|
32676
|
+
return {
|
|
32677
|
+
output: "",
|
|
32678
|
+
error: toonError("not-found", `Unknown component: "${query}"`, available),
|
|
32679
|
+
exitCode: 1
|
|
32680
|
+
};
|
|
32681
|
+
}
|
|
31941
32682
|
const error = [
|
|
31942
32683
|
`Unknown component: "${query}"`,
|
|
31943
32684
|
"",
|
|
@@ -31948,27 +32689,37 @@ function getInfo(query, spec) {
|
|
|
31948
32689
|
return { output: "", error, exitCode: 1 };
|
|
31949
32690
|
}
|
|
31950
32691
|
const { name, def } = result;
|
|
32692
|
+
if (options?.toon) {
|
|
32693
|
+
return { output: toonComponent(name, def, { full: options?.full }), error: null, exitCode: 0 };
|
|
32694
|
+
}
|
|
31951
32695
|
const output = def.compound ? formatCompound(name, def) : formatSimple(name, def);
|
|
31952
32696
|
return { output, error: null, exitCode: 0 };
|
|
31953
32697
|
}
|
|
31954
32698
|
function runInfo(args, spec) {
|
|
31955
32699
|
if (args.length === 0 || args[0] === "--help") {
|
|
31956
|
-
console.log("Usage: dryui info <component>");
|
|
32700
|
+
console.log("Usage: dryui info <component> [--toon] [--full]");
|
|
31957
32701
|
console.log("");
|
|
31958
32702
|
console.log("Show API reference for a DryUI component or composed output.");
|
|
31959
32703
|
console.log("");
|
|
32704
|
+
console.log("Options:");
|
|
32705
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
32706
|
+
console.log(" --full Include full example code (disables truncation)");
|
|
32707
|
+
console.log("");
|
|
31960
32708
|
console.log("Examples:");
|
|
31961
32709
|
console.log(" dryui info Button");
|
|
31962
32710
|
console.log(" dryui info card # case-insensitive");
|
|
31963
|
-
console.log(
|
|
32711
|
+
console.log(" dryui info Button --toon");
|
|
31964
32712
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
31965
32713
|
}
|
|
31966
|
-
const
|
|
32714
|
+
const toon = args.includes("--toon");
|
|
32715
|
+
const full = args.includes("--full");
|
|
32716
|
+
const query = args.find((a) => !a.startsWith("--"));
|
|
31967
32717
|
if (!query) {
|
|
31968
32718
|
console.error("Error: missing component name");
|
|
31969
32719
|
process.exit(1);
|
|
31970
32720
|
}
|
|
31971
|
-
|
|
32721
|
+
const mode = toon ? "toon" : "text";
|
|
32722
|
+
runCommand(getInfo(query, spec, { toon, full }), mode);
|
|
31972
32723
|
}
|
|
31973
32724
|
|
|
31974
32725
|
// src/format.ts
|
|
@@ -32028,7 +32779,7 @@ function formatWorkspaceReport(report, options) {
|
|
|
32028
32779
|
// src/commands/project-planner.ts
|
|
32029
32780
|
function importStatement(name, def, subpath = false) {
|
|
32030
32781
|
if (subpath && def.import === "@dryui/ui") {
|
|
32031
|
-
return `import { ${name} } from '${def.import}/${
|
|
32782
|
+
return `import { ${name} } from '${def.import}/${componentDir2(name)}';`;
|
|
32032
32783
|
}
|
|
32033
32784
|
return `import { ${name} } from '${def.import}';`;
|
|
32034
32785
|
}
|
|
@@ -32150,7 +32901,7 @@ function formatAddPlan(plan) {
|
|
|
32150
32901
|
`);
|
|
32151
32902
|
}
|
|
32152
32903
|
function buildAddSnippet(query, spec, options = {}) {
|
|
32153
|
-
const component =
|
|
32904
|
+
const component = findComponent3(spec, query);
|
|
32154
32905
|
if (!component) {
|
|
32155
32906
|
return {
|
|
32156
32907
|
output: "",
|
|
@@ -32166,342 +32917,6 @@ function buildAddSnippet(query, spec, options = {}) {
|
|
|
32166
32917
|
};
|
|
32167
32918
|
}
|
|
32168
32919
|
|
|
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
32920
|
// src/commands/add.ts
|
|
32506
32921
|
function parseProjectInput(positionals, spec) {
|
|
32507
32922
|
const first = positionals[0];
|
|
@@ -32513,8 +32928,8 @@ function parseProjectInput(positionals, spec) {
|
|
|
32513
32928
|
return { query: first };
|
|
32514
32929
|
}
|
|
32515
32930
|
if (positionals.length === 2) {
|
|
32516
|
-
const firstIsQuery = Boolean(
|
|
32517
|
-
const secondIsQuery = Boolean(
|
|
32931
|
+
const firstIsQuery = Boolean(findComponent3(spec, first));
|
|
32932
|
+
const secondIsQuery = Boolean(findComponent3(spec, second));
|
|
32518
32933
|
if (firstIsQuery && !secondIsQuery) {
|
|
32519
32934
|
return { query: first, cwd: second };
|
|
32520
32935
|
}
|
|
@@ -32641,6 +33056,7 @@ function runAdd(args, spec) {
|
|
|
32641
33056
|
// src/commands/detect.ts
|
|
32642
33057
|
function parseDetectArgs(args) {
|
|
32643
33058
|
let json = false;
|
|
33059
|
+
let toon = false;
|
|
32644
33060
|
let path;
|
|
32645
33061
|
for (let index = 0;index < args.length; index++) {
|
|
32646
33062
|
const arg = args[index];
|
|
@@ -32650,6 +33066,10 @@ function parseDetectArgs(args) {
|
|
|
32650
33066
|
json = true;
|
|
32651
33067
|
continue;
|
|
32652
33068
|
}
|
|
33069
|
+
if (arg === "--toon") {
|
|
33070
|
+
toon = true;
|
|
33071
|
+
continue;
|
|
33072
|
+
}
|
|
32653
33073
|
if (arg.startsWith("--")) {
|
|
32654
33074
|
continue;
|
|
32655
33075
|
}
|
|
@@ -32657,24 +33077,29 @@ function parseDetectArgs(args) {
|
|
|
32657
33077
|
path = arg;
|
|
32658
33078
|
}
|
|
32659
33079
|
}
|
|
32660
|
-
return { json, path };
|
|
33080
|
+
return { json, toon, path };
|
|
32661
33081
|
}
|
|
32662
33082
|
function getDetect(inputPath, spec, options = {}) {
|
|
32663
33083
|
const detection = detectProject(spec, inputPath);
|
|
33084
|
+
if (options.toon) {
|
|
33085
|
+
return { output: toonProjectDetection(detection), error: null, exitCode: 0 };
|
|
33086
|
+
}
|
|
32664
33087
|
return options.json ? { output: JSON.stringify(detection, null, 2), error: null, exitCode: 0 } : { output: formatProjectDetection(detection), error: null, exitCode: 0 };
|
|
32665
33088
|
}
|
|
32666
33089
|
function runDetect(args, spec) {
|
|
32667
33090
|
if (args[0] === "--help") {
|
|
32668
|
-
console.log("Usage: dryui detect [--json] [path]");
|
|
33091
|
+
console.log("Usage: dryui detect [--json] [--toon] [path]");
|
|
32669
33092
|
console.log("");
|
|
32670
33093
|
console.log("Detect the current DryUI project setup.");
|
|
32671
33094
|
console.log("");
|
|
32672
33095
|
console.log("Options:");
|
|
32673
33096
|
console.log(" --json Output raw JSON instead of formatted text");
|
|
33097
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
32674
33098
|
process.exit(0);
|
|
32675
33099
|
}
|
|
32676
|
-
const { json, path } = parseDetectArgs(args);
|
|
32677
|
-
|
|
33100
|
+
const { json, toon, path } = parseDetectArgs(args);
|
|
33101
|
+
const mode = toon ? "toon" : json ? "json" : "text";
|
|
33102
|
+
runCommand(getDetect(path, spec, { json, toon }), mode);
|
|
32678
33103
|
}
|
|
32679
33104
|
|
|
32680
33105
|
// src/commands/install.ts
|
|
@@ -32781,7 +33206,14 @@ function groupByCategory(spec) {
|
|
|
32781
33206
|
}
|
|
32782
33207
|
return groups;
|
|
32783
33208
|
}
|
|
32784
|
-
function getList(category, spec, options
|
|
33209
|
+
function getList(category, spec, options) {
|
|
33210
|
+
if (options?.toon) {
|
|
33211
|
+
return {
|
|
33212
|
+
output: toonComponentList(spec.components, category ?? undefined),
|
|
33213
|
+
error: null,
|
|
33214
|
+
exitCode: 0
|
|
33215
|
+
};
|
|
33216
|
+
}
|
|
32785
33217
|
const sections = [];
|
|
32786
33218
|
const validCategories = getCategories(spec);
|
|
32787
33219
|
if (category && !validCategories.includes(category)) {
|
|
@@ -32815,18 +33247,20 @@ function getList(category, spec, options = {}) {
|
|
|
32815
33247
|
}
|
|
32816
33248
|
function runList(args, spec) {
|
|
32817
33249
|
if (args[0] === "--help") {
|
|
32818
|
-
console.log("Usage: dryui list [--category <category>]");
|
|
33250
|
+
console.log("Usage: dryui list [--category <category>] [--toon]");
|
|
32819
33251
|
console.log("");
|
|
32820
33252
|
console.log("List DryUI components.");
|
|
32821
33253
|
console.log("");
|
|
32822
33254
|
console.log("Options:");
|
|
32823
33255
|
console.log(" --category <cat> Filter by category");
|
|
33256
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
32824
33257
|
console.log("");
|
|
32825
33258
|
console.log("Categories:");
|
|
32826
33259
|
const cats = getCategories(spec);
|
|
32827
33260
|
console.log(` ${cats.join(", ")}`);
|
|
32828
33261
|
process.exit(0);
|
|
32829
33262
|
}
|
|
33263
|
+
const toon = args.includes("--toon");
|
|
32830
33264
|
let filterCategory = null;
|
|
32831
33265
|
const catIdx = args.indexOf("--category");
|
|
32832
33266
|
if (catIdx !== -1) {
|
|
@@ -32837,11 +33271,12 @@ function runList(args, spec) {
|
|
|
32837
33271
|
}
|
|
32838
33272
|
filterCategory = catValue.toLowerCase();
|
|
32839
33273
|
}
|
|
32840
|
-
|
|
33274
|
+
const mode = toon ? "toon" : "text";
|
|
33275
|
+
runCommand(getList(filterCategory, spec, { toon }), mode);
|
|
32841
33276
|
}
|
|
32842
33277
|
|
|
32843
33278
|
// src/commands/review.ts
|
|
32844
|
-
import { readFileSync as readFileSync2
|
|
33279
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
32845
33280
|
|
|
32846
33281
|
// ../mcp/src/utils.ts
|
|
32847
33282
|
function buildLineOffsets(text) {
|
|
@@ -33542,12 +33977,19 @@ function reviewComponent(code, spec, filename) {
|
|
|
33542
33977
|
|
|
33543
33978
|
// src/commands/review.ts
|
|
33544
33979
|
function getReview(filePath, spec, options) {
|
|
33545
|
-
|
|
33546
|
-
|
|
33547
|
-
|
|
33980
|
+
const missing = fileNotFound(filePath, options?.toon);
|
|
33981
|
+
if (missing)
|
|
33982
|
+
return missing;
|
|
33548
33983
|
const code = readFileSync2(filePath, "utf-8");
|
|
33549
33984
|
const filename = filePath.split("/").pop();
|
|
33550
33985
|
const result = reviewComponent(code, spec, filename);
|
|
33986
|
+
if (options?.toon) {
|
|
33987
|
+
return {
|
|
33988
|
+
output: toonReviewResult(result),
|
|
33989
|
+
error: null,
|
|
33990
|
+
exitCode: result.issues.some((i) => i.severity === "error") ? 1 : 0
|
|
33991
|
+
};
|
|
33992
|
+
}
|
|
33551
33993
|
if (options?.json) {
|
|
33552
33994
|
return {
|
|
33553
33995
|
output: JSON.stringify(result, null, 2),
|
|
@@ -33574,7 +34016,7 @@ function getReview(filePath, spec, options) {
|
|
|
33574
34016
|
}
|
|
33575
34017
|
function runReview(args, spec) {
|
|
33576
34018
|
if (args.length === 0 || args[0] === "--help") {
|
|
33577
|
-
console.log("Usage: dryui review [--json] <file.svelte>");
|
|
34019
|
+
console.log("Usage: dryui review [--json] [--toon] <file.svelte>");
|
|
33578
34020
|
console.log("");
|
|
33579
34021
|
console.log("Validate a Svelte component against the DryUI spec.");
|
|
33580
34022
|
console.log("Checks for incorrect component usage, missing props,");
|
|
@@ -33582,20 +34024,23 @@ function runReview(args, spec) {
|
|
|
33582
34024
|
console.log("");
|
|
33583
34025
|
console.log("Options:");
|
|
33584
34026
|
console.log(" --json Output raw JSON instead of formatted text");
|
|
34027
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
33585
34028
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
33586
34029
|
}
|
|
33587
34030
|
const jsonMode = args.includes("--json");
|
|
33588
|
-
const
|
|
34031
|
+
const toon = args.includes("--toon");
|
|
34032
|
+
const fileArgs = args.filter((a) => !a.startsWith("--"));
|
|
33589
34033
|
const filePath = fileArgs[0];
|
|
33590
34034
|
if (!filePath) {
|
|
33591
34035
|
console.error("Error: missing file path");
|
|
33592
34036
|
process.exit(1);
|
|
33593
34037
|
}
|
|
33594
|
-
|
|
34038
|
+
const mode = toon ? "toon" : jsonMode ? "json" : "text";
|
|
34039
|
+
runCommand(getReview(filePath, spec, { json: jsonMode, toon }), mode);
|
|
33595
34040
|
}
|
|
33596
34041
|
|
|
33597
34042
|
// src/commands/diagnose.ts
|
|
33598
|
-
import { readFileSync as readFileSync3
|
|
34043
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
33599
34044
|
|
|
33600
34045
|
// ../mcp/src/theme-checker.ts
|
|
33601
34046
|
function capture(match, index) {
|
|
@@ -34351,11 +34796,18 @@ function diagnoseTheme(css, spec) {
|
|
|
34351
34796
|
|
|
34352
34797
|
// src/commands/diagnose.ts
|
|
34353
34798
|
function getDiagnose(filePath, spec, options) {
|
|
34354
|
-
|
|
34355
|
-
|
|
34356
|
-
|
|
34799
|
+
const missing = fileNotFound(filePath, options?.toon);
|
|
34800
|
+
if (missing)
|
|
34801
|
+
return missing;
|
|
34357
34802
|
const css = readFileSync3(filePath, "utf-8");
|
|
34358
34803
|
const result = diagnoseTheme(css, spec);
|
|
34804
|
+
if (options?.toon) {
|
|
34805
|
+
return {
|
|
34806
|
+
output: toonDiagnoseResult(result),
|
|
34807
|
+
error: null,
|
|
34808
|
+
exitCode: result.issues.some((i) => i.severity === "error") ? 1 : 0
|
|
34809
|
+
};
|
|
34810
|
+
}
|
|
34359
34811
|
if (options?.json) {
|
|
34360
34812
|
return {
|
|
34361
34813
|
output: JSON.stringify(result, null, 2),
|
|
@@ -34388,26 +34840,29 @@ function getDiagnose(filePath, spec, options) {
|
|
|
34388
34840
|
}
|
|
34389
34841
|
function runDiagnose(args, spec) {
|
|
34390
34842
|
if (args.length === 0 || args[0] === "--help") {
|
|
34391
|
-
console.log("Usage: dryui diagnose [--json] <file.css>");
|
|
34843
|
+
console.log("Usage: dryui diagnose [--json] [--toon] <file.css>");
|
|
34392
34844
|
console.log("");
|
|
34393
34845
|
console.log("Validate theme CSS for missing tokens, value errors,");
|
|
34394
34846
|
console.log("contrast issues, and component token problems.");
|
|
34395
34847
|
console.log("");
|
|
34396
34848
|
console.log("Options:");
|
|
34397
34849
|
console.log(" --json Output raw JSON instead of formatted text");
|
|
34850
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
34398
34851
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
34399
34852
|
}
|
|
34400
34853
|
const jsonMode = args.includes("--json");
|
|
34401
|
-
const
|
|
34854
|
+
const toon = args.includes("--toon");
|
|
34855
|
+
const fileArgs = args.filter((a) => !a.startsWith("--"));
|
|
34402
34856
|
const filePath = fileArgs[0];
|
|
34403
34857
|
if (!filePath) {
|
|
34404
34858
|
console.error("Error: missing file path");
|
|
34405
34859
|
process.exit(1);
|
|
34406
34860
|
}
|
|
34407
|
-
|
|
34861
|
+
const mode = toon ? "toon" : jsonMode ? "json" : "text";
|
|
34862
|
+
runCommand(getDiagnose(filePath, spec, { json: jsonMode, toon }), mode);
|
|
34408
34863
|
}
|
|
34409
34864
|
|
|
34410
|
-
//
|
|
34865
|
+
// ../mcp/dist/composition-search.js
|
|
34411
34866
|
var STOP_WORDS = new Set([
|
|
34412
34867
|
"a",
|
|
34413
34868
|
"an",
|
|
@@ -34473,81 +34928,58 @@ function scoreText(tokens, text) {
|
|
|
34473
34928
|
const lower = text.toLowerCase();
|
|
34474
34929
|
return tokens.reduce((score, t) => score + (lower.includes(t) ? 1 : 0), 0);
|
|
34475
34930
|
}
|
|
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
|
-
}
|
|
34931
|
+
function searchComposition(composition, query) {
|
|
34484
34932
|
const q = query.toLowerCase();
|
|
34485
34933
|
const tokens = tokenize(query);
|
|
34486
34934
|
const exactComponentMatches = [];
|
|
34487
34935
|
const exactRecipeMatches = [];
|
|
34488
|
-
for (const comp of Object.values(
|
|
34936
|
+
for (const comp of Object.values(composition.components)) {
|
|
34489
34937
|
if (comp.component.toLowerCase().includes(q) || comp.useWhen.toLowerCase().includes(q) || comp.alternatives.some((a) => a.component.toLowerCase().includes(q) || a.useWhen.toLowerCase().includes(q)) || comp.antiPatterns.some((ap) => ap.pattern.toLowerCase().includes(q))) {
|
|
34490
34938
|
exactComponentMatches.push(comp);
|
|
34491
34939
|
}
|
|
34492
34940
|
}
|
|
34493
|
-
for (const recipe of Object.values(
|
|
34941
|
+
for (const recipe of Object.values(composition.recipes)) {
|
|
34494
34942
|
if (recipe.name.toLowerCase().includes(q) || recipe.description.toLowerCase().includes(q) || recipe.tags.some((t) => t.toLowerCase().includes(q)) || recipe.components.some((c) => c.toLowerCase().includes(q))) {
|
|
34495
34943
|
exactRecipeMatches.push(recipe);
|
|
34496
34944
|
}
|
|
34497
34945
|
}
|
|
34498
|
-
let componentMatches;
|
|
34499
|
-
let recipeMatches;
|
|
34500
34946
|
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
|
-
}
|
|
34947
|
+
return { componentMatches: exactComponentMatches, recipeMatches: exactRecipeMatches };
|
|
34948
|
+
}
|
|
34949
|
+
if (!tokens.length) {
|
|
34950
|
+
return { componentMatches: [], recipeMatches: [] };
|
|
34951
|
+
}
|
|
34952
|
+
const scoredComponents = [];
|
|
34953
|
+
for (const comp of Object.values(composition.components)) {
|
|
34954
|
+
const corpus = [
|
|
34955
|
+
comp.component,
|
|
34956
|
+
comp.useWhen,
|
|
34957
|
+
...comp.alternatives.flatMap((a) => [a.component, a.useWhen]),
|
|
34958
|
+
...comp.antiPatterns.map((ap) => ap.pattern),
|
|
34959
|
+
...comp.combinesWith
|
|
34960
|
+
].join(" ");
|
|
34961
|
+
const score = scoreText(tokens, corpus);
|
|
34962
|
+
if (score > 0)
|
|
34963
|
+
scoredComponents.push({ comp, score });
|
|
34964
|
+
}
|
|
34965
|
+
const scoredRecipes = [];
|
|
34966
|
+
for (const recipe of Object.values(composition.recipes)) {
|
|
34967
|
+
const corpus = [recipe.name, recipe.description, ...recipe.tags, ...recipe.components].join(" ");
|
|
34968
|
+
const score = scoreText(tokens, corpus);
|
|
34969
|
+
if (score > 0)
|
|
34970
|
+
scoredRecipes.push({ recipe, score });
|
|
34971
|
+
}
|
|
34972
|
+
scoredComponents.sort((a, b) => b.score - a.score);
|
|
34973
|
+
scoredRecipes.sort((a, b) => b.score - a.score);
|
|
34974
|
+
const minScore = Math.max(1, Math.floor(tokens.length * 0.3));
|
|
34975
|
+
return {
|
|
34976
|
+
componentMatches: scoredComponents.filter((s) => s.score >= minScore).slice(0, 10).map((s) => s.comp),
|
|
34977
|
+
recipeMatches: scoredRecipes.filter((s) => s.score >= minScore).slice(0, 5).map((s) => s.recipe)
|
|
34978
|
+
};
|
|
34979
|
+
}
|
|
34980
|
+
function formatCompositionResult(results) {
|
|
34545
34981
|
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) {
|
|
34982
|
+
for (const comp of results.componentMatches) {
|
|
34551
34983
|
lines.push(`── ${comp.component} ──────────────────────────────`);
|
|
34552
34984
|
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
34553
34985
|
lines.push(`Use: ${comp.component} — ${comp.useWhen}`);
|
|
@@ -34570,7 +35002,7 @@ Try:
|
|
|
34570
35002
|
}
|
|
34571
35003
|
lines.push("");
|
|
34572
35004
|
}
|
|
34573
|
-
for (const recipe of recipeMatches) {
|
|
35005
|
+
for (const recipe of results.recipeMatches) {
|
|
34574
35006
|
lines.push(`── Recipe: ${recipe.name} ──────────────────────────────`);
|
|
34575
35007
|
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
34576
35008
|
lines.push(recipe.description);
|
|
@@ -34580,29 +35012,88 @@ Try:
|
|
|
34580
35012
|
lines.push(recipe.snippet);
|
|
34581
35013
|
lines.push("");
|
|
34582
35014
|
}
|
|
34583
|
-
return
|
|
34584
|
-
`)
|
|
35015
|
+
return lines.join(`
|
|
35016
|
+
`);
|
|
35017
|
+
}
|
|
35018
|
+
|
|
35019
|
+
// src/commands/compose.ts
|
|
35020
|
+
var SETUP_PREAMBLE = [
|
|
35021
|
+
"⚠ SETUP: Root +layout.svelte must import '@dryui/ui/themes/default.css'",
|
|
35022
|
+
' and dark.css. app.html needs <html class="theme-auto">.',
|
|
35023
|
+
' Not set up? Run: dryui compose "app shell"',
|
|
35024
|
+
""
|
|
35025
|
+
].join(`
|
|
35026
|
+
`);
|
|
35027
|
+
function getCompose(query, spec, options) {
|
|
35028
|
+
if (!spec.composition) {
|
|
35029
|
+
return {
|
|
35030
|
+
output: "",
|
|
35031
|
+
error: "No composition data available. Rebuild the MCP package.",
|
|
35032
|
+
exitCode: 1
|
|
35033
|
+
};
|
|
35034
|
+
}
|
|
35035
|
+
const results = searchComposition(spec.composition, query);
|
|
35036
|
+
if (!results.componentMatches.length && !results.recipeMatches.length) {
|
|
35037
|
+
if (options?.toon) {
|
|
35038
|
+
return {
|
|
35039
|
+
output: "",
|
|
35040
|
+
error: toonError("no-results", `No composition guidance for "${query}"`, [
|
|
35041
|
+
"Try a component name (DatePicker, Avatar)",
|
|
35042
|
+
"Try a UI concept (date input, image placeholder)",
|
|
35043
|
+
"Try a pattern name (search-form, dashboard-page)"
|
|
35044
|
+
]),
|
|
35045
|
+
exitCode: 1
|
|
35046
|
+
};
|
|
35047
|
+
}
|
|
35048
|
+
return {
|
|
35049
|
+
output: "",
|
|
35050
|
+
error: `No composition guidance found for "${query}".
|
|
35051
|
+
|
|
35052
|
+
Try:
|
|
35053
|
+
- A component name (e.g. "DatePicker", "Avatar")
|
|
35054
|
+
- A UI concept (e.g. "date input", "image placeholder")
|
|
35055
|
+
- A pattern name (e.g. "search-form", "dashboard-page")`,
|
|
35056
|
+
exitCode: 1
|
|
35057
|
+
};
|
|
35058
|
+
}
|
|
35059
|
+
if (options?.toon) {
|
|
35060
|
+
return {
|
|
35061
|
+
output: toonComposition(results, { full: options?.full }),
|
|
35062
|
+
error: null,
|
|
35063
|
+
exitCode: 0
|
|
35064
|
+
};
|
|
35065
|
+
}
|
|
35066
|
+
const output = SETUP_PREAMBLE + `
|
|
35067
|
+
` + formatCompositionResult(results);
|
|
35068
|
+
return { output: output.trimEnd(), error: null, exitCode: 0 };
|
|
34585
35069
|
}
|
|
34586
35070
|
function runCompose(args, spec) {
|
|
34587
35071
|
if (args.length === 0 || args[0] === "--help") {
|
|
34588
|
-
console.log("Usage: dryui compose <query>");
|
|
35072
|
+
console.log("Usage: dryui compose <query> [--toon] [--full]");
|
|
34589
35073
|
console.log("");
|
|
34590
35074
|
console.log("Look up composition guidance for building UIs with DryUI.");
|
|
34591
35075
|
console.log("Returns ranked component alternatives, anti-patterns, and recipes.");
|
|
34592
35076
|
console.log("");
|
|
35077
|
+
console.log("Options:");
|
|
35078
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
35079
|
+
console.log(" --full Include full code snippets (disables truncation)");
|
|
35080
|
+
console.log("");
|
|
34593
35081
|
console.log("Examples:");
|
|
34594
35082
|
console.log(' dryui compose "search form"');
|
|
34595
35083
|
console.log(' dryui compose "hotel card"');
|
|
34596
|
-
console.log(' dryui compose "travel booking"');
|
|
35084
|
+
console.log(' dryui compose "travel booking" --toon');
|
|
34597
35085
|
process.exit(args[0] === "--help" ? 0 : 1);
|
|
34598
35086
|
}
|
|
34599
|
-
const
|
|
34600
|
-
|
|
35087
|
+
const toon = args.includes("--toon");
|
|
35088
|
+
const full = args.includes("--full");
|
|
35089
|
+
const query = args.filter((a) => !a.startsWith("--")).join(" ").trim();
|
|
35090
|
+
const mode = toon ? "toon" : "text";
|
|
35091
|
+
runCommand(getCompose(query, spec, { toon, full }), mode);
|
|
34601
35092
|
}
|
|
34602
35093
|
|
|
34603
35094
|
// ../mcp/src/workspace-audit.ts
|
|
34604
35095
|
import { execFileSync } from "node:child_process";
|
|
34605
|
-
import { existsSync as
|
|
35096
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync, realpathSync, statSync as statSync2 } from "node:fs";
|
|
34606
35097
|
import { dirname as dirname2, relative, resolve as resolve2 } from "node:path";
|
|
34607
35098
|
var DEFAULT_INCLUDE = [
|
|
34608
35099
|
"packages/ui",
|
|
@@ -34635,7 +35126,7 @@ function normalizePath(path) {
|
|
|
34635
35126
|
}
|
|
34636
35127
|
function resolveRoot(inputPath) {
|
|
34637
35128
|
const candidate = resolve2(inputPath ?? process.cwd());
|
|
34638
|
-
if (
|
|
35129
|
+
if (existsSync3(candidate) && statSync2(candidate).isFile()) {
|
|
34639
35130
|
return dirname2(candidate);
|
|
34640
35131
|
}
|
|
34641
35132
|
return candidate;
|
|
@@ -34706,7 +35197,7 @@ function collectChangedFiles(root) {
|
|
|
34706
35197
|
}
|
|
34707
35198
|
}
|
|
34708
35199
|
function defaultInclude(root) {
|
|
34709
|
-
const included = DEFAULT_INCLUDE.filter((entry) =>
|
|
35200
|
+
const included = DEFAULT_INCLUDE.filter((entry) => existsSync3(resolve2(root, entry)));
|
|
34710
35201
|
return included.length > 0 ? included : ["."];
|
|
34711
35202
|
}
|
|
34712
35203
|
function isDefaultExcluded(path) {
|
|
@@ -34880,6 +35371,8 @@ function parseWorkspaceArgs(args) {
|
|
|
34880
35371
|
const exclude = [];
|
|
34881
35372
|
let path;
|
|
34882
35373
|
let json = false;
|
|
35374
|
+
let toon = false;
|
|
35375
|
+
let full = false;
|
|
34883
35376
|
let changed = false;
|
|
34884
35377
|
let maxSeverity = "info";
|
|
34885
35378
|
for (let index = 0;index < args.length; index++) {
|
|
@@ -34890,6 +35383,14 @@ function parseWorkspaceArgs(args) {
|
|
|
34890
35383
|
json = true;
|
|
34891
35384
|
continue;
|
|
34892
35385
|
}
|
|
35386
|
+
if (arg === "--toon") {
|
|
35387
|
+
toon = true;
|
|
35388
|
+
continue;
|
|
35389
|
+
}
|
|
35390
|
+
if (arg === "--full") {
|
|
35391
|
+
full = true;
|
|
35392
|
+
continue;
|
|
35393
|
+
}
|
|
34893
35394
|
if (arg === "--changed") {
|
|
34894
35395
|
changed = true;
|
|
34895
35396
|
continue;
|
|
@@ -34918,7 +35419,7 @@ function parseWorkspaceArgs(args) {
|
|
|
34918
35419
|
if (!path)
|
|
34919
35420
|
path = arg;
|
|
34920
35421
|
}
|
|
34921
|
-
return { path, options: { json, include, exclude, maxSeverity, changed } };
|
|
35422
|
+
return { path, options: { json, toon, full, include, exclude, maxSeverity, changed } };
|
|
34922
35423
|
}
|
|
34923
35424
|
|
|
34924
35425
|
// src/commands/doctor.ts
|
|
@@ -34931,6 +35432,13 @@ function getDoctor(inputPath, spec, options = {}) {
|
|
|
34931
35432
|
...options.maxSeverity ? { maxSeverity: options.maxSeverity } : {},
|
|
34932
35433
|
...options.changed === undefined ? {} : { changed: options.changed }
|
|
34933
35434
|
});
|
|
35435
|
+
if (options.toon) {
|
|
35436
|
+
return {
|
|
35437
|
+
output: toonWorkspaceReport(report, { title: "doctor", command: "doctor", full: options.full }),
|
|
35438
|
+
error: null,
|
|
35439
|
+
exitCode: 0
|
|
35440
|
+
};
|
|
35441
|
+
}
|
|
34934
35442
|
return {
|
|
34935
35443
|
output: formatWorkspaceReport(report, {
|
|
34936
35444
|
title: "DryUI workspace doctor",
|
|
@@ -34950,13 +35458,18 @@ function getDoctor(inputPath, spec, options = {}) {
|
|
|
34950
35458
|
}
|
|
34951
35459
|
function runDoctor(args, spec) {
|
|
34952
35460
|
if (args[0] === "--help") {
|
|
34953
|
-
console.log("Usage: dryui doctor [path] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
35461
|
+
console.log("Usage: dryui doctor [path] [--toon] [--full] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
34954
35462
|
console.log("");
|
|
34955
35463
|
console.log("Inspect workspace health with human-readable findings.");
|
|
35464
|
+
console.log("");
|
|
35465
|
+
console.log("Options:");
|
|
35466
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
35467
|
+
console.log(" --full Show all findings (disables truncation at 50)");
|
|
34956
35468
|
process.exit(0);
|
|
34957
35469
|
}
|
|
34958
35470
|
const { path, options } = parseWorkspaceArgs(args);
|
|
34959
|
-
|
|
35471
|
+
const mode = options.toon ? "toon" : "text";
|
|
35472
|
+
runCommand(getDoctor(path, spec, options), mode);
|
|
34960
35473
|
}
|
|
34961
35474
|
|
|
34962
35475
|
// src/commands/lint.ts
|
|
@@ -34969,6 +35482,13 @@ function getLint(inputPath, spec, options = {}) {
|
|
|
34969
35482
|
...options.changed === undefined ? {} : { changed: options.changed },
|
|
34970
35483
|
...options.maxSeverity ? { maxSeverity: options.maxSeverity } : {}
|
|
34971
35484
|
});
|
|
35485
|
+
if (options.toon) {
|
|
35486
|
+
return {
|
|
35487
|
+
output: toonWorkspaceReport(report, { title: "lint", command: "lint", full: options.full }),
|
|
35488
|
+
error: null,
|
|
35489
|
+
exitCode: report.findings.length > 0 ? 1 : 0
|
|
35490
|
+
};
|
|
35491
|
+
}
|
|
34972
35492
|
if (options.json) {
|
|
34973
35493
|
return {
|
|
34974
35494
|
output: JSON.stringify(report, null, 2),
|
|
@@ -34991,23 +35511,28 @@ function getLint(inputPath, spec, options = {}) {
|
|
|
34991
35511
|
}
|
|
34992
35512
|
function runLint(args, spec) {
|
|
34993
35513
|
if (args[0] === "--help") {
|
|
34994
|
-
console.log("Usage: dryui lint [path] [--json] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
35514
|
+
console.log("Usage: dryui lint [path] [--json] [--toon] [--full] [--include <glob>] [--exclude <glob>] [--max-severity <level>] [--changed]");
|
|
34995
35515
|
console.log("");
|
|
34996
35516
|
console.log("Print deterministic workspace findings.");
|
|
35517
|
+
console.log("");
|
|
35518
|
+
console.log("Options:");
|
|
35519
|
+
console.log(" --toon Output in TOON format (token-optimized for agents)");
|
|
35520
|
+
console.log(" --full Show all findings (disables truncation at 50)");
|
|
34997
35521
|
process.exit(0);
|
|
34998
35522
|
}
|
|
34999
35523
|
const { path, options } = parseWorkspaceArgs(args);
|
|
35000
|
-
|
|
35524
|
+
const mode = options.toon ? "toon" : options.json ? "json" : "text";
|
|
35525
|
+
runCommand(getLint(path, spec, options), mode);
|
|
35001
35526
|
}
|
|
35002
35527
|
|
|
35003
35528
|
// src/commands/feedback.ts
|
|
35004
|
-
import { existsSync as
|
|
35529
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
35005
35530
|
import { spawnSync } from "node:child_process";
|
|
35006
35531
|
import { dirname as dirname4, resolve as resolve3 } from "node:path";
|
|
35007
35532
|
import { fileURLToPath } from "node:url";
|
|
35008
35533
|
|
|
35009
35534
|
// ../feedback-server/src/config.ts
|
|
35010
|
-
import { existsSync as
|
|
35535
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "node:fs";
|
|
35011
35536
|
import { homedir } from "node:os";
|
|
35012
35537
|
import { dirname as dirname3, join } from "node:path";
|
|
35013
35538
|
var DEFAULT_FEEDBACK_PORT = 4748;
|
|
@@ -35217,7 +35742,7 @@ __export(exports_util, {
|
|
|
35217
35742
|
finalizeIssue: () => finalizeIssue,
|
|
35218
35743
|
extend: () => extend,
|
|
35219
35744
|
escapeRegex: () => escapeRegex,
|
|
35220
|
-
esc: () =>
|
|
35745
|
+
esc: () => esc2,
|
|
35221
35746
|
defineLazy: () => defineLazy,
|
|
35222
35747
|
createTransparentProxy: () => createTransparentProxy,
|
|
35223
35748
|
cloneDef: () => cloneDef,
|
|
@@ -35368,7 +35893,7 @@ function randomString(length = 10) {
|
|
|
35368
35893
|
}
|
|
35369
35894
|
return str;
|
|
35370
35895
|
}
|
|
35371
|
-
function
|
|
35896
|
+
function esc2(str) {
|
|
35372
35897
|
return JSON.stringify(str);
|
|
35373
35898
|
}
|
|
35374
35899
|
function slugify(input) {
|
|
@@ -36853,10 +37378,10 @@ function isValidJWT(token, algorithm = null) {
|
|
|
36853
37378
|
const tokensParts = token.split(".");
|
|
36854
37379
|
if (tokensParts.length !== 3)
|
|
36855
37380
|
return false;
|
|
36856
|
-
const [
|
|
36857
|
-
if (!
|
|
37381
|
+
const [header2] = tokensParts;
|
|
37382
|
+
if (!header2)
|
|
36858
37383
|
return false;
|
|
36859
|
-
const parsedHeader = JSON.parse(atob(
|
|
37384
|
+
const parsedHeader = JSON.parse(atob(header2));
|
|
36860
37385
|
if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT")
|
|
36861
37386
|
return false;
|
|
36862
37387
|
if (!parsedHeader.alg)
|
|
@@ -37137,7 +37662,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
37137
37662
|
const doc = new Doc(["shape", "payload", "ctx"]);
|
|
37138
37663
|
const normalized = _normalized.value;
|
|
37139
37664
|
const parseStr = (key) => {
|
|
37140
|
-
const k =
|
|
37665
|
+
const k = esc2(key);
|
|
37141
37666
|
return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
37142
37667
|
};
|
|
37143
37668
|
doc.write(`const input = payload.value;`);
|
|
@@ -37149,7 +37674,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
37149
37674
|
doc.write(`const newResult = {};`);
|
|
37150
37675
|
for (const key of normalized.keys) {
|
|
37151
37676
|
const id = ids[key];
|
|
37152
|
-
const k =
|
|
37677
|
+
const k = esc2(key);
|
|
37153
37678
|
const schema = shape[key];
|
|
37154
37679
|
const isOptionalOut = schema?._zod?.optout === "optional";
|
|
37155
37680
|
doc.write(`const ${id} = ${parseStr(key)};`);
|
|
@@ -40733,7 +41258,7 @@ function usage() {
|
|
|
40733
41258
|
`);
|
|
40734
41259
|
}
|
|
40735
41260
|
function resolveServerEntry() {
|
|
40736
|
-
if (
|
|
41261
|
+
if (existsSync5(SERVER_DIST_PATH))
|
|
40737
41262
|
return SERVER_DIST_PATH;
|
|
40738
41263
|
return SERVER_SRC_PATH;
|
|
40739
41264
|
}
|
|
@@ -40831,32 +41356,52 @@ var USAGE = `Usage: dryui <command> [options]
|
|
|
40831
41356
|
|
|
40832
41357
|
Commands:
|
|
40833
41358
|
init Print setup snippets for a new DryUI app
|
|
40834
|
-
detect [--json] [path]
|
|
40835
|
-
|
|
41359
|
+
detect [--json] [--toon] [path]
|
|
41360
|
+
Detect DryUI project setup
|
|
41361
|
+
install [--json] [--toon] [path]
|
|
41362
|
+
Print a project install plan
|
|
40836
41363
|
add <component> Print a copyable starter snippet for a component
|
|
40837
|
-
info <component>
|
|
40838
|
-
list [--category <cat>]
|
|
40839
|
-
|
|
40840
|
-
|
|
40841
|
-
|
|
40842
|
-
|
|
41364
|
+
info <component> [--toon] Show component API reference
|
|
41365
|
+
list [--category <cat>] [--toon]
|
|
41366
|
+
List all components
|
|
41367
|
+
compose <query> [--toon] Look up composition guidance
|
|
41368
|
+
review [--json] [--toon] <file.svelte>
|
|
41369
|
+
Validate a Svelte file against DryUI spec
|
|
41370
|
+
diagnose [--json] [--toon] <file.css>
|
|
41371
|
+
Validate theme CSS
|
|
41372
|
+
doctor [path] [--toon] [--include <glob>] [--exclude <glob>] [--changed]
|
|
40843
41373
|
Inspect workspace health
|
|
40844
|
-
lint [path] [--json] [--
|
|
41374
|
+
lint [path] [--json] [--toon] [--include <glob>] [--exclude <glob>] [--changed]
|
|
40845
41375
|
Print deterministic workspace findings
|
|
40846
41376
|
feedback <subcommand> Start or inspect the feedback server
|
|
40847
41377
|
|
|
40848
41378
|
Options:
|
|
40849
41379
|
--help Show help for a command
|
|
40850
|
-
--version Show version
|
|
41380
|
+
--version Show version
|
|
41381
|
+
--toon Token-optimized output for AI agents (per-command)
|
|
41382
|
+
--full Disable truncation (use with --toon)`;
|
|
40851
41383
|
function main() {
|
|
40852
41384
|
const args = process.argv.slice(2);
|
|
40853
41385
|
const command = args[0];
|
|
40854
|
-
if (
|
|
41386
|
+
if (command === "--version" || command === "-v") {
|
|
41387
|
+
console.log(VERSION);
|
|
41388
|
+
process.exit(0);
|
|
41389
|
+
}
|
|
41390
|
+
if (command === "--help" || command === "-h") {
|
|
40855
41391
|
console.log(USAGE);
|
|
40856
41392
|
process.exit(0);
|
|
40857
41393
|
}
|
|
40858
|
-
if (command
|
|
40859
|
-
|
|
41394
|
+
if (!command) {
|
|
41395
|
+
try {
|
|
41396
|
+
const detection = detectProject(spec_default, undefined);
|
|
41397
|
+
if (detection.status === "ready" || detection.status === "partial") {
|
|
41398
|
+
console.log(`dryui v${VERSION}
|
|
41399
|
+
`);
|
|
41400
|
+
console.log(toonProjectDetection(detection));
|
|
41401
|
+
process.exit(0);
|
|
41402
|
+
}
|
|
41403
|
+
} catch {}
|
|
41404
|
+
console.log(USAGE);
|
|
40860
41405
|
process.exit(0);
|
|
40861
41406
|
}
|
|
40862
41407
|
const commandArgs = args.slice(1);
|