@dryui/mcp 0.1.3 → 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/architecture.d.ts +0 -1
- package/dist/architecture.js +0 -3
- package/dist/architecture.json +12 -8
- package/dist/composition-search.d.ts +15 -0
- package/dist/composition-search.js +202 -0
- package/dist/contract.v1.json +29 -11
- package/dist/index.js +628 -278
- package/dist/project-planner.js +4 -3
- package/dist/spec.json +29 -11
- package/dist/toon.d.ts +43 -0
- package/dist/toon.js +480 -0
- package/dist/workspace-audit.js +4 -3
- package/package.json +15 -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
|
}
|
|
@@ -8146,7 +8146,8 @@ function detectProject(spec, inputPath) {
|
|
|
8146
8146
|
const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
|
|
8147
8147
|
const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
|
|
8148
8148
|
const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
|
|
8149
|
-
const
|
|
8149
|
+
const themeAuto = appHtml ? hasThemeAuto(appHtml) : false;
|
|
8150
|
+
const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!themeAuto);
|
|
8150
8151
|
const warnings = [];
|
|
8151
8152
|
if (!packageJsonPath)
|
|
8152
8153
|
warnings.push("No package.json found above the provided path.");
|
|
@@ -8172,7 +8173,7 @@ function detectProject(spec, inputPath) {
|
|
|
8172
8173
|
theme: {
|
|
8173
8174
|
defaultImported,
|
|
8174
8175
|
darkImported,
|
|
8175
|
-
themeAuto
|
|
8176
|
+
themeAuto
|
|
8176
8177
|
},
|
|
8177
8178
|
warnings
|
|
8178
8179
|
};
|
|
@@ -8243,7 +8244,7 @@ function planInstall(spec, inputPath) {
|
|
|
8243
8244
|
title: "Set html theme mode to auto",
|
|
8244
8245
|
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.',
|
|
8245
8246
|
path: detection.files.appHtml,
|
|
8246
|
-
snippet: '
|
|
8247
|
+
snippet: 'class="theme-auto"'
|
|
8247
8248
|
});
|
|
8248
8249
|
}
|
|
8249
8250
|
if (steps.length === 0) {
|
|
@@ -8773,6 +8774,582 @@ function formatSimple(name, def) {
|
|
|
8773
8774
|
`);
|
|
8774
8775
|
}
|
|
8775
8776
|
|
|
8777
|
+
// src/composition-search.ts
|
|
8778
|
+
var STOP_WORDS = new Set([
|
|
8779
|
+
"a",
|
|
8780
|
+
"an",
|
|
8781
|
+
"the",
|
|
8782
|
+
"and",
|
|
8783
|
+
"or",
|
|
8784
|
+
"but",
|
|
8785
|
+
"in",
|
|
8786
|
+
"on",
|
|
8787
|
+
"at",
|
|
8788
|
+
"to",
|
|
8789
|
+
"for",
|
|
8790
|
+
"of",
|
|
8791
|
+
"with",
|
|
8792
|
+
"by",
|
|
8793
|
+
"from",
|
|
8794
|
+
"is",
|
|
8795
|
+
"it",
|
|
8796
|
+
"that",
|
|
8797
|
+
"this",
|
|
8798
|
+
"as",
|
|
8799
|
+
"be",
|
|
8800
|
+
"are",
|
|
8801
|
+
"was",
|
|
8802
|
+
"were",
|
|
8803
|
+
"been",
|
|
8804
|
+
"has",
|
|
8805
|
+
"have",
|
|
8806
|
+
"had",
|
|
8807
|
+
"do",
|
|
8808
|
+
"does",
|
|
8809
|
+
"did",
|
|
8810
|
+
"will",
|
|
8811
|
+
"would",
|
|
8812
|
+
"could",
|
|
8813
|
+
"should",
|
|
8814
|
+
"may",
|
|
8815
|
+
"might",
|
|
8816
|
+
"can",
|
|
8817
|
+
"i",
|
|
8818
|
+
"me",
|
|
8819
|
+
"my",
|
|
8820
|
+
"we",
|
|
8821
|
+
"our",
|
|
8822
|
+
"you",
|
|
8823
|
+
"your",
|
|
8824
|
+
"need",
|
|
8825
|
+
"want",
|
|
8826
|
+
"like",
|
|
8827
|
+
"create",
|
|
8828
|
+
"make",
|
|
8829
|
+
"build",
|
|
8830
|
+
"add",
|
|
8831
|
+
"using",
|
|
8832
|
+
"use",
|
|
8833
|
+
"show",
|
|
8834
|
+
"display"
|
|
8835
|
+
]);
|
|
8836
|
+
function tokenize(text) {
|
|
8837
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter((w) => w.length > 1 && !STOP_WORDS.has(w));
|
|
8838
|
+
}
|
|
8839
|
+
function scoreText(tokens, text) {
|
|
8840
|
+
const lower = text.toLowerCase();
|
|
8841
|
+
return tokens.reduce((score, t) => score + (lower.includes(t) ? 1 : 0), 0);
|
|
8842
|
+
}
|
|
8843
|
+
function searchComposition(composition, query) {
|
|
8844
|
+
const q = query.toLowerCase();
|
|
8845
|
+
const tokens = tokenize(query);
|
|
8846
|
+
const exactComponentMatches = [];
|
|
8847
|
+
const exactRecipeMatches = [];
|
|
8848
|
+
for (const comp of Object.values(composition.components)) {
|
|
8849
|
+
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))) {
|
|
8850
|
+
exactComponentMatches.push(comp);
|
|
8851
|
+
}
|
|
8852
|
+
}
|
|
8853
|
+
for (const recipe of Object.values(composition.recipes)) {
|
|
8854
|
+
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))) {
|
|
8855
|
+
exactRecipeMatches.push(recipe);
|
|
8856
|
+
}
|
|
8857
|
+
}
|
|
8858
|
+
if (exactComponentMatches.length || exactRecipeMatches.length) {
|
|
8859
|
+
return { componentMatches: exactComponentMatches, recipeMatches: exactRecipeMatches };
|
|
8860
|
+
}
|
|
8861
|
+
if (!tokens.length) {
|
|
8862
|
+
return { componentMatches: [], recipeMatches: [] };
|
|
8863
|
+
}
|
|
8864
|
+
const scoredComponents = [];
|
|
8865
|
+
for (const comp of Object.values(composition.components)) {
|
|
8866
|
+
const corpus = [
|
|
8867
|
+
comp.component,
|
|
8868
|
+
comp.useWhen,
|
|
8869
|
+
...comp.alternatives.flatMap((a) => [a.component, a.useWhen]),
|
|
8870
|
+
...comp.antiPatterns.map((ap) => ap.pattern),
|
|
8871
|
+
...comp.combinesWith
|
|
8872
|
+
].join(" ");
|
|
8873
|
+
const score = scoreText(tokens, corpus);
|
|
8874
|
+
if (score > 0)
|
|
8875
|
+
scoredComponents.push({ comp, score });
|
|
8876
|
+
}
|
|
8877
|
+
const scoredRecipes = [];
|
|
8878
|
+
for (const recipe of Object.values(composition.recipes)) {
|
|
8879
|
+
const corpus = [recipe.name, recipe.description, ...recipe.tags, ...recipe.components].join(" ");
|
|
8880
|
+
const score = scoreText(tokens, corpus);
|
|
8881
|
+
if (score > 0)
|
|
8882
|
+
scoredRecipes.push({ recipe, score });
|
|
8883
|
+
}
|
|
8884
|
+
scoredComponents.sort((a, b) => b.score - a.score);
|
|
8885
|
+
scoredRecipes.sort((a, b) => b.score - a.score);
|
|
8886
|
+
const minScore = Math.max(1, Math.floor(tokens.length * 0.3));
|
|
8887
|
+
return {
|
|
8888
|
+
componentMatches: scoredComponents.filter((s) => s.score >= minScore).slice(0, 10).map((s) => s.comp),
|
|
8889
|
+
recipeMatches: scoredRecipes.filter((s) => s.score >= minScore).slice(0, 5).map((s) => s.recipe)
|
|
8890
|
+
};
|
|
8891
|
+
}
|
|
8892
|
+
function formatCompositionResult(results) {
|
|
8893
|
+
const lines = [];
|
|
8894
|
+
for (const comp of results.componentMatches) {
|
|
8895
|
+
lines.push(`── ${comp.component} ──────────────────────────────`);
|
|
8896
|
+
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
8897
|
+
lines.push(`Use: ${comp.component} — ${comp.useWhen}`);
|
|
8898
|
+
lines.push("");
|
|
8899
|
+
for (const alt of comp.alternatives) {
|
|
8900
|
+
lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
|
|
8901
|
+
lines.push(alt.snippet.split(`
|
|
8902
|
+
`).map((l) => ` ${l}`).join(`
|
|
8903
|
+
`));
|
|
8904
|
+
lines.push("");
|
|
8905
|
+
}
|
|
8906
|
+
for (const ap of comp.antiPatterns) {
|
|
8907
|
+
lines.push(`⚠ Anti-pattern: ${ap.pattern}`);
|
|
8908
|
+
lines.push(` Why: ${ap.reason}`);
|
|
8909
|
+
lines.push(` Use ${ap.fix} instead`);
|
|
8910
|
+
lines.push("");
|
|
8911
|
+
}
|
|
8912
|
+
if (comp.combinesWith.length) {
|
|
8913
|
+
lines.push(`Combines with: ${comp.combinesWith.join(", ")}`);
|
|
8914
|
+
}
|
|
8915
|
+
lines.push("");
|
|
8916
|
+
}
|
|
8917
|
+
for (const recipe of results.recipeMatches) {
|
|
8918
|
+
lines.push(`── Recipe: ${recipe.name} ──────────────────────────────`);
|
|
8919
|
+
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
8920
|
+
lines.push(recipe.description);
|
|
8921
|
+
lines.push(`Components: ${recipe.components.join(", ")}`);
|
|
8922
|
+
lines.push("");
|
|
8923
|
+
lines.push(`[CODE — use this in your Svelte file]`);
|
|
8924
|
+
lines.push(recipe.snippet);
|
|
8925
|
+
lines.push("");
|
|
8926
|
+
}
|
|
8927
|
+
return lines.join(`
|
|
8928
|
+
`);
|
|
8929
|
+
}
|
|
8930
|
+
|
|
8931
|
+
// src/toon.ts
|
|
8932
|
+
function esc(value) {
|
|
8933
|
+
if (value.includes(",") || value.includes(`
|
|
8934
|
+
`)) {
|
|
8935
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
8936
|
+
}
|
|
8937
|
+
return value;
|
|
8938
|
+
}
|
|
8939
|
+
function header(resource, count, fields) {
|
|
8940
|
+
return `${resource}[${count}]{${fields.join(",")}}:`;
|
|
8941
|
+
}
|
|
8942
|
+
function row(...values) {
|
|
8943
|
+
return " " + values.map((v) => esc(String(v))).join(",");
|
|
8944
|
+
}
|
|
8945
|
+
function truncate(text, maxLen, hint) {
|
|
8946
|
+
if (text.length <= maxLen)
|
|
8947
|
+
return text;
|
|
8948
|
+
return `(truncated, ${text.length} chars -- ${hint})`;
|
|
8949
|
+
}
|
|
8950
|
+
function buildContextualHelp(ctx) {
|
|
8951
|
+
const hints = [];
|
|
8952
|
+
switch (ctx.command) {
|
|
8953
|
+
case "info":
|
|
8954
|
+
if (ctx.componentName) {
|
|
8955
|
+
hints.push(`compose "${ctx.componentName.toLowerCase()}" -- see composition patterns`);
|
|
8956
|
+
hints.push(`add ${ctx.componentName} -- get starter snippet`);
|
|
8957
|
+
}
|
|
8958
|
+
break;
|
|
8959
|
+
case "list":
|
|
8960
|
+
hints.push("info <Component> -- see full API reference");
|
|
8961
|
+
break;
|
|
8962
|
+
case "compose":
|
|
8963
|
+
if (ctx.componentName) {
|
|
8964
|
+
hints.push(`info ${ctx.componentName} -- full API reference`);
|
|
8965
|
+
hints.push(`add ${ctx.componentName} -- starter snippet`);
|
|
8966
|
+
}
|
|
8967
|
+
break;
|
|
8968
|
+
case "review":
|
|
8969
|
+
if (ctx.hasErrors) {
|
|
8970
|
+
hints.push("info <Component> -- check API for errored component");
|
|
8971
|
+
hints.push("diagnose <file.css> -- check theme if theme warnings present");
|
|
8972
|
+
} else if (ctx.isEmpty) {
|
|
8973
|
+
hints.push("lint . -- check entire workspace");
|
|
8974
|
+
}
|
|
8975
|
+
break;
|
|
8976
|
+
case "diagnose":
|
|
8977
|
+
if (ctx.hasErrors) {
|
|
8978
|
+
hints.push('compose "app shell" -- get correct theme setup');
|
|
8979
|
+
} else if (ctx.isEmpty) {
|
|
8980
|
+
hints.push("review <file.svelte> -- validate component usage");
|
|
8981
|
+
}
|
|
8982
|
+
break;
|
|
8983
|
+
case "doctor":
|
|
8984
|
+
case "lint":
|
|
8985
|
+
if (ctx.hasFindings) {
|
|
8986
|
+
hints.push("lint --max-severity error -- focus on errors only");
|
|
8987
|
+
hints.push("review <file.svelte> -- check specific file");
|
|
8988
|
+
} else if (ctx.isEmpty) {
|
|
8989
|
+
hints.push("detect -- verify project setup");
|
|
8990
|
+
}
|
|
8991
|
+
break;
|
|
8992
|
+
case "detect":
|
|
8993
|
+
if (ctx.status === "partial" || ctx.status === "unsupported") {
|
|
8994
|
+
hints.push("install -- see step-by-step setup plan");
|
|
8995
|
+
} else if (ctx.status === "ready") {
|
|
8996
|
+
hints.push("list -- see available components");
|
|
8997
|
+
hints.push('compose "app shell" -- get root layout template');
|
|
8998
|
+
}
|
|
8999
|
+
break;
|
|
9000
|
+
case "install":
|
|
9001
|
+
hints.push("detect -- verify project after completing steps");
|
|
9002
|
+
break;
|
|
9003
|
+
}
|
|
9004
|
+
return hints;
|
|
9005
|
+
}
|
|
9006
|
+
function formatHelp(hints) {
|
|
9007
|
+
if (hints.length === 0)
|
|
9008
|
+
return "";
|
|
9009
|
+
const lines = [`next[${hints.length}]:`];
|
|
9010
|
+
for (const hint of hints) {
|
|
9011
|
+
lines.push(" " + hint);
|
|
9012
|
+
}
|
|
9013
|
+
return lines.join(`
|
|
9014
|
+
`);
|
|
9015
|
+
}
|
|
9016
|
+
function toonComponent(name, def, opts) {
|
|
9017
|
+
const full = opts?.full ?? false;
|
|
9018
|
+
const lines = [];
|
|
9019
|
+
lines.push(`${name} -- ${def.description}`, `category: ${def.category} | tags: ${def.tags.join(",")}`, `import: import { ${name} } from '${def.import}'`, `compound: ${def.compound}`);
|
|
9020
|
+
if (def.compound && def.structure?.tree.length) {
|
|
9021
|
+
lines.push("", header("structure", def.structure.tree.length, ["node"]));
|
|
9022
|
+
for (const node of def.structure.tree) {
|
|
9023
|
+
lines.push(" " + node);
|
|
9024
|
+
}
|
|
9025
|
+
if (def.structure.note)
|
|
9026
|
+
lines.push(` note: ${def.structure.note}`);
|
|
9027
|
+
}
|
|
9028
|
+
if (def.compound && def.parts) {
|
|
9029
|
+
const partEntries = Object.entries(def.parts);
|
|
9030
|
+
lines.push("", header("parts", partEntries.length, ["part"]));
|
|
9031
|
+
for (const [partName, partDef] of partEntries) {
|
|
9032
|
+
lines.push(` ${name}.${partName}`);
|
|
9033
|
+
const props = Object.entries(partDef.props ?? {});
|
|
9034
|
+
if (props.length > 0) {
|
|
9035
|
+
for (const [propName, propDef] of props) {
|
|
9036
|
+
const flags = [propDef.type];
|
|
9037
|
+
if (propDef.required)
|
|
9038
|
+
flags.push("required");
|
|
9039
|
+
if (propDef.default !== undefined)
|
|
9040
|
+
flags.push(`default:${propDef.default}`);
|
|
9041
|
+
if (propDef.acceptedValues?.length)
|
|
9042
|
+
flags.push(`values:${propDef.acceptedValues.join("|")}`);
|
|
9043
|
+
lines.push(` ${propName}: ${flags.join(" | ")}`);
|
|
9044
|
+
}
|
|
9045
|
+
}
|
|
9046
|
+
}
|
|
9047
|
+
} else if (def.props) {
|
|
9048
|
+
const propEntries = Object.entries(def.props);
|
|
9049
|
+
if (propEntries.length > 0) {
|
|
9050
|
+
lines.push("", header("props", propEntries.length, ["name", "type", "required", "default"]));
|
|
9051
|
+
for (const [propName, propDef] of propEntries) {
|
|
9052
|
+
lines.push(row(propName, propDef.type, propDef.required ? "yes" : "no", propDef.default ?? "-"));
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
}
|
|
9056
|
+
const cssEntries = Object.entries(def.cssVars);
|
|
9057
|
+
if (cssEntries.length > 0) {
|
|
9058
|
+
lines.push("", header("css-vars", cssEntries.length, ["name", "description"]));
|
|
9059
|
+
for (const [varName, desc] of cssEntries) {
|
|
9060
|
+
lines.push(row(varName, desc));
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
if (def.dataAttributes.length > 0) {
|
|
9064
|
+
lines.push("", header("data-attrs", def.dataAttributes.length, ["name", "values"]));
|
|
9065
|
+
for (const attr of def.dataAttributes) {
|
|
9066
|
+
lines.push(row(attr.name, attr.values?.join("|") ?? "-"));
|
|
9067
|
+
}
|
|
9068
|
+
}
|
|
9069
|
+
if (def.example) {
|
|
9070
|
+
const example = full ? def.example : truncate(def.example, 400, `use add ${name} for full snippet`);
|
|
9071
|
+
lines.push("", "example:", example);
|
|
9072
|
+
}
|
|
9073
|
+
const help = buildContextualHelp({ command: "info", componentName: name });
|
|
9074
|
+
if (help.length > 0) {
|
|
9075
|
+
lines.push("", formatHelp(help));
|
|
9076
|
+
}
|
|
9077
|
+
return lines.join(`
|
|
9078
|
+
`);
|
|
9079
|
+
}
|
|
9080
|
+
function toonComponentList(components, category) {
|
|
9081
|
+
const entries = Object.entries(components);
|
|
9082
|
+
const filtered = category ? entries.filter(([, def]) => def.category.toLowerCase() === category.toLowerCase()) : entries;
|
|
9083
|
+
if (filtered.length === 0) {
|
|
9084
|
+
const cats = [...new Set(entries.map(([, d]) => d.category))].sort();
|
|
9085
|
+
return `components[0]: no matches
|
|
9086
|
+
available categories: ${cats.join(", ")}`;
|
|
9087
|
+
}
|
|
9088
|
+
const groups = {};
|
|
9089
|
+
for (const entry of filtered) {
|
|
9090
|
+
const cat = entry[1].category;
|
|
9091
|
+
(groups[cat] ??= []).push(entry);
|
|
9092
|
+
}
|
|
9093
|
+
const lines = [header("components", filtered.length, ["name", "category", "compound", "description"])];
|
|
9094
|
+
const sortedCats = Object.keys(groups).sort();
|
|
9095
|
+
for (const cat of sortedCats) {
|
|
9096
|
+
const items = groups[cat] ?? [];
|
|
9097
|
+
for (const [name, def] of items) {
|
|
9098
|
+
lines.push(row(name, cat, def.compound, def.description));
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9101
|
+
const help = buildContextualHelp({ command: "list" });
|
|
9102
|
+
if (help.length > 0) {
|
|
9103
|
+
lines.push("", formatHelp(help));
|
|
9104
|
+
}
|
|
9105
|
+
return lines.join(`
|
|
9106
|
+
`);
|
|
9107
|
+
}
|
|
9108
|
+
function toonComposition(results, opts) {
|
|
9109
|
+
const full = opts?.full ?? false;
|
|
9110
|
+
const lines = [];
|
|
9111
|
+
const totalMatches = results.componentMatches.length + results.recipeMatches.length;
|
|
9112
|
+
if (totalMatches === 0) {
|
|
9113
|
+
return "matches[0]: none";
|
|
9114
|
+
}
|
|
9115
|
+
for (const comp of results.componentMatches) {
|
|
9116
|
+
lines.push(`-- ${comp.component}: ${comp.useWhen}`);
|
|
9117
|
+
for (const alt of comp.alternatives) {
|
|
9118
|
+
const snippet = full ? alt.snippet : truncate(alt.snippet, 500, `use info ${alt.component} for full snippet`);
|
|
9119
|
+
lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
|
|
9120
|
+
lines.push(snippet.split(`
|
|
9121
|
+
`).map((l) => " " + l).join(`
|
|
9122
|
+
`));
|
|
9123
|
+
}
|
|
9124
|
+
for (const ap of comp.antiPatterns) {
|
|
9125
|
+
lines.push(` anti-pattern: ${ap.pattern}`);
|
|
9126
|
+
lines.push(` reason: ${ap.reason} | fix: ${ap.fix}`);
|
|
9127
|
+
}
|
|
9128
|
+
if (comp.combinesWith.length) {
|
|
9129
|
+
lines.push(` combines-with: ${comp.combinesWith.join(",")}`);
|
|
9130
|
+
}
|
|
9131
|
+
lines.push("");
|
|
9132
|
+
}
|
|
9133
|
+
for (const recipe of results.recipeMatches) {
|
|
9134
|
+
const snippet = full ? recipe.snippet : truncate(recipe.snippet, 500, `use compose "${recipe.name}" --full for complete code`);
|
|
9135
|
+
lines.push(`-- recipe: ${recipe.name}`);
|
|
9136
|
+
lines.push(` ${recipe.description}`);
|
|
9137
|
+
lines.push(` components: ${recipe.components.join(",")}`);
|
|
9138
|
+
lines.push(" code:");
|
|
9139
|
+
lines.push(snippet.split(`
|
|
9140
|
+
`).map((l) => " " + l).join(`
|
|
9141
|
+
`));
|
|
9142
|
+
lines.push("");
|
|
9143
|
+
}
|
|
9144
|
+
const firstComponent = results.componentMatches[0]?.alternatives[0]?.component ?? results.recipeMatches[0]?.components[0] ?? undefined;
|
|
9145
|
+
const ctx = { command: "compose" };
|
|
9146
|
+
if (firstComponent)
|
|
9147
|
+
ctx.componentName = firstComponent;
|
|
9148
|
+
const help = buildContextualHelp(ctx);
|
|
9149
|
+
if (help.length > 0) {
|
|
9150
|
+
lines.push(formatHelp(help));
|
|
9151
|
+
}
|
|
9152
|
+
return lines.join(`
|
|
9153
|
+
`).trimEnd();
|
|
9154
|
+
}
|
|
9155
|
+
function toonReviewResult(result) {
|
|
9156
|
+
const lines = [];
|
|
9157
|
+
const hasBlockers = result.issues.some((i) => i.severity === "error");
|
|
9158
|
+
const autoFixable = result.issues.filter((i) => i.fix !== null).length;
|
|
9159
|
+
if (result.issues.length === 0) {
|
|
9160
|
+
lines.push("issues[0]: clean");
|
|
9161
|
+
lines.push(`hasBlockers: false | autoFixable: 0`);
|
|
9162
|
+
} else {
|
|
9163
|
+
lines.push(header("issues", result.issues.length, ["severity", "line", "code", "message"]));
|
|
9164
|
+
for (const issue of result.issues) {
|
|
9165
|
+
lines.push(row(issue.severity, issue.line, issue.code, issue.message));
|
|
9166
|
+
if (issue.fix) {
|
|
9167
|
+
lines.push(` fix: ${issue.fix}`);
|
|
9168
|
+
}
|
|
9169
|
+
}
|
|
9170
|
+
lines.push(`hasBlockers: ${hasBlockers} | autoFixable: ${autoFixable}`);
|
|
9171
|
+
}
|
|
9172
|
+
lines.push(result.summary);
|
|
9173
|
+
const help = buildContextualHelp({
|
|
9174
|
+
command: "review",
|
|
9175
|
+
hasErrors: hasBlockers,
|
|
9176
|
+
isEmpty: result.issues.length === 0
|
|
9177
|
+
});
|
|
9178
|
+
if (help.length > 0) {
|
|
9179
|
+
lines.push("", formatHelp(help));
|
|
9180
|
+
}
|
|
9181
|
+
return lines.join(`
|
|
9182
|
+
`);
|
|
9183
|
+
}
|
|
9184
|
+
function toonDiagnoseResult(result) {
|
|
9185
|
+
const lines = [];
|
|
9186
|
+
const { variables } = result;
|
|
9187
|
+
const missingCount = result.issues.filter((i) => i.code === "missing-token").length;
|
|
9188
|
+
const totalRequired = variables.required + missingCount;
|
|
9189
|
+
const coverage = totalRequired > 0 ? Math.round(variables.required / totalRequired * 100) : 100;
|
|
9190
|
+
lines.push(`tokens: ${variables.found} found, ${variables.required} required, ${variables.extra} extra | coverage: ${coverage}%`);
|
|
9191
|
+
if (result.issues.length === 0) {
|
|
9192
|
+
lines.push("issues[0]: clean");
|
|
9193
|
+
} else {
|
|
9194
|
+
lines.push(header("issues", result.issues.length, ["severity", "code", "variable", "message"]));
|
|
9195
|
+
for (const issue of result.issues) {
|
|
9196
|
+
lines.push(row(issue.severity, issue.code, issue.variable, issue.message));
|
|
9197
|
+
if (issue.fix) {
|
|
9198
|
+
lines.push(` fix: ${issue.fix}`);
|
|
9199
|
+
}
|
|
9200
|
+
}
|
|
9201
|
+
}
|
|
9202
|
+
lines.push(result.summary);
|
|
9203
|
+
const hasErrors = result.issues.some((i) => i.severity === "error");
|
|
9204
|
+
const help = buildContextualHelp({
|
|
9205
|
+
command: "diagnose",
|
|
9206
|
+
hasErrors,
|
|
9207
|
+
isEmpty: result.issues.length === 0
|
|
9208
|
+
});
|
|
9209
|
+
if (help.length > 0) {
|
|
9210
|
+
lines.push("", formatHelp(help));
|
|
9211
|
+
}
|
|
9212
|
+
return lines.join(`
|
|
9213
|
+
`);
|
|
9214
|
+
}
|
|
9215
|
+
var MAX_FINDINGS_DEFAULT = 50;
|
|
9216
|
+
function toonWorkspaceReport(report, opts) {
|
|
9217
|
+
const full = opts?.full ?? false;
|
|
9218
|
+
const title = opts?.title ?? "workspace";
|
|
9219
|
+
const command = opts?.command ?? (title.includes("lint") ? "lint" : "doctor");
|
|
9220
|
+
const lines = [];
|
|
9221
|
+
lines.push(`${title} | root: ${report.root}`);
|
|
9222
|
+
lines.push(`scanned: ${report.scannedFiles} files | errors: ${report.summary.error} | warnings: ${report.summary.warning} | info: ${report.summary.info}`);
|
|
9223
|
+
if (report.summary.byRule && Object.keys(report.summary.byRule).length > 0) {
|
|
9224
|
+
const sorted = Object.entries(report.summary.byRule).sort(([, a], [, b]) => b - a);
|
|
9225
|
+
const topRule = sorted[0];
|
|
9226
|
+
if (topRule) {
|
|
9227
|
+
lines.push(`top-rule: ${topRule[0]} (${topRule[1]} occurrences)`);
|
|
9228
|
+
}
|
|
9229
|
+
}
|
|
9230
|
+
if (report.findings.length === 0) {
|
|
9231
|
+
lines.push("findings[0]: clean");
|
|
9232
|
+
} else {
|
|
9233
|
+
const findings = full ? report.findings : report.findings.slice(0, MAX_FINDINGS_DEFAULT);
|
|
9234
|
+
const truncated = !full && report.findings.length > MAX_FINDINGS_DEFAULT;
|
|
9235
|
+
lines.push(header("findings", findings.length, ["severity", "rule", "file", "line", "message"]));
|
|
9236
|
+
for (const f of findings) {
|
|
9237
|
+
lines.push(row(f.severity, f.ruleId, f.file, f.line ?? "-", f.message));
|
|
9238
|
+
if (f.suggestedFixes.length > 0) {
|
|
9239
|
+
for (const fix of f.suggestedFixes) {
|
|
9240
|
+
lines.push(` fix: ${fix.description}${fix.replacement ? ` -> ${fix.replacement}` : ""}`);
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
}
|
|
9244
|
+
if (truncated) {
|
|
9245
|
+
lines.push(` (showing ${MAX_FINDINGS_DEFAULT} of ${report.findings.length} -- use --full to see all)`);
|
|
9246
|
+
}
|
|
9247
|
+
}
|
|
9248
|
+
if (report.warnings.length > 0) {
|
|
9249
|
+
lines.push("", header("warnings", report.warnings.length, ["message"]));
|
|
9250
|
+
for (const w of report.warnings) {
|
|
9251
|
+
lines.push(" " + w);
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
9254
|
+
const help = buildContextualHelp({
|
|
9255
|
+
command,
|
|
9256
|
+
hasFindings: report.findings.length > 0,
|
|
9257
|
+
isEmpty: report.findings.length === 0
|
|
9258
|
+
});
|
|
9259
|
+
if (help.length > 0) {
|
|
9260
|
+
lines.push("", formatHelp(help));
|
|
9261
|
+
}
|
|
9262
|
+
return lines.join(`
|
|
9263
|
+
`);
|
|
9264
|
+
}
|
|
9265
|
+
function toonProjectDetection(detection) {
|
|
9266
|
+
const lines = [];
|
|
9267
|
+
lines.push(`project: ${detection.status} | framework: ${detection.framework} | pkg-manager: ${detection.packageManager}`);
|
|
9268
|
+
lines.push(`root: ${detection.root ?? "(not found)"}`);
|
|
9269
|
+
lines.push(`deps: ui=${detection.dependencies.ui}, primitives=${detection.dependencies.primitives}`);
|
|
9270
|
+
lines.push(`theme: default=${detection.theme.defaultImported}, dark=${detection.theme.darkImported}, auto=${detection.theme.themeAuto}`);
|
|
9271
|
+
if (detection.warnings.length > 0) {
|
|
9272
|
+
lines.push(header("warnings", detection.warnings.length, ["message"]));
|
|
9273
|
+
for (const w of detection.warnings) {
|
|
9274
|
+
lines.push(" " + w);
|
|
9275
|
+
}
|
|
9276
|
+
}
|
|
9277
|
+
const help = buildContextualHelp({ command: "detect", status: detection.status });
|
|
9278
|
+
if (help.length > 0) {
|
|
9279
|
+
lines.push("", formatHelp(help));
|
|
9280
|
+
}
|
|
9281
|
+
return lines.join(`
|
|
9282
|
+
`);
|
|
9283
|
+
}
|
|
9284
|
+
function toonStep(step, index) {
|
|
9285
|
+
const parts = [`${index + 1}. [${step.status}] ${step.title}: ${step.description}`];
|
|
9286
|
+
if (step.command)
|
|
9287
|
+
parts.push(` cmd: ${step.command}`);
|
|
9288
|
+
if (step.path)
|
|
9289
|
+
parts.push(` file: ${step.path}`);
|
|
9290
|
+
return parts.join(`
|
|
9291
|
+
`);
|
|
9292
|
+
}
|
|
9293
|
+
function toonInstallPlan(plan) {
|
|
9294
|
+
const lines = [];
|
|
9295
|
+
lines.push(toonProjectDetection(plan.detection));
|
|
9296
|
+
lines.push("");
|
|
9297
|
+
if (plan.steps.length === 0) {
|
|
9298
|
+
lines.push("steps[0]: none needed");
|
|
9299
|
+
} else {
|
|
9300
|
+
lines.push(header("steps", plan.steps.length, ["status", "title", "description"]));
|
|
9301
|
+
for (const [i, step] of plan.steps.entries()) {
|
|
9302
|
+
lines.push(toonStep(step, i));
|
|
9303
|
+
}
|
|
9304
|
+
}
|
|
9305
|
+
const help = buildContextualHelp({ command: "install" });
|
|
9306
|
+
if (help.length > 0) {
|
|
9307
|
+
lines.push("", formatHelp(help));
|
|
9308
|
+
}
|
|
9309
|
+
return lines.join(`
|
|
9310
|
+
`);
|
|
9311
|
+
}
|
|
9312
|
+
function toonAddPlan(plan) {
|
|
9313
|
+
const lines = [];
|
|
9314
|
+
lines.push(`add: ${plan.name} | target: ${plan.target ?? "(choose)"}`);
|
|
9315
|
+
if (plan.importStatement) {
|
|
9316
|
+
lines.push(`import: ${plan.importStatement}`);
|
|
9317
|
+
}
|
|
9318
|
+
lines.push("");
|
|
9319
|
+
if (plan.installPlan.steps.length > 0) {
|
|
9320
|
+
lines.push(header("install-steps", plan.installPlan.steps.length, ["status", "title"]));
|
|
9321
|
+
for (const [i, step] of plan.installPlan.steps.entries()) {
|
|
9322
|
+
lines.push(toonStep(step, i));
|
|
9323
|
+
}
|
|
9324
|
+
lines.push("");
|
|
9325
|
+
}
|
|
9326
|
+
if (plan.steps.length > 0) {
|
|
9327
|
+
lines.push(header("add-steps", plan.steps.length, ["status", "title"]));
|
|
9328
|
+
for (const [i, step] of plan.steps.entries()) {
|
|
9329
|
+
lines.push(toonStep(step, i));
|
|
9330
|
+
}
|
|
9331
|
+
}
|
|
9332
|
+
if (plan.warnings.length > 0) {
|
|
9333
|
+
lines.push("", header("warnings", plan.warnings.length, ["message"]));
|
|
9334
|
+
for (const w of plan.warnings) {
|
|
9335
|
+
lines.push(" " + w);
|
|
9336
|
+
}
|
|
9337
|
+
}
|
|
9338
|
+
return lines.join(`
|
|
9339
|
+
`);
|
|
9340
|
+
}
|
|
9341
|
+
function toonError(code, message, suggestions) {
|
|
9342
|
+
const lines = [`error[1]{code,message}: ${esc(code)},${esc(message)}`];
|
|
9343
|
+
if (suggestions?.length) {
|
|
9344
|
+
lines.push(header("suggestions", suggestions.length, ["value"]));
|
|
9345
|
+
for (const s of suggestions) {
|
|
9346
|
+
lines.push(" " + s);
|
|
9347
|
+
}
|
|
9348
|
+
}
|
|
9349
|
+
return lines.join(`
|
|
9350
|
+
`);
|
|
9351
|
+
}
|
|
9352
|
+
|
|
8776
9353
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v3/helpers/util.js
|
|
8777
9354
|
var util;
|
|
8778
9355
|
(function(util2) {
|
|
@@ -9639,10 +10216,10 @@ function isValidJWT(jwt, alg) {
|
|
|
9639
10216
|
if (!jwtRegex.test(jwt))
|
|
9640
10217
|
return false;
|
|
9641
10218
|
try {
|
|
9642
|
-
const [
|
|
9643
|
-
if (!
|
|
10219
|
+
const [header2] = jwt.split(".");
|
|
10220
|
+
if (!header2)
|
|
9644
10221
|
return false;
|
|
9645
|
-
const base64 =
|
|
10222
|
+
const base64 = header2.replace(/-/g, "+").replace(/_/g, "/").padEnd(header2.length + (4 - header2.length % 4) % 4, "=");
|
|
9646
10223
|
const decoded = JSON.parse(atob(base64));
|
|
9647
10224
|
if (typeof decoded !== "object" || decoded === null)
|
|
9648
10225
|
return false;
|
|
@@ -12981,7 +13558,7 @@ __export(exports_util, {
|
|
|
12981
13558
|
finalizeIssue: () => finalizeIssue,
|
|
12982
13559
|
extend: () => extend,
|
|
12983
13560
|
escapeRegex: () => escapeRegex,
|
|
12984
|
-
esc: () =>
|
|
13561
|
+
esc: () => esc2,
|
|
12985
13562
|
defineLazy: () => defineLazy,
|
|
12986
13563
|
createTransparentProxy: () => createTransparentProxy,
|
|
12987
13564
|
cloneDef: () => cloneDef,
|
|
@@ -13132,7 +13709,7 @@ function randomString(length = 10) {
|
|
|
13132
13709
|
}
|
|
13133
13710
|
return str;
|
|
13134
13711
|
}
|
|
13135
|
-
function
|
|
13712
|
+
function esc2(str) {
|
|
13136
13713
|
return JSON.stringify(str);
|
|
13137
13714
|
}
|
|
13138
13715
|
function slugify(input) {
|
|
@@ -14961,10 +15538,10 @@ function isValidJWT2(token, algorithm = null) {
|
|
|
14961
15538
|
const tokensParts = token.split(".");
|
|
14962
15539
|
if (tokensParts.length !== 3)
|
|
14963
15540
|
return false;
|
|
14964
|
-
const [
|
|
14965
|
-
if (!
|
|
15541
|
+
const [header2] = tokensParts;
|
|
15542
|
+
if (!header2)
|
|
14966
15543
|
return false;
|
|
14967
|
-
const parsedHeader = JSON.parse(atob(
|
|
15544
|
+
const parsedHeader = JSON.parse(atob(header2));
|
|
14968
15545
|
if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT")
|
|
14969
15546
|
return false;
|
|
14970
15547
|
if (!parsedHeader.alg)
|
|
@@ -15358,7 +15935,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
15358
15935
|
const doc = new Doc(["shape", "payload", "ctx"]);
|
|
15359
15936
|
const normalized = _normalized.value;
|
|
15360
15937
|
const parseStr = (key) => {
|
|
15361
|
-
const k =
|
|
15938
|
+
const k = esc2(key);
|
|
15362
15939
|
return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
15363
15940
|
};
|
|
15364
15941
|
doc.write(`const input = payload.value;`);
|
|
@@ -15370,7 +15947,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
15370
15947
|
doc.write(`const newResult = {};`);
|
|
15371
15948
|
for (const key of normalized.keys) {
|
|
15372
15949
|
const id = ids[key];
|
|
15373
|
-
const k =
|
|
15950
|
+
const k = esc2(key);
|
|
15374
15951
|
const schema = shape[key];
|
|
15375
15952
|
const isOptionalOut = schema?._zod?.optout === "optional";
|
|
15376
15953
|
doc.write(`const ${id} = ${parseStr(key)};`);
|
|
@@ -30688,11 +31265,14 @@ function toolError(e) {
|
|
|
30688
31265
|
var SERVER_INSTRUCTIONS = [
|
|
30689
31266
|
"DryUI is a zero-dependency Svelte 5 component library. Follow these rules:",
|
|
30690
31267
|
"",
|
|
31268
|
+
"OUTPUT FORMAT: All tool responses use TOON (token-optimized) format. Each response ends with",
|
|
31269
|
+
" `next[]` suggesting logical next commands. Empty results show `issues[0]: clean` or similar.",
|
|
31270
|
+
"",
|
|
30691
31271
|
'1. SET UP THE APP SHELL FIRST: Run `compose "app shell"` to get the root layout template.',
|
|
30692
31272
|
' Your +layout.svelte must import the theme CSS, and app.html needs <html class="theme-auto">.',
|
|
30693
31273
|
"",
|
|
30694
|
-
"2. PAGE LAYOUT: Use
|
|
30695
|
-
"
|
|
31274
|
+
"2. PAGE LAYOUT: Use CSS grid for layout — not flexbox. Use Container for constrained content",
|
|
31275
|
+
" width. Use `@container` queries for responsive sizing (never `@media` for layout).",
|
|
30696
31276
|
"",
|
|
30697
31277
|
"3. CORRECT CSS TOKENS: Background is --dry-color-bg-base (not --dry-color-bg).",
|
|
30698
31278
|
' Text is --dry-color-text-strong (not --dry-color-text). See compose "app shell" for the full reset.',
|
|
@@ -30704,10 +31284,9 @@ var SERVER_INSTRUCTIONS = [
|
|
|
30704
31284
|
" the compose tool are instructions for YOU, not content for the page. Never render them as",
|
|
30705
31285
|
" visible text in the UI. Only use the code snippets from compose output.",
|
|
30706
31286
|
"",
|
|
30707
|
-
"6. USE DRYUI
|
|
30708
|
-
" of
|
|
30709
|
-
"
|
|
30710
|
-
" structure when a DryUI component exists."
|
|
31287
|
+
"6. USE DRYUI COMPONENTS FOR UI ELEMENTS: Use Field.Root + Label for form fields, Button instead",
|
|
31288
|
+
" of raw `<button>`, Separator instead of `<hr>`. Always call `info` or `compose` to check",
|
|
31289
|
+
" if a DryUI component exists before writing raw HTML."
|
|
30711
31290
|
].join(`
|
|
30712
31291
|
`);
|
|
30713
31292
|
var server = new McpServer({ name: "@dryui/mcp", version: version2 }, {
|
|
@@ -30729,7 +31308,7 @@ var REVIEW_DESC = [
|
|
|
30729
31308
|
`- **Suggestions:** Manual flex/grid where DryUI layout components apply, hardcoded colors, raw <hr> instead of <Separator>
|
|
30730
31309
|
|
|
30731
31310
|
`,
|
|
30732
|
-
`Output:
|
|
31311
|
+
`Output: TOON format with issues, hasBlockers/autoFixable aggregates, and next-step suggestions. When projectCss is provided, theme diagnosis results are appended.
|
|
30733
31312
|
|
|
30734
31313
|
`,
|
|
30735
31314
|
`Example:
|
|
@@ -30753,25 +31332,7 @@ var REVIEW_DESC = [
|
|
|
30753
31332
|
`Output:
|
|
30754
31333
|
|
|
30755
31334
|
`,
|
|
30756
|
-
|
|
30757
|
-
{
|
|
30758
|
-
"issues": [
|
|
30759
|
-
{
|
|
30760
|
-
"severity": "error",
|
|
30761
|
-
"code": "bare-compound",
|
|
30762
|
-
"message": "Card is a compound component — use <Card.Root> instead of <Card>",
|
|
30763
|
-
"line": 5
|
|
30764
|
-
},
|
|
30765
|
-
{
|
|
30766
|
-
"severity": "suggestion",
|
|
30767
|
-
"code": "prefer-separator",
|
|
30768
|
-
"message": "Use <Separator /> instead of raw <hr>",
|
|
30769
|
-
"line": 11
|
|
30770
|
-
}
|
|
30771
|
-
],
|
|
30772
|
-
"summary": "1 error, 0 warnings, 1 suggestion"
|
|
30773
|
-
}
|
|
30774
|
-
\`\`\``
|
|
31335
|
+
"```\nissues[2]{severity,line,code,message}:\n error,5,bare-compound,Card is a compound component — use <Card.Root> instead of <Card>\n suggestion,11,prefer-separator,Use <Separator /> instead of raw <hr>\nhasBlockers: true | autoFixable: 0\n1 error, 0 warnings, 1 suggestion\n```"
|
|
30775
31336
|
].join(`
|
|
30776
31337
|
`);
|
|
30777
31338
|
var DIAGNOSE_DESC = [
|
|
@@ -30797,7 +31358,7 @@ var DIAGNOSE_DESC = [
|
|
|
30797
31358
|
`- To validate a custom theme before shipping
|
|
30798
31359
|
|
|
30799
31360
|
`,
|
|
30800
|
-
`Output:
|
|
31361
|
+
`Output: TOON format with token coverage percentage, issues, and next-step suggestions.
|
|
30801
31362
|
|
|
30802
31363
|
`,
|
|
30803
31364
|
`Example:
|
|
@@ -30824,18 +31385,15 @@ server.tool("review", REVIEW_DESC, {
|
|
|
30824
31385
|
const spec = getSpec();
|
|
30825
31386
|
const parts = [];
|
|
30826
31387
|
const result = reviewComponent(code, spec, filename);
|
|
30827
|
-
parts.push({ type: "text", text:
|
|
31388
|
+
parts.push({ type: "text", text: toonReviewResult(result) });
|
|
30828
31389
|
if (projectCss) {
|
|
30829
31390
|
const diagnosis = diagnoseTheme(projectCss, spec);
|
|
30830
31391
|
if (diagnosis.issues.length > 0) {
|
|
30831
31392
|
parts.push({
|
|
30832
31393
|
type: "text",
|
|
30833
31394
|
text: `
|
|
30834
|
-
|
|
30835
31395
|
--- THEME DIAGNOSIS ---
|
|
30836
|
-
|
|
30837
|
-
|
|
30838
|
-
` + JSON.stringify(diagnosis, null, 2)
|
|
31396
|
+
` + toonDiagnoseResult(diagnosis)
|
|
30839
31397
|
});
|
|
30840
31398
|
}
|
|
30841
31399
|
}
|
|
@@ -30863,7 +31421,7 @@ server.tool("diagnose", DIAGNOSE_DESC, {
|
|
|
30863
31421
|
};
|
|
30864
31422
|
}
|
|
30865
31423
|
const result = diagnoseTheme(content, getSpec());
|
|
30866
|
-
return { content: [{ type: "text", text:
|
|
31424
|
+
return { content: [{ type: "text", text: toonDiagnoseResult(result) }] };
|
|
30867
31425
|
} catch (e) {
|
|
30868
31426
|
return toolError(e);
|
|
30869
31427
|
}
|
|
@@ -30874,7 +31432,7 @@ var DETECT_PROJECT_DESC = [
|
|
|
30874
31432
|
`,
|
|
30875
31433
|
`Input: Optional cwd or project path.
|
|
30876
31434
|
`,
|
|
30877
|
-
`Output:
|
|
31435
|
+
`Output: TOON format with project status, framework, deps, theme state, and next-step suggestions.
|
|
30878
31436
|
|
|
30879
31437
|
`,
|
|
30880
31438
|
"Use this before planning installation or project-aware component adoption."
|
|
@@ -30884,7 +31442,7 @@ server.tool("detect_project", DETECT_PROJECT_DESC, {
|
|
|
30884
31442
|
}, async ({ cwd }) => {
|
|
30885
31443
|
try {
|
|
30886
31444
|
const result = detectProject(getSpec(), cwd);
|
|
30887
|
-
return { content: [{ type: "text", text:
|
|
31445
|
+
return { content: [{ type: "text", text: toonProjectDetection(result) }] };
|
|
30888
31446
|
} catch (e) {
|
|
30889
31447
|
return toolError(e);
|
|
30890
31448
|
}
|
|
@@ -30895,7 +31453,7 @@ var PLAN_INSTALL_DESC = [
|
|
|
30895
31453
|
`,
|
|
30896
31454
|
`Input: Optional cwd or project path.
|
|
30897
31455
|
`,
|
|
30898
|
-
`Output:
|
|
31456
|
+
`Output: TOON format with project detection plus ordered install steps. The result never mutates files.
|
|
30899
31457
|
|
|
30900
31458
|
`,
|
|
30901
31459
|
"Use this to prepare a repo for DryUI before inserting components."
|
|
@@ -30905,7 +31463,7 @@ server.tool("plan_install", PLAN_INSTALL_DESC, {
|
|
|
30905
31463
|
}, async ({ cwd }) => {
|
|
30906
31464
|
try {
|
|
30907
31465
|
const result = planInstall(getSpec(), cwd);
|
|
30908
|
-
return { content: [{ type: "text", text:
|
|
31466
|
+
return { content: [{ type: "text", text: toonInstallPlan(result) }] };
|
|
30909
31467
|
} catch (e) {
|
|
30910
31468
|
return toolError(e);
|
|
30911
31469
|
}
|
|
@@ -30916,7 +31474,7 @@ var PLAN_ADD_DESC = [
|
|
|
30916
31474
|
`,
|
|
30917
31475
|
`Input: Component/composed-output name plus optional cwd, target file path, subpath flag, and withTheme flag.
|
|
30918
31476
|
`,
|
|
30919
|
-
`Output:
|
|
31477
|
+
`Output: TOON format with install prerequisites, chosen import/source, target suggestions, and next steps. The result never mutates files.
|
|
30920
31478
|
|
|
30921
31479
|
`,
|
|
30922
31480
|
"Use this when an agent needs project-aware adoption guidance without editing the repo directly."
|
|
@@ -30936,7 +31494,7 @@ server.tool("plan_add", PLAN_ADD_DESC, {
|
|
|
30936
31494
|
...withTheme !== undefined ? { withTheme } : {}
|
|
30937
31495
|
};
|
|
30938
31496
|
const result = planAdd(getSpec(), name, options);
|
|
30939
|
-
return { content: [{ type: "text", text:
|
|
31497
|
+
return { content: [{ type: "text", text: toonAddPlan(result) }] };
|
|
30940
31498
|
} catch (e) {
|
|
30941
31499
|
return toolError(e);
|
|
30942
31500
|
}
|
|
@@ -30949,7 +31507,7 @@ var DOCTOR_DESC = [
|
|
|
30949
31507
|
`,
|
|
30950
31508
|
`Input: Optional cwd/path plus include/exclude globs, maxSeverity, and changed-only filtering.
|
|
30951
31509
|
`,
|
|
30952
|
-
`Output:
|
|
31510
|
+
`Output: TOON format with top-rule aggregate, findings (capped at 50, use --full for all), and next-step suggestions.
|
|
30953
31511
|
|
|
30954
31512
|
`,
|
|
30955
31513
|
"Use this for repo-wide health summaries across Svelte files, theme CSS, and project setup."
|
|
@@ -30969,7 +31527,7 @@ server.tool("doctor", DOCTOR_DESC, {
|
|
|
30969
31527
|
...maxSeverity ? { maxSeverity } : {},
|
|
30970
31528
|
...changed !== undefined ? { changed } : {}
|
|
30971
31529
|
});
|
|
30972
|
-
return { content: [{ type: "text", text:
|
|
31530
|
+
return { content: [{ type: "text", text: toonWorkspaceReport(result, { title: "doctor", command: "doctor" }) }] };
|
|
30973
31531
|
} catch (e) {
|
|
30974
31532
|
return toolError(e);
|
|
30975
31533
|
}
|
|
@@ -30980,7 +31538,7 @@ var LINT_DESC = [
|
|
|
30980
31538
|
`,
|
|
30981
31539
|
`Input: Optional cwd/path plus include/exclude globs, maxSeverity, and changed-only filtering.
|
|
30982
31540
|
`,
|
|
30983
|
-
`Output:
|
|
31541
|
+
`Output: TOON format with sorted findings (capped at 50) and next-step suggestions.
|
|
30984
31542
|
|
|
30985
31543
|
`,
|
|
30986
31544
|
"Use this when you need machine-oriented DryUI audit output for CI or automation."
|
|
@@ -31000,7 +31558,7 @@ server.tool("lint", LINT_DESC, {
|
|
|
31000
31558
|
...maxSeverity ? { maxSeverity } : {},
|
|
31001
31559
|
...changed !== undefined ? { changed } : {}
|
|
31002
31560
|
});
|
|
31003
|
-
return { content: [{ type: "text", text:
|
|
31561
|
+
return { content: [{ type: "text", text: toonWorkspaceReport(result, { title: "lint", command: "lint" }) }] };
|
|
31004
31562
|
} catch (e) {
|
|
31005
31563
|
return toolError(e);
|
|
31006
31564
|
}
|
|
@@ -31014,10 +31572,7 @@ var INFO_DESC = [
|
|
|
31014
31572
|
`,
|
|
31015
31573
|
`Input: Component name (case-insensitive, e.g. "Button", "card").
|
|
31016
31574
|
`,
|
|
31017
|
-
|
|
31018
|
-
"whether it is compound or simple, root and subpath imports when available, all props ",
|
|
31019
|
-
"(with type, accepted values when known, required, bindable, default, and notes), native/rest-prop forwarding, ",
|
|
31020
|
-
`compound structure guidance, CSS variables, data attributes, and example code.
|
|
31575
|
+
`Output: TOON format with props, structure, CSS vars, data attributes, truncated example, and next-step suggestions.
|
|
31021
31576
|
|
|
31022
31577
|
`,
|
|
31023
31578
|
"Use this tool to understand what the component supports before generating code."
|
|
@@ -31044,8 +31599,7 @@ Available components:
|
|
|
31044
31599
|
};
|
|
31045
31600
|
}
|
|
31046
31601
|
const { name: canonical, def } = result;
|
|
31047
|
-
|
|
31048
|
-
return { content: [{ type: "text", text: output }] };
|
|
31602
|
+
return { content: [{ type: "text", text: toonComponent(canonical, def) }] };
|
|
31049
31603
|
} catch (e) {
|
|
31050
31604
|
return toolError(e);
|
|
31051
31605
|
}
|
|
@@ -31056,7 +31610,7 @@ var LIST_DESC = [
|
|
|
31056
31610
|
`,
|
|
31057
31611
|
`Input: Optional category name to filter results.
|
|
31058
31612
|
`,
|
|
31059
|
-
`Output:
|
|
31613
|
+
`Output: TOON format with components listed as name, category, compound, description.
|
|
31060
31614
|
|
|
31061
31615
|
`,
|
|
31062
31616
|
"Use this tool to discover which components are available before looking up details with the info tool."
|
|
@@ -31066,42 +31620,7 @@ server.tool("list", LIST_DESC, {
|
|
|
31066
31620
|
}, async ({ category }) => {
|
|
31067
31621
|
try {
|
|
31068
31622
|
const components = getComponents();
|
|
31069
|
-
|
|
31070
|
-
const groups = {};
|
|
31071
|
-
for (const [name, def] of entries) {
|
|
31072
|
-
const cat = def.category;
|
|
31073
|
-
if (category && cat.toLowerCase() !== category.toLowerCase())
|
|
31074
|
-
continue;
|
|
31075
|
-
(groups[cat] ??= []).push({ name, description: def.description });
|
|
31076
|
-
}
|
|
31077
|
-
if (Object.keys(groups).length === 0) {
|
|
31078
|
-
const allCats = [...new Set(entries.map(([, d]) => d.category))].sort();
|
|
31079
|
-
return {
|
|
31080
|
-
content: [
|
|
31081
|
-
{
|
|
31082
|
-
type: "text",
|
|
31083
|
-
text: `No components found for category: "${category}"
|
|
31084
|
-
|
|
31085
|
-
Available categories:
|
|
31086
|
-
${allCats.join(", ")}`
|
|
31087
|
-
}
|
|
31088
|
-
],
|
|
31089
|
-
isError: true
|
|
31090
|
-
};
|
|
31091
|
-
}
|
|
31092
|
-
const lines = [];
|
|
31093
|
-
const sortedCats = Object.keys(groups).sort();
|
|
31094
|
-
for (const cat of sortedCats) {
|
|
31095
|
-
const items = groups[cat] ?? [];
|
|
31096
|
-
lines.push(`${cat.toUpperCase()}`);
|
|
31097
|
-
const maxNameLen = Math.max(...items.map((c) => c.name.length));
|
|
31098
|
-
for (const item of items) {
|
|
31099
|
-
lines.push(` ${pad(item.name, maxNameLen + 2)}${item.description}`);
|
|
31100
|
-
}
|
|
31101
|
-
lines.push("");
|
|
31102
|
-
}
|
|
31103
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
31104
|
-
`).trimEnd() }] };
|
|
31623
|
+
return { content: [{ type: "text", text: toonComponentList(components, category) }] };
|
|
31105
31624
|
} catch (e) {
|
|
31106
31625
|
return toolError(e);
|
|
31107
31626
|
}
|
|
@@ -31110,7 +31629,7 @@ var COMPOSE_DESC = [
|
|
|
31110
31629
|
`Look up composition guidance for building UIs with DryUI.
|
|
31111
31630
|
|
|
31112
31631
|
`,
|
|
31113
|
-
`Returns ranked component alternatives with code snippets, anti-patterns to avoid, and
|
|
31632
|
+
`Returns ranked component alternatives with truncated code snippets, anti-patterns to avoid, and composition recipes in TOON format.
|
|
31114
31633
|
`,
|
|
31115
31634
|
`Call this BEFORE writing any DryUI layout to ensure correct component selection.
|
|
31116
31635
|
|
|
@@ -31126,177 +31645,12 @@ var COMPOSE_DESC = [
|
|
|
31126
31645
|
`,
|
|
31127
31646
|
"Output: Ranked alternatives with snippets, anti-patterns, and matching recipes."
|
|
31128
31647
|
].join("");
|
|
31129
|
-
var STOP_WORDS = new Set([
|
|
31130
|
-
"a",
|
|
31131
|
-
"an",
|
|
31132
|
-
"the",
|
|
31133
|
-
"and",
|
|
31134
|
-
"or",
|
|
31135
|
-
"but",
|
|
31136
|
-
"in",
|
|
31137
|
-
"on",
|
|
31138
|
-
"at",
|
|
31139
|
-
"to",
|
|
31140
|
-
"for",
|
|
31141
|
-
"of",
|
|
31142
|
-
"with",
|
|
31143
|
-
"by",
|
|
31144
|
-
"from",
|
|
31145
|
-
"is",
|
|
31146
|
-
"it",
|
|
31147
|
-
"that",
|
|
31148
|
-
"this",
|
|
31149
|
-
"as",
|
|
31150
|
-
"be",
|
|
31151
|
-
"are",
|
|
31152
|
-
"was",
|
|
31153
|
-
"were",
|
|
31154
|
-
"been",
|
|
31155
|
-
"has",
|
|
31156
|
-
"have",
|
|
31157
|
-
"had",
|
|
31158
|
-
"do",
|
|
31159
|
-
"does",
|
|
31160
|
-
"did",
|
|
31161
|
-
"will",
|
|
31162
|
-
"would",
|
|
31163
|
-
"could",
|
|
31164
|
-
"should",
|
|
31165
|
-
"may",
|
|
31166
|
-
"might",
|
|
31167
|
-
"can",
|
|
31168
|
-
"i",
|
|
31169
|
-
"me",
|
|
31170
|
-
"my",
|
|
31171
|
-
"we",
|
|
31172
|
-
"our",
|
|
31173
|
-
"you",
|
|
31174
|
-
"your",
|
|
31175
|
-
"need",
|
|
31176
|
-
"want",
|
|
31177
|
-
"like",
|
|
31178
|
-
"create",
|
|
31179
|
-
"make",
|
|
31180
|
-
"build",
|
|
31181
|
-
"add",
|
|
31182
|
-
"using",
|
|
31183
|
-
"use",
|
|
31184
|
-
"show",
|
|
31185
|
-
"display"
|
|
31186
|
-
]);
|
|
31187
|
-
function tokenize(text) {
|
|
31188
|
-
return text.toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter((w) => w.length > 1 && !STOP_WORDS.has(w));
|
|
31189
|
-
}
|
|
31190
|
-
function scoreText(tokens, text) {
|
|
31191
|
-
const lower = text.toLowerCase();
|
|
31192
|
-
return tokens.reduce((score, t) => score + (lower.includes(t) ? 1 : 0), 0);
|
|
31193
|
-
}
|
|
31194
31648
|
function findComposition(query) {
|
|
31195
31649
|
const compositionData = getSpec().composition;
|
|
31196
31650
|
if (!compositionData)
|
|
31197
|
-
return {
|
|
31198
|
-
|
|
31199
|
-
recipeMatches: []
|
|
31200
|
-
};
|
|
31201
|
-
const q = query.toLowerCase();
|
|
31202
|
-
const tokens = tokenize(query);
|
|
31203
|
-
const exactComponentMatches = [];
|
|
31204
|
-
const exactRecipeMatches = [];
|
|
31205
|
-
for (const comp of Object.values(compositionData.components)) {
|
|
31206
|
-
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))) {
|
|
31207
|
-
exactComponentMatches.push(comp);
|
|
31208
|
-
}
|
|
31209
|
-
}
|
|
31210
|
-
for (const recipe of Object.values(compositionData.recipes)) {
|
|
31211
|
-
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))) {
|
|
31212
|
-
exactRecipeMatches.push(recipe);
|
|
31213
|
-
}
|
|
31214
|
-
}
|
|
31215
|
-
if (exactComponentMatches.length || exactRecipeMatches.length) {
|
|
31216
|
-
return { componentMatches: exactComponentMatches, recipeMatches: exactRecipeMatches };
|
|
31217
|
-
}
|
|
31218
|
-
if (!tokens.length)
|
|
31219
|
-
return {
|
|
31220
|
-
componentMatches: [],
|
|
31221
|
-
recipeMatches: []
|
|
31222
|
-
};
|
|
31223
|
-
const scoredComponents = [];
|
|
31224
|
-
for (const comp of Object.values(compositionData.components)) {
|
|
31225
|
-
const corpus = [
|
|
31226
|
-
comp.component,
|
|
31227
|
-
comp.useWhen,
|
|
31228
|
-
...comp.alternatives.flatMap((a) => [a.component, a.useWhen]),
|
|
31229
|
-
...comp.antiPatterns.map((ap) => ap.pattern),
|
|
31230
|
-
...comp.combinesWith
|
|
31231
|
-
].join(" ");
|
|
31232
|
-
const score = scoreText(tokens, corpus);
|
|
31233
|
-
if (score > 0)
|
|
31234
|
-
scoredComponents.push({ comp, score });
|
|
31235
|
-
}
|
|
31236
|
-
const scoredRecipes = [];
|
|
31237
|
-
for (const recipe of Object.values(compositionData.recipes)) {
|
|
31238
|
-
const corpus = [recipe.name, recipe.description, ...recipe.tags, ...recipe.components].join(" ");
|
|
31239
|
-
const score = scoreText(tokens, corpus);
|
|
31240
|
-
if (score > 0)
|
|
31241
|
-
scoredRecipes.push({ recipe, score });
|
|
31242
|
-
}
|
|
31243
|
-
scoredComponents.sort((a, b) => b.score - a.score);
|
|
31244
|
-
scoredRecipes.sort((a, b) => b.score - a.score);
|
|
31245
|
-
const minScore = Math.max(1, Math.floor(tokens.length * 0.3));
|
|
31246
|
-
return {
|
|
31247
|
-
componentMatches: scoredComponents.filter((s) => s.score >= minScore).slice(0, 10).map((s) => s.comp),
|
|
31248
|
-
recipeMatches: scoredRecipes.filter((s) => s.score >= minScore).slice(0, 5).map((s) => s.recipe)
|
|
31249
|
-
};
|
|
31250
|
-
}
|
|
31251
|
-
function formatCompositionResult(results) {
|
|
31252
|
-
const lines = [];
|
|
31253
|
-
for (const comp of results.componentMatches) {
|
|
31254
|
-
lines.push(`── ${comp.component} ──────────────────────────────`);
|
|
31255
|
-
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
31256
|
-
lines.push(`Use: ${comp.component} — ${comp.useWhen}`);
|
|
31257
|
-
lines.push("");
|
|
31258
|
-
for (const alt of comp.alternatives) {
|
|
31259
|
-
lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
|
|
31260
|
-
lines.push(alt.snippet.split(`
|
|
31261
|
-
`).map((l) => ` ${l}`).join(`
|
|
31262
|
-
`));
|
|
31263
|
-
lines.push("");
|
|
31264
|
-
}
|
|
31265
|
-
for (const ap of comp.antiPatterns) {
|
|
31266
|
-
lines.push(`⚠ Anti-pattern: ${ap.pattern}`);
|
|
31267
|
-
lines.push(` Why: ${ap.reason}`);
|
|
31268
|
-
lines.push(` Use ${ap.fix} instead`);
|
|
31269
|
-
lines.push("");
|
|
31270
|
-
}
|
|
31271
|
-
if (comp.combinesWith.length) {
|
|
31272
|
-
lines.push(`Combines with: ${comp.combinesWith.join(", ")}`);
|
|
31273
|
-
}
|
|
31274
|
-
lines.push("");
|
|
31275
|
-
}
|
|
31276
|
-
for (const recipe of results.recipeMatches) {
|
|
31277
|
-
lines.push(`── Recipe: ${recipe.name} ──────────────────────────────`);
|
|
31278
|
-
lines.push(`[DEV GUIDANCE — do not render as page content]`);
|
|
31279
|
-
lines.push(recipe.description);
|
|
31280
|
-
lines.push(`Components: ${recipe.components.join(", ")}`);
|
|
31281
|
-
lines.push("");
|
|
31282
|
-
lines.push(`[CODE — use this in your Svelte file]`);
|
|
31283
|
-
lines.push(recipe.snippet);
|
|
31284
|
-
lines.push("");
|
|
31285
|
-
}
|
|
31286
|
-
return lines.join(`
|
|
31287
|
-
`);
|
|
31651
|
+
return { componentMatches: [], recipeMatches: [] };
|
|
31652
|
+
return searchComposition(compositionData, query);
|
|
31288
31653
|
}
|
|
31289
|
-
var COMPOSE_PREAMBLE = [
|
|
31290
|
-
"⚠ SETUP: Your root +layout.svelte must import '@dryui/ui/themes/default.css'",
|
|
31291
|
-
` and '@dryui/ui/themes/dark.css'. app.html needs <html class="theme-auto">.`,
|
|
31292
|
-
' If not set up yet, run: compose "app shell"',
|
|
31293
|
-
"",
|
|
31294
|
-
"⚠ THIS OUTPUT IS DEVELOPER GUIDANCE — do NOT render recipe names, descriptions,",
|
|
31295
|
-
" or component lists as visible page content. Only use the code snippets.",
|
|
31296
|
-
"",
|
|
31297
|
-
""
|
|
31298
|
-
].join(`
|
|
31299
|
-
`);
|
|
31300
31654
|
server.tool("compose", COMPOSE_DESC, {
|
|
31301
31655
|
query: exports_external.string().describe('Short keywords: component name, pattern name, or UI concept (e.g. "search form", "date input", "hotel card", "dashboard")')
|
|
31302
31656
|
}, async ({ query }) => {
|
|
@@ -31307,12 +31661,8 @@ server.tool("compose", COMPOSE_DESC, {
|
|
|
31307
31661
|
content: [
|
|
31308
31662
|
{
|
|
31309
31663
|
type: "text",
|
|
31310
|
-
text: `
|
|
31311
|
-
|
|
31312
|
-
Try:
|
|
31313
|
-
- A component name (e.g. "DatePicker", "Avatar")
|
|
31314
|
-
- A UI concept (e.g. "date input", "image placeholder")
|
|
31315
|
-
- A pattern name (e.g. "search-form", "dashboard-page")`
|
|
31664
|
+
text: `matches[0]: none for "${query}"
|
|
31665
|
+
Try: component name (DatePicker, Avatar), UI concept (date input), or pattern (search-form)`
|
|
31316
31666
|
}
|
|
31317
31667
|
]
|
|
31318
31668
|
};
|
|
@@ -31321,7 +31671,7 @@ Try:
|
|
|
31321
31671
|
content: [
|
|
31322
31672
|
{
|
|
31323
31673
|
type: "text",
|
|
31324
|
-
text:
|
|
31674
|
+
text: toonComposition(results)
|
|
31325
31675
|
}
|
|
31326
31676
|
]
|
|
31327
31677
|
};
|
|
@@ -31439,7 +31789,7 @@ server.registerPrompt("dryui-install", {
|
|
|
31439
31789
|
role: "user",
|
|
31440
31790
|
content: { type: "text", text: "Plan the DryUI installation for this project." }
|
|
31441
31791
|
},
|
|
31442
|
-
{ role: "assistant", content: { type: "text", text:
|
|
31792
|
+
{ role: "assistant", content: { type: "text", text: toonInstallPlan(result) } }
|
|
31443
31793
|
]
|
|
31444
31794
|
};
|
|
31445
31795
|
});
|
|
@@ -31463,7 +31813,7 @@ server.registerPrompt("dryui-add", {
|
|
|
31463
31813
|
role: "user",
|
|
31464
31814
|
content: { type: "text", text: `Plan how to add ${name} to this project.` }
|
|
31465
31815
|
},
|
|
31466
|
-
{ role: "assistant", content: { type: "text", text:
|
|
31816
|
+
{ role: "assistant", content: { type: "text", text: toonAddPlan(result) } }
|
|
31467
31817
|
]
|
|
31468
31818
|
};
|
|
31469
31819
|
});
|