@calibrate-ds/cli 0.1.0

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 (201) hide show
  1. package/dist/__tests__/component-slug.test.d.ts +2 -0
  2. package/dist/__tests__/component-slug.test.d.ts.map +1 -0
  3. package/dist/__tests__/component-slug.test.js +54 -0
  4. package/dist/__tests__/component-slug.test.js.map +1 -0
  5. package/dist/__tests__/lock-changes.test.d.ts +2 -0
  6. package/dist/__tests__/lock-changes.test.d.ts.map +1 -0
  7. package/dist/__tests__/lock-changes.test.js +81 -0
  8. package/dist/__tests__/lock-changes.test.js.map +1 -0
  9. package/dist/__tests__/permissions.test.d.ts +2 -0
  10. package/dist/__tests__/permissions.test.d.ts.map +1 -0
  11. package/dist/__tests__/permissions.test.js +67 -0
  12. package/dist/__tests__/permissions.test.js.map +1 -0
  13. package/dist/__tests__/stamp-in-lock.test.d.ts +2 -0
  14. package/dist/__tests__/stamp-in-lock.test.d.ts.map +1 -0
  15. package/dist/__tests__/stamp-in-lock.test.js +102 -0
  16. package/dist/__tests__/stamp-in-lock.test.js.map +1 -0
  17. package/dist/__tests__/status-logic.test.d.ts +2 -0
  18. package/dist/__tests__/status-logic.test.d.ts.map +1 -0
  19. package/dist/__tests__/status-logic.test.js +74 -0
  20. package/dist/__tests__/status-logic.test.js.map +1 -0
  21. package/dist/__tests__/team-roster.test.d.ts +2 -0
  22. package/dist/__tests__/team-roster.test.d.ts.map +1 -0
  23. package/dist/__tests__/team-roster.test.js +97 -0
  24. package/dist/__tests__/team-roster.test.js.map +1 -0
  25. package/dist/ai/claude-provider.d.ts +13 -0
  26. package/dist/ai/claude-provider.d.ts.map +1 -0
  27. package/dist/ai/claude-provider.js +335 -0
  28. package/dist/ai/claude-provider.js.map +1 -0
  29. package/dist/ai/enhance-doc.d.ts +24 -0
  30. package/dist/ai/enhance-doc.d.ts.map +1 -0
  31. package/dist/ai/enhance-doc.js +272 -0
  32. package/dist/ai/enhance-doc.js.map +1 -0
  33. package/dist/ai/openai-provider.d.ts +13 -0
  34. package/dist/ai/openai-provider.d.ts.map +1 -0
  35. package/dist/ai/openai-provider.js +345 -0
  36. package/dist/ai/openai-provider.js.map +1 -0
  37. package/dist/ai/prompt-utils.d.ts +23 -0
  38. package/dist/ai/prompt-utils.d.ts.map +1 -0
  39. package/dist/ai/prompt-utils.js +67 -0
  40. package/dist/ai/prompt-utils.js.map +1 -0
  41. package/dist/commands/ai.d.ts +8 -0
  42. package/dist/commands/ai.d.ts.map +1 -0
  43. package/dist/commands/ai.js +576 -0
  44. package/dist/commands/ai.js.map +1 -0
  45. package/dist/commands/apply.d.ts +4 -0
  46. package/dist/commands/apply.d.ts.map +1 -0
  47. package/dist/commands/apply.js +98 -0
  48. package/dist/commands/apply.js.map +1 -0
  49. package/dist/commands/assign.d.ts +5 -0
  50. package/dist/commands/assign.d.ts.map +1 -0
  51. package/dist/commands/assign.js +180 -0
  52. package/dist/commands/assign.js.map +1 -0
  53. package/dist/commands/ci.d.ts +4 -0
  54. package/dist/commands/ci.d.ts.map +1 -0
  55. package/dist/commands/ci.js +84 -0
  56. package/dist/commands/ci.js.map +1 -0
  57. package/dist/commands/context.d.ts +19 -0
  58. package/dist/commands/context.d.ts.map +1 -0
  59. package/dist/commands/context.js +297 -0
  60. package/dist/commands/context.js.map +1 -0
  61. package/dist/commands/diff.d.ts +2 -0
  62. package/dist/commands/diff.d.ts.map +1 -0
  63. package/dist/commands/diff.js +119 -0
  64. package/dist/commands/diff.js.map +1 -0
  65. package/dist/commands/doctor.d.ts +5 -0
  66. package/dist/commands/doctor.d.ts.map +1 -0
  67. package/dist/commands/doctor.js +348 -0
  68. package/dist/commands/doctor.js.map +1 -0
  69. package/dist/commands/document-system.d.ts +5 -0
  70. package/dist/commands/document-system.d.ts.map +1 -0
  71. package/dist/commands/document-system.js +84 -0
  72. package/dist/commands/document-system.js.map +1 -0
  73. package/dist/commands/document.d.ts +6 -0
  74. package/dist/commands/document.d.ts.map +1 -0
  75. package/dist/commands/document.js +276 -0
  76. package/dist/commands/document.js.map +1 -0
  77. package/dist/commands/export.d.ts +11 -0
  78. package/dist/commands/export.d.ts.map +1 -0
  79. package/dist/commands/export.js +74 -0
  80. package/dist/commands/export.js.map +1 -0
  81. package/dist/commands/generate-components.d.ts +4 -0
  82. package/dist/commands/generate-components.d.ts.map +1 -0
  83. package/dist/commands/generate-components.js +62 -0
  84. package/dist/commands/generate-components.js.map +1 -0
  85. package/dist/commands/generate-composites.d.ts +2 -0
  86. package/dist/commands/generate-composites.d.ts.map +1 -0
  87. package/dist/commands/generate-composites.js +31 -0
  88. package/dist/commands/generate-composites.js.map +1 -0
  89. package/dist/commands/generate-tokens.d.ts +2 -0
  90. package/dist/commands/generate-tokens.d.ts.map +1 -0
  91. package/dist/commands/generate-tokens.js +48 -0
  92. package/dist/commands/generate-tokens.js.map +1 -0
  93. package/dist/commands/help-topics.d.ts +2 -0
  94. package/dist/commands/help-topics.d.ts.map +1 -0
  95. package/dist/commands/help-topics.js +324 -0
  96. package/dist/commands/help-topics.js.map +1 -0
  97. package/dist/commands/implement.d.ts +5 -0
  98. package/dist/commands/implement.d.ts.map +1 -0
  99. package/dist/commands/implement.js +595 -0
  100. package/dist/commands/implement.js.map +1 -0
  101. package/dist/commands/import-tokens.d.ts +2 -0
  102. package/dist/commands/import-tokens.d.ts.map +1 -0
  103. package/dist/commands/import-tokens.js +42 -0
  104. package/dist/commands/import-tokens.js.map +1 -0
  105. package/dist/commands/init.d.ts +6 -0
  106. package/dist/commands/init.d.ts.map +1 -0
  107. package/dist/commands/init.js +410 -0
  108. package/dist/commands/init.js.map +1 -0
  109. package/dist/commands/inspect-component.d.ts +2 -0
  110. package/dist/commands/inspect-component.d.ts.map +1 -0
  111. package/dist/commands/inspect-component.js +146 -0
  112. package/dist/commands/inspect-component.js.map +1 -0
  113. package/dist/commands/log.d.ts +4 -0
  114. package/dist/commands/log.d.ts.map +1 -0
  115. package/dist/commands/log.js +109 -0
  116. package/dist/commands/log.js.map +1 -0
  117. package/dist/commands/onboarding.d.ts +9 -0
  118. package/dist/commands/onboarding.d.ts.map +1 -0
  119. package/dist/commands/onboarding.js +63 -0
  120. package/dist/commands/onboarding.js.map +1 -0
  121. package/dist/commands/plan.d.ts +3 -0
  122. package/dist/commands/plan.d.ts.map +1 -0
  123. package/dist/commands/plan.js +108 -0
  124. package/dist/commands/plan.js.map +1 -0
  125. package/dist/commands/prompt.d.ts +5 -0
  126. package/dist/commands/prompt.d.ts.map +1 -0
  127. package/dist/commands/prompt.js +77 -0
  128. package/dist/commands/prompt.js.map +1 -0
  129. package/dist/commands/prune.d.ts +2 -0
  130. package/dist/commands/prune.d.ts.map +1 -0
  131. package/dist/commands/prune.js +95 -0
  132. package/dist/commands/prune.js.map +1 -0
  133. package/dist/commands/remove.d.ts +2 -0
  134. package/dist/commands/remove.d.ts.map +1 -0
  135. package/dist/commands/remove.js +76 -0
  136. package/dist/commands/remove.js.map +1 -0
  137. package/dist/commands/scaffold.d.ts +2 -0
  138. package/dist/commands/scaffold.d.ts.map +1 -0
  139. package/dist/commands/scaffold.js +47 -0
  140. package/dist/commands/scaffold.js.map +1 -0
  141. package/dist/commands/scan.d.ts +2 -0
  142. package/dist/commands/scan.d.ts.map +1 -0
  143. package/dist/commands/scan.js +91 -0
  144. package/dist/commands/scan.js.map +1 -0
  145. package/dist/commands/stamp.d.ts +11 -0
  146. package/dist/commands/stamp.d.ts.map +1 -0
  147. package/dist/commands/stamp.js +147 -0
  148. package/dist/commands/stamp.js.map +1 -0
  149. package/dist/commands/status.d.ts +7 -0
  150. package/dist/commands/status.d.ts.map +1 -0
  151. package/dist/commands/status.js +219 -0
  152. package/dist/commands/status.js.map +1 -0
  153. package/dist/commands/team.d.ts +7 -0
  154. package/dist/commands/team.d.ts.map +1 -0
  155. package/dist/commands/team.js +173 -0
  156. package/dist/commands/team.js.map +1 -0
  157. package/dist/index.d.ts +3 -0
  158. package/dist/index.d.ts.map +1 -0
  159. package/dist/index.js +303 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/onboarding/content.d.ts +39 -0
  162. package/dist/onboarding/content.d.ts.map +1 -0
  163. package/dist/onboarding/content.js +82 -0
  164. package/dist/onboarding/content.js.map +1 -0
  165. package/dist/utils/component-slug.d.ts +10 -0
  166. package/dist/utils/component-slug.d.ts.map +1 -0
  167. package/dist/utils/component-slug.js +13 -0
  168. package/dist/utils/component-slug.js.map +1 -0
  169. package/dist/utils/framework-registry.d.ts +41 -0
  170. package/dist/utils/framework-registry.d.ts.map +1 -0
  171. package/dist/utils/framework-registry.js +82 -0
  172. package/dist/utils/framework-registry.js.map +1 -0
  173. package/dist/utils/gitignore.d.ts +4 -0
  174. package/dist/utils/gitignore.d.ts.map +1 -0
  175. package/dist/utils/gitignore.js +67 -0
  176. package/dist/utils/gitignore.js.map +1 -0
  177. package/dist/utils/lock-changes.d.ts +14 -0
  178. package/dist/utils/lock-changes.d.ts.map +1 -0
  179. package/dist/utils/lock-changes.js +26 -0
  180. package/dist/utils/lock-changes.js.map +1 -0
  181. package/dist/utils/logger.d.ts +9 -0
  182. package/dist/utils/logger.d.ts.map +1 -0
  183. package/dist/utils/logger.js +32 -0
  184. package/dist/utils/logger.js.map +1 -0
  185. package/dist/utils/permissions.d.ts +17 -0
  186. package/dist/utils/permissions.d.ts.map +1 -0
  187. package/dist/utils/permissions.js +37 -0
  188. package/dist/utils/permissions.js.map +1 -0
  189. package/dist/utils/status-logic.d.ts +16 -0
  190. package/dist/utils/status-logic.d.ts.map +1 -0
  191. package/dist/utils/status-logic.js +32 -0
  192. package/dist/utils/status-logic.js.map +1 -0
  193. package/dist/utils/storybook-setup.d.ts +5 -0
  194. package/dist/utils/storybook-setup.d.ts.map +1 -0
  195. package/dist/utils/storybook-setup.js +104 -0
  196. package/dist/utils/storybook-setup.js.map +1 -0
  197. package/dist/utils/team-roster.d.ts +35 -0
  198. package/dist/utils/team-roster.d.ts.map +1 -0
  199. package/dist/utils/team-roster.js +99 -0
  200. package/dist/utils/team-roster.js.map +1 -0
  201. package/package.json +44 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=component-slug.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-slug.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/component-slug.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { toComponentSlug, findComponentSlug } from "../utils/component-slug.js";
3
+ describe("toComponentSlug", () => {
4
+ it("lowercases and hyphenates spaces", () => {
5
+ expect(toComponentSlug("Button")).toBe("button");
6
+ expect(toComponentSlug("Notification Item")).toBe("notification-item");
7
+ });
8
+ it("collapses consecutive non-alphanumeric chars to a single hyphen", () => {
9
+ expect(toComponentSlug("Nav Arrow/Down")).toBe("nav-arrow-down");
10
+ });
11
+ it("strips leading and trailing hyphens", () => {
12
+ expect(toComponentSlug(" Button ")).toBe("button");
13
+ expect(toComponentSlug("-button-")).toBe("button");
14
+ });
15
+ it("handles already-slugged strings unchanged", () => {
16
+ expect(toComponentSlug("notification-item")).toBe("notification-item");
17
+ });
18
+ it("handles special characters", () => {
19
+ expect(toComponentSlug("Icon/Arrow.Up")).toBe("icon-arrow-up");
20
+ });
21
+ it("handles empty string", () => {
22
+ expect(toComponentSlug("")).toBe("");
23
+ });
24
+ it("handles numeric names", () => {
25
+ expect(toComponentSlug("H1 Heading")).toBe("h1-heading");
26
+ });
27
+ });
28
+ describe("findComponentSlug", () => {
29
+ const components = {
30
+ "button": { displayName: "Button" },
31
+ "notification-item": { displayName: "Notification Item" },
32
+ "nav-arrow-down": { displayName: "Nav Arrow Down" },
33
+ };
34
+ it("matches by exact slug key", () => {
35
+ expect(findComponentSlug("button", components)).toBe("button");
36
+ expect(findComponentSlug("notification-item", components)).toBe("notification-item");
37
+ });
38
+ it("matches by display name (case-insensitive)", () => {
39
+ expect(findComponentSlug("Button", components)).toBe("button");
40
+ expect(findComponentSlug("notification item", components)).toBe("notification-item");
41
+ expect(findComponentSlug("NOTIFICATION ITEM", components)).toBe("notification-item");
42
+ });
43
+ it("derives slug from name and matches", () => {
44
+ expect(findComponentSlug("Nav Arrow Down", components)).toBe("nav-arrow-down");
45
+ });
46
+ it("returns undefined for no match", () => {
47
+ expect(findComponentSlug("Tooltip", components)).toBeUndefined();
48
+ expect(findComponentSlug("", components)).toBeUndefined();
49
+ });
50
+ it("returns undefined for empty component map", () => {
51
+ expect(findComponentSlug("Button", {})).toBeUndefined();
52
+ });
53
+ });
54
+ //# sourceMappingURL=component-slug.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-slug.test.js","sourceRoot":"","sources":["../../src/__tests__/component-slug.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=lock-changes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-changes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/lock-changes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { detectLockChanges } from "../utils/lock-changes.js";
3
+ function lock(components) {
4
+ return { schemaVersion: "1.0.0", components };
5
+ }
6
+ const base = {
7
+ button: { displayName: "Button", designHash: "aaa", stampedHash: "aaa" },
8
+ badge: { displayName: "Badge", designHash: "bbb" },
9
+ };
10
+ describe("detectLockChanges", () => {
11
+ it("returns empty array when nothing changed", () => {
12
+ expect(detectLockChanges(lock(base), lock(base))).toEqual([]);
13
+ });
14
+ it("detects a new component", () => {
15
+ const next = lock({ ...base, tooltip: { displayName: "Tooltip", designHash: "ccc" } });
16
+ const changes = detectLockChanges(next, lock(base));
17
+ expect(changes).toContainEqual({ event: "component added", displayName: "Tooltip" });
18
+ });
19
+ it("detects a removed component", () => {
20
+ const next = lock({ button: base.button });
21
+ const changes = detectLockChanges(next, lock(base));
22
+ expect(changes).toContainEqual({ event: "component removed", displayName: "Badge" });
23
+ });
24
+ it("detects a stamp (stampedHash changes from absent to present)", () => {
25
+ const next = lock({
26
+ ...base,
27
+ badge: { displayName: "Badge", designHash: "bbb", stampedHash: "bbb" },
28
+ });
29
+ const changes = detectLockChanges(next, lock(base));
30
+ expect(changes).toContainEqual({ event: "stamped", displayName: "Badge", stampedBy: undefined });
31
+ });
32
+ it("includes stampedBy when present in the entry", () => {
33
+ const next = lock({
34
+ ...base,
35
+ badge: { displayName: "Badge", designHash: "bbb", stampedHash: "bbb", stampedBy: "Alice <alice@example.com>" },
36
+ });
37
+ const changes = detectLockChanges(next, lock(base));
38
+ expect(changes).toContainEqual({
39
+ event: "stamped",
40
+ displayName: "Badge",
41
+ stampedBy: "Alice <alice@example.com>",
42
+ });
43
+ });
44
+ it("detects a design hash change (rescan updated designHash)", () => {
45
+ const next = lock({
46
+ ...base,
47
+ button: { displayName: "Button", designHash: "aaa-NEW", stampedHash: "aaa" },
48
+ });
49
+ const changes = detectLockChanges(next, lock(base));
50
+ expect(changes).toContainEqual({ event: "design changed", displayName: "Button" });
51
+ });
52
+ it("does not report design change when designHash was already different in previous", () => {
53
+ // Same as base — no diff
54
+ const changes = detectLockChanges(lock(base), lock(base));
55
+ expect(changes.filter(c => c.event === "design changed")).toHaveLength(0);
56
+ });
57
+ it("handles multiple changes in one diff", () => {
58
+ const next = lock({
59
+ button: { displayName: "Button", designHash: "aaa-NEW", stampedHash: "aaa" }, // design changed
60
+ badge: { displayName: "Badge", designHash: "bbb", stampedHash: "bbb" }, // stamped
61
+ tooltip: { displayName: "Tooltip", designHash: "ccc" }, // added
62
+ // modal removed
63
+ });
64
+ const prev = lock({
65
+ ...base,
66
+ modal: { displayName: "Modal", designHash: "ddd" },
67
+ });
68
+ const changes = detectLockChanges(next, prev);
69
+ expect(changes).toContainEqual({ event: "design changed", displayName: "Button" });
70
+ expect(changes).toContainEqual({ event: "stamped", displayName: "Badge", stampedBy: undefined });
71
+ expect(changes).toContainEqual({ event: "component added", displayName: "Tooltip" });
72
+ expect(changes).toContainEqual({ event: "component removed", displayName: "Modal" });
73
+ });
74
+ it("does not report unstamped re-stamp as a change", () => {
75
+ // stampedHash was absent before and still absent — not a stamp event
76
+ const prev = lock({ button: { displayName: "Button", designHash: "aaa" } });
77
+ const next = lock({ button: { displayName: "Button", designHash: "aaa" } });
78
+ expect(detectLockChanges(next, prev)).toEqual([]);
79
+ });
80
+ });
81
+ //# sourceMappingURL=lock-changes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-changes.test.js","sourceRoot":"","sources":["../../src/__tests__/lock-changes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=permissions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/permissions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { canAssignToOther, isSelfAssign, canClearAssignment, canStampComponent, } from "../utils/permissions.js";
3
+ describe("canAssignToOther", () => {
4
+ it("allows leads", () => {
5
+ expect(canAssignToOther("lead")).toBe(true);
6
+ });
7
+ it("blocks members", () => {
8
+ expect(canAssignToOther("member")).toBe(false);
9
+ });
10
+ });
11
+ describe("isSelfAssign", () => {
12
+ it("matches same handle without @ prefix", () => {
13
+ expect(isSelfAssign("alice-smith", "@alice-smith")).toBe(true);
14
+ });
15
+ it("matches same handle with @ on both", () => {
16
+ expect(isSelfAssign("@alice-smith", "@alice-smith")).toBe(true);
17
+ });
18
+ it("is case-insensitive", () => {
19
+ expect(isSelfAssign("Alice-Smith", "@alice-smith")).toBe(true);
20
+ });
21
+ it("returns false for different handles", () => {
22
+ expect(isSelfAssign("bob-jones", "@alice-smith")).toBe(false);
23
+ });
24
+ it("returns false when myHandle is undefined", () => {
25
+ expect(isSelfAssign("@alice-smith", undefined)).toBe(false);
26
+ });
27
+ it("returns false when myHandle is empty string", () => {
28
+ expect(isSelfAssign("@alice-smith", "")).toBe(false);
29
+ });
30
+ });
31
+ describe("canClearAssignment", () => {
32
+ it("leads can always clear", () => {
33
+ expect(canClearAssignment("lead", "@alice", "@alice")).toBe(true);
34
+ expect(canClearAssignment("lead", "@alice", "@bob")).toBe(true);
35
+ expect(canClearAssignment("lead", "@alice", undefined)).toBe(true);
36
+ });
37
+ it("members can clear their own assignment", () => {
38
+ expect(canClearAssignment("member", "@alice", "@alice")).toBe(true);
39
+ });
40
+ it("members cannot clear someone else's assignment", () => {
41
+ expect(canClearAssignment("member", "@alice", "@bob")).toBe(false);
42
+ });
43
+ it("members without a handle cannot clear", () => {
44
+ expect(canClearAssignment("member", "@alice", undefined)).toBe(false);
45
+ });
46
+ });
47
+ describe("canStampComponent", () => {
48
+ it("leads can stamp anything", () => {
49
+ expect(canStampComponent("lead", "@alice", "@alice")).toBe(true);
50
+ expect(canStampComponent("lead", "@alice", "@bob")).toBe(true);
51
+ expect(canStampComponent("lead", undefined, undefined)).toBe(true);
52
+ });
53
+ it("members can stamp unassigned components", () => {
54
+ expect(canStampComponent("member", undefined, "@alice")).toBe(true);
55
+ expect(canStampComponent("member", undefined, undefined)).toBe(true);
56
+ });
57
+ it("members can stamp their own assignment", () => {
58
+ expect(canStampComponent("member", "@alice", "@alice")).toBe(true);
59
+ });
60
+ it("members cannot stamp someone else's assignment", () => {
61
+ expect(canStampComponent("member", "@alice", "@bob")).toBe(false);
62
+ });
63
+ it("members without a handle cannot stamp an assigned component", () => {
64
+ expect(canStampComponent("member", "@alice", undefined)).toBe(false);
65
+ });
66
+ });
67
+ //# sourceMappingURL=permissions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.test.js","sourceRoot":"","sources":["../../src/__tests__/permissions.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stamp-in-lock.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp-in-lock.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/stamp-in-lock.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import * as fs from "node:fs/promises";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+ import { stampInLock } from "../commands/stamp.js";
6
+ // ── helpers ──────────────────────────────────────────────────────────────────
7
+ function minimalConfig(rootDir) {
8
+ return {
9
+ designTool: { provider: "figma", fileKey: "TEST", accessTokenEnv: "FIGMA_ACCESS_TOKEN" },
10
+ project: { rootDir },
11
+ output: { componentsDir: "src/components", tokensDir: "src/tokens", metadataDir: ".ptb" },
12
+ framework: { name: "react", typescript: true, styling: "css-modules" },
13
+ generation: { stories: false, tests: false, types: true, indexFiles: true, cssFiles: false },
14
+ naming: { componentCase: "PascalCase", folderCase: "kebab-case", tokenCase: "camelCase" },
15
+ classification: { treatComponentSetsAsCanonical: true, treatStandaloneComponentsAsCanonical: true, treatNestedInstancesAsReferences: true },
16
+ };
17
+ }
18
+ function baseLock() {
19
+ return {
20
+ schemaVersion: "1.0.0",
21
+ components: {
22
+ button: { displayName: "Button", designHash: "aaa111" },
23
+ tooltip: { displayName: "Tooltip", designHash: "bbb222" },
24
+ },
25
+ };
26
+ }
27
+ async function writeLock(dir, lock) {
28
+ await fs.writeFile(path.join(dir, "ptb.lock"), JSON.stringify(lock, null, 2) + "\n", "utf-8");
29
+ }
30
+ async function readLock(dir) {
31
+ return JSON.parse(await fs.readFile(path.join(dir, "ptb.lock"), "utf-8"));
32
+ }
33
+ // ── setup / teardown ─────────────────────────────────────────────────────────
34
+ let tmpDir;
35
+ beforeEach(async () => {
36
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "ptb-test-"));
37
+ });
38
+ afterEach(async () => {
39
+ await fs.rm(tmpDir, { recursive: true, force: true });
40
+ });
41
+ // ── tests ─────────────────────────────────────────────────────────────────────
42
+ describe("stampInLock", () => {
43
+ it("returns false when ptb.lock does not exist", async () => {
44
+ const config = minimalConfig(tmpDir);
45
+ const result = await stampInLock(config, "button");
46
+ expect(result).toBe(false);
47
+ });
48
+ it("stamps a component by slug and returns true", async () => {
49
+ await writeLock(tmpDir, baseLock());
50
+ const config = minimalConfig(tmpDir);
51
+ const result = await stampInLock(config, "button");
52
+ expect(result).toBe(true);
53
+ const lock = await readLock(tmpDir);
54
+ expect(lock.components.button.stampedHash).toBe("aaa111");
55
+ expect(lock.components.button.stampedAt).toBeDefined();
56
+ });
57
+ it("stamps a component by display name (case-insensitive)", async () => {
58
+ await writeLock(tmpDir, baseLock());
59
+ const config = minimalConfig(tmpDir);
60
+ const result = await stampInLock(config, "Tooltip");
61
+ expect(result).toBe(true);
62
+ const lock = await readLock(tmpDir);
63
+ expect(lock.components.tooltip.stampedHash).toBe("bbb222");
64
+ });
65
+ it("returns false when the component is not in the lock", async () => {
66
+ await writeLock(tmpDir, baseLock());
67
+ const config = minimalConfig(tmpDir);
68
+ const result = await stampInLock(config, "nonexistent-component");
69
+ expect(result).toBe(false);
70
+ });
71
+ it("does not modify other components when stamping one", async () => {
72
+ await writeLock(tmpDir, baseLock());
73
+ const config = minimalConfig(tmpDir);
74
+ await stampInLock(config, "button");
75
+ const lock = await readLock(tmpDir);
76
+ // tooltip should remain untouched
77
+ expect(lock.components.tooltip.stampedHash).toBeUndefined();
78
+ expect(lock.components.tooltip.stampedAt).toBeUndefined();
79
+ });
80
+ it("attaches a message when provided", async () => {
81
+ await writeLock(tmpDir, baseLock());
82
+ const config = minimalConfig(tmpDir);
83
+ await stampInLock(config, "button", "implements focus ring per v3 spec");
84
+ const lock = await readLock(tmpDir);
85
+ expect(lock.components.button.stampedMessage).toBe("implements focus ring per v3 spec");
86
+ });
87
+ it("overwrites a previous stamp with the latest designHash", async () => {
88
+ const initial = baseLock();
89
+ initial.components.button = {
90
+ displayName: "Button",
91
+ designHash: "aaa-new",
92
+ stampedHash: "aaa-old", // stale stamp
93
+ stampedAt: "2024-01-01T00:00:00.000Z",
94
+ };
95
+ await writeLock(tmpDir, initial);
96
+ const config = minimalConfig(tmpDir);
97
+ await stampInLock(config, "button");
98
+ const lock = await readLock(tmpDir);
99
+ expect(lock.components.button.stampedHash).toBe("aaa-new");
100
+ });
101
+ });
102
+ //# sourceMappingURL=stamp-in-lock.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp-in-lock.test.js","sourceRoot":"","sources":["../../src/__tests__/stamp-in-lock.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=status-logic.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-logic.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/status-logic.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { computeComponentStatus, computeStatusSummary, isAssignedToHandle, } from "../utils/status-logic.js";
3
+ function entry(overrides = {}) {
4
+ return {
5
+ displayName: "Button",
6
+ designHash: "abc123",
7
+ ...overrides,
8
+ };
9
+ }
10
+ function lock(components) {
11
+ return { schemaVersion: "1.0.0", components };
12
+ }
13
+ describe("computeComponentStatus", () => {
14
+ it("returns 'not-implemented' when stampedHash is absent", () => {
15
+ expect(computeComponentStatus(entry())).toBe("not-implemented");
16
+ expect(computeComponentStatus(entry({ stampedHash: undefined }))).toBe("not-implemented");
17
+ });
18
+ it("returns 'ready' when stampedHash equals designHash", () => {
19
+ expect(computeComponentStatus(entry({ stampedHash: "abc123" }))).toBe("ready");
20
+ });
21
+ it("returns 'stale' when stampedHash differs from designHash", () => {
22
+ expect(computeComponentStatus(entry({ stampedHash: "old999" }))).toBe("stale");
23
+ });
24
+ });
25
+ describe("computeStatusSummary", () => {
26
+ it("counts all three statuses correctly", () => {
27
+ const l = lock({
28
+ button: entry({ stampedHash: "abc123" }), // ready
29
+ badge: entry({ stampedHash: "old999" }), // stale
30
+ tooltip: entry(), // not-implemented
31
+ modal: entry({ stampedHash: "abc123" }), // ready
32
+ });
33
+ expect(computeStatusSummary(l)).toEqual({ ready: 2, stale: 1, notImplemented: 1 });
34
+ });
35
+ it("returns all zeros for an empty lock", () => {
36
+ expect(computeStatusSummary(lock({}))).toEqual({ ready: 0, stale: 0, notImplemented: 0 });
37
+ });
38
+ it("counts all ready", () => {
39
+ const l = lock({
40
+ button: entry({ stampedHash: "abc123" }),
41
+ badge: entry({ designHash: "xyz", stampedHash: "xyz" }),
42
+ });
43
+ expect(computeStatusSummary(l)).toEqual({ ready: 2, stale: 0, notImplemented: 0 });
44
+ });
45
+ it("counts all not-implemented", () => {
46
+ const l = lock({
47
+ button: entry(),
48
+ tooltip: entry(),
49
+ });
50
+ expect(computeStatusSummary(l)).toEqual({ ready: 0, stale: 0, notImplemented: 2 });
51
+ });
52
+ });
53
+ describe("isAssignedToHandle", () => {
54
+ it("matches when assignedTo has @ prefix", () => {
55
+ expect(isAssignedToHandle("@alice-smith", "alice-smith")).toBe(true);
56
+ });
57
+ it("matches without @ prefix", () => {
58
+ expect(isAssignedToHandle("alice-smith", "alice-smith")).toBe(true);
59
+ });
60
+ it("is case-insensitive", () => {
61
+ expect(isAssignedToHandle("@Alice-Smith", "alice-smith")).toBe(true);
62
+ expect(isAssignedToHandle("@alice-smith", "Alice-Smith")).toBe(true);
63
+ });
64
+ it("returns false for a different handle", () => {
65
+ expect(isAssignedToHandle("@alice-smith", "bob-jones")).toBe(false);
66
+ });
67
+ it("returns false when assignedTo is undefined", () => {
68
+ expect(isAssignedToHandle(undefined, "alice-smith")).toBe(false);
69
+ });
70
+ it("returns false when assignedTo is empty string", () => {
71
+ expect(isAssignedToHandle("", "alice-smith")).toBe(false);
72
+ });
73
+ });
74
+ //# sourceMappingURL=status-logic.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-logic.test.js","sourceRoot":"","sources":["../../src/__tests__/status-logic.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACH,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,GACrB,MAAM,0BAA0B,CAAC;AAGlC,SAAS,KAAK,CAAC,YAAmC,EAAE;IAChD,OAAO;QACH,WAAW,EAAE,QAAQ;QACrB,UAAU,EAAE,QAAQ;QACpB,GAAG,SAAS;KACf,CAAC;AACN,CAAC;AAED,SAAS,IAAI,CAAC,UAAwC;IAClD,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AAClD,CAAC;AAED,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,sBAAsB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChE,MAAM,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,IAAI,CAAC;YACX,MAAM,EAAG,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAc,QAAQ;YAC/D,KAAK,EAAI,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAc,QAAQ;YAC/D,OAAO,EAAE,KAAK,EAAE,EAAwC,kBAAkB;YAC1E,KAAK,EAAI,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAc,QAAQ;SAClE,CAAC,CAAC;QACH,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC;YACX,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;YACxC,KAAK,EAAG,KAAK,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;SAC3D,CAAC,CAAC;QACH,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC;YACX,MAAM,EAAG,KAAK,EAAE;YAChB,OAAO,EAAE,KAAK,EAAE;SACnB,CAAC,CAAC;QACH,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,kBAAkB,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,kBAAkB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,kBAAkB,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,MAAM,CAAC,kBAAkB,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,kBAAkB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,kBAAkB,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=team-roster.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-roster.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/team-roster.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { toHandle, findMember, findClosestMember, getCurrentUserRole, memberMatchesAssignee, } from "../utils/team-roster.js";
3
+ function member(overrides = {}) {
4
+ return {
5
+ name: "Alice Smith",
6
+ email: "alice@example.com",
7
+ handle: "alice-smith",
8
+ joinedAt: "2024-01-01T00:00:00Z",
9
+ role: "member",
10
+ ...overrides,
11
+ };
12
+ }
13
+ function roster(members) {
14
+ const obj = {};
15
+ for (const m of members)
16
+ obj[m.email] = m;
17
+ return { members: obj };
18
+ }
19
+ describe("toHandle", () => {
20
+ it("lowercases and hyphenates spaces", () => {
21
+ expect(toHandle("Alice Smith")).toBe("alice-smith");
22
+ });
23
+ it("strips special characters", () => {
24
+ expect(toHandle("O'Brien")).toBe("obrien");
25
+ });
26
+ it("trims whitespace", () => {
27
+ expect(toHandle(" bob ")).toBe("bob");
28
+ });
29
+ it("collapses multiple spaces into one hyphen", () => {
30
+ expect(toHandle("Jean Claude")).toBe("jean-claude");
31
+ });
32
+ });
33
+ describe("findMember", () => {
34
+ const alice = member();
35
+ const bob = member({ name: "Bob Jones", email: "bob@example.com", handle: "bob-jones" });
36
+ const r = roster([alice, bob]);
37
+ it("finds by exact handle (without @)", () => {
38
+ expect(findMember("alice-smith", r)).toBe(alice);
39
+ });
40
+ it("finds by handle with @ prefix", () => {
41
+ expect(findMember("@alice-smith", r)).toBe(alice);
42
+ });
43
+ it("finds by email", () => {
44
+ expect(findMember("bob@example.com", r)).toBe(bob);
45
+ });
46
+ it("finds by name (case-insensitive)", () => {
47
+ expect(findMember("alice smith", r)).toBe(alice);
48
+ });
49
+ it("returns undefined for no match", () => {
50
+ expect(findMember("charlie", r)).toBeUndefined();
51
+ });
52
+ });
53
+ describe("findClosestMember", () => {
54
+ const alice = member({ handle: "alice" });
55
+ const bob = member({ name: "Bob Jones", email: "bob@example.com", handle: "bob" });
56
+ const r = roster([alice, bob]);
57
+ it("finds member with 1-char typo", () => {
58
+ const result = findClosestMember("alicee", r);
59
+ expect(result?.member).toBe(alice);
60
+ expect(result?.distance).toBe(1);
61
+ });
62
+ it("returns undefined when typo is too large", () => {
63
+ expect(findClosestMember("xxxxxx", r)).toBeUndefined();
64
+ });
65
+ it("picks the closer match when both are within threshold", () => {
66
+ const result = findClosestMember("boba", r);
67
+ expect(result?.member).toBe(bob);
68
+ });
69
+ });
70
+ describe("getCurrentUserRole", () => {
71
+ it("returns 'lead' for empty roster (solo/bootstrap scenario)", () => {
72
+ expect(getCurrentUserRole(roster([]))).toBe("lead");
73
+ });
74
+ it("returns 'lead' when no members have a role set (pre-role migration)", () => {
75
+ const oldMember = { ...member(), role: undefined };
76
+ expect(getCurrentUserRole(roster([oldMember]))).toBe("lead");
77
+ });
78
+ });
79
+ describe("memberMatchesAssignee", () => {
80
+ const alice = member();
81
+ it("matches by handle without @", () => {
82
+ expect(memberMatchesAssignee(alice, "alice-smith")).toBe(true);
83
+ });
84
+ it("matches by handle with @ prefix", () => {
85
+ expect(memberMatchesAssignee(alice, "@alice-smith")).toBe(true);
86
+ });
87
+ it("matches by email", () => {
88
+ expect(memberMatchesAssignee(alice, "alice@example.com")).toBe(true);
89
+ });
90
+ it("matches by name (case-insensitive)", () => {
91
+ expect(memberMatchesAssignee(alice, "alice smith")).toBe(true);
92
+ });
93
+ it("does not match a different user", () => {
94
+ expect(memberMatchesAssignee(alice, "bob")).toBe(false);
95
+ });
96
+ });
97
+ //# sourceMappingURL=team-roster.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-roster.test.js","sourceRoot":"","sources":["../../src/__tests__/team-roster.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ImplementationProvider, ComponentImplementationResult, ComponentPromptPackage } from "@calibrate-ds/shared-types";
2
+ export declare class ClaudeProvider implements ImplementationProvider {
3
+ name: string;
4
+ private client;
5
+ private model;
6
+ constructor(apiKey: string, model?: string);
7
+ implementComponent(params: {
8
+ componentName: string;
9
+ framework: string | null;
10
+ promptPackage: ComponentPromptPackage;
11
+ }): Promise<ComponentImplementationResult>;
12
+ }
13
+ //# sourceMappingURL=claude-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-provider.d.ts","sourceRoot":"","sources":["../../src/ai/claude-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,sBAAsB,EAAiB,MAAM,4BAA4B,CAAC;AAoS/I,qBAAa,cAAe,YAAW,sBAAsB;IACzD,IAAI,SAAwB;IAC5B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,SAAsB;IAKjD,kBAAkB,CAAC,MAAM,EAAE;QAC7B,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,aAAa,EAAE,sBAAsB,CAAC;KACzC,GAAG,OAAO,CAAC,6BAA6B,CAAC;CA+C7C"}