@stayicon/drift-guard 0.2.1 โ†’ 0.2.3

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/README.md CHANGED
@@ -7,9 +7,18 @@
7
7
  [![npm version](https://img.shields.io/npm/v/@stayicon/drift-guard)](https://www.npmjs.com/package/@stayicon/drift-guard)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Tests](https://img.shields.io/badge/tests-130%2F130-brightgreen)](https://github.com/Hwani-Net/drift-guard)
10
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-blue)](https://www.npmjs.com/package/@stayicon/drift-guard)
10
11
 
11
12
  <p align="center">
12
- <img src="docs/assets/cli-demo.png" alt="drift-guard CLI demo" width="680">
13
+ <img src="docs/assets/cli-demo.png" alt="drift-guard CLI demo showing design drift detection" width="680">
14
+ </p>
15
+
16
+ <p align="center">
17
+ <b>
18
+ <a href="https://hwani-net.github.io/drift-guard/">๐ŸŽฎ Try the Interactive Demo</a> ยท
19
+ <a href="https://www.npmjs.com/package/@stayicon/drift-guard">๐Ÿ“ฆ npm</a> ยท
20
+ <a href="#quick-start">๐Ÿš€ Quick Start</a>
21
+ </b>
13
22
  </p>
14
23
 
15
24
  ---
@@ -39,6 +48,10 @@ npx drift-guard rules
39
48
  npx drift-guard check
40
49
  ```
41
50
 
51
+ **Zero token overhead.** No MCP server. No configuration. Just CLI.
52
+
53
+ > ๐Ÿ“บ **[Try the Interactive Demo โ†’](https://hwani-net.github.io/drift-guard/)** โ€” See drift-guard in action, right in your browser.
54
+
42
55
  ## How It Works
43
56
 
44
57
  ```
@@ -115,7 +128,7 @@ npx drift-guard snapshot update
115
128
  ## What Gets Protected
116
129
 
117
130
  | Category | Properties | Example |
118
- |----------|-----------|---------|
131
+ |----------|-----------|---------|
119
132
  | ๐ŸŽจ Colors | `color`, `background-color`, `border-color`, CSS variables | `--primary: #1a73e8` |
120
133
  | ๐Ÿ“ Fonts | `font-family`, `font-size`, `font-weight`, `line-height` | `font-family: Inter` |
121
134
  | ๐Ÿ“ Spacing | `margin`, `padding`, `gap` | `padding: 16px 24px` |
@@ -125,9 +138,9 @@ npx drift-guard snapshot update
125
138
  | โœจ Effects | `backdrop-filter`, `filter`, `animation`, `transition` | `backdrop-filter: blur(10px)` |
126
139
  | ๐Ÿ—๏ธ Structure | Semantic tags, DOM depth, layout hash, child sequence | `<header> โ†’ <nav> โ†’ <main> โ†’ <footer>` |
127
140
 
128
- ### DOM Structure Detection (v0.2.0)
141
+ ### DOM Structure Detection (v0.2.0+)
129
142
 
130
- drift-guard now fingerprints your HTML structure โ€” not just CSS tokens.
143
+ drift-guard fingerprints your HTML structure โ€” not just CSS tokens.
131
144
 
132
145
  ```bash
133
146
  # Initialize with structure tracking (on by default)
@@ -216,6 +229,12 @@ const rules = generateRules(snapshot, 'claude-md');
216
229
  | **Token overhead** | 0 tokens (CLI) | N/A |
217
230
  | **Cost** | Free, forever | Percy: $99+/mo for teams |
218
231
 
232
+ ## Why CLI, Not MCP?
233
+
234
+ > MCP tool registration costs ~10,000+ tokens per server at conversation start. drift-guard's CLI costs **0 tokens**. AI agents already know how to run `npx` commands.
235
+
236
+ Read more: [ADR-007: CLI-First Strategy](docs/DECISIONS.md#adr-007-cli-first-์ „๋žต--mcp-๋ž˜ํผ-๋ฐฐํฌ-๋ณด๋ฅ˜-2026-03-12)
237
+
219
238
  ## Philosophy
220
239
 
221
240
  > AI should **add features**, not **destroy design**.
package/dist/cli/index.js CHANGED
@@ -24,6 +24,8 @@ import { Command as Command2 } from "commander";
24
24
  import chalk6 from "chalk";
25
25
 
26
26
  // src/cli/init.ts
27
+ import fs from "fs";
28
+ import path from "path";
27
29
  import chalk from "chalk";
28
30
  async function initCommand(options) {
29
31
  const projectRoot = process.cwd();
@@ -65,12 +67,94 @@ async function initCommand(options) {
65
67
  console.log(chalk.dim(" \u{1F3D7}\uFE0F layout hash: ") + chalk.white(s.layoutHash));
66
68
  }
67
69
  console.log();
68
- console.log(chalk.dim("Next steps:"));
69
- console.log(chalk.cyan(" 1. ") + "Add .design-guard/ to .gitignore (optional)");
70
- console.log(chalk.cyan(" 2. ") + chalk.bold("npx drift-guard rules") + " \u2014 Generate AI agent protection rules");
71
- console.log(chalk.cyan(" 3. ") + chalk.bold("npx drift-guard check") + " \u2014 Check for design drift anytime");
70
+ if (!options.skipRules) {
71
+ try {
72
+ const content = generateRules(snapshot, "agents-md");
73
+ const rulesPath = saveRules(projectRoot, "agents-md", content, false);
74
+ console.log(chalk.green("\u2705 AI agent rules generated: ") + chalk.white(rulesPath));
75
+ } catch (error) {
76
+ console.log(chalk.yellow("\u26A0\uFE0F Could not generate rules: ") + chalk.dim(error.message));
77
+ }
78
+ } else {
79
+ console.log(chalk.dim(" \u23ED Skipped rules generation (--skip-rules)"));
80
+ }
81
+ if (!options.skipHook) {
82
+ const hasGit = fs.existsSync(path.join(projectRoot, ".git"));
83
+ if (hasGit) {
84
+ try {
85
+ installHookSilent(projectRoot, threshold.toString());
86
+ console.log(chalk.green("\u2705 Pre-commit hook installed: ") + chalk.dim("drift-guard check runs before every commit"));
87
+ } catch (error) {
88
+ console.log(chalk.yellow("\u26A0\uFE0F Could not install hook: ") + chalk.dim(error.message));
89
+ }
90
+ } else {
91
+ console.log(chalk.dim(" \u23ED No .git directory found \u2014 skipping hook install"));
92
+ }
93
+ } else {
94
+ console.log(chalk.dim(" \u23ED Skipped hook installation (--skip-hook)"));
95
+ }
96
+ console.log();
97
+ console.log(chalk.green.bold(" \u{1F6E1}\uFE0F Protection active!"));
98
+ console.log(chalk.dim(" Run ") + chalk.cyan("npx drift-guard check") + chalk.dim(" anytime to verify design integrity."));
99
+ if (!options.skipRules) {
100
+ console.log(chalk.dim(" Run ") + chalk.cyan("npx drift-guard rules --format all") + chalk.dim(" for additional AI tool formats."));
101
+ }
72
102
  console.log();
73
103
  }
104
+ function installHookSilent(projectRoot, threshold) {
105
+ const hasHusky = fs.existsSync(path.join(projectRoot, ".husky"));
106
+ const hookCommand = `npx drift-guard check --threshold ${threshold} --ci`;
107
+ if (hasHusky) {
108
+ const hookFile2 = path.join(projectRoot, ".husky", "pre-commit");
109
+ if (fs.existsSync(hookFile2)) {
110
+ const existing = fs.readFileSync(hookFile2, "utf-8");
111
+ if (existing.includes("drift-guard")) return;
112
+ fs.appendFileSync(hookFile2, `
113
+ ${hookCommand}
114
+ `);
115
+ } else {
116
+ fs.writeFileSync(hookFile2, `#!/usr/bin/env sh
117
+ . "$(dirname -- "$0")/_/husky.sh"
118
+
119
+ ${hookCommand}
120
+ `);
121
+ }
122
+ return;
123
+ }
124
+ const hooksDir = path.join(projectRoot, ".git", "hooks");
125
+ const hookFile = path.join(hooksDir, "pre-commit");
126
+ const hookScript = `#!/usr/bin/env sh
127
+ # drift-guard pre-commit hook
128
+ # Checks for design token drift before commits
129
+ # Bypass: git commit --no-verify
130
+
131
+ npx drift-guard check --threshold ${threshold} --ci
132
+
133
+ if [ $? -ne 0 ]; then
134
+ echo ""
135
+ echo "\\033[31m\u2717 Design drift detected! Commit blocked.\\033[0m"
136
+ echo " Run 'npx drift-guard check' for details."
137
+ echo " If changes are intentional, run 'npx drift-guard snapshot update'"
138
+ echo " Or use 'git commit --no-verify' to bypass."
139
+ exit 1
140
+ fi
141
+ `;
142
+ if (!fs.existsSync(hooksDir)) {
143
+ fs.mkdirSync(hooksDir, { recursive: true });
144
+ }
145
+ if (fs.existsSync(hookFile)) {
146
+ const existing = fs.readFileSync(hookFile, "utf-8");
147
+ if (existing.includes("drift-guard")) return;
148
+ fs.appendFileSync(hookFile, `
149
+ ${hookScript}`);
150
+ } else {
151
+ fs.writeFileSync(hookFile, hookScript);
152
+ try {
153
+ fs.chmodSync(hookFile, 493);
154
+ } catch {
155
+ }
156
+ }
157
+ }
74
158
 
75
159
  // src/cli/check.ts
76
160
  import chalk2 from "chalk";
@@ -85,6 +169,13 @@ async function checkCommand(options) {
85
169
  const config = loadConfig(projectRoot);
86
170
  const threshold = options.threshold ? parseInt(options.threshold, 10) : config.threshold;
87
171
  console.log(chalk2.bold("\n\u{1F6E1}\uFE0F drift-guard check\n"));
172
+ const snapshotAge = Date.now() - new Date(snapshot.createdAt).getTime();
173
+ const ageDays = Math.floor(snapshotAge / (1e3 * 60 * 60 * 24));
174
+ if (ageDays >= 7) {
175
+ const ageStr = ageDays >= 30 ? `${Math.floor(ageDays / 30)} month(s)` : `${ageDays} days`;
176
+ console.log(chalk2.yellow(`\u26A0\uFE0F Snapshot is ${ageStr} old (created ${snapshot.createdAt.split("T")[0]}).`));
177
+ console.log(chalk2.yellow(" If your design has changed, run: ") + chalk2.cyan("drift-guard init --from <latest.html>\n"));
178
+ }
88
179
  console.log(chalk2.dim(`Comparing against snapshot from ${snapshot.createdAt}...
89
180
  `));
90
181
  const report = await detectDrift(projectRoot, snapshot, threshold);
@@ -221,32 +312,32 @@ async function snapshotCommand(options) {
221
312
  }
222
313
 
223
314
  // src/cli/hook.ts
224
- import fs from "fs";
225
- import path from "path";
315
+ import fs2 from "fs";
316
+ import path2 from "path";
226
317
  import chalk5 from "chalk";
227
318
  async function hookInstallCommand(options) {
228
319
  const cwd = process.cwd();
229
320
  const threshold = options.threshold ?? "10";
230
- if (!fs.existsSync(path.join(cwd, ".git"))) {
321
+ if (!fs2.existsSync(path2.join(cwd, ".git"))) {
231
322
  console.error(chalk5.red("\u2717 Not a git repository. Run this from a git project root."));
232
323
  process.exit(1);
233
324
  }
234
- const hasHusky = fs.existsSync(path.join(cwd, ".husky"));
235
- const hasLefthook = fs.existsSync(path.join(cwd, "lefthook.yml")) || fs.existsSync(path.join(cwd, ".lefthook.yml"));
325
+ const hasHusky = fs2.existsSync(path2.join(cwd, ".husky"));
326
+ const hasLefthook = fs2.existsSync(path2.join(cwd, "lefthook.yml")) || fs2.existsSync(path2.join(cwd, ".lefthook.yml"));
236
327
  if (hasHusky) {
237
- const hookFile2 = path.join(cwd, ".husky", "pre-commit");
328
+ const hookFile2 = path2.join(cwd, ".husky", "pre-commit");
238
329
  const hookCommand = `npx drift-guard check --threshold ${threshold} --ci`;
239
- if (fs.existsSync(hookFile2)) {
240
- const existing = fs.readFileSync(hookFile2, "utf-8");
330
+ if (fs2.existsSync(hookFile2)) {
331
+ const existing = fs2.readFileSync(hookFile2, "utf-8");
241
332
  if (existing.includes("drift-guard")) {
242
333
  console.log(chalk5.yellow("\u26A0 drift-guard hook already exists in .husky/pre-commit"));
243
334
  return;
244
335
  }
245
- fs.appendFileSync(hookFile2, `
336
+ fs2.appendFileSync(hookFile2, `
246
337
  ${hookCommand}
247
338
  `);
248
339
  } else {
249
- fs.writeFileSync(hookFile2, `#!/usr/bin/env sh
340
+ fs2.writeFileSync(hookFile2, `#!/usr/bin/env sh
250
341
  . "$(dirname -- "$0")/_/husky.sh"
251
342
 
252
343
  ${hookCommand}
@@ -265,8 +356,8 @@ pre-commit:
265
356
  `));
266
357
  return;
267
358
  }
268
- const hooksDir = path.join(cwd, ".git", "hooks");
269
- const hookFile = path.join(hooksDir, "pre-commit");
359
+ const hooksDir = path2.join(cwd, ".git", "hooks");
360
+ const hookFile = path2.join(hooksDir, "pre-commit");
270
361
  const hookScript = `#!/usr/bin/env sh
271
362
  # drift-guard pre-commit hook
272
363
  # Checks for design token drift before commits
@@ -283,22 +374,22 @@ if [ $? -ne 0 ]; then
283
374
  exit 1
284
375
  fi
285
376
  `;
286
- if (!fs.existsSync(hooksDir)) {
287
- fs.mkdirSync(hooksDir, { recursive: true });
377
+ if (!fs2.existsSync(hooksDir)) {
378
+ fs2.mkdirSync(hooksDir, { recursive: true });
288
379
  }
289
- if (fs.existsSync(hookFile)) {
290
- const existing = fs.readFileSync(hookFile, "utf-8");
380
+ if (fs2.existsSync(hookFile)) {
381
+ const existing = fs2.readFileSync(hookFile, "utf-8");
291
382
  if (existing.includes("drift-guard")) {
292
383
  console.log(chalk5.yellow("\u26A0 drift-guard hook already installed"));
293
384
  return;
294
385
  }
295
- fs.appendFileSync(hookFile, `
386
+ fs2.appendFileSync(hookFile, `
296
387
  ${hookScript}`);
297
388
  console.log(chalk5.green("\u2713 Appended drift-guard to existing pre-commit hook"));
298
389
  } else {
299
- fs.writeFileSync(hookFile, hookScript);
390
+ fs2.writeFileSync(hookFile, hookScript);
300
391
  try {
301
- fs.chmodSync(hookFile, 493);
392
+ fs2.chmodSync(hookFile, 493);
302
393
  } catch {
303
394
  }
304
395
  console.log(chalk5.green("\u2713 Installed drift-guard pre-commit hook"));
@@ -308,26 +399,26 @@ ${hookScript}`);
308
399
  }
309
400
  async function hookUninstallCommand() {
310
401
  const cwd = process.cwd();
311
- const huskyHook = path.join(cwd, ".husky", "pre-commit");
312
- if (fs.existsSync(huskyHook)) {
313
- const content = fs.readFileSync(huskyHook, "utf-8");
402
+ const huskyHook = path2.join(cwd, ".husky", "pre-commit");
403
+ if (fs2.existsSync(huskyHook)) {
404
+ const content = fs2.readFileSync(huskyHook, "utf-8");
314
405
  if (content.includes("drift-guard")) {
315
406
  const cleaned = content.replace(/\n?npx drift-guard.*\n?/g, "\n");
316
- fs.writeFileSync(huskyHook, cleaned);
407
+ fs2.writeFileSync(huskyHook, cleaned);
317
408
  console.log(chalk5.green("\u2713 Removed drift-guard from .husky/pre-commit"));
318
409
  return;
319
410
  }
320
411
  }
321
- const gitHook = path.join(cwd, ".git", "hooks", "pre-commit");
322
- if (fs.existsSync(gitHook)) {
323
- const content = fs.readFileSync(gitHook, "utf-8");
412
+ const gitHook = path2.join(cwd, ".git", "hooks", "pre-commit");
413
+ if (fs2.existsSync(gitHook)) {
414
+ const content = fs2.readFileSync(gitHook, "utf-8");
324
415
  if (content.includes("drift-guard")) {
325
416
  if (content.includes("# drift-guard pre-commit hook") && !content.includes("\n#!/usr/bin/env sh\n")) {
326
- fs.unlinkSync(gitHook);
417
+ fs2.unlinkSync(gitHook);
327
418
  console.log(chalk5.green("\u2713 Removed drift-guard pre-commit hook"));
328
419
  } else {
329
420
  const cleaned = content.replace(/# drift-guard pre-commit hook[\s\S]*?fi\n?/g, "");
330
- fs.writeFileSync(gitHook, cleaned);
421
+ fs2.writeFileSync(gitHook, cleaned);
331
422
  console.log(chalk5.green("\u2713 Removed drift-guard from pre-commit hook"));
332
423
  }
333
424
  return;
@@ -338,8 +429,8 @@ async function hookUninstallCommand() {
338
429
 
339
430
  // src/cli/sync.ts
340
431
  import { Command } from "commander";
341
- import path2 from "path";
342
- import fs2 from "fs";
432
+ import path3 from "path";
433
+ import fs3 from "fs";
343
434
  import fg from "fast-glob";
344
435
  var syncCommand = new Command("sync").description("Synchronize design tokens between Stitch and your codebase").requiredOption(
345
436
  "-d, --direction <direction>",
@@ -440,12 +531,12 @@ async function handleToCode(projectRoot, options) {
440
531
  );
441
532
  process.exit(1);
442
533
  }
443
- const absPath = path2.resolve(projectRoot, stitchHtmlPath);
444
- if (!fs2.existsSync(absPath)) {
534
+ const absPath = path3.resolve(projectRoot, stitchHtmlPath);
535
+ if (!fs3.existsSync(absPath)) {
445
536
  console.error(`\u274C Stitch HTML file not found: ${absPath}`);
446
537
  process.exit(1);
447
538
  }
448
- const htmlContent = fs2.readFileSync(absPath, "utf-8");
539
+ const htmlContent = fs3.readFileSync(absPath, "utf-8");
449
540
  const stitchTokens = [];
450
541
  const htmlTokens = parseHtml(htmlContent, stitchHtmlPath);
451
542
  stitchTokens.push(...htmlTokens);
@@ -471,10 +562,10 @@ async function handleToCode(projectRoot, options) {
471
562
  if (localStitchFiles.length > 0) {
472
563
  console.log("\u{1F4C4} Step 1: Full HTML replacement");
473
564
  for (const localFile of localStitchFiles) {
474
- const localAbsPath = path2.resolve(projectRoot, localFile);
475
- const localContent = fs2.readFileSync(localAbsPath, "utf-8");
565
+ const localAbsPath = path3.resolve(projectRoot, localFile);
566
+ const localContent = fs3.readFileSync(localAbsPath, "utf-8");
476
567
  if (localContent !== htmlContent) {
477
- fs2.writeFileSync(localAbsPath, htmlContent, "utf-8");
568
+ fs3.writeFileSync(localAbsPath, htmlContent, "utf-8");
478
569
  console.log(` \u2705 Replaced: ${localFile} (Stitch \u2192 local)`);
479
570
  htmlReplaced = true;
480
571
  } else {
@@ -498,8 +589,8 @@ async function handleToCode(projectRoot, options) {
498
589
  const { modifiedFiles, appliedCount } = applySyncChanges(result.changes, cssFileMap);
499
590
  if (appliedCount > 0) {
500
591
  for (const [file, content] of modifiedFiles) {
501
- const fullPath = path2.resolve(projectRoot, file);
502
- fs2.writeFileSync(fullPath, content, "utf-8");
592
+ const fullPath = path3.resolve(projectRoot, file);
593
+ fs3.writeFileSync(fullPath, content, "utf-8");
503
594
  console.log(` \u2705 Patched: ${file}`);
504
595
  }
505
596
  }
@@ -511,7 +602,7 @@ async function handleToCode(projectRoot, options) {
511
602
  const { execSync } = await import("child_process");
512
603
  try {
513
604
  execSync(
514
- `node "${path2.resolve(projectRoot, "..", "drift-guard", "dist", "cli", "index.js")}" snapshot update`,
605
+ `node "${path3.resolve(projectRoot, "..", "drift-guard", "dist", "cli", "index.js")}" snapshot update`,
515
606
  { cwd: projectRoot, stdio: "pipe" }
516
607
  );
517
608
  } catch {
@@ -530,8 +621,8 @@ async function handleToCode(projectRoot, options) {
530
621
  if (localStitchFiles.length > 0) {
531
622
  let allMatch = true;
532
623
  for (const localFile of localStitchFiles) {
533
- const localAbsPath = path2.resolve(projectRoot, localFile);
534
- const localContent = fs2.readFileSync(localAbsPath, "utf-8");
624
+ const localAbsPath = path3.resolve(projectRoot, localFile);
625
+ const localContent = fs3.readFileSync(localAbsPath, "utf-8");
535
626
  if (localContent === htmlContent) {
536
627
  console.log(` \u2705 ${localFile} \u2014 100% identical to Stitch`);
537
628
  } else {
@@ -567,12 +658,12 @@ async function handleToCode(projectRoot, options) {
567
658
  console.log("");
568
659
  console.log("\u2501".repeat(60));
569
660
  if (!options.dryRun) {
570
- const patchPath = path2.join(
661
+ const patchPath = path3.join(
571
662
  projectRoot,
572
663
  ".design-guard",
573
664
  "sync-patch.css"
574
665
  );
575
- fs2.writeFileSync(patchPath, result.patchFile, "utf-8");
666
+ fs3.writeFileSync(patchPath, result.patchFile, "utf-8");
576
667
  console.log(`
577
668
  \u{1F4BE} Patch saved to: ${patchPath}`);
578
669
  console.log(" Use --apply for full HTML content + token sync.");
@@ -594,7 +685,7 @@ async function findLocalStitchHtml(projectRoot, sourceAbsPath, targetPath) {
594
685
  absolute: false
595
686
  }
596
687
  );
597
- const sourceRelative = path2.relative(projectRoot, sourceAbsPath);
688
+ const sourceRelative = path3.relative(projectRoot, sourceAbsPath);
598
689
  return stitchFiles.filter((f) => f !== sourceRelative);
599
690
  }
600
691
  async function loadProjectCssFiles(projectRoot, config) {
@@ -606,9 +697,9 @@ async function loadProjectCssFiles(projectRoot, config) {
606
697
  absolute: false
607
698
  });
608
699
  for (const file of matches) {
609
- const absPath = path2.resolve(projectRoot, file);
610
- if (fs2.existsSync(absPath)) {
611
- cssFileMap.set(file, fs2.readFileSync(absPath, "utf-8"));
700
+ const absPath = path3.resolve(projectRoot, file);
701
+ if (fs3.existsSync(absPath)) {
702
+ cssFileMap.set(file, fs3.readFileSync(absPath, "utf-8"));
612
703
  }
613
704
  }
614
705
  }
@@ -620,9 +711,9 @@ async function loadProjectCssFiles(projectRoot, config) {
620
711
  absolute: false
621
712
  });
622
713
  for (const file of htmlMatches) {
623
- const absPath = path2.resolve(projectRoot, file);
624
- if (fs2.existsSync(absPath)) {
625
- cssFileMap.set(file, fs2.readFileSync(absPath, "utf-8"));
714
+ const absPath = path3.resolve(projectRoot, file);
715
+ if (fs3.existsSync(absPath)) {
716
+ cssFileMap.set(file, fs3.readFileSync(absPath, "utf-8"));
626
717
  }
627
718
  }
628
719
  return cssFileMap;
@@ -633,7 +724,7 @@ var program = new Command2();
633
724
  program.name("drift-guard").description(
634
725
  chalk6.bold("\u{1F6E1}\uFE0F drift-guard") + " \u2014 Protect your UI from AI coding agents' design drift.\n\n Detect and prevent design token changes during AI-assisted development.\n Lock your colors, fonts, spacing, and layout before AI agents touch your code."
635
726
  ).version("0.2.0");
636
- program.command("init").description("Initialize drift-guard and create a design snapshot").option("--from <path>", "Create snapshot from a Stitch/HTML file").option("--threshold <number>", "Set default drift threshold percentage", "10").action(initCommand);
727
+ program.command("init").description("Initialize drift-guard: snapshot + AI rules + git hook (all-in-one)").option("--from <path>", "Create snapshot from a Stitch/HTML file").option("--threshold <number>", "Set default drift threshold percentage", "10").option("--skip-rules", "Skip auto-generating AGENTS.md rules file").option("--skip-hook", "Skip auto-installing pre-commit git hook").action(initCommand);
637
728
  program.command("check").description("Check for design drift against the saved snapshot").option("--threshold <number>", "Override drift threshold percentage").option("--output <format>", "Output format: text or json", "text").option("--ci", "CI mode: exit with code 1 on drift exceeding threshold").action(checkCommand);
638
729
  program.command("rules").description("Generate AI agent rule files from the design snapshot").option("--format <type>", "Rule format: cursorrules, claude-md, agents-md, copilot, clinerules, all", "all").option("--append", "Append to existing rule files instead of overwriting").action(rulesCommand);
639
730
  program.command("snapshot").description("Manage design snapshots").command("update").description("Update the snapshot to reflect current design (after intentional changes)").option("--from <path>", "Update from a specific Stitch/HTML file").action(snapshotCommand);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/init.ts","../../src/cli/check.ts","../../src/cli/rules.ts","../../src/cli/snapshot-cmd.ts","../../src/cli/hook.ts","../../src/cli/sync.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { initCommand } from './init.js';\nimport { checkCommand } from './check.js';\nimport { rulesCommand } from './rules.js';\nimport { snapshotCommand } from './snapshot-cmd.js';\nimport { hookInstallCommand, hookUninstallCommand } from './hook.js';\nimport { syncCommand } from './sync.js';\n\nconst program = new Command();\n\nprogram\n .name('drift-guard')\n .description(\n chalk.bold('๐Ÿ›ก๏ธ drift-guard') +\n ' โ€” Protect your UI from AI coding agents\\' design drift.\\n\\n' +\n ' Detect and prevent design token changes during AI-assisted development.\\n' +\n ' Lock your colors, fonts, spacing, and layout before AI agents touch your code.'\n )\n .version('0.2.0');\n\nprogram\n .command('init')\n .description('Initialize drift-guard and create a design snapshot')\n .option('--from <path>', 'Create snapshot from a Stitch/HTML file')\n .option('--threshold <number>', 'Set default drift threshold percentage', '10')\n .action(initCommand);\n\nprogram\n .command('check')\n .description('Check for design drift against the saved snapshot')\n .option('--threshold <number>', 'Override drift threshold percentage')\n .option('--output <format>', 'Output format: text or json', 'text')\n .option('--ci', 'CI mode: exit with code 1 on drift exceeding threshold')\n .action(checkCommand);\n\nprogram\n .command('rules')\n .description('Generate AI agent rule files from the design snapshot')\n .option('--format <type>', 'Rule format: cursorrules, claude-md, agents-md, copilot, clinerules, all', 'all')\n .option('--append', 'Append to existing rule files instead of overwriting')\n .action(rulesCommand);\n\nprogram\n .command('snapshot')\n .description('Manage design snapshots')\n .command('update')\n .description('Update the snapshot to reflect current design (after intentional changes)')\n .option('--from <path>', 'Update from a specific Stitch/HTML file')\n .action(snapshotCommand);\n\nconst hook = program\n .command('hook')\n .description('Manage pre-commit hook for automatic drift checking');\n\nhook\n .command('install')\n .description('Install a pre-commit hook that runs drift-guard check')\n .option('--threshold <number>', 'Drift threshold percentage for the hook', '10')\n .action(hookInstallCommand);\n\nhook\n .command('uninstall')\n .description('Remove the drift-guard pre-commit hook')\n .action(hookUninstallCommand);\n\nprogram.addCommand(syncCommand);\n\nprogram.parse();\n\n","import chalk from 'chalk';\nimport { createSnapshot, saveSnapshot, saveConfig } from '../core/snapshot.js';\nimport { DEFAULT_CONFIG } from '../types/index.js';\n\ninterface InitOptions {\n from?: string;\n threshold?: string;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n const projectRoot = process.cwd();\n const threshold = parseInt(options.threshold ?? '10', 10);\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard init\\n'));\n console.log(chalk.dim('Scanning project for design tokens...\\n'));\n\n // Save config\n const config = { ...DEFAULT_CONFIG, threshold };\n saveConfig(projectRoot, config);\n\n // Create snapshot\n const snapshot = await createSnapshot(projectRoot, options.from);\n\n if (snapshot.tokens.length === 0) {\n console.log(chalk.yellow('โš ๏ธ No design tokens found.'));\n console.log(chalk.dim(' Make sure you have CSS files or use --from <stitch.html>'));\n console.log(chalk.dim(' Supported patterns: src/**/*.css, app/**/*.css, styles/**/*.css\\n'));\n return;\n }\n\n // Save snapshot\n const snapshotPath = saveSnapshot(projectRoot, snapshot);\n\n // Report\n console.log(chalk.green('โœ… Design snapshot created!\\n'));\n console.log(chalk.dim(' Snapshot: ') + chalk.white(snapshotPath));\n console.log(chalk.dim(' Files scanned: ') + chalk.white(snapshot.sourceFiles.length.toString()));\n console.log(chalk.dim(' Tokens locked: ') + chalk.white(snapshot.tokens.length.toString()));\n console.log(chalk.dim(' Threshold: ') + chalk.white(`${threshold}%`));\n console.log();\n\n // Token summary\n console.log(chalk.bold(' Token Summary:'));\n const categories = ['color', 'font', 'spacing', 'shadow', 'radius', 'layout'] as const;\n for (const cat of categories) {\n const count = snapshot.summary[cat];\n if (count > 0) {\n const icon = { color: '๐ŸŽจ', font: '๐Ÿ“', spacing: '๐Ÿ“', shadow: '๐ŸŒซ๏ธ', radius: 'โญ•', layout: '๐Ÿ“' }[cat];\n console.log(chalk.dim(` ${icon} ${cat}: `) + chalk.white(count.toString()));\n }\n }\n\n // Structure fingerprint summary (v0.2.0+)\n if (snapshot.structure) {\n const s = snapshot.structure;\n const tagList = Object.entries(s.semanticTags)\n .filter(([, count]) => count > 0)\n .map(([tag, count]) => `${tag}(${count})`)\n .join(', ');\n console.log();\n console.log(chalk.bold(' Structure Fingerprint:'));\n console.log(chalk.dim(' ๐Ÿ—๏ธ semantic tags: ') + chalk.white(tagList));\n console.log(chalk.dim(' ๐Ÿ—๏ธ max depth: ') + chalk.white(s.maxDepth.toString()));\n console.log(chalk.dim(' ๐Ÿ—๏ธ layout hash: ') + chalk.white(s.layoutHash));\n }\n\n console.log();\n console.log(chalk.dim('Next steps:'));\n console.log(chalk.cyan(' 1. ') + 'Add .design-guard/ to .gitignore (optional)');\n console.log(chalk.cyan(' 2. ') + chalk.bold('npx drift-guard rules') + ' โ€” Generate AI agent protection rules');\n console.log(chalk.cyan(' 3. ') + chalk.bold('npx drift-guard check') + ' โ€” Check for design drift anytime');\n console.log();\n}\n","import chalk from 'chalk';\nimport { loadSnapshot, loadConfig } from '../core/snapshot.js';\nimport { detectDrift } from '../core/drift.js';\nimport type { DriftReport, DriftItem } from '../types/index.js';\n\ninterface CheckOptions {\n threshold?: string;\n output?: string;\n ci?: boolean;\n}\n\nexport async function checkCommand(options: CheckOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n // Load snapshot\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.log(chalk.red('\\nโŒ No snapshot found.'));\n console.log(chalk.dim(' Run ') + chalk.cyan('npx drift-guard init') + chalk.dim(' first.\\n'));\n process.exit(1);\n }\n\n const config = loadConfig(projectRoot);\n const threshold = options.threshold\n ? parseInt(options.threshold, 10)\n : config.threshold;\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard check\\n'));\n console.log(chalk.dim(`Comparing against snapshot from ${snapshot.createdAt}...\\n`));\n\n // Detect drift\n const report = await detectDrift(projectRoot, snapshot, threshold);\n\n if (options.output === 'json') {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printTextReport(report);\n }\n\n // Exit code for CI\n if (!report.passed && (options.ci || process.env['CI'])) {\n process.exit(1);\n }\n\n if (!report.passed) {\n process.exitCode = 1;\n }\n}\n\nfunction printTextReport(report: DriftReport): void {\n // Score display\n const scoreColor = report.passed ? chalk.green : chalk.red;\n const icon = report.passed ? 'โœ…' : '๐Ÿšจ';\n\n console.log(`${icon} ${chalk.bold('Drift Score:')} ${scoreColor(`${report.driftScore}%`)} (threshold: ${report.threshold}%)`);\n console.log(chalk.dim(` ${report.changedTokens} of ${report.totalTokens} tokens changed\\n`));\n\n if (report.items.length === 0 && !(report.structureDrift?.changed)) {\n console.log(chalk.green(' No design drift detected. Your design is intact! ๐ŸŽ‰\\n'));\n return;\n }\n\n if (report.items.length > 0) {\n // Category breakdown\n console.log(chalk.bold(' Category Breakdown:'));\n const categories = ['color', 'font', 'spacing', 'shadow', 'radius', 'layout'] as const;\n for (const cat of categories) {\n const summary = report.categorySummary[cat];\n if (summary.total === 0) continue;\n\n const catIcon = { color: '๐ŸŽจ', font: '๐Ÿ“', spacing: '๐Ÿ“', shadow: '๐ŸŒซ๏ธ', radius: 'โญ•', layout: '๐Ÿ“' }[cat];\n const catColor = summary.changed > 0 ? chalk.red : chalk.green;\n console.log(` ${catIcon} ${cat}: ${catColor(`${summary.changed}/${summary.total}`)} (${summary.driftPercent}%)`);\n }\n console.log();\n\n // Detailed changes (max 20)\n const itemsToShow = report.items.slice(0, 20);\n if (itemsToShow.length > 0) {\n console.log(chalk.bold(' Changes:'));\n for (const item of itemsToShow) {\n printDriftItem(item);\n }\n\n if (report.items.length > 20) {\n console.log(chalk.dim(` ... and ${report.items.length - 20} more changes\\n`));\n }\n }\n\n console.log();\n }\n\n // Structure drift (v0.2.0+)\n if (report.structureDrift) {\n if (report.structureDrift.changed) {\n console.log(chalk.bold(' ๐Ÿ—๏ธ Structure Drift:'));\n for (const detail of report.structureDrift.details) {\n console.log(chalk.yellow(` โš ๏ธ ${detail}`));\n }\n console.log();\n } else {\n console.log(chalk.green(' ๐Ÿ—๏ธ DOM structure: No changes detected โœ…\\n'));\n }\n }\n\n if (!report.passed) {\n console.log(chalk.yellow(' ๐Ÿ’ก To accept these changes, run:'));\n console.log(chalk.cyan(' npx drift-guard snapshot update\\n'));\n }\n}\n\nfunction printDriftItem(item: DriftItem): void {\n const { original, current, changeType } = item;\n\n switch (changeType) {\n case 'modified':\n console.log(\n chalk.yellow(' ~') +\n chalk.dim(` ${original.file} `) +\n chalk.white(`${original.property}: `) +\n chalk.red(original.value) +\n chalk.dim(' โ†’ ') +\n chalk.green(current?.value ?? 'removed')\n );\n break;\n case 'deleted':\n console.log(\n chalk.red(' -') +\n chalk.dim(` ${original.file} `) +\n chalk.white(`${original.property}: `) +\n chalk.red(original.value) +\n chalk.dim(' [deleted]')\n );\n break;\n case 'added':\n console.log(\n chalk.green(' +') +\n chalk.dim(` ${original.file} `) +\n chalk.white(`${original.property}: `) +\n chalk.green(original.value)\n );\n break;\n }\n}\n","import chalk from 'chalk';\nimport { loadSnapshot } from '../core/snapshot.js';\nimport { generateRules, saveRules } from '../core/rules-generator.js';\nimport type { RuleFormat } from '../types/index.js';\n\ninterface RulesOptions {\n format?: string;\n append?: boolean;\n}\n\nconst ALL_FORMATS: RuleFormat[] = ['cursorrules', 'claude-md', 'agents-md', 'copilot', 'clinerules'];\n\nexport async function rulesCommand(options: RulesOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.log(chalk.red('\\nโŒ No snapshot found.'));\n console.log(chalk.dim(' Run ') + chalk.cyan('npx drift-guard init') + chalk.dim(' first.\\n'));\n process.exit(1);\n }\n\n const formats: RuleFormat[] = options.format === 'all' || !options.format\n ? ALL_FORMATS\n : [options.format as RuleFormat];\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard rules\\n'));\n console.log(chalk.dim(`Generating AI protection rules from ${snapshot.tokens.length} locked tokens...\\n`));\n\n for (const format of formats) {\n try {\n const content = generateRules(snapshot, format);\n const filePath = saveRules(projectRoot, format, content, options.append ?? false);\n console.log(chalk.green(' โœ… ') + chalk.white(filePath));\n } catch (error) {\n console.log(chalk.red(' โŒ ') + chalk.white(format) + chalk.dim(`: ${(error as Error).message}`));\n }\n }\n\n console.log();\n console.log(chalk.dim('Your AI coding agents will now protect these design tokens.'));\n console.log(chalk.dim('Supported tools: Cursor, Claude Code, Codex, GitHub Copilot, Cline\\n'));\n}\n","import chalk from 'chalk';\nimport { createSnapshot, saveSnapshot } from '../core/snapshot.js';\n\ninterface SnapshotOptions {\n from?: string;\n}\n\nexport async function snapshotCommand(options: SnapshotOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard snapshot update\\n'));\n console.log(chalk.dim('Re-scanning project and updating snapshot...\\n'));\n\n const snapshot = await createSnapshot(projectRoot, options.from);\n\n if (snapshot.tokens.length === 0) {\n console.log(chalk.yellow('โš ๏ธ No design tokens found.\\n'));\n return;\n }\n\n const snapshotPath = saveSnapshot(projectRoot, snapshot);\n\n console.log(chalk.green('โœ… Snapshot updated!\\n'));\n console.log(chalk.dim(' File: ') + chalk.white(snapshotPath));\n console.log(chalk.dim(' Tokens: ') + chalk.white(snapshot.tokens.length.toString()));\n console.log(chalk.dim(' Updated: ') + chalk.white(snapshot.createdAt));\n console.log();\n console.log(chalk.dim('๐Ÿ’ก Remember to regenerate rules: ') + chalk.cyan('npx drift-guard rules\\n'));\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\n\n/**\n * Install a pre-commit hook that runs drift-guard check\n */\nexport async function hookInstallCommand(options: { threshold?: string }): Promise<void> {\n const cwd = process.cwd();\n const threshold = options.threshold ?? '10';\n\n // Check if git repo\n if (!fs.existsSync(path.join(cwd, '.git'))) {\n console.error(chalk.red('โœ— Not a git repository. Run this from a git project root.'));\n process.exit(1);\n }\n\n // Detect hook manager\n const hasHusky = fs.existsSync(path.join(cwd, '.husky'));\n const hasLefthook = fs.existsSync(path.join(cwd, 'lefthook.yml')) ||\n fs.existsSync(path.join(cwd, '.lefthook.yml'));\n\n if (hasHusky) {\n // Add to existing husky setup\n const hookFile = path.join(cwd, '.husky', 'pre-commit');\n const hookCommand = `npx drift-guard check --threshold ${threshold} --ci`;\n\n if (fs.existsSync(hookFile)) {\n const existing = fs.readFileSync(hookFile, 'utf-8');\n if (existing.includes('drift-guard')) {\n console.log(chalk.yellow('โš  drift-guard hook already exists in .husky/pre-commit'));\n return;\n }\n fs.appendFileSync(hookFile, `\\n${hookCommand}\\n`);\n } else {\n fs.writeFileSync(hookFile, `#!/usr/bin/env sh\\n. \"$(dirname -- \"$0\")/_/husky.sh\"\\n\\n${hookCommand}\\n`);\n }\n\n console.log(chalk.green('โœ“ Added drift-guard check to .husky/pre-commit'));\n return;\n }\n\n if (hasLefthook) {\n console.log(chalk.yellow('โš  lefthook detected. Add this to your lefthook.yml:'));\n console.log(chalk.cyan(`\npre-commit:\n commands:\n drift-guard:\n run: npx drift-guard check --threshold ${threshold} --ci\n`));\n return;\n }\n\n // No hook manager โ€” install via .git/hooks directly\n const hooksDir = path.join(cwd, '.git', 'hooks');\n const hookFile = path.join(hooksDir, 'pre-commit');\n const hookScript = `#!/usr/bin/env sh\n# drift-guard pre-commit hook\n# Checks for design token drift before commits\n# Use --force flag to bypass: git commit --no-verify\n\nnpx drift-guard check --threshold ${threshold} --ci\n\nif [ $? -ne 0 ]; then\n echo \"\"\n echo \"\\\\033[31mโœ— Design drift detected! Commit blocked.\\\\033[0m\"\n echo \" Run 'npx drift-guard check' for details.\"\n echo \" If changes are intentional, run 'npx drift-guard snapshot update'\"\n echo \" Or use 'git commit --no-verify' to bypass.\"\n exit 1\nfi\n`;\n\n if (!fs.existsSync(hooksDir)) {\n fs.mkdirSync(hooksDir, { recursive: true });\n }\n\n if (fs.existsSync(hookFile)) {\n const existing = fs.readFileSync(hookFile, 'utf-8');\n if (existing.includes('drift-guard')) {\n console.log(chalk.yellow('โš  drift-guard hook already installed'));\n return;\n }\n // Append to existing hook\n fs.appendFileSync(hookFile, `\\n${hookScript}`);\n console.log(chalk.green('โœ“ Appended drift-guard to existing pre-commit hook'));\n } else {\n fs.writeFileSync(hookFile, hookScript);\n // Make executable on Unix\n try {\n fs.chmodSync(hookFile, 0o755);\n } catch {\n // Windows may not support chmod, skip\n }\n console.log(chalk.green('โœ“ Installed drift-guard pre-commit hook'));\n }\n\n console.log(chalk.dim(` Threshold: ${threshold}%`));\n console.log(chalk.dim(' Bypass: git commit --no-verify'));\n}\n\n/**\n * Uninstall the pre-commit hook\n */\nexport async function hookUninstallCommand(): Promise<void> {\n const cwd = process.cwd();\n\n // Check husky\n const huskyHook = path.join(cwd, '.husky', 'pre-commit');\n if (fs.existsSync(huskyHook)) {\n const content = fs.readFileSync(huskyHook, 'utf-8');\n if (content.includes('drift-guard')) {\n const cleaned = content.replace(/\\n?npx drift-guard.*\\n?/g, '\\n');\n fs.writeFileSync(huskyHook, cleaned);\n console.log(chalk.green('โœ“ Removed drift-guard from .husky/pre-commit'));\n return;\n }\n }\n\n // Check .git/hooks\n const gitHook = path.join(cwd, '.git', 'hooks', 'pre-commit');\n if (fs.existsSync(gitHook)) {\n const content = fs.readFileSync(gitHook, 'utf-8');\n if (content.includes('drift-guard')) {\n // If the entire file is our hook, remove it\n if (content.includes('# drift-guard pre-commit hook') && !content.includes('\\n#!/usr/bin/env sh\\n')) {\n fs.unlinkSync(gitHook);\n console.log(chalk.green('โœ“ Removed drift-guard pre-commit hook'));\n } else {\n // Remove just our part\n const cleaned = content.replace(/# drift-guard pre-commit hook[\\s\\S]*?fi\\n?/g, '');\n fs.writeFileSync(gitHook, cleaned);\n console.log(chalk.green('โœ“ Removed drift-guard from pre-commit hook'));\n }\n return;\n }\n }\n\n console.log(chalk.yellow('โš  No drift-guard hook found to uninstall'));\n}\n","import { Command } from 'commander';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport fg from 'fast-glob';\nimport { loadSnapshot, loadConfig } from '../core/snapshot.js';\nimport { detectDrift } from '../core/drift.js';\nimport {\n syncToStitch,\n syncFromStitch,\n applySyncChanges,\n} from '../core/sync.js';\nimport { parseCss, extractCssVariables } from '../parsers/css-parser.js';\nimport { parseHtml, extractStyleBlocks, extractTailwindConfig } from '../parsers/html-parser.js';\nimport type { DesignToken, SyncDirection } from '../types/index.js';\n\nexport const syncCommand = new Command('sync')\n .description('Synchronize design tokens between Stitch and your codebase')\n .requiredOption(\n '-d, --direction <direction>',\n 'Sync direction: to-stitch (push code changes to Stitch) or to-code (pull Stitch changes)',\n )\n .option(\n '--stitch-html <path>',\n 'Path to Stitch HTML file (for to-code direction)',\n )\n .option(\n '--stitch-project <id>',\n 'Stitch project ID (for to-stitch direction)',\n )\n .option(\n '--stitch-screen <id>',\n 'Stitch screen ID (for to-stitch direction)',\n )\n .option('--apply', 'Auto-apply changes: full HTML replacement + CSS patching (to-code only)', false)\n .option('--target <path>', 'Target local HTML file to replace with Stitch version (to-code --apply)')\n .option('--dry-run', 'Preview changes without applying', false)\n .option('--json', 'Output as JSON', false)\n .action(async (options) => {\n const projectRoot = process.cwd();\n const direction = options.direction as SyncDirection;\n\n if (direction !== 'to-stitch' && direction !== 'to-code') {\n console.error(\n 'โŒ Invalid direction. Use \"to-stitch\" or \"to-code\".',\n );\n process.exit(1);\n }\n\n if (direction === 'to-stitch') {\n await handleToStitch(projectRoot, options);\n } else {\n await handleToCode(projectRoot, options);\n }\n });\n\n/**\n * Handle to-stitch direction:\n * Compare current code against snapshot โ†’ generate Stitch edit prompt\n */\nasync function handleToStitch(\n projectRoot: string,\n options: { dryRun: boolean; json: boolean; stitchProject?: string; stitchScreen?: string },\n): Promise<void> {\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.error('โŒ No snapshot found. Run \"drift-guard init\" first.');\n process.exit(1);\n }\n\n // Detect drift (code vs snapshot)\n const report = await detectDrift(projectRoot, snapshot, 0);\n\n if (report.items.length === 0) {\n console.log('โœ… No changes detected. Stitch and code are in sync!');\n return;\n }\n\n const result = syncToStitch(report.items);\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n // Pretty output\n console.log('');\n console.log('๐Ÿ”„ drift-guard sync โ†’ to-stitch');\n console.log(` ${result.changes.length} change(s) detected\\n`);\n\n for (const change of result.changes) {\n const icon =\n change.action === 'update' ? 'โœ๏ธ' :\n change.action === 'add' ? 'โž•' : '๐Ÿ—‘๏ธ';\n console.log(\n ` ${icon} ${change.property}: ${change.fromValue || '(none)'} โ†’ ${change.toValue || '(removed)'}`,\n );\n }\n\n console.log('');\n console.log('โ”'.repeat(60));\n console.log('๐Ÿ“‹ Stitch edit_screens prompt:');\n console.log('โ”'.repeat(60));\n console.log('');\n console.log(result.prompt);\n console.log('');\n console.log('โ”'.repeat(60));\n\n const projectId = options.stitchProject;\n const screenId = options.stitchScreen;\n\n if (projectId && screenId) {\n console.log('');\n console.log('๐Ÿ’ก To apply these changes in Stitch, run:');\n console.log('');\n console.log(` edit_screens({`);\n console.log(` projectId: \"${projectId}\",`);\n console.log(` selectedScreenIds: [\"${screenId}\"],`);\n console.log(` prompt: \"<the prompt above>\"`);\n console.log(` })`);\n } else {\n console.log('');\n console.log(\n '๐Ÿ’ก Copy the prompt above and use it with Stitch MCP edit_screens',\n );\n console.log(\n ' or pass --stitch-project and --stitch-screen for a ready-to-use call.',\n );\n }\n}\n\n/**\n * Handle to-code direction:\n * Compare Stitch HTML against snapshot โ†’ generate CSS patch โ†’ optionally apply.\n *\n * With --apply: Stitch HTML is the SOURCE OF TRUTH.\n * 1. Replace the local Stitch HTML file entirely (fixes text/layout/content diffs)\n * 2. Patch design tokens in other CSS files\n * 3. Update snapshot\n * 4. Verify 0% drift\n */\nasync function handleToCode(\n projectRoot: string,\n options: { dryRun: boolean; json: boolean; apply: boolean; stitchHtml?: string; target?: string },\n): Promise<void> {\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.error('โŒ No snapshot found. Run \"drift-guard init\" first.');\n process.exit(1);\n }\n\n // Get Stitch HTML path (new/downloaded version)\n const config = loadConfig(projectRoot);\n const stitchHtmlPath =\n options.stitchHtml ?? config.stitch?.htmlPath;\n\n if (!stitchHtmlPath) {\n console.error(\n 'โŒ No Stitch HTML file specified.',\n );\n console.error(\n ' Use --stitch-html <path> or set stitch.htmlPath in .design-guard/config.json',\n );\n process.exit(1);\n }\n\n const absPath = path.resolve(projectRoot, stitchHtmlPath);\n if (!fs.existsSync(absPath)) {\n console.error(`โŒ Stitch HTML file not found: ${absPath}`);\n process.exit(1);\n }\n\n // Parse Stitch HTML for tokens\n const htmlContent = fs.readFileSync(absPath, 'utf-8');\n const stitchTokens: DesignToken[] = [];\n\n const htmlTokens = parseHtml(htmlContent, stitchHtmlPath);\n stitchTokens.push(...htmlTokens);\n\n const styleBlocks = extractStyleBlocks(htmlContent);\n for (const block of styleBlocks) {\n stitchTokens.push(...parseCss(block, stitchHtmlPath));\n stitchTokens.push(...extractCssVariables(block, stitchHtmlPath));\n }\n\n const twTokens = extractTailwindConfig(htmlContent, stitchHtmlPath);\n stitchTokens.push(...twTokens);\n\n // Compare Stitch tokens against snapshot\n const result = syncFromStitch(stitchTokens, snapshot.tokens);\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n // === FULL SYNC (--apply): Stitch is source of truth ===\n if (options.apply && !options.dryRun) {\n console.log('');\n console.log('๐Ÿ”„ drift-guard sync โ†’ to-code (FULL SYNC)');\n console.log(' Stitch HTML is the source of truth.\\n');\n\n // Step 1: Find local Stitch HTML files to replace\n const targetPath = options.target;\n const localStitchFiles = await findLocalStitchHtml(projectRoot, absPath, targetPath);\n let htmlReplaced = false;\n\n if (localStitchFiles.length > 0) {\n console.log('๐Ÿ“„ Step 1: Full HTML replacement');\n for (const localFile of localStitchFiles) {\n const localAbsPath = path.resolve(projectRoot, localFile);\n const localContent = fs.readFileSync(localAbsPath, 'utf-8');\n\n if (localContent !== htmlContent) {\n fs.writeFileSync(localAbsPath, htmlContent, 'utf-8');\n console.log(` โœ… Replaced: ${localFile} (Stitch โ†’ local)`);\n htmlReplaced = true;\n } else {\n console.log(` โ„น๏ธ Already identical: ${localFile}`);\n }\n }\n } else {\n console.log('๐Ÿ“„ Step 1: No local Stitch HTML found to replace.');\n console.log(' ๐Ÿ’ก Use --target <path> to specify the target file.');\n }\n\n // Step 2: Patch design tokens in CSS files\n if (result.changes.length > 0) {\n console.log(`\\n๐Ÿ“ Step 2: Patching ${result.changes.length} design token(s) in CSS files`);\n for (const change of result.changes) {\n const icon =\n change.action === 'update' ? 'โœ๏ธ' :\n change.action === 'add' ? 'โž•' : '๐Ÿ—‘๏ธ';\n console.log(\n ` ${icon} ${change.property}: ${change.fromValue || '(none)'} โ†’ ${change.toValue || '(removed)'}`,\n );\n }\n\n const cssFileMap = await loadProjectCssFiles(projectRoot, config);\n const { modifiedFiles, appliedCount } = applySyncChanges(result.changes, cssFileMap);\n\n if (appliedCount > 0) {\n for (const [file, content] of modifiedFiles) {\n const fullPath = path.resolve(projectRoot, file);\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(` โœ… Patched: ${file}`);\n }\n }\n } else {\n console.log('\\n๐Ÿ“ Step 2: No design token changes โ€” tokens already match.');\n }\n\n // Step 3: Update snapshot to reflect new state\n if (htmlReplaced || result.changes.length > 0) {\n console.log('\\n๐Ÿ“ธ Step 3: Updating snapshot...');\n\n const { execSync } = await import('node:child_process');\n try {\n execSync(\n `node \"${path.resolve(projectRoot, '..', 'drift-guard', 'dist', 'cli', 'index.js')}\" snapshot update`,\n { cwd: projectRoot, stdio: 'pipe' },\n );\n } catch {\n // Try the global command\n try {\n execSync('npx drift-guard snapshot update', {\n cwd: projectRoot,\n stdio: 'pipe',\n });\n } catch {\n console.log(' โš ๏ธ Could not auto-update snapshot. Run: drift-guard snapshot update');\n }\n }\n console.log(' โœ… Snapshot updated.');\n }\n\n // Step 4: Verify full sync\n console.log('\\n๐Ÿ” Step 4: Verifying full sync...');\n\n // Compare the Stitch source HTML with the local file\n if (localStitchFiles.length > 0) {\n let allMatch = true;\n for (const localFile of localStitchFiles) {\n const localAbsPath = path.resolve(projectRoot, localFile);\n const localContent = fs.readFileSync(localAbsPath, 'utf-8');\n if (localContent === htmlContent) {\n console.log(` โœ… ${localFile} โ€” 100% identical to Stitch`);\n } else {\n console.log(` โŒ ${localFile} โ€” content differs!`);\n allMatch = false;\n }\n }\n if (allMatch) {\n console.log('\\n ๐ŸŽ‰ FULL SYNC VERIFIED โ€” Stitch and code are 100% identical!');\n }\n }\n } else if (result.changes.length === 0) {\n console.log('โœ… Stitch and code design tokens are already in sync!');\n console.log(' ๐Ÿ’ก Use --apply for full HTML content sync (text, layout, structure).');\n } else {\n // Show token diff and patch\n console.log('');\n console.log('๐Ÿ”„ drift-guard sync โ†’ to-code');\n console.log(` ${result.changes.length} design token change(s) from Stitch\\n`);\n\n for (const change of result.changes) {\n const icon =\n change.action === 'update' ? 'โœ๏ธ' :\n change.action === 'add' ? 'โž•' : '๐Ÿ—‘๏ธ';\n console.log(\n ` ${icon} ${change.property}: ${change.fromValue || '(none)'} โ†’ ${change.toValue || '(removed)'}`,\n );\n }\n\n if (result.patchFile) {\n console.log('');\n console.log('โ”'.repeat(60));\n console.log('๐Ÿ“‹ CSS patch to apply:');\n console.log('โ”'.repeat(60));\n console.log('');\n console.log(result.patchFile);\n console.log('');\n console.log('โ”'.repeat(60));\n\n if (!options.dryRun) {\n const patchPath = path.join(\n projectRoot,\n '.design-guard',\n 'sync-patch.css',\n );\n fs.writeFileSync(patchPath, result.patchFile, 'utf-8');\n console.log(`\\n๐Ÿ’พ Patch saved to: ${patchPath}`);\n console.log(' Use --apply for full HTML content + token sync.');\n } else {\n console.log('\\n๐Ÿ” Dry run โ€” no files written.');\n }\n }\n }\n}\n\n/**\n * Find local Stitch HTML files that should be replaced.\n * Searches for stitch-design.html or files matching --target.\n */\nasync function findLocalStitchHtml(\n projectRoot: string,\n sourceAbsPath: string,\n targetPath?: string,\n): Promise<string[]> {\n if (targetPath) {\n return [targetPath];\n }\n\n // Search for stitch-design.html files in the project\n const stitchFiles = await fg(\n ['**/stitch-design.html', '**/stitch*.html'],\n {\n cwd: projectRoot,\n ignore: ['node_modules/**', 'dist/**', 'build/**'],\n absolute: false,\n },\n );\n\n // Exclude the source file itself\n const sourceRelative = path.relative(projectRoot, sourceAbsPath);\n return stitchFiles.filter(f => f !== sourceRelative);\n}\n\n/**\n * Load all CSS files from the project into a map\n */\nasync function loadProjectCssFiles(\n projectRoot: string,\n config: { cssFiles: string[]; ignore: string[] },\n): Promise<Map<string, string>> {\n const cssFileMap = new Map<string, string>();\n\n for (const pattern of config.cssFiles) {\n const matches = await fg(pattern, {\n cwd: projectRoot,\n ignore: config.ignore,\n absolute: false,\n });\n\n for (const file of matches) {\n const absPath = path.resolve(projectRoot, file);\n if (fs.existsSync(absPath)) {\n cssFileMap.set(file, fs.readFileSync(absPath, 'utf-8'));\n }\n }\n }\n\n // Also include HTML files that might have Tailwind config\n const htmlPatterns = ['**/*.html'];\n const htmlIgnore = ['node_modules/**', 'dist/**', 'build/**', '.next/**'];\n const htmlMatches = await fg(htmlPatterns, {\n cwd: projectRoot,\n ignore: htmlIgnore,\n absolute: false,\n });\n\n for (const file of htmlMatches) {\n const absPath = path.resolve(projectRoot, file);\n if (fs.existsSync(absPath)) {\n cssFileMap.set(file, fs.readFileSync(absPath, 'utf-8'));\n }\n }\n\n return cssFileMap;\n}\n"],"mappings":"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","names":["Command","chalk","chalk","chalk","chalk","chalk","chalk","chalk","chalk","hookFile","path","fs","path","fs","Command","chalk"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/init.ts","../../src/cli/check.ts","../../src/cli/rules.ts","../../src/cli/snapshot-cmd.ts","../../src/cli/hook.ts","../../src/cli/sync.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { initCommand } from './init.js';\nimport { checkCommand } from './check.js';\nimport { rulesCommand } from './rules.js';\nimport { snapshotCommand } from './snapshot-cmd.js';\nimport { hookInstallCommand, hookUninstallCommand } from './hook.js';\nimport { syncCommand } from './sync.js';\n\nconst program = new Command();\n\nprogram\n .name('drift-guard')\n .description(\n chalk.bold('๐Ÿ›ก๏ธ drift-guard') +\n ' โ€” Protect your UI from AI coding agents\\' design drift.\\n\\n' +\n ' Detect and prevent design token changes during AI-assisted development.\\n' +\n ' Lock your colors, fonts, spacing, and layout before AI agents touch your code.'\n )\n .version('0.2.0');\n\nprogram\n .command('init')\n .description('Initialize drift-guard: snapshot + AI rules + git hook (all-in-one)')\n .option('--from <path>', 'Create snapshot from a Stitch/HTML file')\n .option('--threshold <number>', 'Set default drift threshold percentage', '10')\n .option('--skip-rules', 'Skip auto-generating AGENTS.md rules file')\n .option('--skip-hook', 'Skip auto-installing pre-commit git hook')\n .action(initCommand);\n\nprogram\n .command('check')\n .description('Check for design drift against the saved snapshot')\n .option('--threshold <number>', 'Override drift threshold percentage')\n .option('--output <format>', 'Output format: text or json', 'text')\n .option('--ci', 'CI mode: exit with code 1 on drift exceeding threshold')\n .action(checkCommand);\n\nprogram\n .command('rules')\n .description('Generate AI agent rule files from the design snapshot')\n .option('--format <type>', 'Rule format: cursorrules, claude-md, agents-md, copilot, clinerules, all', 'all')\n .option('--append', 'Append to existing rule files instead of overwriting')\n .action(rulesCommand);\n\nprogram\n .command('snapshot')\n .description('Manage design snapshots')\n .command('update')\n .description('Update the snapshot to reflect current design (after intentional changes)')\n .option('--from <path>', 'Update from a specific Stitch/HTML file')\n .action(snapshotCommand);\n\nconst hook = program\n .command('hook')\n .description('Manage pre-commit hook for automatic drift checking');\n\nhook\n .command('install')\n .description('Install a pre-commit hook that runs drift-guard check')\n .option('--threshold <number>', 'Drift threshold percentage for the hook', '10')\n .action(hookInstallCommand);\n\nhook\n .command('uninstall')\n .description('Remove the drift-guard pre-commit hook')\n .action(hookUninstallCommand);\n\nprogram.addCommand(syncCommand);\n\nprogram.parse();\n\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { createSnapshot, saveSnapshot, saveConfig } from '../core/snapshot.js';\nimport { generateRules, saveRules } from '../core/rules-generator.js';\nimport { DEFAULT_CONFIG } from '../types/index.js';\n\ninterface InitOptions {\n from?: string;\n threshold?: string;\n skipRules?: boolean;\n skipHook?: boolean;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n const projectRoot = process.cwd();\n const threshold = parseInt(options.threshold ?? '10', 10);\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard init\\n'));\n console.log(chalk.dim('Scanning project for design tokens...\\n'));\n\n // Save config\n const config = { ...DEFAULT_CONFIG, threshold };\n saveConfig(projectRoot, config);\n\n // Create snapshot\n const snapshot = await createSnapshot(projectRoot, options.from);\n\n if (snapshot.tokens.length === 0) {\n console.log(chalk.yellow('โš ๏ธ No design tokens found.'));\n console.log(chalk.dim(' Make sure you have CSS files or use --from <stitch.html>'));\n console.log(chalk.dim(' Supported patterns: src/**/*.css, app/**/*.css, styles/**/*.css\\n'));\n return;\n }\n\n // Save snapshot\n const snapshotPath = saveSnapshot(projectRoot, snapshot);\n\n // Report\n console.log(chalk.green('โœ… Design snapshot created!\\n'));\n console.log(chalk.dim(' Snapshot: ') + chalk.white(snapshotPath));\n console.log(chalk.dim(' Files scanned: ') + chalk.white(snapshot.sourceFiles.length.toString()));\n console.log(chalk.dim(' Tokens locked: ') + chalk.white(snapshot.tokens.length.toString()));\n console.log(chalk.dim(' Threshold: ') + chalk.white(`${threshold}%`));\n console.log();\n\n // Token summary\n console.log(chalk.bold(' Token Summary:'));\n const categories = ['color', 'font', 'spacing', 'shadow', 'radius', 'layout'] as const;\n for (const cat of categories) {\n const count = snapshot.summary[cat];\n if (count > 0) {\n const icon = { color: '๐ŸŽจ', font: '๐Ÿ“', spacing: '๐Ÿ“', shadow: '๐ŸŒซ๏ธ', radius: 'โญ•', layout: '๐Ÿ“' }[cat];\n console.log(chalk.dim(` ${icon} ${cat}: `) + chalk.white(count.toString()));\n }\n }\n\n // Structure fingerprint summary (v0.2.0+)\n if (snapshot.structure) {\n const s = snapshot.structure;\n const tagList = Object.entries(s.semanticTags)\n .filter(([, count]) => count > 0)\n .map(([tag, count]) => `${tag}(${count})`)\n .join(', ');\n console.log();\n console.log(chalk.bold(' Structure Fingerprint:'));\n console.log(chalk.dim(' ๐Ÿ—๏ธ semantic tags: ') + chalk.white(tagList));\n console.log(chalk.dim(' ๐Ÿ—๏ธ max depth: ') + chalk.white(s.maxDepth.toString()));\n console.log(chalk.dim(' ๐Ÿ—๏ธ layout hash: ') + chalk.white(s.layoutHash));\n }\n\n console.log();\n\n // โ”€โ”€โ”€ Auto-generate AI agent rules (AGENTS.md) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n if (!options.skipRules) {\n try {\n const content = generateRules(snapshot, 'agents-md');\n const rulesPath = saveRules(projectRoot, 'agents-md', content, false);\n console.log(chalk.green('โœ… AI agent rules generated: ') + chalk.white(rulesPath));\n } catch (error) {\n console.log(chalk.yellow('โš ๏ธ Could not generate rules: ') + chalk.dim((error as Error).message));\n }\n } else {\n console.log(chalk.dim(' โญ Skipped rules generation (--skip-rules)'));\n }\n\n // โ”€โ”€โ”€ Auto-install git pre-commit hook โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n if (!options.skipHook) {\n const hasGit = fs.existsSync(path.join(projectRoot, '.git'));\n if (hasGit) {\n try {\n installHookSilent(projectRoot, threshold.toString());\n console.log(chalk.green('โœ… Pre-commit hook installed: ') + chalk.dim('drift-guard check runs before every commit'));\n } catch (error) {\n console.log(chalk.yellow('โš ๏ธ Could not install hook: ') + chalk.dim((error as Error).message));\n }\n } else {\n console.log(chalk.dim(' โญ No .git directory found โ€” skipping hook install'));\n }\n } else {\n console.log(chalk.dim(' โญ Skipped hook installation (--skip-hook)'));\n }\n\n console.log();\n console.log(chalk.green.bold(' ๐Ÿ›ก๏ธ Protection active!'));\n console.log(chalk.dim(' Run ') + chalk.cyan('npx drift-guard check') + chalk.dim(' anytime to verify design integrity.'));\n if (!options.skipRules) {\n console.log(chalk.dim(' Run ') + chalk.cyan('npx drift-guard rules --format all') + chalk.dim(' for additional AI tool formats.'));\n }\n console.log();\n}\n\n/**\n * Install pre-commit hook without verbose output (used by init auto-setup)\n */\nfunction installHookSilent(projectRoot: string, threshold: string): void {\n const hasHusky = fs.existsSync(path.join(projectRoot, '.husky'));\n const hookCommand = `npx drift-guard check --threshold ${threshold} --ci`;\n\n if (hasHusky) {\n const hookFile = path.join(projectRoot, '.husky', 'pre-commit');\n if (fs.existsSync(hookFile)) {\n const existing = fs.readFileSync(hookFile, 'utf-8');\n if (existing.includes('drift-guard')) return; // already installed\n fs.appendFileSync(hookFile, `\\n${hookCommand}\\n`);\n } else {\n fs.writeFileSync(hookFile, `#!/usr/bin/env sh\\n. \"$(dirname -- \"$0\")/_/husky.sh\"\\n\\n${hookCommand}\\n`);\n }\n return;\n }\n\n // Direct .git/hooks installation\n const hooksDir = path.join(projectRoot, '.git', 'hooks');\n const hookFile = path.join(hooksDir, 'pre-commit');\n const hookScript = `#!/usr/bin/env sh\n# drift-guard pre-commit hook\n# Checks for design token drift before commits\n# Bypass: git commit --no-verify\n\nnpx drift-guard check --threshold ${threshold} --ci\n\nif [ $? -ne 0 ]; then\n echo \"\"\n echo \"\\\\033[31mโœ— Design drift detected! Commit blocked.\\\\033[0m\"\n echo \" Run 'npx drift-guard check' for details.\"\n echo \" If changes are intentional, run 'npx drift-guard snapshot update'\"\n echo \" Or use 'git commit --no-verify' to bypass.\"\n exit 1\nfi\n`;\n\n if (!fs.existsSync(hooksDir)) {\n fs.mkdirSync(hooksDir, { recursive: true });\n }\n\n if (fs.existsSync(hookFile)) {\n const existing = fs.readFileSync(hookFile, 'utf-8');\n if (existing.includes('drift-guard')) return; // already installed\n fs.appendFileSync(hookFile, `\\n${hookScript}`);\n } else {\n fs.writeFileSync(hookFile, hookScript);\n try {\n fs.chmodSync(hookFile, 0o755);\n } catch {\n // Windows may not support chmod\n }\n }\n}\n","import chalk from 'chalk';\nimport { loadSnapshot, loadConfig } from '../core/snapshot.js';\nimport { detectDrift } from '../core/drift.js';\nimport type { DriftReport, DriftItem } from '../types/index.js';\n\ninterface CheckOptions {\n threshold?: string;\n output?: string;\n ci?: boolean;\n}\n\nexport async function checkCommand(options: CheckOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n // Load snapshot\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.log(chalk.red('\\nโŒ No snapshot found.'));\n console.log(chalk.dim(' Run ') + chalk.cyan('npx drift-guard init') + chalk.dim(' first.\\n'));\n process.exit(1);\n }\n\n const config = loadConfig(projectRoot);\n const threshold = options.threshold\n ? parseInt(options.threshold, 10)\n : config.threshold;\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard check\\n'));\n\n // Snapshot age warning\n const snapshotAge = Date.now() - new Date(snapshot.createdAt).getTime();\n const ageDays = Math.floor(snapshotAge / (1000 * 60 * 60 * 24));\n if (ageDays >= 7) {\n const ageStr = ageDays >= 30 ? `${Math.floor(ageDays / 30)} month(s)` : `${ageDays} days`;\n console.log(chalk.yellow(`โš ๏ธ Snapshot is ${ageStr} old (created ${snapshot.createdAt.split('T')[0]}).`));\n console.log(chalk.yellow(' If your design has changed, run: ') + chalk.cyan('drift-guard init --from <latest.html>\\n'));\n }\n\n console.log(chalk.dim(`Comparing against snapshot from ${snapshot.createdAt}...\\n`));\n\n // Detect drift\n const report = await detectDrift(projectRoot, snapshot, threshold);\n\n if (options.output === 'json') {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printTextReport(report);\n }\n\n // Exit code for CI\n if (!report.passed && (options.ci || process.env['CI'])) {\n process.exit(1);\n }\n\n if (!report.passed) {\n process.exitCode = 1;\n }\n}\n\nfunction printTextReport(report: DriftReport): void {\n // Score display\n const scoreColor = report.passed ? chalk.green : chalk.red;\n const icon = report.passed ? 'โœ…' : '๐Ÿšจ';\n\n console.log(`${icon} ${chalk.bold('Drift Score:')} ${scoreColor(`${report.driftScore}%`)} (threshold: ${report.threshold}%)`);\n console.log(chalk.dim(` ${report.changedTokens} of ${report.totalTokens} tokens changed\\n`));\n\n if (report.items.length === 0 && !(report.structureDrift?.changed)) {\n console.log(chalk.green(' No design drift detected. Your design is intact! ๐ŸŽ‰\\n'));\n return;\n }\n\n if (report.items.length > 0) {\n // Category breakdown\n console.log(chalk.bold(' Category Breakdown:'));\n const categories = ['color', 'font', 'spacing', 'shadow', 'radius', 'layout'] as const;\n for (const cat of categories) {\n const summary = report.categorySummary[cat];\n if (summary.total === 0) continue;\n\n const catIcon = { color: '๐ŸŽจ', font: '๐Ÿ“', spacing: '๐Ÿ“', shadow: '๐ŸŒซ๏ธ', radius: 'โญ•', layout: '๐Ÿ“' }[cat];\n const catColor = summary.changed > 0 ? chalk.red : chalk.green;\n console.log(` ${catIcon} ${cat}: ${catColor(`${summary.changed}/${summary.total}`)} (${summary.driftPercent}%)`);\n }\n console.log();\n\n // Detailed changes (max 20)\n const itemsToShow = report.items.slice(0, 20);\n if (itemsToShow.length > 0) {\n console.log(chalk.bold(' Changes:'));\n for (const item of itemsToShow) {\n printDriftItem(item);\n }\n\n if (report.items.length > 20) {\n console.log(chalk.dim(` ... and ${report.items.length - 20} more changes\\n`));\n }\n }\n\n console.log();\n }\n\n // Structure drift (v0.2.0+)\n if (report.structureDrift) {\n if (report.structureDrift.changed) {\n console.log(chalk.bold(' ๐Ÿ—๏ธ Structure Drift:'));\n for (const detail of report.structureDrift.details) {\n console.log(chalk.yellow(` โš ๏ธ ${detail}`));\n }\n console.log();\n } else {\n console.log(chalk.green(' ๐Ÿ—๏ธ DOM structure: No changes detected โœ…\\n'));\n }\n }\n\n if (!report.passed) {\n console.log(chalk.yellow(' ๐Ÿ’ก To accept these changes, run:'));\n console.log(chalk.cyan(' npx drift-guard snapshot update\\n'));\n }\n}\n\nfunction printDriftItem(item: DriftItem): void {\n const { original, current, changeType } = item;\n\n switch (changeType) {\n case 'modified':\n console.log(\n chalk.yellow(' ~') +\n chalk.dim(` ${original.file} `) +\n chalk.white(`${original.property}: `) +\n chalk.red(original.value) +\n chalk.dim(' โ†’ ') +\n chalk.green(current?.value ?? 'removed')\n );\n break;\n case 'deleted':\n console.log(\n chalk.red(' -') +\n chalk.dim(` ${original.file} `) +\n chalk.white(`${original.property}: `) +\n chalk.red(original.value) +\n chalk.dim(' [deleted]')\n );\n break;\n case 'added':\n console.log(\n chalk.green(' +') +\n chalk.dim(` ${original.file} `) +\n chalk.white(`${original.property}: `) +\n chalk.green(original.value)\n );\n break;\n }\n}\n","import chalk from 'chalk';\nimport { loadSnapshot } from '../core/snapshot.js';\nimport { generateRules, saveRules } from '../core/rules-generator.js';\nimport type { RuleFormat } from '../types/index.js';\n\ninterface RulesOptions {\n format?: string;\n append?: boolean;\n}\n\nconst ALL_FORMATS: RuleFormat[] = ['cursorrules', 'claude-md', 'agents-md', 'copilot', 'clinerules'];\n\nexport async function rulesCommand(options: RulesOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.log(chalk.red('\\nโŒ No snapshot found.'));\n console.log(chalk.dim(' Run ') + chalk.cyan('npx drift-guard init') + chalk.dim(' first.\\n'));\n process.exit(1);\n }\n\n const formats: RuleFormat[] = options.format === 'all' || !options.format\n ? ALL_FORMATS\n : [options.format as RuleFormat];\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard rules\\n'));\n console.log(chalk.dim(`Generating AI protection rules from ${snapshot.tokens.length} locked tokens...\\n`));\n\n for (const format of formats) {\n try {\n const content = generateRules(snapshot, format);\n const filePath = saveRules(projectRoot, format, content, options.append ?? false);\n console.log(chalk.green(' โœ… ') + chalk.white(filePath));\n } catch (error) {\n console.log(chalk.red(' โŒ ') + chalk.white(format) + chalk.dim(`: ${(error as Error).message}`));\n }\n }\n\n console.log();\n console.log(chalk.dim('Your AI coding agents will now protect these design tokens.'));\n console.log(chalk.dim('Supported tools: Cursor, Claude Code, Codex, GitHub Copilot, Cline\\n'));\n}\n","import chalk from 'chalk';\nimport { createSnapshot, saveSnapshot } from '../core/snapshot.js';\n\ninterface SnapshotOptions {\n from?: string;\n}\n\nexport async function snapshotCommand(options: SnapshotOptions): Promise<void> {\n const projectRoot = process.cwd();\n\n console.log(chalk.bold('\\n๐Ÿ›ก๏ธ drift-guard snapshot update\\n'));\n console.log(chalk.dim('Re-scanning project and updating snapshot...\\n'));\n\n const snapshot = await createSnapshot(projectRoot, options.from);\n\n if (snapshot.tokens.length === 0) {\n console.log(chalk.yellow('โš ๏ธ No design tokens found.\\n'));\n return;\n }\n\n const snapshotPath = saveSnapshot(projectRoot, snapshot);\n\n console.log(chalk.green('โœ… Snapshot updated!\\n'));\n console.log(chalk.dim(' File: ') + chalk.white(snapshotPath));\n console.log(chalk.dim(' Tokens: ') + chalk.white(snapshot.tokens.length.toString()));\n console.log(chalk.dim(' Updated: ') + chalk.white(snapshot.createdAt));\n console.log();\n console.log(chalk.dim('๐Ÿ’ก Remember to regenerate rules: ') + chalk.cyan('npx drift-guard rules\\n'));\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\n\n/**\n * Install a pre-commit hook that runs drift-guard check\n */\nexport async function hookInstallCommand(options: { threshold?: string }): Promise<void> {\n const cwd = process.cwd();\n const threshold = options.threshold ?? '10';\n\n // Check if git repo\n if (!fs.existsSync(path.join(cwd, '.git'))) {\n console.error(chalk.red('โœ— Not a git repository. Run this from a git project root.'));\n process.exit(1);\n }\n\n // Detect hook manager\n const hasHusky = fs.existsSync(path.join(cwd, '.husky'));\n const hasLefthook = fs.existsSync(path.join(cwd, 'lefthook.yml')) ||\n fs.existsSync(path.join(cwd, '.lefthook.yml'));\n\n if (hasHusky) {\n // Add to existing husky setup\n const hookFile = path.join(cwd, '.husky', 'pre-commit');\n const hookCommand = `npx drift-guard check --threshold ${threshold} --ci`;\n\n if (fs.existsSync(hookFile)) {\n const existing = fs.readFileSync(hookFile, 'utf-8');\n if (existing.includes('drift-guard')) {\n console.log(chalk.yellow('โš  drift-guard hook already exists in .husky/pre-commit'));\n return;\n }\n fs.appendFileSync(hookFile, `\\n${hookCommand}\\n`);\n } else {\n fs.writeFileSync(hookFile, `#!/usr/bin/env sh\\n. \"$(dirname -- \"$0\")/_/husky.sh\"\\n\\n${hookCommand}\\n`);\n }\n\n console.log(chalk.green('โœ“ Added drift-guard check to .husky/pre-commit'));\n return;\n }\n\n if (hasLefthook) {\n console.log(chalk.yellow('โš  lefthook detected. Add this to your lefthook.yml:'));\n console.log(chalk.cyan(`\npre-commit:\n commands:\n drift-guard:\n run: npx drift-guard check --threshold ${threshold} --ci\n`));\n return;\n }\n\n // No hook manager โ€” install via .git/hooks directly\n const hooksDir = path.join(cwd, '.git', 'hooks');\n const hookFile = path.join(hooksDir, 'pre-commit');\n const hookScript = `#!/usr/bin/env sh\n# drift-guard pre-commit hook\n# Checks for design token drift before commits\n# Use --force flag to bypass: git commit --no-verify\n\nnpx drift-guard check --threshold ${threshold} --ci\n\nif [ $? -ne 0 ]; then\n echo \"\"\n echo \"\\\\033[31mโœ— Design drift detected! Commit blocked.\\\\033[0m\"\n echo \" Run 'npx drift-guard check' for details.\"\n echo \" If changes are intentional, run 'npx drift-guard snapshot update'\"\n echo \" Or use 'git commit --no-verify' to bypass.\"\n exit 1\nfi\n`;\n\n if (!fs.existsSync(hooksDir)) {\n fs.mkdirSync(hooksDir, { recursive: true });\n }\n\n if (fs.existsSync(hookFile)) {\n const existing = fs.readFileSync(hookFile, 'utf-8');\n if (existing.includes('drift-guard')) {\n console.log(chalk.yellow('โš  drift-guard hook already installed'));\n return;\n }\n // Append to existing hook\n fs.appendFileSync(hookFile, `\\n${hookScript}`);\n console.log(chalk.green('โœ“ Appended drift-guard to existing pre-commit hook'));\n } else {\n fs.writeFileSync(hookFile, hookScript);\n // Make executable on Unix\n try {\n fs.chmodSync(hookFile, 0o755);\n } catch {\n // Windows may not support chmod, skip\n }\n console.log(chalk.green('โœ“ Installed drift-guard pre-commit hook'));\n }\n\n console.log(chalk.dim(` Threshold: ${threshold}%`));\n console.log(chalk.dim(' Bypass: git commit --no-verify'));\n}\n\n/**\n * Uninstall the pre-commit hook\n */\nexport async function hookUninstallCommand(): Promise<void> {\n const cwd = process.cwd();\n\n // Check husky\n const huskyHook = path.join(cwd, '.husky', 'pre-commit');\n if (fs.existsSync(huskyHook)) {\n const content = fs.readFileSync(huskyHook, 'utf-8');\n if (content.includes('drift-guard')) {\n const cleaned = content.replace(/\\n?npx drift-guard.*\\n?/g, '\\n');\n fs.writeFileSync(huskyHook, cleaned);\n console.log(chalk.green('โœ“ Removed drift-guard from .husky/pre-commit'));\n return;\n }\n }\n\n // Check .git/hooks\n const gitHook = path.join(cwd, '.git', 'hooks', 'pre-commit');\n if (fs.existsSync(gitHook)) {\n const content = fs.readFileSync(gitHook, 'utf-8');\n if (content.includes('drift-guard')) {\n // If the entire file is our hook, remove it\n if (content.includes('# drift-guard pre-commit hook') && !content.includes('\\n#!/usr/bin/env sh\\n')) {\n fs.unlinkSync(gitHook);\n console.log(chalk.green('โœ“ Removed drift-guard pre-commit hook'));\n } else {\n // Remove just our part\n const cleaned = content.replace(/# drift-guard pre-commit hook[\\s\\S]*?fi\\n?/g, '');\n fs.writeFileSync(gitHook, cleaned);\n console.log(chalk.green('โœ“ Removed drift-guard from pre-commit hook'));\n }\n return;\n }\n }\n\n console.log(chalk.yellow('โš  No drift-guard hook found to uninstall'));\n}\n","import { Command } from 'commander';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport fg from 'fast-glob';\nimport { loadSnapshot, loadConfig } from '../core/snapshot.js';\nimport { detectDrift } from '../core/drift.js';\nimport {\n syncToStitch,\n syncFromStitch,\n applySyncChanges,\n} from '../core/sync.js';\nimport { parseCss, extractCssVariables } from '../parsers/css-parser.js';\nimport { parseHtml, extractStyleBlocks, extractTailwindConfig } from '../parsers/html-parser.js';\nimport type { DesignToken, SyncDirection } from '../types/index.js';\n\nexport const syncCommand = new Command('sync')\n .description('Synchronize design tokens between Stitch and your codebase')\n .requiredOption(\n '-d, --direction <direction>',\n 'Sync direction: to-stitch (push code changes to Stitch) or to-code (pull Stitch changes)',\n )\n .option(\n '--stitch-html <path>',\n 'Path to Stitch HTML file (for to-code direction)',\n )\n .option(\n '--stitch-project <id>',\n 'Stitch project ID (for to-stitch direction)',\n )\n .option(\n '--stitch-screen <id>',\n 'Stitch screen ID (for to-stitch direction)',\n )\n .option('--apply', 'Auto-apply changes: full HTML replacement + CSS patching (to-code only)', false)\n .option('--target <path>', 'Target local HTML file to replace with Stitch version (to-code --apply)')\n .option('--dry-run', 'Preview changes without applying', false)\n .option('--json', 'Output as JSON', false)\n .action(async (options) => {\n const projectRoot = process.cwd();\n const direction = options.direction as SyncDirection;\n\n if (direction !== 'to-stitch' && direction !== 'to-code') {\n console.error(\n 'โŒ Invalid direction. Use \"to-stitch\" or \"to-code\".',\n );\n process.exit(1);\n }\n\n if (direction === 'to-stitch') {\n await handleToStitch(projectRoot, options);\n } else {\n await handleToCode(projectRoot, options);\n }\n });\n\n/**\n * Handle to-stitch direction:\n * Compare current code against snapshot โ†’ generate Stitch edit prompt\n */\nasync function handleToStitch(\n projectRoot: string,\n options: { dryRun: boolean; json: boolean; stitchProject?: string; stitchScreen?: string },\n): Promise<void> {\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.error('โŒ No snapshot found. Run \"drift-guard init\" first.');\n process.exit(1);\n }\n\n // Detect drift (code vs snapshot)\n const report = await detectDrift(projectRoot, snapshot, 0);\n\n if (report.items.length === 0) {\n console.log('โœ… No changes detected. Stitch and code are in sync!');\n return;\n }\n\n const result = syncToStitch(report.items);\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n // Pretty output\n console.log('');\n console.log('๐Ÿ”„ drift-guard sync โ†’ to-stitch');\n console.log(` ${result.changes.length} change(s) detected\\n`);\n\n for (const change of result.changes) {\n const icon =\n change.action === 'update' ? 'โœ๏ธ' :\n change.action === 'add' ? 'โž•' : '๐Ÿ—‘๏ธ';\n console.log(\n ` ${icon} ${change.property}: ${change.fromValue || '(none)'} โ†’ ${change.toValue || '(removed)'}`,\n );\n }\n\n console.log('');\n console.log('โ”'.repeat(60));\n console.log('๐Ÿ“‹ Stitch edit_screens prompt:');\n console.log('โ”'.repeat(60));\n console.log('');\n console.log(result.prompt);\n console.log('');\n console.log('โ”'.repeat(60));\n\n const projectId = options.stitchProject;\n const screenId = options.stitchScreen;\n\n if (projectId && screenId) {\n console.log('');\n console.log('๐Ÿ’ก To apply these changes in Stitch, run:');\n console.log('');\n console.log(` edit_screens({`);\n console.log(` projectId: \"${projectId}\",`);\n console.log(` selectedScreenIds: [\"${screenId}\"],`);\n console.log(` prompt: \"<the prompt above>\"`);\n console.log(` })`);\n } else {\n console.log('');\n console.log(\n '๐Ÿ’ก Copy the prompt above and use it with Stitch MCP edit_screens',\n );\n console.log(\n ' or pass --stitch-project and --stitch-screen for a ready-to-use call.',\n );\n }\n}\n\n/**\n * Handle to-code direction:\n * Compare Stitch HTML against snapshot โ†’ generate CSS patch โ†’ optionally apply.\n *\n * With --apply: Stitch HTML is the SOURCE OF TRUTH.\n * 1. Replace the local Stitch HTML file entirely (fixes text/layout/content diffs)\n * 2. Patch design tokens in other CSS files\n * 3. Update snapshot\n * 4. Verify 0% drift\n */\nasync function handleToCode(\n projectRoot: string,\n options: { dryRun: boolean; json: boolean; apply: boolean; stitchHtml?: string; target?: string },\n): Promise<void> {\n const snapshot = loadSnapshot(projectRoot);\n if (!snapshot) {\n console.error('โŒ No snapshot found. Run \"drift-guard init\" first.');\n process.exit(1);\n }\n\n // Get Stitch HTML path (new/downloaded version)\n const config = loadConfig(projectRoot);\n const stitchHtmlPath =\n options.stitchHtml ?? config.stitch?.htmlPath;\n\n if (!stitchHtmlPath) {\n console.error(\n 'โŒ No Stitch HTML file specified.',\n );\n console.error(\n ' Use --stitch-html <path> or set stitch.htmlPath in .design-guard/config.json',\n );\n process.exit(1);\n }\n\n const absPath = path.resolve(projectRoot, stitchHtmlPath);\n if (!fs.existsSync(absPath)) {\n console.error(`โŒ Stitch HTML file not found: ${absPath}`);\n process.exit(1);\n }\n\n // Parse Stitch HTML for tokens\n const htmlContent = fs.readFileSync(absPath, 'utf-8');\n const stitchTokens: DesignToken[] = [];\n\n const htmlTokens = parseHtml(htmlContent, stitchHtmlPath);\n stitchTokens.push(...htmlTokens);\n\n const styleBlocks = extractStyleBlocks(htmlContent);\n for (const block of styleBlocks) {\n stitchTokens.push(...parseCss(block, stitchHtmlPath));\n stitchTokens.push(...extractCssVariables(block, stitchHtmlPath));\n }\n\n const twTokens = extractTailwindConfig(htmlContent, stitchHtmlPath);\n stitchTokens.push(...twTokens);\n\n // Compare Stitch tokens against snapshot\n const result = syncFromStitch(stitchTokens, snapshot.tokens);\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n // === FULL SYNC (--apply): Stitch is source of truth ===\n if (options.apply && !options.dryRun) {\n console.log('');\n console.log('๐Ÿ”„ drift-guard sync โ†’ to-code (FULL SYNC)');\n console.log(' Stitch HTML is the source of truth.\\n');\n\n // Step 1: Find local Stitch HTML files to replace\n const targetPath = options.target;\n const localStitchFiles = await findLocalStitchHtml(projectRoot, absPath, targetPath);\n let htmlReplaced = false;\n\n if (localStitchFiles.length > 0) {\n console.log('๐Ÿ“„ Step 1: Full HTML replacement');\n for (const localFile of localStitchFiles) {\n const localAbsPath = path.resolve(projectRoot, localFile);\n const localContent = fs.readFileSync(localAbsPath, 'utf-8');\n\n if (localContent !== htmlContent) {\n fs.writeFileSync(localAbsPath, htmlContent, 'utf-8');\n console.log(` โœ… Replaced: ${localFile} (Stitch โ†’ local)`);\n htmlReplaced = true;\n } else {\n console.log(` โ„น๏ธ Already identical: ${localFile}`);\n }\n }\n } else {\n console.log('๐Ÿ“„ Step 1: No local Stitch HTML found to replace.');\n console.log(' ๐Ÿ’ก Use --target <path> to specify the target file.');\n }\n\n // Step 2: Patch design tokens in CSS files\n if (result.changes.length > 0) {\n console.log(`\\n๐Ÿ“ Step 2: Patching ${result.changes.length} design token(s) in CSS files`);\n for (const change of result.changes) {\n const icon =\n change.action === 'update' ? 'โœ๏ธ' :\n change.action === 'add' ? 'โž•' : '๐Ÿ—‘๏ธ';\n console.log(\n ` ${icon} ${change.property}: ${change.fromValue || '(none)'} โ†’ ${change.toValue || '(removed)'}`,\n );\n }\n\n const cssFileMap = await loadProjectCssFiles(projectRoot, config);\n const { modifiedFiles, appliedCount } = applySyncChanges(result.changes, cssFileMap);\n\n if (appliedCount > 0) {\n for (const [file, content] of modifiedFiles) {\n const fullPath = path.resolve(projectRoot, file);\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(` โœ… Patched: ${file}`);\n }\n }\n } else {\n console.log('\\n๐Ÿ“ Step 2: No design token changes โ€” tokens already match.');\n }\n\n // Step 3: Update snapshot to reflect new state\n if (htmlReplaced || result.changes.length > 0) {\n console.log('\\n๐Ÿ“ธ Step 3: Updating snapshot...');\n\n const { execSync } = await import('node:child_process');\n try {\n execSync(\n `node \"${path.resolve(projectRoot, '..', 'drift-guard', 'dist', 'cli', 'index.js')}\" snapshot update`,\n { cwd: projectRoot, stdio: 'pipe' },\n );\n } catch {\n // Try the global command\n try {\n execSync('npx drift-guard snapshot update', {\n cwd: projectRoot,\n stdio: 'pipe',\n });\n } catch {\n console.log(' โš ๏ธ Could not auto-update snapshot. Run: drift-guard snapshot update');\n }\n }\n console.log(' โœ… Snapshot updated.');\n }\n\n // Step 4: Verify full sync\n console.log('\\n๐Ÿ” Step 4: Verifying full sync...');\n\n // Compare the Stitch source HTML with the local file\n if (localStitchFiles.length > 0) {\n let allMatch = true;\n for (const localFile of localStitchFiles) {\n const localAbsPath = path.resolve(projectRoot, localFile);\n const localContent = fs.readFileSync(localAbsPath, 'utf-8');\n if (localContent === htmlContent) {\n console.log(` โœ… ${localFile} โ€” 100% identical to Stitch`);\n } else {\n console.log(` โŒ ${localFile} โ€” content differs!`);\n allMatch = false;\n }\n }\n if (allMatch) {\n console.log('\\n ๐ŸŽ‰ FULL SYNC VERIFIED โ€” Stitch and code are 100% identical!');\n }\n }\n } else if (result.changes.length === 0) {\n console.log('โœ… Stitch and code design tokens are already in sync!');\n console.log(' ๐Ÿ’ก Use --apply for full HTML content sync (text, layout, structure).');\n } else {\n // Show token diff and patch\n console.log('');\n console.log('๐Ÿ”„ drift-guard sync โ†’ to-code');\n console.log(` ${result.changes.length} design token change(s) from Stitch\\n`);\n\n for (const change of result.changes) {\n const icon =\n change.action === 'update' ? 'โœ๏ธ' :\n change.action === 'add' ? 'โž•' : '๐Ÿ—‘๏ธ';\n console.log(\n ` ${icon} ${change.property}: ${change.fromValue || '(none)'} โ†’ ${change.toValue || '(removed)'}`,\n );\n }\n\n if (result.patchFile) {\n console.log('');\n console.log('โ”'.repeat(60));\n console.log('๐Ÿ“‹ CSS patch to apply:');\n console.log('โ”'.repeat(60));\n console.log('');\n console.log(result.patchFile);\n console.log('');\n console.log('โ”'.repeat(60));\n\n if (!options.dryRun) {\n const patchPath = path.join(\n projectRoot,\n '.design-guard',\n 'sync-patch.css',\n );\n fs.writeFileSync(patchPath, result.patchFile, 'utf-8');\n console.log(`\\n๐Ÿ’พ Patch saved to: ${patchPath}`);\n console.log(' Use --apply for full HTML content + token sync.');\n } else {\n console.log('\\n๐Ÿ” Dry run โ€” no files written.');\n }\n }\n }\n}\n\n/**\n * Find local Stitch HTML files that should be replaced.\n * Searches for stitch-design.html or files matching --target.\n */\nasync function findLocalStitchHtml(\n projectRoot: string,\n sourceAbsPath: string,\n targetPath?: string,\n): Promise<string[]> {\n if (targetPath) {\n return [targetPath];\n }\n\n // Search for stitch-design.html files in the project\n const stitchFiles = await fg(\n ['**/stitch-design.html', '**/stitch*.html'],\n {\n cwd: projectRoot,\n ignore: ['node_modules/**', 'dist/**', 'build/**'],\n absolute: false,\n },\n );\n\n // Exclude the source file itself\n const sourceRelative = path.relative(projectRoot, sourceAbsPath);\n return stitchFiles.filter(f => f !== sourceRelative);\n}\n\n/**\n * Load all CSS files from the project into a map\n */\nasync function loadProjectCssFiles(\n projectRoot: string,\n config: { cssFiles: string[]; ignore: string[] },\n): Promise<Map<string, string>> {\n const cssFileMap = new Map<string, string>();\n\n for (const pattern of config.cssFiles) {\n const matches = await fg(pattern, {\n cwd: projectRoot,\n ignore: config.ignore,\n absolute: false,\n });\n\n for (const file of matches) {\n const absPath = path.resolve(projectRoot, file);\n if (fs.existsSync(absPath)) {\n cssFileMap.set(file, fs.readFileSync(absPath, 'utf-8'));\n }\n }\n }\n\n // Also include HTML files that might have Tailwind config\n const htmlPatterns = ['**/*.html'];\n const htmlIgnore = ['node_modules/**', 'dist/**', 'build/**', '.next/**'];\n const htmlMatches = await fg(htmlPatterns, {\n cwd: projectRoot,\n ignore: htmlIgnore,\n absolute: false,\n });\n\n for (const file of htmlMatches) {\n const absPath = path.resolve(projectRoot, file);\n if (fs.existsSync(absPath)) {\n cssFileMap.set(file, fs.readFileSync(absPath, 'utf-8'));\n }\n }\n\n return cssFileMap;\n}\n"],"mappings":"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","names":["Command","chalk","hookFile","chalk","chalk","chalk","chalk","chalk","chalk","fs","path","chalk","hookFile","path","fs","path","fs","Command","chalk"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stayicon/drift-guard",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Protect your UI from AI coding agents' design drift. Detect, prevent, and lock design tokens during AI-assisted development.",
5
5
  "keywords": [
6
6
  "design-drift",