maestro-flow 0.3.46 → 0.3.48

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 (241) hide show
  1. package/.claude/agents/ui-design-agent.md +1 -0
  2. package/.claude/agents/workflow-executor.md +3 -0
  3. package/.claude/commands/learn-decompose.md +91 -146
  4. package/.claude/commands/learn-follow.md +102 -137
  5. package/.claude/commands/learn-investigate.md +102 -167
  6. package/.claude/commands/learn-retro.md +100 -243
  7. package/.claude/commands/learn-second-opinion.md +95 -135
  8. package/.claude/commands/maestro-amend.md +95 -232
  9. package/.claude/commands/maestro-analyze.md +1 -6
  10. package/.claude/commands/maestro-collab.md +104 -265
  11. package/.claude/commands/maestro-composer.md +113 -293
  12. package/.claude/commands/maestro-execute.md +10 -17
  13. package/.claude/commands/maestro-impeccable.md +89 -0
  14. package/.claude/commands/maestro-plan.md +1 -6
  15. package/.claude/commands/maestro-player.md +111 -340
  16. package/.claude/commands/maestro-quick.md +9 -0
  17. package/.claude/commands/maestro-ralph-execute.md +167 -210
  18. package/.claude/commands/maestro-ralph.md +245 -426
  19. package/.claude/commands/maestro-ui-codify.md +13 -0
  20. package/.claude/commands/maestro-ui-craft.md +364 -0
  21. package/.claude/commands/maestro-ui-design.md +12 -1
  22. package/.claude/commands/maestro-verify.md +12 -13
  23. package/.claude/commands/maestro.md +142 -72
  24. package/.claude/commands/manage-knowhow-capture.md +45 -170
  25. package/.claude/commands/quality-auto-test.md +9 -0
  26. package/.claude/commands/quality-debug.md +11 -25
  27. package/.claude/commands/quality-refactor.md +9 -0
  28. package/.claude/commands/quality-review.md +5 -14
  29. package/.claude/commands/spec-add.md +1 -1
  30. package/.claude/commands/spec-load.md +3 -2
  31. package/.claude/skills/maestro-impeccable/SKILL.md +169 -0
  32. package/.codex/skills/learn-decompose/SKILL.md +1 -1
  33. package/.codex/skills/learn-investigate/SKILL.md +2 -1
  34. package/.codex/skills/maestro/SKILL.md +420 -313
  35. package/.codex/skills/maestro-analyze/SKILL.md +126 -417
  36. package/.codex/skills/maestro-brainstorm/SKILL.md +129 -451
  37. package/.codex/skills/maestro-collab/SKILL.md +134 -547
  38. package/.codex/skills/maestro-execute/SKILL.md +3 -1
  39. package/.codex/skills/maestro-impeccable/SKILL.md +112 -0
  40. package/.codex/skills/maestro-plan/SKILL.md +88 -437
  41. package/.codex/skills/maestro-player/SKILL.md +191 -333
  42. package/.codex/skills/maestro-quick/SKILL.md +2 -0
  43. package/.codex/skills/maestro-ralph/SKILL.md +327 -710
  44. package/.codex/skills/maestro-roadmap/SKILL.md +201 -518
  45. package/.codex/skills/maestro-ui-codify/SKILL.md +1 -0
  46. package/.codex/skills/maestro-ui-craft/SKILL.md +341 -0
  47. package/.codex/skills/maestro-ui-design/SKILL.md +10 -0
  48. package/.codex/skills/maestro-verify/SKILL.md +116 -409
  49. package/.codex/skills/quality-auto-test/SKILL.md +145 -443
  50. package/.codex/skills/quality-refactor/SKILL.md +1 -1
  51. package/.codex/skills/quality-test/SKILL.md +229 -517
  52. package/.codex/skills/spec-add/SKILL.md +1 -1
  53. package/README.md +4 -1
  54. package/README.zh-CN.md +3 -1
  55. package/dashboard/dist-server/dashboard/src/server/agents/codex-cli-adapter.js +3 -0
  56. package/dashboard/dist-server/dashboard/src/server/agents/codex-cli-adapter.js.map +1 -1
  57. package/dashboard/dist-server/dashboard/src/server/routes/install.js +110 -1
  58. package/dashboard/dist-server/dashboard/src/server/routes/install.js.map +1 -1
  59. package/dashboard/dist-server/dashboard/src/server/routes/settings.js +56 -0
  60. package/dashboard/dist-server/dashboard/src/server/routes/settings.js.map +1 -1
  61. package/dashboard/dist-server/dashboard/src/server/routes/wiki.js +2 -0
  62. package/dashboard/dist-server/dashboard/src/server/routes/wiki.js.map +1 -1
  63. package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.js +2 -2
  64. package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.js.map +1 -1
  65. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js +2 -0
  66. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js.map +1 -1
  67. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-types.d.ts +3 -1
  68. package/dashboard/dist-server/dashboard/src/shared/constants.d.ts +2 -0
  69. package/dashboard/dist-server/dashboard/src/shared/constants.js +2 -0
  70. package/dashboard/dist-server/dashboard/src/shared/constants.js.map +1 -1
  71. package/dist/src/agents/cli-agent-runner.d.ts.map +1 -1
  72. package/dist/src/agents/cli-agent-runner.js +1 -3
  73. package/dist/src/agents/cli-agent-runner.js.map +1 -1
  74. package/dist/src/agents/cli-history-store.d.ts +5 -0
  75. package/dist/src/agents/cli-history-store.d.ts.map +1 -1
  76. package/dist/src/agents/cli-history-store.js +65 -13
  77. package/dist/src/agents/cli-history-store.js.map +1 -1
  78. package/dist/src/cli.js +13 -0
  79. package/dist/src/cli.js.map +1 -1
  80. package/dist/src/commands/command-help.d.ts +3 -0
  81. package/dist/src/commands/command-help.d.ts.map +1 -0
  82. package/dist/src/commands/command-help.js +60 -0
  83. package/dist/src/commands/command-help.js.map +1 -0
  84. package/dist/src/commands/config.d.ts.map +1 -1
  85. package/dist/src/commands/config.js +17 -0
  86. package/dist/src/commands/config.js.map +1 -1
  87. package/dist/src/commands/delegate.d.ts.map +1 -1
  88. package/dist/src/commands/delegate.js +12 -2
  89. package/dist/src/commands/delegate.js.map +1 -1
  90. package/dist/src/commands/impeccable.d.ts +10 -0
  91. package/dist/src/commands/impeccable.d.ts.map +1 -0
  92. package/dist/src/commands/impeccable.js +181 -0
  93. package/dist/src/commands/impeccable.js.map +1 -0
  94. package/dist/src/commands/spec.js +1 -1
  95. package/dist/src/commands/spec.js.map +1 -1
  96. package/dist/src/commands/wiki.d.ts.map +1 -1
  97. package/dist/src/commands/wiki.js +5 -1
  98. package/dist/src/commands/wiki.js.map +1 -1
  99. package/dist/src/config/cli-tools-config.d.ts.map +1 -1
  100. package/dist/src/config/cli-tools-config.js +10 -7
  101. package/dist/src/config/cli-tools-config.js.map +1 -1
  102. package/dist/src/core/addon-registry.d.ts +31 -0
  103. package/dist/src/core/addon-registry.d.ts.map +1 -0
  104. package/dist/src/core/addon-registry.js +28 -0
  105. package/dist/src/core/addon-registry.js.map +1 -0
  106. package/dist/src/hooks/plugins/spec-injection-plugin.js +2 -0
  107. package/dist/src/hooks/plugins/spec-injection-plugin.js.map +1 -1
  108. package/dist/src/hooks/spec-injector.js +2 -2
  109. package/dist/src/hooks/spec-injector.js.map +1 -1
  110. package/dist/src/index.d.ts +2 -0
  111. package/dist/src/index.d.ts.map +1 -1
  112. package/dist/src/index.js +1 -0
  113. package/dist/src/index.js.map +1 -1
  114. package/dist/src/tools/impeccable/critique-storage.d.ts +28 -0
  115. package/dist/src/tools/impeccable/critique-storage.d.ts.map +1 -0
  116. package/dist/src/tools/impeccable/critique-storage.js +120 -0
  117. package/dist/src/tools/impeccable/critique-storage.js.map +1 -0
  118. package/dist/src/tools/impeccable/design-parser.d.ts +90 -0
  119. package/dist/src/tools/impeccable/design-parser.d.ts.map +1 -0
  120. package/dist/src/tools/impeccable/design-parser.js +696 -0
  121. package/dist/src/tools/impeccable/design-parser.js.map +1 -0
  122. package/dist/src/tools/impeccable/detect-csp.d.ts +6 -0
  123. package/dist/src/tools/impeccable/detect-csp.d.ts.map +1 -0
  124. package/dist/src/tools/impeccable/detect-csp.js +130 -0
  125. package/dist/src/tools/impeccable/detect-csp.js.map +1 -0
  126. package/dist/src/tools/impeccable/is-generated.d.ts +4 -0
  127. package/dist/src/tools/impeccable/is-generated.d.ts.map +1 -0
  128. package/dist/src/tools/impeccable/is-generated.js +56 -0
  129. package/dist/src/tools/impeccable/is-generated.js.map +1 -0
  130. package/dist/src/tools/impeccable/live/accept.d.ts +50 -0
  131. package/dist/src/tools/impeccable/live/accept.d.ts.map +1 -0
  132. package/dist/src/tools/impeccable/live/accept.js +556 -0
  133. package/dist/src/tools/impeccable/live/accept.js.map +1 -0
  134. package/dist/src/tools/impeccable/live/bootstrap.d.ts +2 -0
  135. package/dist/src/tools/impeccable/live/bootstrap.d.ts.map +1 -0
  136. package/dist/src/tools/impeccable/live/bootstrap.js +244 -0
  137. package/dist/src/tools/impeccable/live/bootstrap.js.map +1 -0
  138. package/dist/src/tools/impeccable/live/complete.d.ts +7 -0
  139. package/dist/src/tools/impeccable/live/complete.d.ts.map +1 -0
  140. package/dist/src/tools/impeccable/live/complete.js +67 -0
  141. package/dist/src/tools/impeccable/live/complete.js.map +1 -0
  142. package/dist/src/tools/impeccable/live/completion.d.ts +24 -0
  143. package/dist/src/tools/impeccable/live/completion.d.ts.map +1 -0
  144. package/dist/src/tools/impeccable/live/completion.js +26 -0
  145. package/dist/src/tools/impeccable/live/completion.js.map +1 -0
  146. package/dist/src/tools/impeccable/live/inject.d.ts +41 -0
  147. package/dist/src/tools/impeccable/live/inject.d.ts.map +1 -0
  148. package/dist/src/tools/impeccable/live/inject.js +394 -0
  149. package/dist/src/tools/impeccable/live/inject.js.map +1 -0
  150. package/dist/src/tools/impeccable/live/poll.d.ts +24 -0
  151. package/dist/src/tools/impeccable/live/poll.d.ts.map +1 -0
  152. package/dist/src/tools/impeccable/live/poll.js +180 -0
  153. package/dist/src/tools/impeccable/live/poll.js.map +1 -0
  154. package/dist/src/tools/impeccable/live/resume.d.ts +5 -0
  155. package/dist/src/tools/impeccable/live/resume.d.ts.map +1 -0
  156. package/dist/src/tools/impeccable/live/resume.js +30 -0
  157. package/dist/src/tools/impeccable/live/resume.js.map +1 -0
  158. package/dist/src/tools/impeccable/live/server.d.ts +6 -0
  159. package/dist/src/tools/impeccable/live/server.d.ts.map +1 -0
  160. package/dist/src/tools/impeccable/live/server.js +867 -0
  161. package/dist/src/tools/impeccable/live/server.js.map +1 -0
  162. package/dist/src/tools/impeccable/live/session-store.d.ts +72 -0
  163. package/dist/src/tools/impeccable/live/session-store.d.ts.map +1 -0
  164. package/dist/src/tools/impeccable/live/session-store.js +281 -0
  165. package/dist/src/tools/impeccable/live/session-store.js.map +1 -0
  166. package/dist/src/tools/impeccable/live/static/live-browser-session.js +123 -0
  167. package/dist/src/tools/impeccable/live/static/live-browser.js +4860 -0
  168. package/dist/src/tools/impeccable/live/static/modern-screenshot.umd.js +14 -0
  169. package/dist/src/tools/impeccable/live/status.d.ts +2 -0
  170. package/dist/src/tools/impeccable/live/status.d.ts.map +1 -0
  171. package/dist/src/tools/impeccable/live/status.js +52 -0
  172. package/dist/src/tools/impeccable/live/status.js.map +1 -0
  173. package/dist/src/tools/impeccable/live/wrap.d.ts +33 -0
  174. package/dist/src/tools/impeccable/live/wrap.d.ts.map +1 -0
  175. package/dist/src/tools/impeccable/live/wrap.js +572 -0
  176. package/dist/src/tools/impeccable/live/wrap.js.map +1 -0
  177. package/dist/src/tools/impeccable/load-context.d.ts +13 -0
  178. package/dist/src/tools/impeccable/load-context.d.ts.map +1 -0
  179. package/dist/src/tools/impeccable/load-context.js +79 -0
  180. package/dist/src/tools/impeccable/load-context.js.map +1 -0
  181. package/dist/src/tools/impeccable/paths.d.ts +34 -0
  182. package/dist/src/tools/impeccable/paths.d.ts.map +1 -0
  183. package/dist/src/tools/impeccable/paths.js +102 -0
  184. package/dist/src/tools/impeccable/paths.js.map +1 -0
  185. package/dist/src/tools/spec-entry-parser.d.ts +1 -1
  186. package/dist/src/tools/spec-entry-parser.d.ts.map +1 -1
  187. package/dist/src/tools/spec-entry-parser.js +1 -1
  188. package/dist/src/tools/spec-entry-parser.js.map +1 -1
  189. package/dist/src/tools/spec-init.d.ts.map +1 -1
  190. package/dist/src/tools/spec-init.js +26 -1
  191. package/dist/src/tools/spec-init.js.map +1 -1
  192. package/dist/src/tools/spec-loader.d.ts +1 -1
  193. package/dist/src/tools/spec-loader.d.ts.map +1 -1
  194. package/dist/src/tools/spec-loader.js +2 -0
  195. package/dist/src/tools/spec-loader.js.map +1 -1
  196. package/package.json +2 -2
  197. package/workflows/claude-instructions.md +17 -5
  198. package/workflows/cli-tools-usage.md +10 -3
  199. package/workflows/delegate-usage.md +3 -2
  200. package/workflows/impeccable/adapt.md +190 -0
  201. package/workflows/impeccable/animate.md +175 -0
  202. package/workflows/impeccable/audit.md +133 -0
  203. package/workflows/impeccable/bolder.md +113 -0
  204. package/workflows/impeccable/brand.md +118 -0
  205. package/workflows/impeccable/clarify.md +174 -0
  206. package/workflows/impeccable/codex.md +105 -0
  207. package/workflows/impeccable/cognitive-load.md +106 -0
  208. package/workflows/impeccable/color-and-contrast.md +105 -0
  209. package/workflows/impeccable/colorize.md +154 -0
  210. package/workflows/impeccable/craft.md +123 -0
  211. package/workflows/impeccable/critique.md +261 -0
  212. package/workflows/impeccable/delight.md +302 -0
  213. package/workflows/impeccable/distill.md +111 -0
  214. package/workflows/impeccable/document.md +439 -0
  215. package/workflows/impeccable/extract.md +69 -0
  216. package/workflows/impeccable/harden.md +347 -0
  217. package/workflows/impeccable/heuristics-scoring.md +234 -0
  218. package/workflows/impeccable/interaction-design.md +195 -0
  219. package/workflows/impeccable/layout.md +141 -0
  220. package/workflows/impeccable/live.md +622 -0
  221. package/workflows/impeccable/motion-design.md +109 -0
  222. package/workflows/impeccable/onboard.md +234 -0
  223. package/workflows/impeccable/optimize.md +258 -0
  224. package/workflows/impeccable/overdrive.md +130 -0
  225. package/workflows/impeccable/personas.md +179 -0
  226. package/workflows/impeccable/polish.md +242 -0
  227. package/workflows/impeccable/product.md +62 -0
  228. package/workflows/impeccable/quieter.md +99 -0
  229. package/workflows/impeccable/responsive-design.md +114 -0
  230. package/workflows/impeccable/shape.md +165 -0
  231. package/workflows/impeccable/spatial-design.md +100 -0
  232. package/workflows/impeccable/teach.md +168 -0
  233. package/workflows/impeccable/typeset.md +124 -0
  234. package/workflows/impeccable/typography.md +159 -0
  235. package/workflows/impeccable/ux-writing.md +107 -0
  236. package/workflows/impeccable.md +164 -0
  237. package/workflows/maestro.md +7 -3
  238. package/workflows/skill-authoring.md +265 -0
  239. package/workflows/specs-add.md +3 -2
  240. package/workflows/specs-load.md +2 -1
  241. package/workflows/specs-setup.md +21 -1
@@ -0,0 +1,696 @@
1
+ // Copyright 2024 Paul Bakaus (https://github.com/pbakaus/impeccable)
2
+ // Licensed under the Apache License, Version 2.0
3
+ // Modifications: Converted to TypeScript, adapted for maestro CLI architecture.
4
+ // Parse a DESIGN.md (Stitch-spec format) into a structured JSON model that
5
+ // the live-mode design-system panel can render. Deterministic, dependency-free.
6
+ //
7
+ // Two-layer: YAML frontmatter (machine-readable tokens) + markdown body
8
+ // (prose with six canonical H2 sections). When frontmatter is present, it's
9
+ // exposed on `model.frontmatter` alongside the prose-scraped sections;
10
+ // consumers can prefer frontmatter values and fall back to prose.
11
+ const CANONICAL_SECTIONS = [
12
+ 'Overview',
13
+ 'Colors',
14
+ 'Typography',
15
+ 'Elevation',
16
+ 'Components',
17
+ "Do's and Don'ts",
18
+ ];
19
+ // ---------- Frontmatter (Stitch YAML subset) ----------
20
+ function parseFrontmatterRaw(md) {
21
+ const lines = md.split(/\r?\n/);
22
+ if (lines[0]?.trim() !== '---')
23
+ return { frontmatter: null, body: md };
24
+ let end = -1;
25
+ for (let i = 1; i < lines.length; i++) {
26
+ if (lines[i].trim() === '---') {
27
+ end = i;
28
+ break;
29
+ }
30
+ }
31
+ if (end === -1)
32
+ return { frontmatter: null, body: md };
33
+ const yaml = lines.slice(1, end).join('\n');
34
+ const body = lines.slice(end + 1).join('\n');
35
+ try {
36
+ return { frontmatter: parseYamlSubset(yaml), body };
37
+ }
38
+ catch {
39
+ return { frontmatter: null, body: md };
40
+ }
41
+ }
42
+ function parseYamlSubset(yaml) {
43
+ const lines = yaml.split(/\r?\n/);
44
+ const root = {};
45
+ const stack = [{ indent: -1, obj: root }];
46
+ for (const raw of lines) {
47
+ if (!raw.trim() || /^\s*#/.test(raw))
48
+ continue;
49
+ const indent = raw.match(/^\s*/)?.[0].length ?? 0;
50
+ const content = raw.slice(indent);
51
+ const colonIdx = findTopLevelColon(content);
52
+ if (colonIdx === -1)
53
+ continue;
54
+ while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
55
+ stack.pop();
56
+ }
57
+ const key = content.slice(0, colonIdx).trim();
58
+ const rest = content.slice(colonIdx + 1).trim();
59
+ const parent = stack[stack.length - 1].obj;
60
+ if (rest === '') {
61
+ const obj = {};
62
+ parent[key] = obj;
63
+ stack.push({ indent, obj });
64
+ }
65
+ else {
66
+ parent[key] = parseScalar(rest);
67
+ }
68
+ }
69
+ return root;
70
+ }
71
+ function findTopLevelColon(s) {
72
+ let inQuote = null;
73
+ for (let i = 0; i < s.length; i++) {
74
+ const ch = s[i];
75
+ if (inQuote) {
76
+ if (ch === inQuote && s[i - 1] !== '\\')
77
+ inQuote = null;
78
+ }
79
+ else if (ch === '"' || ch === "'") {
80
+ inQuote = ch;
81
+ }
82
+ else if (ch === ':') {
83
+ return i;
84
+ }
85
+ }
86
+ return -1;
87
+ }
88
+ function parseScalar(raw) {
89
+ const s = raw.trim();
90
+ if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
91
+ return s.slice(1, -1);
92
+ }
93
+ if (s === 'true')
94
+ return true;
95
+ if (s === 'false')
96
+ return false;
97
+ if (s === 'null' || s === '~')
98
+ return null;
99
+ if (/^-?\d+$/.test(s))
100
+ return Number(s);
101
+ if (/^-?\d*\.\d+$/.test(s))
102
+ return Number(s);
103
+ return s;
104
+ }
105
+ const HEX_RE = /#[0-9a-fA-F]{3,8}\b/g;
106
+ const OKLCH_RE = /oklch\([^)]+\)/gi;
107
+ // ---------- Section splitting ----------
108
+ function splitSections(md) {
109
+ const lines = md.split(/\r?\n/);
110
+ let title = null;
111
+ const sections = {};
112
+ let current = null;
113
+ for (const raw of lines) {
114
+ const line = raw.trimEnd();
115
+ if (!title && line.startsWith('# ') && !line.startsWith('## ')) {
116
+ title = line.replace(/^#\s+/, '').trim();
117
+ continue;
118
+ }
119
+ const h2 = line.match(/^##\s+(?:\d+\.\s*)?([^:\n]+?)(?::\s*(.+))?$/);
120
+ if (h2) {
121
+ const rawName = normalizeApostrophes(h2[1].trim());
122
+ const subtitle = h2[2] ? h2[2].trim() : null;
123
+ const canonical = matchCanonicalSection(rawName);
124
+ if (canonical) {
125
+ current = { name: canonical, subtitle, lines: [] };
126
+ sections[canonical] = current;
127
+ continue;
128
+ }
129
+ current = null;
130
+ continue;
131
+ }
132
+ if (current)
133
+ current.lines.push(raw);
134
+ }
135
+ return { title, sections };
136
+ }
137
+ function normalizeApostrophes(s) {
138
+ return s.replace(/[\u2018\u2019]/g, "'");
139
+ }
140
+ function matchCanonicalSection(name) {
141
+ const normalized = normalizeApostrophes(name).toLowerCase();
142
+ for (const c of CANONICAL_SECTIONS) {
143
+ if (normalizeApostrophes(c).toLowerCase() === normalized)
144
+ return c;
145
+ }
146
+ for (const c of CANONICAL_SECTIONS) {
147
+ const key = normalizeApostrophes(c).toLowerCase();
148
+ const pattern = new RegExp(`\\b${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
149
+ if (pattern.test(normalized))
150
+ return c;
151
+ }
152
+ return null;
153
+ }
154
+ // ---------- Subsection splitting ----------
155
+ function splitSubsections(lines) {
156
+ const subs = [{ name: null, lines: [] }];
157
+ let current = subs[0];
158
+ for (const raw of lines) {
159
+ const h3 = raw.match(/^###\s+(.+?)\s*$/);
160
+ if (h3) {
161
+ current = { name: h3[1].trim(), lines: [] };
162
+ subs.push(current);
163
+ continue;
164
+ }
165
+ current.lines.push(raw);
166
+ }
167
+ return subs;
168
+ }
169
+ // ---------- Generic helpers ----------
170
+ function collectParagraphs(lines) {
171
+ const paragraphs = [];
172
+ let buf = [];
173
+ const flush = () => {
174
+ if (buf.length) {
175
+ paragraphs.push(buf.join(' ').trim());
176
+ buf = [];
177
+ }
178
+ };
179
+ for (const raw of lines) {
180
+ const trimmed = raw.trim();
181
+ if (trimmed === '') {
182
+ flush();
183
+ continue;
184
+ }
185
+ if (/^(?:-{3,}|\*{3,}|_{3,})$/.test(trimmed)) {
186
+ flush();
187
+ continue;
188
+ }
189
+ if (raw.startsWith('#') || raw.match(/^[-*]\s/)) {
190
+ flush();
191
+ continue;
192
+ }
193
+ buf.push(trimmed);
194
+ }
195
+ flush();
196
+ return paragraphs.filter(Boolean);
197
+ }
198
+ function collectBullets(lines) {
199
+ const bullets = [];
200
+ let current = null;
201
+ for (const raw of lines) {
202
+ const m = raw.match(/^\s*[-*]\s+(.+)$/);
203
+ if (m) {
204
+ if (current)
205
+ bullets.push(current);
206
+ current = m[1];
207
+ continue;
208
+ }
209
+ if (current && raw.match(/^\s{2,}\S/)) {
210
+ current += ' ' + raw.trim();
211
+ continue;
212
+ }
213
+ if (raw.trim() === '' && current) {
214
+ bullets.push(current);
215
+ current = null;
216
+ }
217
+ }
218
+ if (current)
219
+ bullets.push(current);
220
+ return bullets;
221
+ }
222
+ function stripBold(s) {
223
+ return s.replace(/\*\*(.+?)\*\*/g, '$1');
224
+ }
225
+ function extractNamedRules(lines) {
226
+ const rules = [];
227
+ const seen = new Set();
228
+ const joined = lines.join('\n');
229
+ const inlineStart = /\*\*(The [^*]+?Rule)\.\*\*/g;
230
+ const inlineMatches = [];
231
+ let m;
232
+ while ((m = inlineStart.exec(joined)) !== null) {
233
+ inlineMatches.push({ name: m[1], start: m.index, end: inlineStart.lastIndex });
234
+ }
235
+ for (let i = 0; i < inlineMatches.length; i++) {
236
+ const mm = inlineMatches[i];
237
+ const bodyEnd = i + 1 < inlineMatches.length ? inlineMatches[i + 1].start : joined.length;
238
+ const body = joined
239
+ .slice(mm.end, bodyEnd)
240
+ .replace(/\n##[^\n]*$/s, '')
241
+ .replace(/\n###[^\n]*$/s, '')
242
+ .trim();
243
+ const name = stripBold(mm.name).trim();
244
+ seen.add(name.toLowerCase());
245
+ rules.push({ name, body: stripBold(body) });
246
+ }
247
+ for (let i = 0; i < lines.length; i++) {
248
+ const h3 = lines[i].match(/^###\s+(.+?)\s*$/);
249
+ if (!h3)
250
+ continue;
251
+ const headerName = stripBold(h3[1]).replace(/[""""]/g, '').trim();
252
+ if (!/^The\b.*\b(Rule|Fallback|Principle)\b/i.test(headerName))
253
+ continue;
254
+ if (seen.has(headerName.toLowerCase()))
255
+ continue;
256
+ const bodyLines = [];
257
+ for (let j = i + 1; j < lines.length; j++) {
258
+ if (/^##\s|^###\s/.test(lines[j]))
259
+ break;
260
+ bodyLines.push(lines[j]);
261
+ }
262
+ const body = stripBold(bodyLines.join('\n').replace(/\n+/g, ' ')).trim();
263
+ if (body) {
264
+ seen.add(headerName.toLowerCase());
265
+ rules.push({ name: headerName, body });
266
+ }
267
+ }
268
+ for (const b of collectBullets(lines)) {
269
+ const mm = b.match(/^\*\*([^*]+?)\*\*\s*(.+)$/);
270
+ if (!mm)
271
+ continue;
272
+ const nameRaw = mm[1].replace(/[.:]\s*$/, '').replace(/[""""]/g, '').trim();
273
+ if (!/^The\b.+\b(Rule|Fallback|Principle)$/i.test(nameRaw))
274
+ continue;
275
+ if (seen.has(nameRaw.toLowerCase()))
276
+ continue;
277
+ seen.add(nameRaw.toLowerCase());
278
+ rules.push({ name: nameRaw, body: stripBold(mm[2]).trim() });
279
+ }
280
+ return rules;
281
+ }
282
+ // ---------- Per-section extractors ----------
283
+ function extractOverview(section) {
284
+ if (!section)
285
+ return null;
286
+ const text = section.lines.join('\n');
287
+ const northStar = text.match(/\*\*Creative North Star:\s*"([^"]+)"\*\*/);
288
+ const keyChars = [];
289
+ const keyCharMatch = text.match(/\*\*Key Characteristics:\*\*\s*\n([\s\S]+?)(?:\n##|\n###|$)/);
290
+ if (keyCharMatch) {
291
+ for (const line of keyCharMatch[1].split('\n')) {
292
+ const m = line.match(/^\s*[-*]\s+(.+)$/);
293
+ if (m)
294
+ keyChars.push(stripBold(m[1].trim()));
295
+ }
296
+ }
297
+ const paragraphs = collectParagraphs(section.lines).filter((p) => !p.startsWith('**Creative North Star') &&
298
+ !p.startsWith('**Key Characteristics'));
299
+ return {
300
+ subtitle: section.subtitle,
301
+ creativeNorthStar: northStar ? northStar[1] : null,
302
+ philosophy: paragraphs,
303
+ keyCharacteristics: keyChars,
304
+ };
305
+ }
306
+ function extractColors(section) {
307
+ if (!section)
308
+ return null;
309
+ const subs = splitSubsections(section.lines);
310
+ const description = collectParagraphs(subs[0].lines).join(' ');
311
+ const groups = [];
312
+ const ROLE_KEYWORDS = /^(primary|secondary|tertiary|neutral|accent)\b/i;
313
+ for (const sub of subs.slice(1)) {
314
+ if (!sub.name || /Named Rules?/i.test(sub.name) || /^The\s/i.test(sub.name))
315
+ continue;
316
+ const bullets = collectBullets(sub.lines);
317
+ const parsed = bullets.map((b) => parseColorBullet(b)).filter(Boolean);
318
+ if (parsed.length === 0)
319
+ continue;
320
+ const allRoleBullets = parsed.length > 0 && parsed.every((p) => p.name && ROLE_KEYWORDS.test(p.name));
321
+ if (allRoleBullets) {
322
+ for (const p of parsed) {
323
+ groups.push({ role: p.name, colors: [p] });
324
+ }
325
+ }
326
+ else {
327
+ groups.push({ role: sub.name, colors: parsed });
328
+ }
329
+ }
330
+ if (groups.length === 0) {
331
+ const flat = collectBullets(section.lines)
332
+ .map((b) => parseColorBullet(b))
333
+ .filter(Boolean);
334
+ if (flat.length) {
335
+ for (const p of flat) {
336
+ if (p.name && ROLE_KEYWORDS.test(p.name)) {
337
+ groups.push({ role: p.name, colors: [p] });
338
+ }
339
+ else {
340
+ const fallback = groups.find((g) => g.role === 'Palette');
341
+ if (fallback)
342
+ fallback.colors.push(p);
343
+ else
344
+ groups.push({ role: 'Palette', colors: [p] });
345
+ }
346
+ }
347
+ }
348
+ }
349
+ return {
350
+ subtitle: section.subtitle,
351
+ description: description || null,
352
+ groups,
353
+ rules: extractNamedRules(section.lines),
354
+ };
355
+ }
356
+ function parseColorBullet(bullet) {
357
+ const text = bullet.trim();
358
+ const bold = text.match(/^\*\*(.+?)\*\*\s*(.*)$/);
359
+ if (bold && bold[2].startsWith('(')) {
360
+ const value = extractParenGroup(bold[2]);
361
+ if (value !== null) {
362
+ const after = bold[2].slice(value.length + 2).trimStart();
363
+ if (after.startsWith(':')) {
364
+ return buildColor(bold[1], value, after.slice(1).trim());
365
+ }
366
+ }
367
+ }
368
+ const stitch = text.match(/^\*\*([^*]+?)\s*\(([^)]+)\):\*\*\s*(.*)$/);
369
+ if (stitch) {
370
+ return buildColor(stitch[1].trim(), stitch[2], stitch[3]);
371
+ }
372
+ const values = collectColorValues(text);
373
+ if (values.length) {
374
+ return buildColor(null, values.join(' to '), text);
375
+ }
376
+ return null;
377
+ }
378
+ function extractParenGroup(s) {
379
+ if (s[0] !== '(')
380
+ return null;
381
+ let depth = 0;
382
+ for (let i = 0; i < s.length; i++) {
383
+ if (s[i] === '(')
384
+ depth++;
385
+ else if (s[i] === ')') {
386
+ depth--;
387
+ if (depth === 0)
388
+ return s.slice(1, i);
389
+ }
390
+ }
391
+ return null;
392
+ }
393
+ function buildColor(name, rawValue, description) {
394
+ const values = collectColorValues(rawValue);
395
+ const primary = values[0] ?? rawValue.trim();
396
+ return {
397
+ name: name ? stripBold(name).trim() : null,
398
+ value: primary,
399
+ valueRange: values.length > 1 ? values : null,
400
+ format: detectFormat(primary),
401
+ description: stripBold(description || '').trim() || null,
402
+ };
403
+ }
404
+ function collectColorValues(s) {
405
+ const out = [];
406
+ s.replace(HEX_RE, (v) => { out.push(v); return v; });
407
+ s.replace(OKLCH_RE, (v) => { out.push(v); return v; });
408
+ return out;
409
+ }
410
+ function detectFormat(v) {
411
+ if (!v)
412
+ return 'unknown';
413
+ if (v.startsWith('#'))
414
+ return 'hex';
415
+ if (/^oklch/i.test(v))
416
+ return 'oklch';
417
+ if (/^rgb/i.test(v))
418
+ return 'rgb';
419
+ return 'unknown';
420
+ }
421
+ function extractTypography(section) {
422
+ if (!section)
423
+ return null;
424
+ const text = section.lines.join('\n');
425
+ const fonts = {};
426
+ const fontLineRe = /\*\*([\w\s/]+?)Font:\*\*\s*([^\n(]+?)(?:\s*\(with\s+([^)]+)\))?\s*$/gm;
427
+ let fm;
428
+ while ((fm = fontLineRe.exec(text)) !== null) {
429
+ const rawRole = fm[1].trim().toLowerCase().replace(/\s+/g, '-');
430
+ const role = normalizeFontRole(rawRole) || 'display';
431
+ fonts[role] = {
432
+ family: fm[2].trim(),
433
+ fallback: fm[3] ? fm[3].trim() : null,
434
+ };
435
+ }
436
+ if (Object.keys(fonts).length === 0) {
437
+ const stitchRe = /\*\*([\w\s&/]+?)\s*\(([^)]+)\):\*\*\s*(.+)/g;
438
+ let sm;
439
+ while ((sm = stitchRe.exec(text)) !== null) {
440
+ const rawRole = sm[1]
441
+ .trim()
442
+ .toLowerCase()
443
+ .replace(/\s*&\s*/g, '-')
444
+ .replace(/\s+/g, '-');
445
+ const role = normalizeFontRole(rawRole) || rawRole;
446
+ fonts[role] = { family: sm[2].trim(), fallback: null, purpose: sm[3].trim() };
447
+ }
448
+ }
449
+ const characterMatch = text.match(/\*\*Character:\*\*\s*([^\n]+(?:\n[^\n]+)*?)(?=\n\n|\n###|\n##|$)/);
450
+ let character = characterMatch ? characterMatch[1].replace(/\n/g, ' ').trim() : null;
451
+ if (!character) {
452
+ const paragraphs = collectParagraphs(section.lines).filter((p) => !/^\*\*[\w\s/&]+Font/i.test(p) && !/^\*\*[\w\s/&]+\([^)]+\)/.test(p));
453
+ if (paragraphs.length)
454
+ character = paragraphs[0];
455
+ }
456
+ const subs = splitSubsections(section.lines);
457
+ let hierarchy = [];
458
+ const hierSub = subs.find((s) => s.name && /hierarch/i.test(s.name));
459
+ if (hierSub) {
460
+ const bullets = collectBullets(hierSub.lines);
461
+ hierarchy = bullets.map(parseTypeBullet).filter(Boolean);
462
+ }
463
+ return {
464
+ subtitle: section.subtitle,
465
+ fonts,
466
+ character,
467
+ hierarchy,
468
+ rules: extractNamedRules(section.lines),
469
+ };
470
+ }
471
+ function normalizeFontRole(raw) {
472
+ const tokens = raw.split(/[-/&\s]+/).filter(Boolean);
473
+ const priority = ['display', 'headline', 'body', 'ui', 'label', 'mono'];
474
+ const canonical = { headline: 'display', ui: 'body' };
475
+ for (const p of priority) {
476
+ if (tokens.includes(p))
477
+ return canonical[p] || p;
478
+ }
479
+ return null;
480
+ }
481
+ function parseTypeBullet(bullet) {
482
+ const m = bullet.match(/^\*\*(.+?)\*\*\s*\(([^)]+)\):\s*(.*)$/);
483
+ if (!m)
484
+ return null;
485
+ const name = m[1].trim();
486
+ const specs = m[2].split(',').map((s) => s.trim());
487
+ return {
488
+ name,
489
+ specs,
490
+ purpose: stripBold(m[3] || '').trim() || null,
491
+ };
492
+ }
493
+ function extractElevation(section) {
494
+ if (!section)
495
+ return null;
496
+ const subs = splitSubsections(section.lines);
497
+ const description = collectParagraphs(subs[0].lines).join(' ') || null;
498
+ const shadows = [];
499
+ const seen = new Set();
500
+ const dedupe = (entry) => {
501
+ const key = (entry.name || '') + '::' + entry.value;
502
+ if (seen.has(key))
503
+ return;
504
+ seen.add(key);
505
+ shadows.push(entry);
506
+ };
507
+ for (const b of collectBullets(section.lines)) {
508
+ const parsed = parseShadowBullet(b);
509
+ if (parsed)
510
+ dedupe(parsed);
511
+ }
512
+ for (const p of collectParagraphs(section.lines)) {
513
+ for (const inline of extractInlineShadows(p))
514
+ dedupe(inline);
515
+ }
516
+ for (const b of collectBullets(section.lines)) {
517
+ for (const inline of extractInlineShadows(b))
518
+ dedupe(inline);
519
+ }
520
+ return {
521
+ subtitle: section.subtitle,
522
+ description,
523
+ shadows,
524
+ rules: extractNamedRules(section.lines),
525
+ };
526
+ }
527
+ function extractInlineShadows(text) {
528
+ const out = [];
529
+ const re = /box-shadow\s*:\s*([^`;\n]+)/gi;
530
+ let m;
531
+ while ((m = re.exec(text)) !== null) {
532
+ const value = m[1].replace(/[`.)]+$/, '').trim();
533
+ if (!value)
534
+ continue;
535
+ const before = text.slice(0, m.index);
536
+ const nameMatch = before.match(/\b([A-Za-z][A-Za-z\- ]{2,40})\s+shadow\b[^A-Za-z0-9]*$/i);
537
+ let name = null;
538
+ if (nameMatch) {
539
+ const stripped = nameMatch[1]
540
+ .replace(/^(?:use|using|apply|applying|is|are|looks? like)\s+/i, '')
541
+ .replace(/^(?:a|an|the)\s+/i, '')
542
+ .trim();
543
+ if (stripped) {
544
+ name = stripped.charAt(0).toUpperCase() + stripped.slice(1) + ' shadow';
545
+ }
546
+ }
547
+ out.push({ name, value, purpose: null });
548
+ }
549
+ return out;
550
+ }
551
+ function parseShadowBullet(bullet) {
552
+ const m = bullet.match(/^\*\*(.+?)\*\*\s*\(`?([^`]+?)`?\):\s*(.*)$/);
553
+ if (!m)
554
+ return null;
555
+ const rawValue = m[2].replace(/^box-shadow:\s*/i, '').trim();
556
+ const looksLikeShadow = /box-shadow|rgba?\(|\bpx\b|\brem\b|^-?\d+\s/i.test(rawValue) &&
557
+ /\d/.test(rawValue);
558
+ if (!looksLikeShadow)
559
+ return null;
560
+ const name = stripBold(m[1]).trim();
561
+ return {
562
+ name,
563
+ value: rawValue,
564
+ purpose: stripBold(m[3] || '').trim() || null,
565
+ };
566
+ }
567
+ function extractComponents(section) {
568
+ if (!section)
569
+ return null;
570
+ const subs = splitSubsections(section.lines);
571
+ const components = [];
572
+ for (const sub of subs.slice(1)) {
573
+ if (!sub.name)
574
+ continue;
575
+ const bullets = collectBullets(sub.lines);
576
+ const paragraphs = collectParagraphs(sub.lines);
577
+ const variants = [];
578
+ const properties = {};
579
+ for (const b of bullets) {
580
+ const m = b.match(/^\*\*(.+?):?\*\*:?\s*(.+)$/);
581
+ if (m) {
582
+ const key = stripBold(m[1]).trim();
583
+ const value = stripBold(m[2]).trim();
584
+ if (/^(primary|secondary|tertiary|ghost|hover|focus|active|disabled|default|error|selected|unselected|state)$/i.test(key.split(/[\s/]/)[0])) {
585
+ variants.push({ name: key, description: value });
586
+ }
587
+ else {
588
+ properties[key.toLowerCase()] = value;
589
+ }
590
+ }
591
+ }
592
+ components.push({
593
+ name: sub.name,
594
+ description: paragraphs.join(' ') || null,
595
+ properties,
596
+ variants,
597
+ });
598
+ }
599
+ return {
600
+ subtitle: section.subtitle,
601
+ components,
602
+ };
603
+ }
604
+ function extractDosDonts(section) {
605
+ if (!section)
606
+ return null;
607
+ const subs = splitSubsections(section.lines);
608
+ const dos = [];
609
+ const donts = [];
610
+ for (const sub of subs.slice(1)) {
611
+ if (!sub.name)
612
+ continue;
613
+ const subName = normalizeApostrophes(sub.name);
614
+ const bullets = collectBullets(sub.lines).map((b) => stripBold(b).trim());
615
+ if (/^do'?t?:?$/i.test(subName) || /^do:?$/i.test(subName)) {
616
+ dos.push(...bullets);
617
+ }
618
+ else if (/^don'?t:?$/i.test(subName)) {
619
+ donts.push(...bullets);
620
+ }
621
+ }
622
+ for (const b of collectBullets(section.lines)) {
623
+ const stripped = normalizeApostrophes(stripBold(b).trim());
624
+ if (/^don'?t\b/i.test(stripped)) {
625
+ if (!donts.some((d) => normalizeApostrophes(d) === stripped))
626
+ donts.push(stripped);
627
+ }
628
+ else if (/^do\b/i.test(stripped)) {
629
+ if (!dos.some((d) => normalizeApostrophes(d) === stripped))
630
+ dos.push(stripped);
631
+ }
632
+ }
633
+ return { dos, donts };
634
+ }
635
+ export function assessCoverage(model) {
636
+ const report = {};
637
+ report.overview = model.overview
638
+ ? {
639
+ northStar: Boolean(model.overview.creativeNorthStar),
640
+ philosophy: model.overview.philosophy.length > 0,
641
+ keyCharacteristics: model.overview.keyCharacteristics.length,
642
+ }
643
+ : 'missing';
644
+ report.colors = model.colors
645
+ ? {
646
+ groups: model.colors.groups.length,
647
+ totalColors: model.colors.groups.reduce((n, g) => n + g.colors.length, 0),
648
+ rules: model.colors.rules.length,
649
+ }
650
+ : 'missing';
651
+ report.typography = model.typography
652
+ ? {
653
+ fonts: Object.keys(model.typography.fonts).length,
654
+ hierarchyEntries: model.typography.hierarchy.length,
655
+ character: Boolean(model.typography.character),
656
+ rules: model.typography.rules.length,
657
+ }
658
+ : 'missing';
659
+ report.elevation = model.elevation
660
+ ? {
661
+ shadows: model.elevation.shadows.length,
662
+ rules: model.elevation.rules.length,
663
+ description: Boolean(model.elevation.description),
664
+ }
665
+ : 'missing';
666
+ report.components = model.components
667
+ ? {
668
+ count: model.components.components.length,
669
+ variantTotal: model.components.components.reduce((n, c) => n + c.variants.length, 0),
670
+ }
671
+ : 'missing';
672
+ report.dosDonts = model.dosDonts
673
+ ? {
674
+ dos: model.dosDonts.dos.length,
675
+ donts: model.dosDonts.donts.length,
676
+ }
677
+ : 'missing';
678
+ return report;
679
+ }
680
+ // ---------- Main ----------
681
+ export function parseDesignMd(md) {
682
+ const { frontmatter, body } = parseFrontmatterRaw(md);
683
+ const { title, sections } = splitSections(body);
684
+ return {
685
+ schemaVersion: 2,
686
+ title,
687
+ frontmatter,
688
+ overview: extractOverview(sections['Overview']),
689
+ colors: extractColors(sections['Colors']),
690
+ typography: extractTypography(sections['Typography']),
691
+ elevation: extractElevation(sections['Elevation']),
692
+ components: extractComponents(sections['Components']),
693
+ dosDonts: extractDosDonts(sections["Do's and Don'ts"]),
694
+ };
695
+ }
696
+ //# sourceMappingURL=design-parser.js.map