| 66 | argsSchema = AnalyzeDesignSystemArgs; |
| 67 | |
| 68 | async execute(args: z.infer<typeof AnalyzeDesignSystemArgs>, ctx: ToolContext): Promise<ToolResult> { |
| 69 | const root = args.path ? path.join(ctx.cwd, args.path) : ctx.cwd; |
| 70 | const out: string[] = []; |
| 71 | out.push(`# Design System Tokens`); |
| 72 | out.push(''); |
| 73 | |
| 74 | // Tailwind config |
| 75 | const twPaths = ['tailwind.config.ts', 'tailwind.config.js', 'tailwind.config.mjs', 'tailwind.config.cjs']; |
| 76 | let twContent = ''; |
| 77 | for (const p of twPaths) { |
| 78 | try { |
| 79 | twContent = await fs.readFile(path.join(root, p), 'utf-8'); |
| 80 | out.push(`## Tailwind config: ${p}`); |
| 81 | break; |
| 82 | } catch { /* try next */ } |
| 83 | } |
| 84 | if (twContent) { |
| 85 | // Extract theme keys |
| 86 | const themeMatch = /theme\s*:\s*\{([\s\S]*?)\n\s*\}\s*,?\s*(plugins|\})/m.exec(twContent); |
| 87 | if (themeMatch) { |
| 88 | const t = themeMatch[1]!; |
| 89 | // Colors |
| 90 | const colorBlock = /colors?\s*:\s*\{([\s\S]*?)\n\s*\}/.exec(t); |
| 91 | if (colorBlock) { |
| 92 | out.push(''); |
| 93 | out.push(`### Custom colors`); |
| 94 | const colorLines = colorBlock[1]!.split('\n').slice(0, 30).map(l => l.trim()).filter(l => l && !l.startsWith('//')); |
| 95 | for (const l of colorLines) out.push(` ${l}`); |
| 96 | } |
| 97 | // Custom font family |
| 98 | const fontBlock = /fontFamily\s*:\s*\{([\s\S]*?)\n\s*\}/.exec(t); |
| 99 | if (fontBlock) { |
| 100 | out.push(''); |
| 101 | out.push(`### Custom fonts`); |
| 102 | const lines = fontBlock[1]!.split('\n').slice(0, 20).map(l => l.trim()).filter(l => l && !l.startsWith('//')); |
| 103 | for (const l of lines) out.push(` ${l}`); |
| 104 | } |
| 105 | // Extend block |
| 106 | const extendBlock = /extend\s*:\s*\{([\s\S]*?)\n\s*\}\s*,?\s*$/m.exec(t); |
| 107 | if (extendBlock) { |
| 108 | out.push(''); |
| 109 | out.push(`### Theme extensions`); |
| 110 | const lines = extendBlock[1]!.split('\n').slice(0, 40).map(l => l.trim()).filter(l => l && !l.startsWith('//')); |
| 111 | for (const l of lines) out.push(` ${l}`); |
| 112 | } |
| 113 | } |
| 114 | } else { |
| 115 | out.push(`(no tailwind.config — checking CSS files for tokens)`); |
| 116 | } |
| 117 | |
| 118 | // Scan CSS files for :root vars (custom properties) |
| 119 | const cssFiles = await walkFiles(root, new Set(['.css', '.scss', '.sass']), 200); |
| 120 | const customProps = new Map<string, { value: string; file: string }>(); |
| 121 | for (const f of cssFiles) { |
| 122 | const re = /--([a-zA-Z0-9-]+)\s*:\s*([^;]+);/g; |
| 123 | let m; |
| 124 | while ((m = re.exec(f.content)) !== null) { |
| 125 | const name = m[1]!; |