@sourcescape/ds-cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,176 +1,29 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ buildPreviewHtml,
4
+ buildRegistry,
5
+ bundleJs,
6
+ descriptionMtime,
7
+ findComponent,
8
+ findComponentSources,
9
+ getSystemInfo,
10
+ listComponents,
11
+ listSystems,
12
+ readComponentJson,
13
+ readComponentMeta,
14
+ readDescription,
15
+ readFile,
16
+ readFullDescription,
17
+ resolveMarkup,
18
+ startPreviewServer,
19
+ suggestComponent,
20
+ systemDir,
21
+ systemExists
22
+ } from "./chunk-U44UTENA.js";
2
23
 
3
24
  // src/cli.ts
4
25
  import { parseArgs } from "util";
5
26
 
6
- // src/systems.ts
7
- import { readFileSync, readdirSync, existsSync, statSync } from "fs";
8
- import { join, resolve, dirname } from "path";
9
- var REPO_ROOT = resolve(dirname(new URL(import.meta.url).pathname), "..", "..");
10
- var SYSTEMS_ROOT = join(REPO_ROOT, "systems");
11
- function setSystemsRoot(dir) {
12
- SYSTEMS_ROOT = resolve(dir);
13
- }
14
- function listSystems() {
15
- return readdirSync(SYSTEMS_ROOT, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
16
- }
17
- function systemDir(system) {
18
- return join(SYSTEMS_ROOT, system);
19
- }
20
- function systemExists(system) {
21
- return existsSync(systemDir(system));
22
- }
23
- function readFirstHeading(filePath) {
24
- if (!existsSync(filePath)) return "";
25
- const content = readFileSync(filePath, "utf-8");
26
- const match = content.match(/^#\s+(.+)$/m);
27
- return match ? match[1].trim() : "";
28
- }
29
- function readFirstParagraph(filePath) {
30
- if (!existsSync(filePath)) return "";
31
- const lines = readFileSync(filePath, "utf-8").split("\n");
32
- let pastHeading = false;
33
- for (const line of lines) {
34
- if (line.startsWith("# ")) {
35
- pastHeading = true;
36
- continue;
37
- }
38
- if (pastHeading && line.trim().length > 0 && !line.startsWith("#")) {
39
- return line.trim();
40
- }
41
- }
42
- return "";
43
- }
44
- function readFile(filePath) {
45
- return readFileSync(filePath, "utf-8");
46
- }
47
- function getSystemInfo(system) {
48
- const descPath = join(systemDir(system), "DESCRIPTION.md");
49
- return {
50
- name: system,
51
- description: readFirstHeading(descPath)
52
- };
53
- }
54
- function readDescription(system) {
55
- const descPath = join(systemDir(system), "DESCRIPTION.md");
56
- if (!existsSync(descPath)) return "";
57
- return readFile(descPath);
58
- }
59
- function listComponents(system) {
60
- const compsDir = join(systemDir(system), "components");
61
- const results = [];
62
- for (const kind of ["molecules", "cells"]) {
63
- const kindDir = join(compsDir, kind);
64
- if (!existsSync(kindDir)) continue;
65
- const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
66
- for (const name of dirs) {
67
- const descPath = join(kindDir, name, "description.md");
68
- results.push({
69
- name,
70
- kind,
71
- description: readFirstParagraph(descPath)
72
- });
73
- }
74
- }
75
- return results;
76
- }
77
- function findComponent(system, name) {
78
- const compsDir = join(systemDir(system), "components");
79
- for (const kind of ["molecules", "cells"]) {
80
- const dir = join(compsDir, kind, name);
81
- if (existsSync(dir) && statSync(dir).isDirectory()) {
82
- return { kind, dir };
83
- }
84
- }
85
- return null;
86
- }
87
- function allComponentNames(system) {
88
- const compsDir = join(systemDir(system), "components");
89
- const results = [];
90
- for (const kind of ["molecules", "cells"]) {
91
- const kindDir = join(compsDir, kind);
92
- if (!existsSync(kindDir)) continue;
93
- const dirs = readdirSync(kindDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => ({ name: d.name, kind }));
94
- results.push(...dirs);
95
- }
96
- return results;
97
- }
98
- function listTokenFiles(system) {
99
- const tokensDir = join(systemDir(system), "tokens");
100
- if (!existsSync(tokensDir)) return [];
101
- return readdirSync(tokensDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", "")).sort();
102
- }
103
- function readTokenFile(system, name) {
104
- const filePath = join(systemDir(system), "tokens", `${name}.json`);
105
- if (!existsSync(filePath)) return null;
106
- return JSON.parse(readFile(filePath));
107
- }
108
- function listPrinciples(system) {
109
- const dir = join(systemDir(system), "principles");
110
- if (!existsSync(dir)) return [];
111
- return readdirSync(dir).filter((f) => f.endsWith(".md") && f !== "index.md").map((f) => {
112
- const name = f.replace(".md", "");
113
- const title = readFirstHeading(join(dir, f));
114
- return { name, title: title || name };
115
- }).sort((a, b) => a.name.localeCompare(b.name));
116
- }
117
- function readPrinciple(system, name) {
118
- const filePath = join(systemDir(system), "principles", `${name}.md`);
119
- if (!existsSync(filePath)) return null;
120
- return readFile(filePath);
121
- }
122
- function hasArchitecture(system) {
123
- return existsSync(join(systemDir(system), "architecture"));
124
- }
125
- function listArchitecture(system, subpath) {
126
- const baseDir = join(systemDir(system), "architecture");
127
- const dir = subpath ? join(baseDir, subpath) : baseDir;
128
- if (!existsSync(dir)) return { entries: [], indexContent: null };
129
- const indexPath = join(dir, "index.md");
130
- const indexContent = existsSync(indexPath) ? readFile(indexPath) : null;
131
- const entries = readdirSync(dir, { withFileTypes: true }).filter((d) => d.name !== "index.md").map((d) => ({ name: d.name, isDir: d.isDirectory() })).sort((a, b) => {
132
- if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
133
- return a.name.localeCompare(b.name);
134
- });
135
- return { entries, indexContent };
136
- }
137
- function readArchitectureFile(system, subpath) {
138
- const filePath = join(systemDir(system), "architecture", subpath);
139
- if (!existsSync(filePath)) return null;
140
- if (statSync(filePath).isDirectory()) return null;
141
- return readFile(filePath);
142
- }
143
- function isArchitectureDir(system, subpath) {
144
- const filePath = join(systemDir(system), "architecture", subpath);
145
- return existsSync(filePath) && statSync(filePath).isDirectory();
146
- }
147
- function listExamples(system) {
148
- const dir = join(systemDir(system), "examples");
149
- if (!existsSync(dir)) return [];
150
- return readdirSync(dir).filter((f) => f.endsWith(".html")).sort();
151
- }
152
- function listReferences(system) {
153
- const dir = join(systemDir(system), "references");
154
- if (!existsSync(dir)) return [];
155
- return readdirSync(dir, { withFileTypes: true }).filter((d) => !d.isDirectory()).map((d) => d.name).sort();
156
- }
157
- function suggestComponent(system, query) {
158
- const all = allComponentNames(system);
159
- const q = query.toLowerCase();
160
- return all.filter((c) => c.name.includes(q) || q.includes(c.name)).map((c) => `${c.name} (${c.kind})`);
161
- }
162
- function findComponentSources(system, name) {
163
- const found = findComponent(system, name);
164
- if (!found) return { css: [], js: [] };
165
- const css = [];
166
- const js = [];
167
- const stylePath = join(found.dir, "style.css");
168
- const scriptPath = join(found.dir, "script.js");
169
- if (existsSync(stylePath)) css.push(stylePath);
170
- if (existsSync(scriptPath)) js.push(scriptPath);
171
- return { css, js };
172
- }
173
-
174
27
  // src/format.ts
175
28
  function heading(text) {
176
29
  console.log(`
@@ -181,12 +34,6 @@ function subheading(text) {
181
34
  console.log(`
182
35
  ${text}`);
183
36
  }
184
- function list(items, indent = 2) {
185
- const pad = " ".repeat(indent);
186
- for (const item of items) {
187
- console.log(`${pad}${item}`);
188
- }
189
- }
190
37
  function descriptionList(items, indent = 2) {
191
38
  if (items.length === 0) return;
192
39
  const maxName = Math.max(...items.map((i) => i.name.length));
@@ -196,9 +43,6 @@ function descriptionList(items, indent = 2) {
196
43
  console.log(`${pad}${item.name.padEnd(maxName)}${desc}`);
197
44
  }
198
45
  }
199
- function countLine(label, count) {
200
- console.log(` ${label}: ${count}`);
201
- }
202
46
  function error(message, suggestions) {
203
47
  console.error(`Error: ${message}`);
204
48
  if (suggestions && suggestions.length > 0) {
@@ -229,82 +73,15 @@ function commandList() {
229
73
  return 0;
230
74
  }
231
75
 
232
- // src/tokens.ts
233
- function isToken(obj) {
234
- return typeof obj === "object" && obj !== null && "$value" in obj;
235
- }
236
- function flattenTokens(obj, prefix = "") {
237
- const tokens = [];
238
- for (const [key, value] of Object.entries(obj)) {
239
- if (key.startsWith("$")) continue;
240
- const path = prefix ? `${prefix}.${key}` : key;
241
- if (isToken(value)) {
242
- tokens.push({
243
- path,
244
- $value: value.$value,
245
- $type: value.$type ?? "unknown"
246
- });
247
- } else if (typeof value === "object" && value !== null) {
248
- tokens.push(...flattenTokens(value, path));
249
- }
250
- }
251
- return tokens;
252
- }
253
- function groupTokens(tokens) {
254
- const groups = /* @__PURE__ */ new Map();
255
- for (const token of tokens) {
256
- const category = token.path.split(".")[0];
257
- if (!groups.has(category)) {
258
- groups.set(category, []);
259
- }
260
- groups.get(category).push(token);
261
- }
262
- return Array.from(groups.entries()).map(([name, tokens2]) => ({
263
- name,
264
- tokens: tokens2
265
- }));
266
- }
267
- function formatValue(value) {
268
- if (typeof value === "string") return value;
269
- if (typeof value === "number") return String(value);
270
- if (typeof value === "object" && value !== null) {
271
- return JSON.stringify(value);
272
- }
273
- return String(value);
274
- }
275
- function renderTokens(data) {
276
- const tokens = flattenTokens(data);
277
- const groups = groupTokens(tokens);
278
- for (const group of groups) {
279
- console.log(`
280
- ${group.name}`);
281
- console.log("\u2500".repeat(Math.min(group.name.length, 40)));
282
- const maxPath = Math.max(...group.tokens.map((t) => t.path.length));
283
- for (const token of group.tokens) {
284
- const typeTag = token.$type !== "unknown" ? ` [${token.$type}]` : "";
285
- console.log(` ${token.path.padEnd(maxPath)} ${formatValue(token.$value)}${typeTag}`);
286
- }
287
- }
288
- console.log();
289
- }
290
- function renderFlat(data) {
291
- const tokens = flattenTokens(data);
292
- for (const token of tokens) {
293
- console.log(`${token.path} = ${formatValue(token.$value)}`);
294
- }
295
- }
296
- function renderJson(data) {
297
- console.log(JSON.stringify(data, null, 2));
298
- }
299
- function tokenSummary(data) {
300
- const tokens = flattenTokens(data);
301
- const types = new Set(tokens.map((t) => t.$type));
302
- const typeList = Array.from(types).filter((t) => t !== "unknown").sort();
303
- const typeSuffix = typeList.length > 0 ? ` (${typeList.join(", ")})` : "";
304
- return `${tokens.length} tokens${typeSuffix}`;
305
- }
306
-
307
76
  // src/commands/show.ts
77
+ import { existsSync, readFileSync } from "fs";
78
+ import { join } from "path";
79
+ function loadManifest(system) {
80
+ const path = join(systemDir(system), "references", "manifest.json");
81
+ if (!existsSync(path)) return [];
82
+ const data = JSON.parse(readFileSync(path, "utf-8"));
83
+ return data.references || [];
84
+ }
308
85
  function commandShow(system) {
309
86
  if (!systemExists(system)) {
310
87
  const available = listSystems();
@@ -321,48 +98,312 @@ function commandShow(system) {
321
98
  if (desc) {
322
99
  markdown(desc);
323
100
  }
324
- heading("Sections");
325
101
  const components = listComponents(system);
326
102
  const molecules = components.filter((c) => c.kind === "molecules");
327
103
  const cells = components.filter((c) => c.kind === "cells");
328
- countLine("Components", components.length);
329
- if (molecules.length > 0) countLine(" Molecules", molecules.length);
330
- if (cells.length > 0) countLine(" Cells", cells.length);
331
- const tokenFiles = listTokenFiles(system);
332
- if (tokenFiles.length > 0) {
333
- countLine("Token files", tokenFiles.length);
334
- for (const name of tokenFiles) {
335
- const data = readTokenFile(system, name);
336
- const summary = data ? tokenSummary(data) : "";
337
- console.log(` ${name}: ${summary}`);
104
+ console.log("\n## Component Inventory");
105
+ function printTable(label, items) {
106
+ const metas = items.map((c) => readComponentMeta(system, c.name));
107
+ console.log(`
108
+ ### ${label} (${items.length} active)
109
+ `);
110
+ console.log("| Component | Tag | Description |");
111
+ console.log("|---|---|---|");
112
+ for (const m of metas) {
113
+ console.log(`| ${m.name} | ${m.tag} | ${m.description} |`);
338
114
  }
339
115
  }
340
- const principles = listPrinciples(system);
341
- if (principles.length > 0) countLine("Principles", principles.length);
342
- if (hasArchitecture(system)) {
343
- console.log(" Architecture: yes");
116
+ if (molecules.length > 0) printTable("Molecules", molecules);
117
+ if (cells.length > 0) printTable("Cells", cells);
118
+ const refs = loadManifest(system);
119
+ if (refs.length > 0) {
120
+ console.log("\n## References\n");
121
+ for (const ref of refs) {
122
+ console.log(`- \`${ref.file}\` \u2014 ${ref.description}`);
123
+ }
344
124
  }
345
- const examples = listExamples(system);
346
- if (examples.length > 0) countLine("Examples", examples.length);
347
- const references = listReferences(system);
348
- if (references.length > 0) countLine("References", references.length);
349
125
  blank();
350
126
  return 0;
351
127
  }
352
128
 
353
129
  // src/commands/browse.ts
354
- import { join as join5 } from "path";
355
- import { existsSync as existsSync6 } from "fs";
356
130
  import { exec } from "child_process";
357
131
 
358
132
  // src/commands/components.ts
359
- import { join as join2, basename as basename2, relative } from "path";
360
- import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
133
+ import { join as join3, basename, relative } from "path";
134
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync } from "fs";
135
+
136
+ // src/search/tfidf.ts
137
+ var STOP_WORDS = /* @__PURE__ */ new Set([
138
+ "a",
139
+ "an",
140
+ "the",
141
+ "and",
142
+ "or",
143
+ "but",
144
+ "in",
145
+ "on",
146
+ "at",
147
+ "to",
148
+ "for",
149
+ "of",
150
+ "with",
151
+ "by",
152
+ "from",
153
+ "is",
154
+ "are",
155
+ "was",
156
+ "were",
157
+ "be",
158
+ "been",
159
+ "has",
160
+ "have",
161
+ "had",
162
+ "do",
163
+ "does",
164
+ "did",
165
+ "will",
166
+ "would",
167
+ "could",
168
+ "should",
169
+ "may",
170
+ "might",
171
+ "can",
172
+ "this",
173
+ "that",
174
+ "these",
175
+ "those",
176
+ "it",
177
+ "its",
178
+ "not",
179
+ "no",
180
+ "as",
181
+ "if",
182
+ "so",
183
+ "than",
184
+ "up",
185
+ "out",
186
+ "about",
187
+ "into",
188
+ "over",
189
+ "after",
190
+ "before",
191
+ "between",
192
+ "under",
193
+ "such",
194
+ "each",
195
+ "all",
196
+ "any",
197
+ "both",
198
+ "more",
199
+ "other",
200
+ "some"
201
+ ]);
202
+ function stripMarkdown(text) {
203
+ return text.replace(/^#{1,6}\s+/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[|─┌┐└┘├┤┬┴┼]/g, " ").replace(/<[^>]+>/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/!\[[^\]]*\]\([^)]+\)/g, "").replace(/^[-*]\s+/gm, "").replace(/^\d+\.\s+/gm, "").replace(/^>\s+/gm, "");
204
+ }
205
+ function tokenize(text) {
206
+ return text.toLowerCase().replace(/[^a-z0-9-]/g, " ").split(/\s+/).filter((t) => t.length > 1 && !STOP_WORDS.has(t));
207
+ }
208
+ function computeTF(tokens) {
209
+ const counts = {};
210
+ for (const t of tokens) {
211
+ counts[t] = (counts[t] || 0) + 1;
212
+ }
213
+ const len = tokens.length || 1;
214
+ const tf = {};
215
+ for (const [term, count] of Object.entries(counts)) {
216
+ tf[term] = count / len;
217
+ }
218
+ return tf;
219
+ }
220
+ function computeIDF(documents) {
221
+ const N = documents.length || 1;
222
+ const docFreq = {};
223
+ for (const tokens of documents) {
224
+ const seen = new Set(tokens);
225
+ for (const t of seen) {
226
+ docFreq[t] = (docFreq[t] || 0) + 1;
227
+ }
228
+ }
229
+ const idf = {};
230
+ for (const [term, df] of Object.entries(docFreq)) {
231
+ idf[term] = Math.log(1 + N / df);
232
+ }
233
+ return idf;
234
+ }
235
+ function computeTFIDF(tf, idf) {
236
+ const tfidf = {};
237
+ for (const [term, tfVal] of Object.entries(tf)) {
238
+ const idfVal = idf[term] ?? Math.log(1 + 1);
239
+ tfidf[term] = tfVal * idfVal;
240
+ }
241
+ return tfidf;
242
+ }
243
+ function cosineSimilarity(a, b) {
244
+ let dot = 0;
245
+ let magA = 0;
246
+ let magB = 0;
247
+ for (const [term, val] of Object.entries(a)) {
248
+ magA += val * val;
249
+ if (term in b) {
250
+ dot += val * b[term];
251
+ }
252
+ }
253
+ for (const val of Object.values(b)) {
254
+ magB += val * val;
255
+ }
256
+ const denom = Math.sqrt(magA) * Math.sqrt(magB);
257
+ return denom === 0 ? 0 : dot / denom;
258
+ }
259
+
260
+ // src/search/indexer.ts
261
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
262
+ import { join as join2 } from "path";
263
+ var INDEX_FILE = ".search-index.json";
264
+ function indexPath(system) {
265
+ return join2(systemDir(system), INDEX_FILE);
266
+ }
267
+ function buildSearchIndex(system) {
268
+ const components = listComponents(system);
269
+ const docTokens = [];
270
+ const rawEntries = [];
271
+ for (const comp of components) {
272
+ const fullDesc = readFullDescription(system, comp.name, comp.kind);
273
+ const plain = stripMarkdown(fullDesc || comp.description);
274
+ const tokens = tokenize(`${comp.name} ${plain}`);
275
+ const unique = [...new Set(tokens)];
276
+ docTokens.push(unique);
277
+ rawEntries.push({
278
+ name: comp.name,
279
+ kind: comp.kind,
280
+ summary: comp.description.slice(0, 200),
281
+ tokens: unique,
282
+ tf: computeTF(tokens),
283
+ mtime: descriptionMtime(system, comp.name, comp.kind)
284
+ });
285
+ }
286
+ const idf = computeIDF(docTokens);
287
+ const entries = rawEntries.map((raw) => ({
288
+ name: raw.name,
289
+ kind: raw.kind,
290
+ summary: raw.summary,
291
+ tfidf: computeTFIDF(raw.tf, idf),
292
+ tokens: raw.tokens,
293
+ descriptionMtime: raw.mtime
294
+ }));
295
+ return {
296
+ version: 1,
297
+ buildTime: (/* @__PURE__ */ new Date()).toISOString(),
298
+ systemName: system,
299
+ documentCount: entries.length,
300
+ idf,
301
+ entries
302
+ };
303
+ }
304
+ function writeSearchIndex(system, index) {
305
+ try {
306
+ writeFileSync(indexPath(system), JSON.stringify(index, null, 2), "utf-8");
307
+ } catch {
308
+ }
309
+ }
310
+ function loadSearchIndex(system) {
311
+ const path = indexPath(system);
312
+ if (!existsSync2(path)) return null;
313
+ try {
314
+ const data = JSON.parse(readFileSync2(path, "utf-8"));
315
+ if (data.version !== 1) return null;
316
+ return data;
317
+ } catch {
318
+ return null;
319
+ }
320
+ }
321
+ function isStale(system, index) {
322
+ for (const entry of index.entries) {
323
+ const currentMtime = descriptionMtime(system, entry.name, entry.kind);
324
+ if (currentMtime !== entry.descriptionMtime) return true;
325
+ }
326
+ const currentCount = listComponents(system).length;
327
+ if (currentCount !== index.documentCount) return true;
328
+ return false;
329
+ }
330
+ function ensureSearchIndex(system) {
331
+ const existing = loadSearchIndex(system);
332
+ if (existing && !isStale(system, existing)) {
333
+ return existing;
334
+ }
335
+ const index = buildSearchIndex(system);
336
+ writeSearchIndex(system, index);
337
+ return index;
338
+ }
339
+
340
+ // src/search/search.ts
341
+ var NAME_WEIGHT = 0.4;
342
+ var KEYWORD_WEIGHT = 0.3;
343
+ var TFIDF_WEIGHT = 0.3;
344
+ function nameScore(queryTokens, componentName) {
345
+ const name = componentName.toLowerCase();
346
+ const parts = name.split("-");
347
+ let matched = 0;
348
+ for (const qt of queryTokens) {
349
+ if (name.includes(qt) || parts.some((p) => p.includes(qt) || qt.includes(p))) {
350
+ matched++;
351
+ }
352
+ }
353
+ return queryTokens.length === 0 ? 0 : matched / queryTokens.length;
354
+ }
355
+ function keywordScore(queryTokens, docTokens) {
356
+ if (queryTokens.length === 0) return 0;
357
+ const docSet = new Set(docTokens);
358
+ let matched = 0;
359
+ for (const qt of queryTokens) {
360
+ if (docSet.has(qt)) {
361
+ matched++;
362
+ } else {
363
+ for (const dt of docTokens) {
364
+ if (dt.includes(qt) || qt.includes(dt)) {
365
+ matched += 0.5;
366
+ break;
367
+ }
368
+ }
369
+ }
370
+ }
371
+ return matched / queryTokens.length;
372
+ }
373
+ function searchComponents(system, query) {
374
+ const index = ensureSearchIndex(system);
375
+ const queryTokens = tokenize(query);
376
+ if (queryTokens.length === 0) return [];
377
+ const queryTF = computeTF(queryTokens);
378
+ const queryVec = computeTFIDF(queryTF, index.idf);
379
+ const results = [];
380
+ for (const entry of index.entries) {
381
+ const ns = nameScore(queryTokens, entry.name);
382
+ const ks = keywordScore(queryTokens, entry.tokens);
383
+ const ts = cosineSimilarity(queryVec, entry.tfidf);
384
+ const score = NAME_WEIGHT * ns + KEYWORD_WEIGHT * ks + TFIDF_WEIGHT * ts;
385
+ if (score > 0.01) {
386
+ results.push({
387
+ name: entry.name,
388
+ kind: entry.kind,
389
+ summary: entry.summary,
390
+ score: Math.round(score * 100) / 100
391
+ });
392
+ }
393
+ }
394
+ results.sort((a, b) => b.score - a.score);
395
+ return results;
396
+ }
397
+
398
+ // src/commands/components.ts
361
399
  async function commandComponents(system, args) {
362
400
  const [subcommand, ...rest] = args;
363
401
  if (!subcommand || subcommand === "list") {
364
402
  return listAllComponents(system);
365
403
  }
404
+ if (subcommand === "inventory") {
405
+ return showInventory(system);
406
+ }
366
407
  if (subcommand === "details") {
367
408
  const name = rest[0];
368
409
  if (!name) {
@@ -371,6 +412,17 @@ async function commandComponents(system, args) {
371
412
  }
372
413
  return showDetails(system, name);
373
414
  }
415
+ if (subcommand === "search") {
416
+ const query = rest.join(" ");
417
+ if (!query) {
418
+ error("Usage: ds <system> components search <query>");
419
+ return 1;
420
+ }
421
+ return showSearch(system, query);
422
+ }
423
+ if (subcommand === "index") {
424
+ return rebuildIndex(system);
425
+ }
374
426
  return showDetails(system, subcommand);
375
427
  }
376
428
  function listAllComponents(system) {
@@ -385,14 +437,20 @@ function listAllComponents(system) {
385
437
  if (molecules.length > 0) {
386
438
  subheading(`Molecules (${molecules.length})`);
387
439
  descriptionList(
388
- molecules.map((c) => ({ name: c.name, description: c.description })),
440
+ molecules.map((c) => ({
441
+ name: c.tag ? `${c.name} ${c.tag}` : c.name,
442
+ description: c.description
443
+ })),
389
444
  4
390
445
  );
391
446
  }
392
447
  if (cells.length > 0) {
393
448
  subheading(`Cells (${cells.length})`);
394
449
  descriptionList(
395
- cells.map((c) => ({ name: c.name, description: c.description })),
450
+ cells.map((c) => ({
451
+ name: c.tag ? `${c.name} ${c.tag}` : c.name,
452
+ description: c.description
453
+ })),
396
454
  4
397
455
  );
398
456
  }
@@ -408,244 +466,188 @@ function showDetails(system, name) {
408
466
  }
409
467
  const sysDir = systemDir(system);
410
468
  const relPath = relative(sysDir, found.dir);
469
+ const json = readComponentJson(found.dir);
411
470
  heading(`${name}`);
412
471
  console.log(` Kind: ${found.kind}`);
472
+ if (json?.tag) console.log(` Tag: ${json.tag}`);
413
473
  console.log(` Path: ${relPath}`);
414
- const files = readdirSync2(found.dir);
474
+ const files = readdirSync(found.dir);
415
475
  console.log(` Files: ${files.join(", ")}`);
416
476
  blank();
417
- const descPath = join2(found.dir, "description.md");
418
- if (existsSync2(descPath)) {
477
+ if (json?.description) {
419
478
  subheading("Description");
420
479
  blank();
480
+ console.log(` ${json.description}`);
481
+ blank();
482
+ }
483
+ if (json?.example) {
484
+ subheading("Example");
485
+ blank();
486
+ console.log("```xml");
487
+ console.log(json.example);
488
+ console.log("```");
489
+ blank();
490
+ }
491
+ const descPath = join3(found.dir, "description.md");
492
+ if (existsSync3(descPath)) {
493
+ subheading("Description (full)");
494
+ blank();
421
495
  console.log(readFile(descPath));
422
496
  }
423
497
  const sources = findComponentSources(system, name);
424
498
  if (sources.css.length > 0) {
425
499
  for (const cssPath of sources.css) {
426
- subheading(`Source CSS: ${basename2(cssPath)}`);
500
+ subheading(`Source CSS: ${basename(cssPath)}`);
427
501
  blank();
428
502
  console.log("```css");
429
- console.log(readFileSync2(cssPath, "utf-8").trimEnd());
503
+ console.log(readFileSync3(cssPath, "utf-8").trimEnd());
430
504
  console.log("```");
431
505
  blank();
432
506
  }
433
507
  }
434
508
  if (sources.js.length > 0) {
435
509
  for (const jsPath of sources.js) {
436
- subheading(`Source JS: ${basename2(jsPath)}`);
510
+ subheading(`Source JS: ${basename(jsPath)}`);
437
511
  blank();
438
512
  console.log("```js");
439
- console.log(readFileSync2(jsPath, "utf-8").trimEnd());
513
+ console.log(readFileSync3(jsPath, "utf-8").trimEnd());
440
514
  console.log("```");
441
515
  blank();
442
516
  }
443
517
  }
444
518
  return 0;
445
519
  }
446
-
447
- // src/commands/render.ts
448
- import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
449
- import { resolve as resolve2 } from "path";
450
-
451
- // src/resolve-deps.ts
452
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
453
- import { join as join3, relative as relative2 } from "path";
454
- function buildRegistry(system) {
520
+ function showInventory(system) {
455
521
  const components = listComponents(system);
456
- const entries = [];
457
- const tagMap = /* @__PURE__ */ new Map();
458
- for (const comp of components) {
459
- const found = findComponent(system, comp.name);
460
- if (!found) continue;
461
- const scriptPath = join3(found.dir, "script.js");
462
- const stylePath = join3(found.dir, "style.css");
463
- const hasJs = existsSync3(scriptPath);
464
- const hasCss = existsSync3(stylePath);
465
- const tags = [];
466
- const dynamicTags = [];
467
- if (hasJs) {
468
- const js = readFileSync3(scriptPath, "utf-8");
469
- for (const m of js.matchAll(/customElements\.define\(\s*['"]([^'"]+)['"]/g)) {
470
- tags.push(m[1]);
471
- }
472
- for (const m of js.matchAll(/document\.createElement\(\s*['"]([a-z][a-z0-9]*-[a-z0-9-]*)['"]\s*\)/g)) {
473
- if (!tags.includes(m[1])) dynamicTags.push(m[1]);
474
- }
475
- for (const m of js.matchAll(/<([a-z][a-z0-9]*-[a-z0-9-]*)[>\s/'"]/g)) {
476
- const tag = m[1];
477
- if (!tags.includes(tag) && !dynamicTags.includes(tag)) {
478
- dynamicTags.push(tag);
479
- }
480
- }
481
- }
482
- if (hasCss) {
483
- const css = readFileSync3(stylePath, "utf-8");
484
- for (const m of css.matchAll(/(?:^|[\s,}>+~])([a-z][a-z0-9]*-[a-z0-9-]*)(?=[\s,{[:.>+~]|$)/gm)) {
485
- const tag = m[1];
486
- if (!tags.includes(tag)) tags.push(tag);
487
- }
488
- }
489
- const dirName = comp.name;
490
- if (dirName.includes("-") && !tags.includes(dirName)) {
491
- tags.push(dirName);
492
- }
493
- const prdName = `prd-${dirName}`;
494
- if (!tags.includes(prdName)) {
495
- tags.push(prdName);
496
- }
497
- const entry = {
498
- name: comp.name,
499
- kind: comp.kind,
500
- dir: found.dir,
501
- tags,
502
- dynamicTags,
503
- hasCss,
504
- hasJs
505
- };
506
- entries.push(entry);
507
- for (const tag of tags) {
508
- if (!tagMap.has(tag)) tagMap.set(tag, entry);
509
- }
522
+ if (components.length === 0) {
523
+ console.log(`No components found in ${system}.`);
524
+ return 0;
510
525
  }
511
- return { tagMap, entries };
512
- }
513
- function resolveMarkup(registry, markup) {
514
- const markupTags = /* @__PURE__ */ new Set();
515
- for (const m of markup.matchAll(/<([a-z][a-z0-9]*-[a-z0-9-]*)/g)) {
516
- markupTags.add(m[1]);
517
- }
518
- const matched = /* @__PURE__ */ new Set();
519
- const unmatchedTags = [];
520
- const resolved = /* @__PURE__ */ new Set();
521
- const queue = [...markupTags];
522
- while (queue.length > 0) {
523
- const tag = queue.pop();
524
- if (resolved.has(tag)) continue;
525
- resolved.add(tag);
526
- const entry = registry.tagMap.get(tag);
527
- if (entry) {
528
- if (!matched.has(entry)) {
529
- matched.add(entry);
530
- for (const dt of entry.dynamicTags) {
531
- if (!resolved.has(dt)) queue.push(dt);
532
- }
533
- }
534
- } else {
535
- unmatchedTags.push(tag);
526
+ const molecules = components.filter((c) => c.kind === "molecules");
527
+ const cells = components.filter((c) => c.kind === "cells");
528
+ const printTable = (items) => {
529
+ console.log("| Name | Tag | Description |");
530
+ console.log("|------|-----|-------------|");
531
+ for (const c of items) {
532
+ const tag = c.tag ? `\`${c.tag}\`` : "";
533
+ console.log(`| ${c.name} | ${tag} | ${c.description} |`);
536
534
  }
535
+ };
536
+ console.log(`# ${system} \u2014 Component Inventory
537
+ `);
538
+ if (molecules.length > 0) {
539
+ console.log(`## Molecules (${molecules.length})
540
+ `);
541
+ printTable(molecules);
542
+ console.log();
537
543
  }
538
- const cssFiles = [];
539
- const jsFiles = [];
540
- for (const entry of matched) {
541
- if (entry.hasCss) cssFiles.push(join3(entry.dir, "style.css"));
542
- if (entry.hasJs) jsFiles.push(join3(entry.dir, "script.js"));
544
+ if (cells.length > 0) {
545
+ console.log(`## Cells (${cells.length})
546
+ `);
547
+ printTable(cells);
548
+ console.log();
543
549
  }
544
- return { cssFiles, jsFiles, unmatchedTags };
550
+ return 0;
545
551
  }
546
- function buildPreviewHtml(system, markup, deps) {
547
- const sysDir = systemDir(system);
548
- const sharedCss = [
549
- join3(sysDir, "src", "_shared", "_tokens.css"),
550
- join3(sysDir, "src", "_shared", "_base.css")
551
- ];
552
- const utilitiesCss = join3(sysDir, "src", "_shared", "_utilities.css");
553
- const blocks = ["@layer tokens, base, components, utilities;"];
554
- for (const p of sharedCss) {
555
- if (existsSync3(p)) {
556
- blocks.push(`/* ${relative2(sysDir, p)} */`);
557
- blocks.push(readFileSync3(p, "utf-8"));
558
- }
552
+ function showSearch(system, query) {
553
+ const results = searchComponents(system, query);
554
+ const total = listComponents(system).length;
555
+ console.log(`
556
+ Search: "${query}" (${total} components indexed)`);
557
+ console.log("\u2500".repeat(50));
558
+ if (results.length === 0) {
559
+ console.log("\n No matches found.\n");
560
+ return 0;
561
+ }
562
+ console.log(`
563
+ Results (${results.length} match${results.length === 1 ? "" : "es"})
564
+ `);
565
+ const maxName = Math.max(...results.map((r) => r.name.length));
566
+ const maxKind = Math.max(...results.map((r) => r.kind.length + 2));
567
+ for (const r of results) {
568
+ const name = r.name.padEnd(maxName);
569
+ const kind = `(${r.kind})`.padEnd(maxKind);
570
+ const score = r.score.toFixed(2);
571
+ console.log(` ${name} ${kind} ${score} ${r.summary}`);
559
572
  }
560
- for (const p of deps.cssFiles) {
561
- blocks.push(`/* ${relative2(sysDir, p)} */`);
562
- blocks.push(readFileSync3(p, "utf-8"));
563
- }
564
- if (existsSync3(utilitiesCss)) {
565
- blocks.push(`/* ${relative2(sysDir, utilitiesCss)} */`);
566
- blocks.push(readFileSync3(utilitiesCss, "utf-8"));
567
- }
568
- const inlinedCss = blocks.join("\n");
569
- const scriptTags = deps.jsFiles.map((p) => ` <script type="module" src="/${relative2(sysDir, p)}"></script>`).join("\n");
570
- return `<!DOCTYPE html>
571
- <html lang="en">
572
- <head>
573
- <meta charset="UTF-8">
574
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
575
- <style>
576
- ${inlinedCss}
577
- </style>
578
- </head>
579
- <body>
580
- ${markup}
581
- ${scriptTags}
582
- </body>
583
- </html>`;
573
+ blank();
574
+ return 0;
575
+ }
576
+ function rebuildIndex(system) {
577
+ console.log(`Building search index for ${system}...`);
578
+ const index = buildSearchIndex(system);
579
+ writeSearchIndex(system, index);
580
+ console.log(` Documents: ${index.documentCount}`);
581
+ console.log(` Terms: ${Object.keys(index.idf).length}`);
582
+ console.log(` Written: ${index.buildTime}`);
583
+ return 0;
584
584
  }
585
585
 
586
- // src/preview-server.ts
587
- import { createServer } from "http";
586
+ // src/commands/references.ts
587
+ import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
588
588
  import { join as join4, extname } from "path";
589
- import { readFile as readFile2 } from "fs/promises";
590
- import { existsSync as existsSync4 } from "fs";
591
- var MIME_TYPES = {
592
- ".html": "text/html; charset=utf-8",
593
- ".css": "text/css; charset=utf-8",
594
- ".js": "application/javascript; charset=utf-8",
595
- ".json": "application/json; charset=utf-8",
596
- ".svg": "image/svg+xml",
597
- ".png": "image/png",
598
- ".jpg": "image/jpeg",
599
- ".jpeg": "image/jpeg",
600
- ".gif": "image/gif",
601
- ".woff": "font/woff",
602
- ".woff2": "font/woff2",
603
- ".ttf": "font/ttf",
604
- ".otf": "font/otf",
605
- ".eot": "application/vnd.ms-fontobject"
606
- };
607
- function startPreviewServer(systemDir2, opts) {
608
- return new Promise((resolve3, reject) => {
609
- const server = createServer(async (req, res) => {
610
- const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
611
- if (url.pathname === "/" && opts.html) {
612
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
613
- res.end(opts.html);
614
- return;
615
- }
616
- const filePath = join4(systemDir2, decodeURIComponent(url.pathname));
617
- if (!existsSync4(filePath)) {
618
- res.writeHead(404);
619
- res.end("Not found");
620
- return;
621
- }
622
- try {
623
- const data = await readFile2(filePath);
624
- const ext = extname(filePath).toLowerCase();
625
- const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
626
- res.writeHead(200, { "Content-Type": contentType });
627
- res.end(data);
628
- } catch {
629
- res.writeHead(500);
630
- res.end("Internal server error");
631
- }
632
- });
633
- server.listen(0, () => {
634
- const addr = server.address();
635
- if (!addr || typeof addr === "string") {
636
- reject(new Error("Failed to get server address"));
637
- return;
589
+ function referencesDir(system) {
590
+ return join4(systemDir(system), "references");
591
+ }
592
+ function loadManifest2(system) {
593
+ const path = join4(referencesDir(system), "manifest.json");
594
+ if (!existsSync4(path)) return null;
595
+ return JSON.parse(readFileSync4(path, "utf-8"));
596
+ }
597
+ function listReferenceFiles(system) {
598
+ const dir = referencesDir(system);
599
+ if (!existsSync4(dir)) return [];
600
+ return readdirSync2(dir).filter((f) => f !== "manifest.json").sort();
601
+ }
602
+ function commandReferences(system, segments) {
603
+ const dir = referencesDir(system);
604
+ if (!existsSync4(dir)) {
605
+ error(`No references directory for "${system}"`);
606
+ return 1;
607
+ }
608
+ if (segments.length === 0) {
609
+ const files = listReferenceFiles(system);
610
+ if (files.length === 0) {
611
+ console.log("No reference files.");
612
+ return 0;
613
+ }
614
+ const manifest = loadManifest2(system);
615
+ const lookup = /* @__PURE__ */ new Map();
616
+ if (manifest) {
617
+ for (const entry of manifest.references) {
618
+ lookup.set(entry.file, entry);
638
619
  }
639
- resolve3({
640
- url: `http://localhost:${addr.port}`,
641
- stop: () => server.close()
642
- });
643
- });
644
- server.on("error", reject);
645
- });
620
+ }
621
+ heading(`References (${files.length})`);
622
+ descriptionList(
623
+ files.map((f) => {
624
+ const entry = lookup.get(f);
625
+ return {
626
+ name: f,
627
+ description: entry ? entry.description : ""
628
+ };
629
+ })
630
+ );
631
+ return 0;
632
+ }
633
+ const fileName = segments.join("/");
634
+ const filePath = join4(dir, fileName);
635
+ if (!existsSync4(filePath)) {
636
+ const files = listReferenceFiles(system);
637
+ const suggestions = files.filter(
638
+ (f) => f.includes(fileName) || fileName.includes(f.replace(extname(f), ""))
639
+ );
640
+ error(`Reference "${fileName}" not found`, suggestions);
641
+ return 1;
642
+ }
643
+ const content = readFileSync4(filePath, "utf-8");
644
+ process.stdout.write(content);
645
+ return 0;
646
646
  }
647
647
 
648
648
  // src/commands/render.ts
649
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
650
+ import { resolve } from "path";
649
651
  async function readStdin() {
650
652
  const chunks = [];
651
653
  for await (const chunk of process.stdin) chunks.push(chunk);
@@ -671,19 +673,20 @@ async function commandRender(system, fileArg, flags) {
671
673
  if (fileArg === "-") {
672
674
  markup = await readStdin();
673
675
  } else {
674
- const filePath = resolve2(fileArg);
676
+ const filePath = resolve(fileArg);
675
677
  if (!existsSync5(filePath)) {
676
678
  error(`File not found: ${filePath}`);
677
679
  return 1;
678
680
  }
679
- markup = readFileSync4(filePath, "utf-8");
681
+ markup = readFileSync5(filePath, "utf-8");
680
682
  }
681
683
  const registry = buildRegistry(system);
682
684
  const deps = resolveMarkup(registry, markup);
683
685
  for (const tag of deps.unmatchedTags) {
684
686
  console.warn(` warn: unmatched custom element <${tag}>`);
685
687
  }
686
- const html = buildPreviewHtml(system, markup, deps);
688
+ const js = await bundleJs(system);
689
+ const html = buildPreviewHtml(system, markup, deps, js);
687
690
  if (flags.html) {
688
691
  process.stdout.write(html);
689
692
  return 0;
@@ -693,14 +696,14 @@ async function commandRender(system, fileArg, flags) {
693
696
  console.log(`Preview server running at ${server.url}`);
694
697
  console.log(`Opening in browser... (Ctrl+C to stop)`);
695
698
  openInBrowser(server.url);
696
- await new Promise((resolve3) => {
699
+ await new Promise((resolve2) => {
697
700
  process.on("SIGINT", () => {
698
701
  server.stop();
699
- resolve3();
702
+ resolve2();
700
703
  });
701
704
  process.on("SIGTERM", () => {
702
705
  server.stop();
703
- resolve3();
706
+ resolve2();
704
707
  });
705
708
  });
706
709
  return 0;
@@ -720,27 +723,12 @@ async function commandBrowse(system, path, flags) {
720
723
  switch (section) {
721
724
  case "components":
722
725
  return commandComponents(system, segments.slice(1));
726
+ case "references":
727
+ return commandReferences(system, segments.slice(1));
723
728
  case "render":
724
729
  return commandRender(system, segments.slice(1).join("/"), flags);
725
- case "tokens":
726
- return handleTokens(system, segments.slice(1), flags);
727
- case "principles":
728
- return handlePrinciples(system, segments.slice(1));
729
- case "architecture":
730
- return handleArchitecture(system, segments.slice(1));
731
- case "examples":
732
- return handleExamples(system, segments.slice(1), flags);
733
- case "references":
734
- return handleReferences(system, segments.slice(1));
735
730
  default:
736
- error(`Unknown section "${section}"`, [
737
- "components",
738
- "tokens",
739
- "principles",
740
- "architecture",
741
- "examples",
742
- "references"
743
- ]);
731
+ error(`Unknown section "${section}"`, ["components", "references"]);
744
732
  return 1;
745
733
  }
746
734
  }
@@ -748,198 +736,6 @@ function openInBrowser(target) {
748
736
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
749
737
  exec(`${cmd} "${target}"`);
750
738
  }
751
- function handleTokens(system, segments, flags) {
752
- if (segments.length === 0) {
753
- return listAllTokens(system);
754
- }
755
- return showTokenFile(system, segments[0], flags);
756
- }
757
- function listAllTokens(system) {
758
- const files = listTokenFiles(system);
759
- if (files.length === 0) {
760
- console.log(`No token files found in ${system}.`);
761
- return 0;
762
- }
763
- heading(`${system} \u2014 Tokens`);
764
- for (const name of files) {
765
- const data = readTokenFile(system, name);
766
- if (!data) continue;
767
- console.log(` ${name}: ${tokenSummary(data)}`);
768
- }
769
- blank();
770
- return 0;
771
- }
772
- function showTokenFile(system, name, flags) {
773
- const data = readTokenFile(system, name);
774
- if (!data) {
775
- const available = listTokenFiles(system);
776
- const suggestions = available.filter(
777
- (f) => f.includes(name) || name.includes(f)
778
- );
779
- error(
780
- `Token file "${name}" not found in ${system}`,
781
- suggestions.length > 0 ? suggestions : available
782
- );
783
- return 1;
784
- }
785
- if (flags.json) {
786
- renderJson(data);
787
- return 0;
788
- }
789
- if (flags.flat) {
790
- renderFlat(data);
791
- return 0;
792
- }
793
- renderTokens(data);
794
- return 0;
795
- }
796
- function handlePrinciples(system, segments) {
797
- if (segments.length === 0) {
798
- return listAllPrinciples(system);
799
- }
800
- return showPrinciple(system, segments[0]);
801
- }
802
- function listAllPrinciples(system) {
803
- const principles = listPrinciples(system);
804
- if (principles.length === 0) {
805
- console.log(`No principles found in ${system}.`);
806
- return 0;
807
- }
808
- heading(`${system} \u2014 Principles`);
809
- descriptionList(
810
- principles.map((p) => ({ name: p.name, description: p.title }))
811
- );
812
- blank();
813
- return 0;
814
- }
815
- function showPrinciple(system, name) {
816
- const content = readPrinciple(system, name);
817
- if (!content) {
818
- const available = listPrinciples(system);
819
- const suggestions = available.filter((p) => p.name.includes(name) || name.includes(p.name)).map((p) => p.name);
820
- error(
821
- `Principle "${name}" not found in ${system}`,
822
- suggestions.length > 0 ? suggestions : available.map((p) => p.name)
823
- );
824
- return 1;
825
- }
826
- markdown(content);
827
- return 0;
828
- }
829
- function handleArchitecture(system, segments) {
830
- if (!hasArchitecture(system)) {
831
- console.log(`No architecture section in ${system}.`);
832
- return 0;
833
- }
834
- const subpath = segments.length > 0 ? segments.join("/") : void 0;
835
- if (subpath) {
836
- const content = readArchitectureFile(system, subpath);
837
- if (content !== null) {
838
- markdown(content);
839
- return 0;
840
- }
841
- const contentMd = readArchitectureFile(system, subpath + ".md");
842
- if (contentMd !== null) {
843
- markdown(contentMd);
844
- return 0;
845
- }
846
- if (isArchitectureDir(system, subpath)) {
847
- return listArchitectureDir(system, subpath);
848
- }
849
- error(`Architecture path "${subpath}" not found in ${system}`);
850
- return 1;
851
- }
852
- return listArchitectureDir(system, void 0);
853
- }
854
- function listArchitectureDir(system, subpath) {
855
- const { entries, indexContent } = listArchitecture(system, subpath);
856
- const label = subpath ? `${system} \u2014 Architecture / ${subpath}` : `${system} \u2014 Architecture`;
857
- heading(label);
858
- if (indexContent) {
859
- markdown(indexContent);
860
- blank();
861
- }
862
- if (entries.length > 0) {
863
- for (const entry of entries) {
864
- const suffix = entry.isDir ? "/" : "";
865
- console.log(` ${entry.name}${suffix}`);
866
- }
867
- blank();
868
- }
869
- return 0;
870
- }
871
- function handleExamples(system, segments, flags) {
872
- if (segments.length === 0) {
873
- return listAllExamples(system);
874
- }
875
- return showExample(system, segments[0], flags);
876
- }
877
- function listAllExamples(system) {
878
- const examples = listExamples(system);
879
- if (examples.length === 0) {
880
- console.log(`No examples found in ${system}.`);
881
- return 0;
882
- }
883
- heading(`${system} \u2014 Examples`);
884
- list(examples);
885
- blank();
886
- return 0;
887
- }
888
- function showExample(system, name, flags) {
889
- let filename = name;
890
- if (!filename.endsWith(".html")) filename += ".html";
891
- const filePath = join5(systemDir(system), "examples", filename);
892
- if (!existsSync6(filePath)) {
893
- const available = listExamples(system);
894
- const suggestions = available.filter(
895
- (e) => e.includes(name) || name.includes(e.replace(".html", ""))
896
- );
897
- error(
898
- `Example "${name}" not found in ${system}`,
899
- suggestions.length > 0 ? suggestions.map((s) => s.replace(".html", "")) : available.map((s) => s.replace(".html", ""))
900
- );
901
- return 1;
902
- }
903
- if (flags.open) {
904
- openInBrowser(filePath);
905
- console.log(`Opening ${filename} in browser...`);
906
- return 0;
907
- }
908
- console.log(`Example: ${filename}`);
909
- console.log(`Path: ${filePath}`);
910
- console.log(`Use --open to view in browser.`);
911
- return 0;
912
- }
913
- function handleReferences(system, segments) {
914
- if (segments.length === 0) {
915
- return listAllReferences(system);
916
- }
917
- const name = segments.join("/");
918
- const filePath = join5(systemDir(system), "references", name);
919
- if (!existsSync6(filePath)) {
920
- const available = listReferences(system);
921
- error(`Reference "${name}" not found in ${system}`, available);
922
- return 1;
923
- }
924
- if (name.endsWith(".md")) {
925
- markdown(readFile(filePath));
926
- return 0;
927
- }
928
- console.log(`Reference: ${name}`);
929
- console.log(`Path: ${filePath}`);
930
- return 0;
931
- }
932
- function listAllReferences(system) {
933
- const refs = listReferences(system);
934
- if (refs.length === 0) {
935
- console.log(`No references found in ${system}.`);
936
- return 0;
937
- }
938
- heading(`${system} \u2014 References`);
939
- list(refs);
940
- blank();
941
- return 0;
942
- }
943
739
 
944
740
  // src/cli.ts
945
741
  function showHelp() {
@@ -950,25 +746,21 @@ Usage:
950
746
  design-system list List all design systems
951
747
  design-system <system> Show system overview
952
748
  design-system <system> components list List all components
749
+ design-system <system> components inventory Generate markdown inventory table
953
750
  design-system <system> components details <n> Component description + source CSS/JS
954
- design-system <system> tokens List token files
955
- design-system <system> tokens/<name> Pretty-print token values
956
- design-system <system> principles List principles
957
- design-system <system> principles/<name> Show principle content
958
- design-system <system> architecture List architecture areas
959
- design-system <system> architecture/<path> Navigate architecture tree
751
+ design-system <system> components search <q> Search components by query
752
+ design-system <system> components index Rebuild search index
753
+ design-system <system> references List reference files
754
+ design-system <system> references <file> Display reference file
960
755
  design-system <system> render <file> Render markup with resolved deps
961
756
  design-system <system> render - Render from stdin
962
- design-system <system> examples List example files
963
- design-system <system> references List reference files
964
757
 
965
758
  Flags:
966
- --open Open HTML file in browser (examples)
967
- --json Output raw JSON (tokens)
968
- --flat Output flat path = value lines (tokens)
969
759
  --html Dump raw HTML to stdout
970
- --systems-dir <path> Override default systems directory
971
760
  --help, -h Show this help message
761
+
762
+ Environment:
763
+ DESIGN_SYSTEMS_DIR Path to directory containing design system definitions (required)
972
764
  `);
973
765
  }
974
766
  async function main() {
@@ -976,18 +768,11 @@ async function main() {
976
768
  args: process.argv.slice(2),
977
769
  options: {
978
770
  help: { type: "boolean", short: "h" },
979
- open: { type: "boolean" },
980
- json: { type: "boolean" },
981
- flat: { type: "boolean" },
982
- html: { type: "boolean" },
983
- "systems-dir": { type: "string" }
771
+ html: { type: "boolean" }
984
772
  },
985
773
  allowPositionals: true,
986
774
  strict: true
987
775
  });
988
- if (values["systems-dir"]) {
989
- setSystemsRoot(values["systems-dir"]);
990
- }
991
776
  if (values.help && positionals.length === 0) {
992
777
  showHelp();
993
778
  process.exit(0);
@@ -1012,9 +797,6 @@ async function main() {
1012
797
  process.exit(await commandRender(first, rest.slice(1).join(" "), { html: values.html }));
1013
798
  }
1014
799
  const flags = {
1015
- open: values.open,
1016
- json: values.json,
1017
- flat: values.flat,
1018
800
  html: values.html
1019
801
  };
1020
802
  process.exit(await commandBrowse(first, second, flags));