@bastani/atomic 0.9.0-alpha.3 → 0.9.0-alpha.4

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 (84) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/package.json +1 -1
  5. package/dist/builtin/subagents/package.json +1 -1
  6. package/dist/builtin/web-access/package.json +1 -1
  7. package/dist/builtin/workflows/CHANGELOG.md +17 -0
  8. package/dist/builtin/workflows/README.md +12 -12
  9. package/dist/builtin/workflows/builtin/goal-prompts.ts +8 -0
  10. package/dist/builtin/workflows/builtin/goal-runner.ts +96 -1
  11. package/dist/builtin/workflows/builtin/goal-types.ts +2 -0
  12. package/dist/builtin/workflows/builtin/goal.d.ts +3 -0
  13. package/dist/builtin/workflows/builtin/goal.ts +12 -1
  14. package/dist/builtin/workflows/builtin/index.d.ts +8 -8
  15. package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +359 -0
  16. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +254 -352
  17. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +256 -414
  18. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +272 -0
  19. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +58 -68
  20. package/dist/builtin/workflows/builtin/open-claude-design.d.ts +5 -9
  21. package/dist/builtin/workflows/builtin/open-claude-design.ts +14 -26
  22. package/dist/builtin/workflows/package.json +1 -1
  23. package/dist/builtin/workflows/skills/impeccable/SKILL.md +14 -23
  24. package/dist/builtin/workflows/skills/impeccable/reference/brand.md +2 -2
  25. package/dist/builtin/workflows/skills/impeccable/reference/live.md +25 -4
  26. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +1 -1
  27. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +724 -29
  28. package/dist/builtin/workflows/skills/impeccable/scripts/critique-storage.mjs +1 -1
  29. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +219 -7
  30. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +57 -11
  31. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +750 -0
  32. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +648 -53
  33. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns.mjs +7 -0
  34. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +29 -4
  35. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +44 -11
  36. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +29 -0
  37. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +27 -1
  38. package/dist/builtin/workflows/skills/impeccable/scripts/detector/node/file-system.mjs +1 -1
  39. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +29 -0
  40. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +401 -46
  41. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/inline-ignores.mjs +148 -0
  42. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +6 -6
  43. package/dist/builtin/workflows/skills/impeccable/scripts/{design-parser.mjs → lib/design-parser.mjs} +8 -1
  44. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +638 -0
  45. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-paths.mjs +128 -0
  46. package/dist/builtin/workflows/skills/impeccable/scripts/{is-generated.mjs → lib/is-generated.mjs} +2 -2
  47. package/dist/builtin/workflows/skills/impeccable/scripts/lib/target-args.mjs +42 -0
  48. package/dist/builtin/workflows/skills/impeccable/scripts/live/browser-script-parts.mjs +49 -0
  49. package/dist/builtin/workflows/skills/impeccable/scripts/{live-completion.mjs → live/completion.mjs} +1 -0
  50. package/dist/builtin/workflows/skills/impeccable/scripts/{live-event-validation.mjs → live/event-validation.mjs} +6 -5
  51. package/dist/builtin/workflows/skills/impeccable/scripts/live/manual-apply.mjs +939 -0
  52. package/dist/builtin/workflows/skills/impeccable/scripts/live/manual-edit-routes.mjs +357 -0
  53. package/dist/builtin/workflows/skills/impeccable/scripts/{live-manual-edits-buffer.mjs → live/manual-edits-buffer.mjs} +1 -1
  54. package/dist/builtin/workflows/skills/impeccable/scripts/{live-session-store.mjs → live/session-store.mjs} +21 -3
  55. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +835 -0
  56. package/dist/builtin/workflows/skills/impeccable/scripts/live/sveltekit-adapter.mjs +274 -0
  57. package/dist/builtin/workflows/skills/impeccable/scripts/live/ui-core.mjs +180 -0
  58. package/dist/builtin/workflows/skills/impeccable/scripts/live/vocabulary.mjs +36 -0
  59. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +185 -60
  60. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-dom.js +146 -0
  61. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +3369 -1026
  62. package/dist/builtin/workflows/skills/impeccable/scripts/live-commit-manual-edits.mjs +2 -2
  63. package/dist/builtin/workflows/skills/impeccable/scripts/live-complete.mjs +2 -2
  64. package/dist/builtin/workflows/skills/impeccable/scripts/live-discard-manual-edits.mjs +1 -1
  65. package/dist/builtin/workflows/skills/impeccable/scripts/live-inject.mjs +133 -9
  66. package/dist/builtin/workflows/skills/impeccable/scripts/live-insert.mjs +42 -2
  67. package/dist/builtin/workflows/skills/impeccable/scripts/live-manual-edit-evidence.mjs +4 -4
  68. package/dist/builtin/workflows/skills/impeccable/scripts/live-poll.mjs +21 -15
  69. package/dist/builtin/workflows/skills/impeccable/scripts/live-resume.mjs +1 -1
  70. package/dist/builtin/workflows/skills/impeccable/scripts/live-server.mjs +205 -1269
  71. package/dist/builtin/workflows/skills/impeccable/scripts/live-status.mjs +2 -2
  72. package/dist/builtin/workflows/skills/impeccable/scripts/live-target.mjs +30 -0
  73. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +69 -26
  74. package/dist/builtin/workflows/skills/impeccable/scripts/live.mjs +73 -22
  75. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  76. package/dist/core/atomic-guide-command.js +5 -5
  77. package/dist/core/atomic-guide-command.js.map +1 -1
  78. package/docs/index.md +2 -2
  79. package/docs/quickstart.md +9 -9
  80. package/docs/workflows.md +42 -23
  81. package/package.json +2 -2
  82. package/dist/builtin/workflows/skills/impeccable/scripts/cleanup-deprecated.mjs +0 -284
  83. package/dist/builtin/workflows/skills/impeccable/scripts/impeccable-paths.mjs +0 -126
  84. /package/dist/builtin/workflows/skills/impeccable/scripts/{live-insert-ui.mjs → live/insert-ui.mjs} +0 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Inline, in-file ignore directives — eslint-disable-style waivers that live at
3
+ * the point they apply and travel with the artifact instead of (or alongside)
4
+ * an ignore in `.impeccable/config.json`.
5
+ *
6
+ * A config ignore is the right default for repo-wide policy. This complements it
7
+ * for the one case config can't cover: a waiver that belongs to a single file and
8
+ * needs to follow that file when it leaves the repo — a generated/exported
9
+ * standalone document, an emailed HTML file, a snippet scanned out of context.
10
+ *
11
+ * Comment-syntax-agnostic: the directive is a raw token matched anywhere on a
12
+ * line, so the same marker works across every comment style impeccable scans —
13
+ * `//`, `/* *\/`, `<!-- -->`, `#`, `{/* *\/}`, `{# #}`. Trailing comment closers
14
+ * are stripped before the rule list is parsed.
15
+ *
16
+ * Syntax (reason optional; eslint `--` or biome `:` separator):
17
+ *
18
+ * impeccable-disable <rule>[, <rule>...] [-- reason] whole file
19
+ * impeccable-disable-line <rule>... [-- reason] the same line
20
+ * impeccable-disable-next-line <rule>... [-- reason] the following line
21
+ * impeccable-disable bare / `*` = every rule
22
+ *
23
+ * Examples:
24
+ *
25
+ * <!-- impeccable-disable overused-font -- exported brand doc, font is first-party -->
26
+ * .brand { font-family: Inter; } /* impeccable-disable-line overused-font *\/
27
+ * // impeccable-disable-next-line bounce-easing: intentional playful affordance
28
+ *
29
+ * Behavior is suppression, for parity with config ignores: a matched directive
30
+ * drops the finding. The inline reason is self-documenting in the diff; it is not
31
+ * required and is discarded at scan time (only used here to keep reason words out
32
+ * of the parsed rule list).
33
+ */
34
+
35
+ const DIRECTIVE_RE = /impeccable-(disable-next-line|disable-line|disable)\b[ \t]*([^\n\r]*)/gi;
36
+
37
+ // Trailing comment closers, so `*/`, `*/}`, `-->`, `*}`, `#}`, `%>`, `}}` don't
38
+ // leak into the rule list. Anchored to end-of-line; the leading `\s*` mops up the
39
+ // space before the closer. `--+>` covers `-->` and any longer dash run.
40
+ const TRAILING_CLOSER_RE = /\s*(?:\*\/\}?|--+>|\*\}|#\}|%>|\}\})\s*$/;
41
+
42
+ function normalizeRule(token) {
43
+ return String(token || '').trim().toLowerCase();
44
+ }
45
+
46
+ // Split the directive remainder into rule tokens, dropping any human reason that
47
+ // follows an eslint-style `--` or biome-style `:` separator. Rule ids only ever
48
+ // contain single hyphens (`overused-font`, `bounce-easing`), so `--` and `:`
49
+ // are unambiguous separators.
50
+ function parseRuleList(remainder) {
51
+ let text = String(remainder || '').replace(TRAILING_CLOSER_RE, '').trim();
52
+ // Cut off a human reason at the first `--` (eslint) or `:` (biome) separator.
53
+ const reasonSep = text.match(/\s*(?:--+|:)\s*/);
54
+ if (reasonSep) text = text.slice(0, reasonSep.index);
55
+ const tokens = text.split(/[\s,]+/).map(normalizeRule).filter(Boolean);
56
+ if (tokens.length === 0 || tokens.includes('*')) return ['*'];
57
+ return tokens;
58
+ }
59
+
60
+ function addRules(set, rules) {
61
+ for (const rule of rules) set.add(rule);
62
+ }
63
+
64
+ function getSet(map, key) {
65
+ let set = map.get(key);
66
+ if (!set) {
67
+ set = new Set();
68
+ map.set(key, set);
69
+ }
70
+ return set;
71
+ }
72
+
73
+ /**
74
+ * Parse every inline ignore directive in a file's raw text.
75
+ *
76
+ * Returns sets keyed by the 1-based line the directive *targets* so matching is a
77
+ * direct lookup:
78
+ * - file: rules disabled for the whole file
79
+ * - line: line -> rules disabled on that exact line (disable-line)
80
+ * - nextLine: line -> rules disabled on that line (disable-next-line on line-1)
81
+ *
82
+ * `*` in any set means "every rule".
83
+ */
84
+ function parseInlineIgnores(content) {
85
+ const result = { file: new Set(), line: new Map(), nextLine: new Map() };
86
+ const text = typeof content === 'string' ? content : '';
87
+ // Cheap bail-out: the substring must be present for any directive to exist.
88
+ // Case-insensitive to match DIRECTIVE_RE's `i` flag (e.g. `Impeccable-Disable`).
89
+ if (!/impeccable-disable/i.test(text)) return result;
90
+
91
+ // Split on `\n` only, exactly as detectText numbers lines, so directive line
92
+ // keys line up with finding `line` values (incl. on `\r`-only line endings).
93
+ // The directive regex excludes `\r`, so a trailing `\r` on `\r\n` files is
94
+ // never captured into the rule list.
95
+ const lines = text.split('\n');
96
+ for (let i = 0; i < lines.length; i++) {
97
+ DIRECTIVE_RE.lastIndex = 0;
98
+ let m;
99
+ while ((m = DIRECTIVE_RE.exec(lines[i])) !== null) {
100
+ const variant = m[1].toLowerCase();
101
+ const rules = parseRuleList(m[2]);
102
+ if (variant === 'disable') {
103
+ addRules(result.file, rules);
104
+ } else if (variant === 'disable-line') {
105
+ addRules(getSet(result.line, i + 1), rules);
106
+ } else {
107
+ // disable-next-line on line i+1 targets line i+2.
108
+ addRules(getSet(result.nextLine, i + 2), rules);
109
+ }
110
+ }
111
+ }
112
+ return result;
113
+ }
114
+
115
+ function setMatches(set, rule) {
116
+ return Boolean(set) && (set.has('*') || set.has(rule));
117
+ }
118
+
119
+ function isInlineIgnored(finding, directives) {
120
+ const rule = normalizeRule(finding && finding.antipattern);
121
+ if (!rule) return false;
122
+ if (setMatches(directives.file, rule)) return true;
123
+ const line = Number(finding && finding.line) || 0;
124
+ if (line > 0) {
125
+ if (setMatches(directives.line.get(line), rule)) return true;
126
+ if (setMatches(directives.nextLine.get(line), rule)) return true;
127
+ }
128
+ return false;
129
+ }
130
+
131
+ function hasDirectives(directives) {
132
+ return directives.file.size > 0 || directives.line.size > 0 || directives.nextLine.size > 0;
133
+ }
134
+
135
+ /**
136
+ * Drop findings waived by an inline directive in the same file's source text.
137
+ * Findings without a usable line number (e.g. static-HTML page-level findings)
138
+ * are only matched by whole-file directives — which is the standalone-document
139
+ * case this primitive exists for.
140
+ */
141
+ function applyInlineIgnores(findings, content) {
142
+ if (!Array.isArray(findings) || findings.length === 0) return findings;
143
+ const directives = parseInlineIgnores(content);
144
+ if (!hasDirectives(directives)) return findings;
145
+ return findings.filter((finding) => !isInlineIgnored(finding, directives));
146
+ }
147
+
148
+ export { parseInlineIgnores, applyInlineIgnores, isInlineIgnored };
@@ -1,13 +1,13 @@
1
1
  /** Check if content looks like a full page (not a component/partial) */
2
2
  function isFullPage(content) {
3
- // Strip comments until stable so a nested/overlapping <!-- ... --> cannot
4
- // survive a single pass (incomplete multi-character sanitization).
5
- let stripped = content;
6
- let prevStripped;
3
+ // Strip comments to a fixpoint so nested/overlapping comment markers cannot
4
+ // survive a single pass (CodeQL: complete sanitization).
5
+ let stripped = String(content);
6
+ let prev;
7
7
  do {
8
- prevStripped = stripped;
8
+ prev = stripped;
9
9
  stripped = stripped.replace(/<!--[\s\S]*?-->/g, '');
10
- } while (stripped !== prevStripped);
10
+ } while (stripped !== prev);
11
11
  return /<!doctype\s|<html[\s>]|<head[\s>]/i.test(stripped);
12
12
  }
13
13
 
@@ -62,7 +62,7 @@ function parseYamlSubset(yaml) {
62
62
  stack.pop();
63
63
  }
64
64
 
65
- const key = content.slice(0, colonIdx).trim();
65
+ const key = unquoteYamlKey(content.slice(0, colonIdx).trim());
66
66
  const rest = stripInlineYamlComment(content.slice(colonIdx + 1).trim());
67
67
  const parent = stack[stack.length - 1].obj;
68
68
 
@@ -93,6 +93,13 @@ function findTopLevelColon(s) {
93
93
  return -1;
94
94
  }
95
95
 
96
+ function unquoteYamlKey(key) {
97
+ if ((key.startsWith('"') && key.endsWith('"')) || (key.startsWith("'") && key.endsWith("'"))) {
98
+ return key.slice(1, -1);
99
+ }
100
+ return key;
101
+ }
102
+
96
103
  function stripInlineYamlComment(s) {
97
104
  let inQuote = null;
98
105
  for (let i = 0; i < s.length; i++) {