@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.
@@ -224,7 +224,8 @@ function detectProject(spec, inputPath) {
224
224
  const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
225
225
  const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
226
226
  const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
227
- const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!(appHtml && hasThemeAuto(appHtml)));
227
+ const themeAuto = appHtml ? hasThemeAuto(appHtml) : false;
228
+ const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!themeAuto);
228
229
  const warnings = [];
229
230
  if (!packageJsonPath)
230
231
  warnings.push("No package.json found above the provided path.");
@@ -250,7 +251,7 @@ function detectProject(spec, inputPath) {
250
251
  theme: {
251
252
  defaultImported,
252
253
  darkImported,
253
- themeAuto: hasThemeAuto(appHtml)
254
+ themeAuto
254
255
  },
255
256
  warnings
256
257
  };
@@ -321,7 +322,7 @@ function planInstall(spec, inputPath) {
321
322
  title: "Set html theme mode to auto",
322
323
  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.',
323
324
  path: detection.files.appHtml,
324
- snippet: '<html lang="en" class="theme-auto">'
325
+ snippet: 'class="theme-auto"'
325
326
  });
326
327
  }
327
328
  if (steps.length === 0) {
package/dist/spec.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.5",
2
+ "version": "0.1.7",
3
3
  "package": "@dryui/ui",
4
4
  "themeImports": {
5
5
  "default": "@dryui/ui/themes/default.css",
@@ -10818,6 +10818,26 @@
10818
10818
  "type": "number",
10819
10819
  "required": false,
10820
10820
  "description": "Step interval used when incrementing numeric values."
10821
+ },
10822
+ "size": {
10823
+ "type": "'sm' | 'md' | 'lg'",
10824
+ "required": false,
10825
+ "acceptedValues": [
10826
+ "sm",
10827
+ "md",
10828
+ "lg"
10829
+ ],
10830
+ "description": "Size preset affecting density, spacing, or typography.",
10831
+ "default": "'md'"
10832
+ },
10833
+ "name": {
10834
+ "type": "string",
10835
+ "required": false,
10836
+ "description": "Field name used during native form submission."
10837
+ },
10838
+ "class": {
10839
+ "type": "string",
10840
+ "required": false
10821
10841
  }
10822
10842
  },
10823
10843
  "forwardedProps": {
@@ -10831,25 +10851,23 @@
10831
10851
  ],
10832
10852
  "note": "Forwards <input> attributes via rest props."
10833
10853
  },
10834
- "cssVars": {
10835
- "--dry-time-input-bg": "Input Bg",
10836
- "--dry-time-input-border": "Input Border",
10837
- "--dry-time-input-color": "Input Color",
10838
- "--dry-time-input-font-size": "Input Font Size",
10839
- "--dry-time-input-padding-x": "Input Padding X",
10840
- "--dry-time-input-padding-y": "Input Padding Y",
10841
- "--dry-time-input-radius": "Input Radius"
10842
- },
10854
+ "cssVars": {},
10843
10855
  "dataAttributes": [
10844
10856
  {
10845
10857
  "name": "data-disabled",
10846
10858
  "description": "Present when the component or part is disabled."
10847
10859
  },
10848
10860
  {
10849
- "name": "data-time-input"
10861
+ "name": "data-placeholder"
10862
+ },
10863
+ {
10864
+ "name": "data-time-display"
10850
10865
  },
10851
10866
  {
10852
10867
  "name": "data-time-input-wrapper"
10868
+ },
10869
+ {
10870
+ "name": "data-time-separator"
10853
10871
  }
10854
10872
  ],
10855
10873
  "example": "<TimeInput>Content</TimeInput>"
package/dist/toon.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { ComponentDef, CompositionComponentDef, CompositionRecipeDef } from './spec-types.js';
2
+ import type { ReviewResult } from './reviewer.js';
3
+ import type { DiagnoseResult } from './theme-checker.js';
4
+ import type { WorkspaceReport } from './workspace-audit.js';
5
+ import type { ProjectDetection, InstallPlan, AddPlan } from './project-planner.js';
6
+ export type HelpCommand = 'info' | 'list' | 'compose' | 'review' | 'diagnose' | 'doctor' | 'lint' | 'detect' | 'install';
7
+ export type HelpContext = {
8
+ command: HelpCommand;
9
+ hasErrors?: boolean;
10
+ hasFindings?: boolean;
11
+ isEmpty?: boolean;
12
+ componentName?: string;
13
+ query?: string;
14
+ status?: string;
15
+ };
16
+ /**
17
+ * Build context-aware next-step suggestions.
18
+ * Returns 1-3 help lines for agents to copy-paste.
19
+ */
20
+ export declare function buildContextualHelp(ctx: HelpContext): string[];
21
+ /** Format help hints as plain next-step suggestions. */
22
+ export declare function formatHelp(hints: string[]): string;
23
+ export declare function toonComponent(name: string, def: ComponentDef, opts?: {
24
+ full?: boolean | undefined;
25
+ }): string;
26
+ export declare function toonComponentList(components: Record<string, ComponentDef>, category?: string): string;
27
+ export declare function toonComposition(results: {
28
+ componentMatches: readonly CompositionComponentDef[];
29
+ recipeMatches: readonly CompositionRecipeDef[];
30
+ }, opts?: {
31
+ full?: boolean | undefined;
32
+ }): string;
33
+ export declare function toonReviewResult(result: ReviewResult): string;
34
+ export declare function toonDiagnoseResult(result: DiagnoseResult): string;
35
+ export declare function toonWorkspaceReport(report: WorkspaceReport, opts?: {
36
+ title?: string | undefined;
37
+ command?: 'doctor' | 'lint' | undefined;
38
+ full?: boolean | undefined;
39
+ }): string;
40
+ export declare function toonProjectDetection(detection: ProjectDetection): string;
41
+ export declare function toonInstallPlan(plan: InstallPlan): string;
42
+ export declare function toonAddPlan(plan: AddPlan): string;
43
+ export declare function toonError(code: string, message: string, suggestions?: string[]): string;
package/dist/toon.js ADDED
@@ -0,0 +1,480 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toESMCache_node;
10
+ var __toESMCache_esm;
11
+ var __toESM = (mod, isNodeMode, target) => {
12
+ var canCache = mod != null && typeof mod === "object";
13
+ if (canCache) {
14
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
15
+ var cached = cache.get(mod);
16
+ if (cached)
17
+ return cached;
18
+ }
19
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
20
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
21
+ for (let key of __getOwnPropNames(mod))
22
+ if (!__hasOwnProp.call(to, key))
23
+ __defProp(to, key, {
24
+ get: __accessProp.bind(mod, key),
25
+ enumerable: true
26
+ });
27
+ if (canCache)
28
+ cache.set(mod, to);
29
+ return to;
30
+ };
31
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
32
+ var __returnValue = (v) => v;
33
+ function __exportSetter(name, newValue) {
34
+ this[name] = __returnValue.bind(null, newValue);
35
+ }
36
+ var __export = (target, all) => {
37
+ for (var name in all)
38
+ __defProp(target, name, {
39
+ get: all[name],
40
+ enumerable: true,
41
+ configurable: true,
42
+ set: __exportSetter.bind(all, name)
43
+ });
44
+ };
45
+
46
+ // src/toon.ts
47
+ function esc(value) {
48
+ if (value.includes(",") || value.includes(`
49
+ `)) {
50
+ return `"${value.replace(/"/g, '""')}"`;
51
+ }
52
+ return value;
53
+ }
54
+ function header(resource, count, fields) {
55
+ return `${resource}[${count}]{${fields.join(",")}}:`;
56
+ }
57
+ function row(...values) {
58
+ return " " + values.map((v) => esc(String(v))).join(",");
59
+ }
60
+ function truncate(text, maxLen, hint) {
61
+ if (text.length <= maxLen)
62
+ return text;
63
+ return `(truncated, ${text.length} chars -- ${hint})`;
64
+ }
65
+ function buildContextualHelp(ctx) {
66
+ const hints = [];
67
+ switch (ctx.command) {
68
+ case "info":
69
+ if (ctx.componentName) {
70
+ hints.push(`compose "${ctx.componentName.toLowerCase()}" -- see composition patterns`);
71
+ hints.push(`add ${ctx.componentName} -- get starter snippet`);
72
+ }
73
+ break;
74
+ case "list":
75
+ hints.push("info <Component> -- see full API reference");
76
+ break;
77
+ case "compose":
78
+ if (ctx.componentName) {
79
+ hints.push(`info ${ctx.componentName} -- full API reference`);
80
+ hints.push(`add ${ctx.componentName} -- starter snippet`);
81
+ }
82
+ break;
83
+ case "review":
84
+ if (ctx.hasErrors) {
85
+ hints.push("info <Component> -- check API for errored component");
86
+ hints.push("diagnose <file.css> -- check theme if theme warnings present");
87
+ } else if (ctx.isEmpty) {
88
+ hints.push("lint . -- check entire workspace");
89
+ }
90
+ break;
91
+ case "diagnose":
92
+ if (ctx.hasErrors) {
93
+ hints.push('compose "app shell" -- get correct theme setup');
94
+ } else if (ctx.isEmpty) {
95
+ hints.push("review <file.svelte> -- validate component usage");
96
+ }
97
+ break;
98
+ case "doctor":
99
+ case "lint":
100
+ if (ctx.hasFindings) {
101
+ hints.push("lint --max-severity error -- focus on errors only");
102
+ hints.push("review <file.svelte> -- check specific file");
103
+ } else if (ctx.isEmpty) {
104
+ hints.push("detect -- verify project setup");
105
+ }
106
+ break;
107
+ case "detect":
108
+ if (ctx.status === "partial" || ctx.status === "unsupported") {
109
+ hints.push("install -- see step-by-step setup plan");
110
+ } else if (ctx.status === "ready") {
111
+ hints.push("list -- see available components");
112
+ hints.push('compose "app shell" -- get root layout template');
113
+ }
114
+ break;
115
+ case "install":
116
+ hints.push("detect -- verify project after completing steps");
117
+ break;
118
+ }
119
+ return hints;
120
+ }
121
+ function formatHelp(hints) {
122
+ if (hints.length === 0)
123
+ return "";
124
+ const lines = [`next[${hints.length}]:`];
125
+ for (const hint of hints) {
126
+ lines.push(" " + hint);
127
+ }
128
+ return lines.join(`
129
+ `);
130
+ }
131
+ function toonComponent(name, def, opts) {
132
+ const full = opts?.full ?? false;
133
+ const lines = [];
134
+ lines.push(`${name} -- ${def.description}`, `category: ${def.category} | tags: ${def.tags.join(",")}`, `import: import { ${name} } from '${def.import}'`, `compound: ${def.compound}`);
135
+ if (def.compound && def.structure?.tree.length) {
136
+ lines.push("", header("structure", def.structure.tree.length, ["node"]));
137
+ for (const node of def.structure.tree) {
138
+ lines.push(" " + node);
139
+ }
140
+ if (def.structure.note)
141
+ lines.push(` note: ${def.structure.note}`);
142
+ }
143
+ if (def.compound && def.parts) {
144
+ const partEntries = Object.entries(def.parts);
145
+ lines.push("", header("parts", partEntries.length, ["part"]));
146
+ for (const [partName, partDef] of partEntries) {
147
+ lines.push(` ${name}.${partName}`);
148
+ const props = Object.entries(partDef.props ?? {});
149
+ if (props.length > 0) {
150
+ for (const [propName, propDef] of props) {
151
+ const flags = [propDef.type];
152
+ if (propDef.required)
153
+ flags.push("required");
154
+ if (propDef.default !== undefined)
155
+ flags.push(`default:${propDef.default}`);
156
+ if (propDef.acceptedValues?.length)
157
+ flags.push(`values:${propDef.acceptedValues.join("|")}`);
158
+ lines.push(` ${propName}: ${flags.join(" | ")}`);
159
+ }
160
+ }
161
+ }
162
+ } else if (def.props) {
163
+ const propEntries = Object.entries(def.props);
164
+ if (propEntries.length > 0) {
165
+ lines.push("", header("props", propEntries.length, ["name", "type", "required", "default"]));
166
+ for (const [propName, propDef] of propEntries) {
167
+ lines.push(row(propName, propDef.type, propDef.required ? "yes" : "no", propDef.default ?? "-"));
168
+ }
169
+ }
170
+ }
171
+ const cssEntries = Object.entries(def.cssVars);
172
+ if (cssEntries.length > 0) {
173
+ lines.push("", header("css-vars", cssEntries.length, ["name", "description"]));
174
+ for (const [varName, desc] of cssEntries) {
175
+ lines.push(row(varName, desc));
176
+ }
177
+ }
178
+ if (def.dataAttributes.length > 0) {
179
+ lines.push("", header("data-attrs", def.dataAttributes.length, ["name", "values"]));
180
+ for (const attr of def.dataAttributes) {
181
+ lines.push(row(attr.name, attr.values?.join("|") ?? "-"));
182
+ }
183
+ }
184
+ if (def.example) {
185
+ const example = full ? def.example : truncate(def.example, 400, `use add ${name} for full snippet`);
186
+ lines.push("", "example:", example);
187
+ }
188
+ const help = buildContextualHelp({ command: "info", componentName: name });
189
+ if (help.length > 0) {
190
+ lines.push("", formatHelp(help));
191
+ }
192
+ return lines.join(`
193
+ `);
194
+ }
195
+ function toonComponentList(components, category) {
196
+ const entries = Object.entries(components);
197
+ const filtered = category ? entries.filter(([, def]) => def.category.toLowerCase() === category.toLowerCase()) : entries;
198
+ if (filtered.length === 0) {
199
+ const cats = [...new Set(entries.map(([, d]) => d.category))].sort();
200
+ return `components[0]: no matches
201
+ available categories: ${cats.join(", ")}`;
202
+ }
203
+ const groups = {};
204
+ for (const entry of filtered) {
205
+ const cat = entry[1].category;
206
+ (groups[cat] ??= []).push(entry);
207
+ }
208
+ const lines = [header("components", filtered.length, ["name", "category", "compound", "description"])];
209
+ const sortedCats = Object.keys(groups).sort();
210
+ for (const cat of sortedCats) {
211
+ const items = groups[cat] ?? [];
212
+ for (const [name, def] of items) {
213
+ lines.push(row(name, cat, def.compound, def.description));
214
+ }
215
+ }
216
+ const help = buildContextualHelp({ command: "list" });
217
+ if (help.length > 0) {
218
+ lines.push("", formatHelp(help));
219
+ }
220
+ return lines.join(`
221
+ `);
222
+ }
223
+ function toonComposition(results, opts) {
224
+ const full = opts?.full ?? false;
225
+ const lines = [];
226
+ const totalMatches = results.componentMatches.length + results.recipeMatches.length;
227
+ if (totalMatches === 0) {
228
+ return "matches[0]: none";
229
+ }
230
+ for (const comp of results.componentMatches) {
231
+ lines.push(`-- ${comp.component}: ${comp.useWhen}`);
232
+ for (const alt of comp.alternatives) {
233
+ const snippet = full ? alt.snippet : truncate(alt.snippet, 500, `use info ${alt.component} for full snippet`);
234
+ lines.push(` ${alt.rank}. ${alt.component} (${alt.useWhen})`);
235
+ lines.push(snippet.split(`
236
+ `).map((l) => " " + l).join(`
237
+ `));
238
+ }
239
+ for (const ap of comp.antiPatterns) {
240
+ lines.push(` anti-pattern: ${ap.pattern}`);
241
+ lines.push(` reason: ${ap.reason} | fix: ${ap.fix}`);
242
+ }
243
+ if (comp.combinesWith.length) {
244
+ lines.push(` combines-with: ${comp.combinesWith.join(",")}`);
245
+ }
246
+ lines.push("");
247
+ }
248
+ for (const recipe of results.recipeMatches) {
249
+ const snippet = full ? recipe.snippet : truncate(recipe.snippet, 500, `use compose "${recipe.name}" --full for complete code`);
250
+ lines.push(`-- recipe: ${recipe.name}`);
251
+ lines.push(` ${recipe.description}`);
252
+ lines.push(` components: ${recipe.components.join(",")}`);
253
+ lines.push(" code:");
254
+ lines.push(snippet.split(`
255
+ `).map((l) => " " + l).join(`
256
+ `));
257
+ lines.push("");
258
+ }
259
+ const firstComponent = results.componentMatches[0]?.alternatives[0]?.component ?? results.recipeMatches[0]?.components[0] ?? undefined;
260
+ const ctx = { command: "compose" };
261
+ if (firstComponent)
262
+ ctx.componentName = firstComponent;
263
+ const help = buildContextualHelp(ctx);
264
+ if (help.length > 0) {
265
+ lines.push(formatHelp(help));
266
+ }
267
+ return lines.join(`
268
+ `).trimEnd();
269
+ }
270
+ function toonReviewResult(result) {
271
+ const lines = [];
272
+ const hasBlockers = result.issues.some((i) => i.severity === "error");
273
+ const autoFixable = result.issues.filter((i) => i.fix !== null).length;
274
+ if (result.issues.length === 0) {
275
+ lines.push("issues[0]: clean");
276
+ lines.push(`hasBlockers: false | autoFixable: 0`);
277
+ } else {
278
+ lines.push(header("issues", result.issues.length, ["severity", "line", "code", "message"]));
279
+ for (const issue of result.issues) {
280
+ lines.push(row(issue.severity, issue.line, issue.code, issue.message));
281
+ if (issue.fix) {
282
+ lines.push(` fix: ${issue.fix}`);
283
+ }
284
+ }
285
+ lines.push(`hasBlockers: ${hasBlockers} | autoFixable: ${autoFixable}`);
286
+ }
287
+ lines.push(result.summary);
288
+ const help = buildContextualHelp({
289
+ command: "review",
290
+ hasErrors: hasBlockers,
291
+ isEmpty: result.issues.length === 0
292
+ });
293
+ if (help.length > 0) {
294
+ lines.push("", formatHelp(help));
295
+ }
296
+ return lines.join(`
297
+ `);
298
+ }
299
+ function toonDiagnoseResult(result) {
300
+ const lines = [];
301
+ const { variables } = result;
302
+ const missingCount = result.issues.filter((i) => i.code === "missing-token").length;
303
+ const totalRequired = variables.required + missingCount;
304
+ const coverage = totalRequired > 0 ? Math.round(variables.required / totalRequired * 100) : 100;
305
+ lines.push(`tokens: ${variables.found} found, ${variables.required} required, ${variables.extra} extra | coverage: ${coverage}%`);
306
+ if (result.issues.length === 0) {
307
+ lines.push("issues[0]: clean");
308
+ } else {
309
+ lines.push(header("issues", result.issues.length, ["severity", "code", "variable", "message"]));
310
+ for (const issue of result.issues) {
311
+ lines.push(row(issue.severity, issue.code, issue.variable, issue.message));
312
+ if (issue.fix) {
313
+ lines.push(` fix: ${issue.fix}`);
314
+ }
315
+ }
316
+ }
317
+ lines.push(result.summary);
318
+ const hasErrors = result.issues.some((i) => i.severity === "error");
319
+ const help = buildContextualHelp({
320
+ command: "diagnose",
321
+ hasErrors,
322
+ isEmpty: result.issues.length === 0
323
+ });
324
+ if (help.length > 0) {
325
+ lines.push("", formatHelp(help));
326
+ }
327
+ return lines.join(`
328
+ `);
329
+ }
330
+ var MAX_FINDINGS_DEFAULT = 50;
331
+ function toonWorkspaceReport(report, opts) {
332
+ const full = opts?.full ?? false;
333
+ const title = opts?.title ?? "workspace";
334
+ const command = opts?.command ?? (title.includes("lint") ? "lint" : "doctor");
335
+ const lines = [];
336
+ lines.push(`${title} | root: ${report.root}`);
337
+ lines.push(`scanned: ${report.scannedFiles} files | errors: ${report.summary.error} | warnings: ${report.summary.warning} | info: ${report.summary.info}`);
338
+ if (report.summary.byRule && Object.keys(report.summary.byRule).length > 0) {
339
+ const sorted = Object.entries(report.summary.byRule).sort(([, a], [, b]) => b - a);
340
+ const topRule = sorted[0];
341
+ if (topRule) {
342
+ lines.push(`top-rule: ${topRule[0]} (${topRule[1]} occurrences)`);
343
+ }
344
+ }
345
+ if (report.findings.length === 0) {
346
+ lines.push("findings[0]: clean");
347
+ } else {
348
+ const findings = full ? report.findings : report.findings.slice(0, MAX_FINDINGS_DEFAULT);
349
+ const truncated = !full && report.findings.length > MAX_FINDINGS_DEFAULT;
350
+ lines.push(header("findings", findings.length, ["severity", "rule", "file", "line", "message"]));
351
+ for (const f of findings) {
352
+ lines.push(row(f.severity, f.ruleId, f.file, f.line ?? "-", f.message));
353
+ if (f.suggestedFixes.length > 0) {
354
+ for (const fix of f.suggestedFixes) {
355
+ lines.push(` fix: ${fix.description}${fix.replacement ? ` -> ${fix.replacement}` : ""}`);
356
+ }
357
+ }
358
+ }
359
+ if (truncated) {
360
+ lines.push(` (showing ${MAX_FINDINGS_DEFAULT} of ${report.findings.length} -- use --full to see all)`);
361
+ }
362
+ }
363
+ if (report.warnings.length > 0) {
364
+ lines.push("", header("warnings", report.warnings.length, ["message"]));
365
+ for (const w of report.warnings) {
366
+ lines.push(" " + w);
367
+ }
368
+ }
369
+ const help = buildContextualHelp({
370
+ command,
371
+ hasFindings: report.findings.length > 0,
372
+ isEmpty: report.findings.length === 0
373
+ });
374
+ if (help.length > 0) {
375
+ lines.push("", formatHelp(help));
376
+ }
377
+ return lines.join(`
378
+ `);
379
+ }
380
+ function toonProjectDetection(detection) {
381
+ const lines = [];
382
+ lines.push(`project: ${detection.status} | framework: ${detection.framework} | pkg-manager: ${detection.packageManager}`);
383
+ lines.push(`root: ${detection.root ?? "(not found)"}`);
384
+ lines.push(`deps: ui=${detection.dependencies.ui}, primitives=${detection.dependencies.primitives}`);
385
+ lines.push(`theme: default=${detection.theme.defaultImported}, dark=${detection.theme.darkImported}, auto=${detection.theme.themeAuto}`);
386
+ if (detection.warnings.length > 0) {
387
+ lines.push(header("warnings", detection.warnings.length, ["message"]));
388
+ for (const w of detection.warnings) {
389
+ lines.push(" " + w);
390
+ }
391
+ }
392
+ const help = buildContextualHelp({ command: "detect", status: detection.status });
393
+ if (help.length > 0) {
394
+ lines.push("", formatHelp(help));
395
+ }
396
+ return lines.join(`
397
+ `);
398
+ }
399
+ function toonStep(step, index) {
400
+ const parts = [`${index + 1}. [${step.status}] ${step.title}: ${step.description}`];
401
+ if (step.command)
402
+ parts.push(` cmd: ${step.command}`);
403
+ if (step.path)
404
+ parts.push(` file: ${step.path}`);
405
+ return parts.join(`
406
+ `);
407
+ }
408
+ function toonInstallPlan(plan) {
409
+ const lines = [];
410
+ lines.push(toonProjectDetection(plan.detection));
411
+ lines.push("");
412
+ if (plan.steps.length === 0) {
413
+ lines.push("steps[0]: none needed");
414
+ } else {
415
+ lines.push(header("steps", plan.steps.length, ["status", "title", "description"]));
416
+ for (const [i, step] of plan.steps.entries()) {
417
+ lines.push(toonStep(step, i));
418
+ }
419
+ }
420
+ const help = buildContextualHelp({ command: "install" });
421
+ if (help.length > 0) {
422
+ lines.push("", formatHelp(help));
423
+ }
424
+ return lines.join(`
425
+ `);
426
+ }
427
+ function toonAddPlan(plan) {
428
+ const lines = [];
429
+ lines.push(`add: ${plan.name} | target: ${plan.target ?? "(choose)"}`);
430
+ if (plan.importStatement) {
431
+ lines.push(`import: ${plan.importStatement}`);
432
+ }
433
+ lines.push("");
434
+ if (plan.installPlan.steps.length > 0) {
435
+ lines.push(header("install-steps", plan.installPlan.steps.length, ["status", "title"]));
436
+ for (const [i, step] of plan.installPlan.steps.entries()) {
437
+ lines.push(toonStep(step, i));
438
+ }
439
+ lines.push("");
440
+ }
441
+ if (plan.steps.length > 0) {
442
+ lines.push(header("add-steps", plan.steps.length, ["status", "title"]));
443
+ for (const [i, step] of plan.steps.entries()) {
444
+ lines.push(toonStep(step, i));
445
+ }
446
+ }
447
+ if (plan.warnings.length > 0) {
448
+ lines.push("", header("warnings", plan.warnings.length, ["message"]));
449
+ for (const w of plan.warnings) {
450
+ lines.push(" " + w);
451
+ }
452
+ }
453
+ return lines.join(`
454
+ `);
455
+ }
456
+ function toonError(code, message, suggestions) {
457
+ const lines = [`error[1]{code,message}: ${esc(code)},${esc(message)}`];
458
+ if (suggestions?.length) {
459
+ lines.push(header("suggestions", suggestions.length, ["value"]));
460
+ for (const s of suggestions) {
461
+ lines.push(" " + s);
462
+ }
463
+ }
464
+ return lines.join(`
465
+ `);
466
+ }
467
+ export {
468
+ toonWorkspaceReport,
469
+ toonReviewResult,
470
+ toonProjectDetection,
471
+ toonInstallPlan,
472
+ toonError,
473
+ toonDiagnoseResult,
474
+ toonComposition,
475
+ toonComponentList,
476
+ toonComponent,
477
+ toonAddPlan,
478
+ formatHelp,
479
+ buildContextualHelp
480
+ };
@@ -1673,7 +1673,8 @@ function detectProject(spec, inputPath) {
1673
1673
  const themeImportFiles = rootLayout && importsAppCss(rootLayout) ? [rootLayout, appCss] : [rootLayout];
1674
1674
  const defaultImported = hasAnyImport(themeImportFiles, spec.themeImports.default);
1675
1675
  const darkImported = hasAnyImport(themeImportFiles, spec.themeImports.dark);
1676
- const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!(appHtml && hasThemeAuto(appHtml)));
1676
+ const themeAuto = appHtml ? hasThemeAuto(appHtml) : false;
1677
+ const stepsNeeded = Number(!dependencyNames.has("@dryui/ui")) + Number(!defaultImported) + Number(!darkImported) + Number(!themeAuto);
1677
1678
  const warnings = [];
1678
1679
  if (!packageJsonPath)
1679
1680
  warnings.push("No package.json found above the provided path.");
@@ -1699,7 +1700,7 @@ function detectProject(spec, inputPath) {
1699
1700
  theme: {
1700
1701
  defaultImported,
1701
1702
  darkImported,
1702
- themeAuto: hasThemeAuto(appHtml)
1703
+ themeAuto
1703
1704
  },
1704
1705
  warnings
1705
1706
  };
@@ -1770,7 +1771,7 @@ function planInstall(spec, inputPath) {
1770
1771
  title: "Set html theme mode to auto",
1771
1772
  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.',
1772
1773
  path: detection.files.appHtml,
1773
- snippet: '<html lang="en" class="theme-auto">'
1774
+ snippet: 'class="theme-auto"'
1774
1775
  });
1775
1776
  }
1776
1777
  if (steps.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dryui/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "author": "Rob Balfre",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -66,11 +66,24 @@
66
66
  "bun": "./src/spec-formatters.ts",
67
67
  "types": "./dist/spec-formatters.d.ts",
68
68
  "default": "./dist/spec-formatters.js"
69
+ },
70
+ "./toon": {
71
+ "bun": "./src/toon.ts",
72
+ "types": "./dist/toon.d.ts",
73
+ "default": "./dist/toon.js"
74
+ },
75
+ "./composition-search": {
76
+ "bun": "./src/composition-search.ts",
77
+ "types": "./dist/composition-search.d.ts",
78
+ "default": "./dist/composition-search.js"
69
79
  }
70
80
  },
71
81
  "files": [
72
82
  "dist"
73
83
  ],
84
+ "publishConfig": {
85
+ "access": "public"
86
+ },
74
87
  "scripts": {
75
88
  "generate-spec": "bun src/generate-spec.ts",
76
89
  "generate-architecture": "bun src/generate-architecture.ts",
@@ -78,7 +91,7 @@
78
91
  "generate-llms": "bun src/generate-llms-txt.ts",
79
92
  "check:contract": "bun src/check-contract.ts",
80
93
  "check": "bun run check:contract",
81
- "build": "bun run generate-spec && bun run generate-architecture && bun run generate-contract && bun run generate-llms && mkdir -p dist && cp src/spec.json dist/spec.json && cp src/architecture.json dist/architecture.json && cp src/contract.v1.json dist/contract.v1.json && cp src/contract.v1.schema.json dist/contract.v1.schema.json && bun build src/index.ts src/architecture.ts src/reviewer.ts src/theme-checker.ts src/utils.ts src/project-planner.ts src/workspace-audit.ts src/spec-types.ts src/spec-formatters.ts src/composition-data.ts --outdir dist --root src --target node && printf '#!/usr/bin/env node\n' | cat - dist/index.js > dist/index.tmp && mv dist/index.tmp dist/index.js && bunx tsc -p tsconfig.build.json"
94
+ "build": "bun run generate-spec && bun run generate-architecture && bun run generate-contract && bun run generate-llms && mkdir -p dist && cp src/spec.json dist/spec.json && cp src/architecture.json dist/architecture.json && cp src/contract.v1.json dist/contract.v1.json && cp src/contract.v1.schema.json dist/contract.v1.schema.json && bun build src/index.ts src/architecture.ts src/reviewer.ts src/theme-checker.ts src/utils.ts src/project-planner.ts src/workspace-audit.ts src/spec-types.ts src/spec-formatters.ts src/composition-data.ts src/toon.ts src/composition-search.ts --outdir dist --root src --target node && printf '#!/usr/bin/env node\n' | cat - dist/index.js > dist/index.tmp && mv dist/index.tmp dist/index.js && bunx tsc -p tsconfig.build.json"
82
95
  },
83
96
  "dependencies": {
84
97
  "@modelcontextprotocol/sdk": "^1.29.0",