@bastani/atomic 0.9.11-alpha.6 → 0.9.11-alpha.7

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.
Files changed (140) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/package.json +1 -1
  5. package/dist/builtin/web-access/package.json +1 -1
  6. package/dist/builtin/workflows/package.json +1 -1
  7. package/dist/builtin/workflows/skills/impeccable/SKILL.md +33 -129
  8. package/dist/builtin/workflows/skills/impeccable/agents/impeccable_asset_producer.toml +11 -3
  9. package/dist/builtin/workflows/skills/impeccable/agents/impeccable_documenter.toml +26 -0
  10. package/dist/builtin/workflows/skills/impeccable/agents/impeccable_finish_reviewer.toml +35 -0
  11. package/dist/builtin/workflows/skills/impeccable/reference/android.md +1 -1
  12. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +72 -189
  13. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +10 -9
  14. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +2 -2
  15. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +19 -108
  16. package/dist/builtin/workflows/skills/impeccable/reference/clarify.md +59 -253
  17. package/dist/builtin/workflows/skills/impeccable/reference/colorize.md +51 -222
  18. package/dist/builtin/workflows/skills/impeccable/reference/craft-floor.md +45 -0
  19. package/dist/builtin/workflows/skills/impeccable/reference/craft.md +3 -121
  20. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +28 -20
  21. package/dist/builtin/workflows/skills/impeccable/reference/degraded/asset-producer.md +97 -0
  22. package/dist/builtin/workflows/skills/impeccable/reference/degraded/documenter.md +23 -0
  23. package/dist/builtin/workflows/skills/impeccable/reference/degraded/finish-reviewer.md +32 -0
  24. package/dist/builtin/workflows/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  25. package/dist/builtin/workflows/skills/impeccable/reference/delight.md +47 -279
  26. package/dist/builtin/workflows/skills/impeccable/reference/distill.md +2 -2
  27. package/dist/builtin/workflows/skills/impeccable/reference/doctor.md +53 -0
  28. package/dist/builtin/workflows/skills/impeccable/reference/document.md +60 -73
  29. package/dist/builtin/workflows/skills/impeccable/reference/harden.md +1 -12
  30. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +17 -4
  31. package/dist/builtin/workflows/skills/impeccable/reference/init.md +64 -163
  32. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +1 -1
  33. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +52 -153
  34. package/dist/builtin/workflows/skills/impeccable/reference/live.md +44 -32
  35. package/dist/builtin/workflows/skills/impeccable/reference/new-work.md +105 -0
  36. package/dist/builtin/workflows/skills/impeccable/reference/{product.md → operate.md} +6 -5
  37. package/dist/builtin/workflows/skills/impeccable/reference/optimize.md +4 -4
  38. package/dist/builtin/workflows/skills/impeccable/reference/overdrive.md +1 -4
  39. package/dist/builtin/workflows/skills/impeccable/reference/polish.md +68 -212
  40. package/dist/builtin/workflows/skills/impeccable/reference/quieter.md +3 -3
  41. package/dist/builtin/workflows/skills/impeccable/reference/routing.md +18 -0
  42. package/dist/builtin/workflows/skills/impeccable/reference/shape.md +38 -144
  43. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +48 -269
  44. package/dist/builtin/workflows/skills/impeccable/reference/visualize.md +38 -0
  45. package/dist/builtin/workflows/skills/impeccable/scripts/command-metadata.json +1 -1
  46. package/dist/builtin/workflows/skills/impeccable/scripts/concept-seed.mjs +584 -0
  47. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +117 -9
  48. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +486 -59
  49. package/dist/builtin/workflows/skills/impeccable/scripts/critique-storage.mjs +16 -45
  50. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +96 -10
  51. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +143 -26
  52. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +181 -12
  53. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +3187 -182
  54. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +102 -7
  55. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +282 -70
  56. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +183 -15
  57. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +22 -7
  58. package/dist/builtin/workflows/skills/impeccable/scripts/detector/findings.mjs +7 -1
  59. package/dist/builtin/workflows/skills/impeccable/scripts/detector/node/file-system.mjs +16 -2
  60. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +155 -42
  61. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +2988 -155
  62. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/constants.mjs +11 -0
  63. package/dist/builtin/workflows/skills/impeccable/scripts/doctor.mjs +336 -0
  64. package/dist/builtin/workflows/skills/impeccable/scripts/generate-image.mjs +232 -0
  65. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +90 -10
  66. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +410 -78
  67. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +25 -8
  68. package/dist/builtin/workflows/skills/impeccable/scripts/lib/artifact-schema.mjs +93 -0
  69. package/dist/builtin/workflows/skills/impeccable/scripts/lib/composition-catalog.mjs +165 -0
  70. package/dist/builtin/workflows/skills/impeccable/scripts/lib/concept-catalog.mjs +329 -0
  71. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +20 -5
  72. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-paths.mjs +16 -8
  73. package/dist/builtin/workflows/skills/impeccable/scripts/lib/provider.mjs +1 -0
  74. package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness-deep.mjs +455 -0
  75. package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness-notice.mjs +169 -0
  76. package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness.mjs +457 -0
  77. package/dist/builtin/workflows/skills/impeccable/scripts/lib/surface-briefs.mjs +151 -0
  78. package/dist/builtin/workflows/skills/impeccable/scripts/lib/target-slug.mjs +33 -0
  79. package/dist/builtin/workflows/skills/impeccable/scripts/lib/template-extensions.mjs +146 -0
  80. package/dist/builtin/workflows/skills/impeccable/scripts/live/completion.mjs +10 -1
  81. package/dist/builtin/workflows/skills/impeccable/scripts/live/event-validation.mjs +15 -0
  82. package/dist/builtin/workflows/skills/impeccable/scripts/live/generation-preflight.mjs +149 -0
  83. package/dist/builtin/workflows/skills/impeccable/scripts/live/poll-lanes.mjs +14 -0
  84. package/dist/builtin/workflows/skills/impeccable/scripts/live/session-store.mjs +109 -32
  85. package/dist/builtin/workflows/skills/impeccable/scripts/live/source-lock.mjs +105 -0
  86. package/dist/builtin/workflows/skills/impeccable/scripts/live/source-search.mjs +105 -0
  87. package/dist/builtin/workflows/skills/impeccable/scripts/live/sveltekit-adapter.mjs +8 -6
  88. package/dist/builtin/workflows/skills/impeccable/scripts/live/tanstack-adapter.mjs +280 -0
  89. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +208 -68
  90. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +563 -112
  91. package/dist/builtin/workflows/skills/impeccable/scripts/live-commit-manual-edits.mjs +3 -0
  92. package/dist/builtin/workflows/skills/impeccable/scripts/live-inject.mjs +198 -13
  93. package/dist/builtin/workflows/skills/impeccable/scripts/live-insert.mjs +24 -6
  94. package/dist/builtin/workflows/skills/impeccable/scripts/live-manual-edit-evidence.mjs +6 -1
  95. package/dist/builtin/workflows/skills/impeccable/scripts/live-poll.mjs +44 -14
  96. package/dist/builtin/workflows/skills/impeccable/scripts/live-server.mjs +398 -33
  97. package/dist/builtin/workflows/skills/impeccable/scripts/live-status.mjs +9 -5
  98. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +81 -67
  99. package/dist/builtin/workflows/skills/impeccable/scripts/live.mjs +7 -2
  100. package/dist/builtin/workflows/skills/impeccable/scripts/palette.mjs +76 -81
  101. package/dist/builtin/workflows/skills/impeccable/scripts/pin.mjs +2 -2
  102. package/dist/builtin/workflows/skills/impeccable/scripts/serve-question.mjs +890 -0
  103. package/dist/builtin/workflows/skills/impeccable/scripts/surface-brief.mjs +74 -0
  104. package/dist/builtin/workflows/src/extension/index.bundle.mjs +201 -22
  105. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +23 -9
  106. package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +1 -0
  107. package/dist/builtin/workflows/src/runs/foreground/postmortem-stage-chat.ts +20 -5
  108. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +10 -0
  109. package/dist/builtin/workflows/src/runs/foreground/stage-delivery-activity.ts +86 -0
  110. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +6 -2
  111. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +19 -13
  112. package/dist/builtin/workflows/src/runs/foreground/stage-runner-send-user-message.ts +8 -0
  113. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +16 -0
  114. package/dist/builtin/workflows/src/tui/stage-chat-view-delivery-activity.ts +121 -0
  115. package/dist/builtin/workflows/src/tui/stage-chat-view-live-events.ts +42 -0
  116. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +16 -0
  117. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +4 -0
  118. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/chat-session-host-actions.js +2 -14
  120. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  121. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/chat-session-host-events.js +9 -0
  123. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  124. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts +25 -0
  125. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  126. package/dist/modes/interactive/components/chat-session-host-runtime.js +53 -0
  127. package/dist/modes/interactive/components/chat-session-host-runtime.js.map +1 -1
  128. package/dist/modes/interactive/components/chat-session-host.d.ts +13 -0
  129. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/chat-session-host.js +22 -1
  131. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  132. package/dist/modes/interactive/interactive-agent-events.js +9 -3
  133. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  134. package/docs/tui.md +7 -2
  135. package/docs/workflows.md +1 -1
  136. package/npm-shrinkwrap.json +23 -23
  137. package/package.json +2 -2
  138. package/dist/builtin/workflows/skills/impeccable/reference/brand.md +0 -108
  139. package/dist/builtin/workflows/skills/impeccable/reference/codex.md +0 -105
  140. package/dist/builtin/workflows/skills/impeccable/reference/interaction-design.md +0 -189
@@ -13,6 +13,8 @@
13
13
  * node hook-admin.mjs ignore-file <glob> # append to ignoreFiles
14
14
  * node hook-admin.mjs ignore-value <rule> <value> # append to shared ignoreValues
15
15
  * node hook-admin.mjs ignore-value <rule> <value> --local
16
+ * node hook-admin.mjs ignore-value <rule> "*" --file <glob> # rule off in <glob> only
17
+ * node hook-admin.mjs ignore-value <rule> "*" # refused: scope it or use ignore-rule
16
18
  * node hook-admin.mjs reset # remove all config + cache
17
19
  *
18
20
  * Designed to be invoked by the LLM from the reference/hooks.md flow.
@@ -43,6 +45,26 @@ const IMPECCABLE_HOOK_COMMAND_MARKERS = [
43
45
  ];
44
46
  const TIMEOUT_SECONDS = 5;
45
47
  const STATUS_MESSAGE = 'Checking UI changes';
48
+ // The Stop deep pass scans every UI file touched in the session with the full
49
+ // rule set, so it gets a longer budget than the per-edit pass. Only Claude
50
+ // Code and Codex dispatch a native Stop hook event, so only those manifests
51
+ // carry the entry. Keep these shapes in sync with
52
+ // scripts/lib/transformers/hooks.js in the repo.
53
+ const STOP_TIMEOUT_SECONDS = 30;
54
+ const STOP_STATUS_MESSAGE = 'Design deep pass';
55
+
56
+ function stopManifestEntry(command) {
57
+ return {
58
+ hooks: [
59
+ {
60
+ type: 'command',
61
+ command,
62
+ timeout: STOP_TIMEOUT_SECONDS,
63
+ statusMessage: STOP_STATUS_MESSAGE,
64
+ },
65
+ ],
66
+ };
67
+ }
46
68
 
47
69
  const HOOK_MANIFEST_TARGETS = [
48
70
  {
@@ -51,7 +73,7 @@ const HOOK_MANIFEST_TARGETS = [
51
73
  destRel: '.claude/settings.local.json',
52
74
  sharedDestRel: '.claude/settings.json',
53
75
  manifest: () => ({
54
- description: 'Impeccable design detector: runs after Edit/Write/MultiEdit on UI files and surfaces findings as system reminders.',
76
+ description: 'Impeccable design detector: immediate-tier checks after Edit/Write/MultiEdit on UI files, full-rule deep pass on Stop.',
55
77
  hooks: {
56
78
  PostToolUse: [
57
79
  {
@@ -66,6 +88,7 @@ const HOOK_MANIFEST_TARGETS = [
66
88
  ],
67
89
  },
68
90
  ],
91
+ Stop: [stopManifestEntry('node "${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs"')],
69
92
  },
70
93
  }),
71
94
  },
@@ -88,6 +111,7 @@ const HOOK_MANIFEST_TARGETS = [
88
111
  ],
89
112
  },
90
113
  ],
114
+ Stop: [stopManifestEntry('node ".agents/skills/impeccable/scripts/hook.mjs"')],
91
115
  },
92
116
  }),
93
117
  },
@@ -247,7 +271,10 @@ function mergeIgnoreValueEntries(existing, incoming) {
247
271
  }
248
272
 
249
273
  function ignoreValueEntryKey(entry) {
250
- const files = Array.isArray(entry.files) && entry.files.length > 0 ? entry.files.join('\x1f') : '';
274
+ // Sorted: a file scope is a set. Comparing stored order made an on-disk scope
275
+ // miss the sorted argv form, so a re-add duplicated the entry and a remove
276
+ // silently failed. Every key that hashes `files` must sort — there are four.
277
+ const files = Array.isArray(entry.files) && entry.files.length > 0 ? [...entry.files].sort().join('\x1f') : '';
251
278
  return `${entry.rule}\0${entry.value}\0${files}`;
252
279
  }
253
280
 
@@ -265,7 +292,14 @@ function statusReport(cwd) {
265
292
  if (info.exists) return relPath;
266
293
  return `${relPath} (${absent})`;
267
294
  };
268
- const ignoreValues = cfg.ignoreValues.map((entry) => `${entry.rule}=${entry.value}`);
295
+ // Show the file scope. Dropping it rendered a file-scoped entry as
296
+ // `design-system-font-size=*`, which reads as the project-wide wildcard this
297
+ // command refuses — the opposite of what is on disk. Matches the
298
+ // `rule=value [files]` shape `impeccable ignores list` already prints.
299
+ const ignoreValues = cfg.ignoreValues.map((entry) => {
300
+ const scope = Array.isArray(entry.files) && entry.files.length ? ` [${entry.files.join(', ')}]` : '';
301
+ return `${entry.rule}=${entry.value}${scope}`;
302
+ });
269
303
 
270
304
  const lines = [
271
305
  `Impeccable design hook`,
@@ -514,14 +548,29 @@ function addIgnoreFile(cwd, glob) {
514
548
  return `Added "${glob}" to detector.ignoreFiles. Current: ${config.ignoreFiles.join(', ')}`;
515
549
  }
516
550
 
551
+ // An empty glob used to be dropped by filter(Boolean), so `--file=` reported
552
+ // success and wrote an entry with no files: the user asked to scope a rule to one
553
+ // file and silently got the project-wide suppression instead. Refuse it.
554
+ function requireGlob(raw, flag) {
555
+ const glob = String(raw ?? '').trim();
556
+ if (!glob) throw new Error(`${flag} requires a non-empty glob`);
557
+ // A following flag is not a glob. `--file --reason "why"` consumed `--reason`
558
+ // as the scope and left the reason text to fold into the value, storing
559
+ // value="* why" files=["--reason"] and reporting success. Same silent-no-op
560
+ // class as an unknown flag folding into the value; refuse it the same way.
561
+ if (glob.startsWith('--')) throw new Error(`${flag} requires a glob, got the flag ${glob}`);
562
+ return glob;
563
+ }
564
+
517
565
  function parseIgnoreValueArgs(args) {
518
566
  const positionals = [];
567
+ const files = [];
519
568
  let shared = false;
520
569
  let local = false;
521
570
  let reason = '';
522
571
 
523
572
  for (let i = 0; i < args.length; i++) {
524
- const arg = args[i];
573
+ const arg = String(args[i] || '');
525
574
  if (arg === '--shared') {
526
575
  shared = true;
527
576
  } else if (arg === '--local') {
@@ -532,8 +581,20 @@ function parseIgnoreValueArgs(args) {
532
581
  chunks.push(args[++i]);
533
582
  }
534
583
  reason = chunks.join(' ').trim();
535
- } else if (String(arg).startsWith('--reason=')) {
536
- reason = String(arg).slice('--reason='.length).trim();
584
+ } else if (arg.startsWith('--reason=')) {
585
+ reason = arg.slice('--reason='.length).trim();
586
+ } else if (arg === '--file' || arg === '--files') {
587
+ if (i + 1 >= args.length) throw new Error(`${arg} requires a glob`);
588
+ files.push(requireGlob(args[++i], arg));
589
+ } else if (arg.startsWith('--file=')) {
590
+ files.push(requireGlob(arg.slice('--file='.length), '--file'));
591
+ } else if (arg.startsWith('--files=')) {
592
+ files.push(requireGlob(arg.slice('--files='.length), '--files'));
593
+ } else if (arg.startsWith('--')) {
594
+ // Otherwise a typo folds into the value: `ignore-value overused-font Inter
595
+ // --shard` stored the value "inter --shard", which matches no finding, and
596
+ // reported success. Matches `impeccable ignores add-value`.
597
+ throw new Error(`Unknown ignore-value flag: ${arg}`);
537
598
  } else {
538
599
  positionals.push(arg);
539
600
  }
@@ -543,6 +604,9 @@ function parseIgnoreValueArgs(args) {
543
604
  return {
544
605
  rule: String(rule || '').trim().toLowerCase(),
545
606
  value: normalizeIgnoreValue(valueParts.join(' ')),
607
+ // Sorted: the dedup key compares the files array, so an unsorted scope made
608
+ // `--file b.css --file a.css` a different entry from `--file a.css --file b.css`.
609
+ files: Array.from(new Set(files.filter(Boolean))).sort(),
546
610
  shared,
547
611
  local,
548
612
  reason,
@@ -559,10 +623,24 @@ function addIgnoreValue(cwd, args) {
559
623
  throw new Error('Pass only one scope flag: --shared or --local');
560
624
  }
561
625
 
626
+ // A bare `*` would suppress the rule everywhere, which is ignore-rule's job and
627
+ // not what a finding in one file justifies. detector.ignoreValues honours a
628
+ // `files` scope, so require one — matching `impeccable ignores add-value`.
629
+ if (parsed.value === '*' && parsed.files.length === 0) {
630
+ // `ignore-rule overused-font` refuses on its own without --all-values, so
631
+ // naming the bare form here would hand the user a second error.
632
+ const projectWide = parsed.rule === 'overused-font'
633
+ ? `${IMPECCABLE_COMMAND} hooks ignore-rule ${parsed.rule} --all-values`
634
+ : `${IMPECCABLE_COMMAND} hooks ignore-rule ${parsed.rule}`;
635
+ throw new Error(`Wildcard value ignores must be scoped with --file <glob>, e.g. ${IMPECCABLE_COMMAND} hooks ignore-value design-system-font-size "*" --file "src/widget.js". To suppress the rule project-wide use ${projectWide}.`);
636
+ }
637
+
562
638
  const local = parsed.local;
563
639
  const config = mergeDetectorConfig(readRawDetectorConfig(cwd, { local }));
564
- const key = `${parsed.rule}\0${parsed.value}`;
565
- const existing = config.ignoreValues.find((entry) => `${entry.rule}\0${entry.value}` === key);
640
+ // Key on the file scope too: the same rule/value legitimately appears more than
641
+ // once with different scopes, and a rule+value-only key overwrote them.
642
+ const key = ignoreValueEntryKey({ rule: parsed.rule, value: parsed.value, files: parsed.files });
643
+ const existing = config.ignoreValues.find((entry) => ignoreValueEntryKey(entry) === key);
566
644
 
567
645
  if (existing) {
568
646
  if (parsed.reason) existing.reason = parsed.reason;
@@ -570,15 +648,17 @@ function addIgnoreValue(cwd, args) {
570
648
  const entry = {
571
649
  rule: parsed.rule,
572
650
  value: parsed.value,
573
- createdAt: new Date().toISOString(),
574
651
  };
652
+ if (parsed.files.length) entry.files = parsed.files;
653
+ entry.createdAt = new Date().toISOString();
575
654
  if (parsed.reason) entry.reason = parsed.reason;
576
655
  config.ignoreValues.push(entry);
577
656
  }
578
657
 
579
658
  const target = writeDetectorConfig(cwd, config, { local });
580
659
  const scope = local ? 'local detector.ignoreValues' : 'shared detector.ignoreValues';
581
- return `Added ${parsed.rule}=${parsed.value} to ${scope} (${path.relative(cwd, target) || target}).`;
660
+ const scopeSuffix = parsed.files.length ? ` scoped to ${parsed.files.join(', ')}` : '';
661
+ return `Added ${parsed.rule}=${parsed.value}${scopeSuffix} to ${scope} (${path.relative(cwd, target) || target}).`;
582
662
  }
583
663
 
584
664
  function reset(cwd) {