@stilhq/core 0.6.0 → 0.6.1

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 (154) hide show
  1. package/dist/changeset/apply.d.ts +11 -0
  2. package/dist/changeset/apply.d.ts.map +1 -0
  3. package/dist/changeset/apply.js +239 -0
  4. package/dist/changeset/apply.js.map +1 -0
  5. package/dist/changeset/ci-report.d.ts +25 -0
  6. package/dist/changeset/ci-report.d.ts.map +1 -0
  7. package/dist/changeset/ci-report.js +206 -0
  8. package/dist/changeset/ci-report.js.map +1 -0
  9. package/dist/changeset/classify.d.ts +77 -0
  10. package/dist/changeset/classify.d.ts.map +1 -0
  11. package/dist/changeset/classify.js +285 -0
  12. package/dist/changeset/classify.js.map +1 -0
  13. package/dist/changeset/cli-gate.d.ts +46 -0
  14. package/dist/changeset/cli-gate.d.ts.map +1 -0
  15. package/dist/changeset/cli-gate.js +409 -0
  16. package/dist/changeset/cli-gate.js.map +1 -0
  17. package/dist/changeset/conflict.d.ts +11 -0
  18. package/dist/changeset/conflict.d.ts.map +1 -0
  19. package/dist/changeset/conflict.js +130 -0
  20. package/dist/changeset/conflict.js.map +1 -0
  21. package/dist/changeset/diff-components.d.ts +80 -0
  22. package/dist/changeset/diff-components.d.ts.map +1 -0
  23. package/dist/changeset/diff-components.js +264 -0
  24. package/dist/changeset/diff-components.js.map +1 -0
  25. package/dist/changeset/diff-text.d.ts +66 -0
  26. package/dist/changeset/diff-text.d.ts.map +1 -0
  27. package/dist/changeset/diff-text.js +284 -0
  28. package/dist/changeset/diff-text.js.map +1 -0
  29. package/dist/changeset/diff-tokens.d.ts +63 -0
  30. package/dist/changeset/diff-tokens.d.ts.map +1 -0
  31. package/dist/changeset/diff-tokens.js +483 -0
  32. package/dist/changeset/diff-tokens.js.map +1 -0
  33. package/dist/changeset/impact.d.ts +31 -0
  34. package/dist/changeset/impact.d.ts.map +1 -0
  35. package/dist/changeset/impact.js +188 -0
  36. package/dist/changeset/impact.js.map +1 -0
  37. package/dist/changeset/pr-body.d.ts +33 -0
  38. package/dist/changeset/pr-body.d.ts.map +1 -0
  39. package/dist/changeset/pr-body.js +421 -0
  40. package/dist/changeset/pr-body.js.map +1 -0
  41. package/dist/changeset/quality-gate.d.ts +10 -0
  42. package/dist/changeset/quality-gate.d.ts.map +1 -0
  43. package/dist/changeset/quality-gate.js +207 -0
  44. package/dist/changeset/quality-gate.js.map +1 -0
  45. package/dist/changeset/rollback.d.ts +19 -0
  46. package/dist/changeset/rollback.d.ts.map +1 -0
  47. package/dist/changeset/rollback.js +163 -0
  48. package/dist/changeset/rollback.js.map +1 -0
  49. package/dist/ci/template-generator.d.ts +22 -0
  50. package/dist/ci/template-generator.d.ts.map +1 -0
  51. package/dist/ci/template-generator.js +215 -0
  52. package/dist/ci/template-generator.js.map +1 -0
  53. package/dist/gate/engine.d.ts +20 -0
  54. package/dist/gate/engine.d.ts.map +1 -0
  55. package/dist/gate/engine.js +109 -0
  56. package/dist/gate/engine.js.map +1 -0
  57. package/dist/gate/types.d.ts +76 -0
  58. package/dist/gate/types.d.ts.map +1 -0
  59. package/dist/gate/types.js +6 -0
  60. package/dist/gate/types.js.map +1 -0
  61. package/dist/index.d.ts +54 -0
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +144 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/pr/comment-manager.d.ts +33 -0
  66. package/dist/pr/comment-manager.d.ts.map +1 -0
  67. package/dist/pr/comment-manager.js +137 -0
  68. package/dist/pr/comment-manager.js.map +1 -0
  69. package/dist/pr/conflict.d.ts +34 -0
  70. package/dist/pr/conflict.d.ts.map +1 -0
  71. package/dist/pr/conflict.js +127 -0
  72. package/dist/pr/conflict.js.map +1 -0
  73. package/dist/pr/generator.d.ts +70 -0
  74. package/dist/pr/generator.d.ts.map +1 -0
  75. package/dist/pr/generator.js +241 -0
  76. package/dist/pr/generator.js.map +1 -0
  77. package/dist/pr/github-api.d.ts +122 -0
  78. package/dist/pr/github-api.d.ts.map +1 -0
  79. package/dist/pr/github-api.js +269 -0
  80. package/dist/pr/github-api.js.map +1 -0
  81. package/dist/report/changeset-report.d.ts +10 -0
  82. package/dist/report/changeset-report.d.ts.map +1 -0
  83. package/dist/report/changeset-report.js +14 -0
  84. package/dist/report/changeset-report.js.map +1 -0
  85. package/dist/report/classification-report.d.ts +12 -0
  86. package/dist/report/classification-report.d.ts.map +1 -0
  87. package/dist/report/classification-report.js +157 -0
  88. package/dist/report/classification-report.js.map +1 -0
  89. package/dist/report/impact-report.d.ts +10 -0
  90. package/dist/report/impact-report.d.ts.map +1 -0
  91. package/dist/report/impact-report.js +47 -0
  92. package/dist/report/impact-report.js.map +1 -0
  93. package/dist/report/summary-report.d.ts +10 -0
  94. package/dist/report/summary-report.d.ts.map +1 -0
  95. package/dist/report/summary-report.js +96 -0
  96. package/dist/report/summary-report.js.map +1 -0
  97. package/dist/report/writer.d.ts +17 -0
  98. package/dist/report/writer.d.ts.map +1 -0
  99. package/dist/report/writer.js +41 -0
  100. package/dist/report/writer.js.map +1 -0
  101. package/dist/rules/engine.d.ts +46 -0
  102. package/dist/rules/engine.d.ts.map +1 -0
  103. package/dist/rules/engine.js +200 -0
  104. package/dist/rules/engine.js.map +1 -0
  105. package/dist/rules/index.d.ts +10 -0
  106. package/dist/rules/index.d.ts.map +1 -0
  107. package/dist/rules/index.js +25 -0
  108. package/dist/rules/index.js.map +1 -0
  109. package/dist/rules/loader.d.ts +20 -0
  110. package/dist/rules/loader.d.ts.map +1 -0
  111. package/dist/rules/loader.js +93 -0
  112. package/dist/rules/loader.js.map +1 -0
  113. package/dist/rules/packs/core.d.ts +14 -0
  114. package/dist/rules/packs/core.d.ts.map +1 -0
  115. package/dist/rules/packs/core.js +377 -0
  116. package/dist/rules/packs/core.js.map +1 -0
  117. package/dist/rules/schema.d.ts +12 -0
  118. package/dist/rules/schema.d.ts.map +1 -0
  119. package/dist/rules/schema.js +149 -0
  120. package/dist/rules/schema.js.map +1 -0
  121. package/dist/rules/types.d.ts +86 -0
  122. package/dist/rules/types.d.ts.map +1 -0
  123. package/dist/rules/types.js +9 -0
  124. package/dist/rules/types.js.map +1 -0
  125. package/dist/types/changeset.d.ts +509 -0
  126. package/dist/types/changeset.d.ts.map +1 -0
  127. package/dist/types/changeset.js +14 -0
  128. package/dist/types/changeset.js.map +1 -0
  129. package/dist/w3c/__tests__/fixtures/tokens-studio-export.json +58 -0
  130. package/dist/w3c/parser.d.ts +44 -0
  131. package/dist/w3c/parser.d.ts.map +1 -0
  132. package/dist/w3c/parser.js +146 -0
  133. package/dist/w3c/parser.js.map +1 -0
  134. package/dist/w3c/types.d.ts +31 -0
  135. package/dist/w3c/types.d.ts.map +1 -0
  136. package/dist/w3c/types.js +7 -0
  137. package/dist/w3c/types.js.map +1 -0
  138. package/dist/w3c/validator.d.ts +14 -0
  139. package/dist/w3c/validator.d.ts.map +1 -0
  140. package/dist/w3c/validator.js +162 -0
  141. package/dist/w3c/validator.js.map +1 -0
  142. package/package.json +3 -2
  143. package/templates/stil-quality-gate.yml +122 -0
  144. package/templates/workspace/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  145. package/templates/workspace/.github/workflows/stil-gate.yml +42 -0
  146. package/templates/workspace/.github/workflows/stil-preview.yml +29 -0
  147. package/templates/workspace/.stil/components/snapshots/.gitkeep +0 -0
  148. package/templates/workspace/.stil/reports/.gitkeep +0 -0
  149. package/templates/workspace/.stil/rules/.gitkeep +0 -0
  150. package/templates/workspace/.stil/text/snapshots/.gitkeep +0 -0
  151. package/templates/workspace/.stil/tokens/config/mapping.json +16 -0
  152. package/templates/workspace/.stil/tokens/raw/.gitkeep +0 -0
  153. package/templates/workspace/README.md +40 -0
  154. package/templates/workspace/package.json +19 -0
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ // impact.ts — Impact v0(最小版)
3
+ //
4
+ // 各レーンの ChangeSet から影響範囲を推定する。
5
+ // v0 は L2 精度(推定値)。完全な依存グラフ解析は v1.5+。
6
+ //
7
+ // - tokenKey → consumers(alias chain を辿る)
8
+ // - i18nKey → 使用ノード数
9
+ // - componentKey → インスタンス数
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.calculateImpact = calculateImpact;
12
+ exports.applyImpact = applyImpact;
13
+ // ========================================
14
+ // Core: calculateImpact
15
+ // ========================================
16
+ /**
17
+ * ChangeSet から ImpactSummary を計算する。
18
+ *
19
+ * @param changeset - 対象 ChangeSet
20
+ * @param context - 外部から渡される使用状況マップ
21
+ * @returns 更新された ImpactSummary
22
+ */
23
+ function calculateImpact(changeset, context = {}) {
24
+ switch (changeset.lane) {
25
+ case "tokens":
26
+ return calculateTokenImpact(changeset.items, context.aliasConsumers || {});
27
+ case "text":
28
+ return calculateTextImpact(changeset.items, context.textUsage || {});
29
+ case "components":
30
+ return calculateComponentImpact(changeset.items, context.componentInstances || {});
31
+ }
32
+ }
33
+ // ========================================
34
+ // Token Impact
35
+ // ========================================
36
+ /**
37
+ * Tokens レーンの影響計算。
38
+ *
39
+ * alias chain を辿って、変更されたトークンを消費している
40
+ * 下流のトークン・コンポーネント数を推定する。
41
+ */
42
+ function calculateTokenImpact(items, aliasConsumers) {
43
+ const details = [];
44
+ const affectedTokenKeys = new Set();
45
+ for (const item of items) {
46
+ // 直接変更されたトークンを記録
47
+ affectedTokenKeys.add(item.tokenKey);
48
+ details.push({
49
+ type: "token",
50
+ id: item.tokenKey,
51
+ name: item.tokenKey,
52
+ reason: item.before === null
53
+ ? "New token"
54
+ : item.after === null
55
+ ? "Removed token"
56
+ : `Value changed: ${item.before} → ${item.after}`,
57
+ });
58
+ // alias chain を辿って consumers を追加
59
+ const consumers = collectAliasConsumers(item.tokenKey, aliasConsumers);
60
+ for (const consumer of consumers) {
61
+ if (!affectedTokenKeys.has(consumer)) {
62
+ affectedTokenKeys.add(consumer);
63
+ details.push({
64
+ type: "token",
65
+ id: consumer,
66
+ name: consumer,
67
+ reason: `Depends on changed token: ${item.tokenKey}`,
68
+ });
69
+ }
70
+ }
71
+ }
72
+ return {
73
+ affectedTokens: affectedTokenKeys.size,
74
+ affectedComponents: 0,
75
+ affectedTextNodes: 0,
76
+ affectedPages: [],
77
+ details,
78
+ };
79
+ }
80
+ /**
81
+ * alias chain を再帰的に辿って、全ての consumers を収集する。
82
+ *
83
+ * 循環参照を検出して無限ループを防止する。
84
+ */
85
+ function collectAliasConsumers(tokenKey, aliasConsumers, visited = new Set()) {
86
+ if (visited.has(tokenKey))
87
+ return []; // 循環参照の防止
88
+ visited.add(tokenKey);
89
+ const directConsumers = aliasConsumers[tokenKey] || [];
90
+ const allConsumers = [...directConsumers];
91
+ for (const consumer of directConsumers) {
92
+ const transitive = collectAliasConsumers(consumer, aliasConsumers, visited);
93
+ allConsumers.push(...transitive);
94
+ }
95
+ return allConsumers;
96
+ }
97
+ // ========================================
98
+ // Text Impact
99
+ // ========================================
100
+ /**
101
+ * Text レーンの影響計算。
102
+ *
103
+ * i18nKey ごとの使用ノード数をカウントする。
104
+ */
105
+ function calculateTextImpact(items, textUsage) {
106
+ const details = [];
107
+ let totalAffectedNodes = 0;
108
+ for (const item of items) {
109
+ const nodeCount = textUsage[item.i18nKey] || 1; // デフォルト 1(自身のみ)
110
+ totalAffectedNodes += nodeCount;
111
+ details.push({
112
+ type: "text",
113
+ id: item.i18nKey,
114
+ name: `${item.i18nKey} [${item.locale}]`,
115
+ reason: item.before === null
116
+ ? `New text binding (${nodeCount} node(s))`
117
+ : item.after === null
118
+ ? `Text removed (${nodeCount} node(s))`
119
+ : `Text changed (${nodeCount} node(s))`,
120
+ });
121
+ }
122
+ return {
123
+ affectedTokens: 0,
124
+ affectedComponents: 0,
125
+ affectedTextNodes: totalAffectedNodes,
126
+ affectedPages: [],
127
+ details,
128
+ };
129
+ }
130
+ // ========================================
131
+ // Component Impact
132
+ // ========================================
133
+ /**
134
+ * Components レーンの影響計算。
135
+ *
136
+ * componentId ごとのインスタンス数をカウントする。
137
+ */
138
+ function calculateComponentImpact(items, componentInstances) {
139
+ const details = [];
140
+ let totalAffectedInstances = 0;
141
+ for (const item of items) {
142
+ const instanceCount = componentInstances[item.componentId] || 0;
143
+ totalAffectedInstances += instanceCount;
144
+ details.push({
145
+ type: "component",
146
+ id: item.componentId,
147
+ name: `${item.componentId} (${item.figmaComponentKey})`,
148
+ reason: describeComponentImpact(item, instanceCount),
149
+ });
150
+ }
151
+ return {
152
+ affectedTokens: 0,
153
+ affectedComponents: items.length,
154
+ affectedTextNodes: 0,
155
+ affectedPages: [],
156
+ details,
157
+ };
158
+ }
159
+ function describeComponentImpact(item, instanceCount) {
160
+ const parts = [];
161
+ for (const [prop, values] of Object.entries(item.variantProps.added)) {
162
+ parts.push(`+${prop}: [${values.join(", ")}]`);
163
+ }
164
+ for (const [prop, values] of Object.entries(item.variantProps.removed)) {
165
+ parts.push(`-${prop}: [${values.join(", ")}]`);
166
+ }
167
+ for (const [oldName, newName] of Object.entries(item.variantProps.renamed)) {
168
+ parts.push(`${oldName} → ${newName}`);
169
+ }
170
+ const changes = parts.join("; ") || item.changeType;
171
+ return `${changes} (${instanceCount} instance(s))`;
172
+ }
173
+ // ========================================
174
+ // Convenience: ChangeSet に Impact を適用
175
+ // ========================================
176
+ /**
177
+ * ChangeSet の impactSummary を再計算して上書きする便利関数。
178
+ *
179
+ * @returns 新しい impactSummary が設定された ChangeSet のコピー
180
+ */
181
+ function applyImpact(changeset, context = {}) {
182
+ const impactSummary = calculateImpact(changeset, context);
183
+ return {
184
+ ...changeset,
185
+ impactSummary,
186
+ };
187
+ }
188
+ //# sourceMappingURL=impact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../src/changeset/impact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { ChangeSet, ImpactSummary } from "../types/changeset";
2
+ import type { GateResult } from "../gate/types";
3
+ /**
4
+ * ChangeSet と ImpactSummary から PR body(Markdown)を生成する。
5
+ *
6
+ * @param changeset - 対象 ChangeSet
7
+ * @param impact - 影響範囲サマリー
8
+ * @param previewUrl - Storybook preview URL(オプション)
9
+ * @returns Markdown 形式の PR body 文字列
10
+ */
11
+ export declare function generatePRBody(changeset: ChangeSet, impact: ImpactSummary, previewUrl?: string): string;
12
+ export interface PRBodyOptions {
13
+ previewUrl?: string;
14
+ includeImpactCard?: boolean;
15
+ includeClassification?: boolean;
16
+ includeQualityGate?: boolean;
17
+ includeActionItems?: boolean;
18
+ maxItemsInBody?: number;
19
+ linkToFullReport?: boolean;
20
+ }
21
+ /**
22
+ * Gate Report を含む PR body を生成。
23
+ * summary.md の内容をベースに、PR 向けにフォーマット。
24
+ */
25
+ export declare function generateGatePRBody(changeSet: ChangeSet, gateResult: GateResult, options?: PRBodyOptions): string;
26
+ /**
27
+ * PR title を生成。
28
+ *
29
+ * Format: "[STIL {LANE}] {summary} ({classification})"
30
+ * Max 72 chars, truncated if necessary.
31
+ */
32
+ export declare function generatePRTitle(changeSet: ChangeSet, gateResult: GateResult): string;
33
+ //# sourceMappingURL=pr-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-body.d.ts","sourceRoot":"","sources":["../../src/changeset/pr-body.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,SAAS,EAIT,aAAa,EACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAMhD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,aAAa,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CA4BR;AAyND,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,aAAa,GACtB,MAAM,CAoLR;AAwBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,GACrB,MAAM,CAgCR"}
@@ -0,0 +1,421 @@
1
+ "use strict";
2
+ // pr-body.ts — PR Body Generator
3
+ //
4
+ // ChangeSet から GitHub PR の body テキスト(Markdown)を生成するロジック。
5
+ // Figma→Code 方向の PR 自動生成時に使用される。
6
+ //
7
+ // ARCHITECTURE_V1.md Section 5.3 の PR Comment Template に基づく。
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.generatePRBody = generatePRBody;
10
+ exports.generateGatePRBody = generateGatePRBody;
11
+ exports.generatePRTitle = generatePRTitle;
12
+ // ========================================
13
+ // Core: generatePRBody
14
+ // ========================================
15
+ /**
16
+ * ChangeSet と ImpactSummary から PR body(Markdown)を生成する。
17
+ *
18
+ * @param changeset - 対象 ChangeSet
19
+ * @param impact - 影響範囲サマリー
20
+ * @param previewUrl - Storybook preview URL(オプション)
21
+ * @returns Markdown 形式の PR body 文字列
22
+ */
23
+ function generatePRBody(changeset, impact, previewUrl) {
24
+ const sections = [];
25
+ // Header
26
+ const shortId = changeset.id.slice(0, 8);
27
+ sections.push(`## Stil ChangeSet: ${shortId}`);
28
+ // Change summary
29
+ sections.push(generateChangeSummary(changeset));
30
+ // Change details table
31
+ sections.push(generateChangeDetails(changeset));
32
+ // Impact section
33
+ sections.push(generateImpactSection(impact));
34
+ // Quality checks
35
+ sections.push(generateQualitySection(changeset));
36
+ // Preview section
37
+ sections.push(generatePreviewSection(previewUrl));
38
+ // Footer
39
+ sections.push("---\nGenerated by [Stil](https://github.com/techang/stil-systems)");
40
+ return sections.join("\n\n");
41
+ }
42
+ // ========================================
43
+ // Section generators
44
+ // ========================================
45
+ function generateChangeSummary(changeset) {
46
+ const laneLabel = formatLane(changeset.lane);
47
+ const directionLabel = formatDirection(changeset.direction);
48
+ const classificationLabel = formatClassification(changeset.classification);
49
+ return [
50
+ "### 変更サマリー",
51
+ `- **Lane**: ${laneLabel}`,
52
+ `- **Direction**: ${directionLabel}`,
53
+ `- **Classification**: ${classificationLabel}`,
54
+ `- **変更件数**: ${changeset.items.length} 件`,
55
+ ].join("\n");
56
+ }
57
+ function generateChangeDetails(changeset) {
58
+ const lines = ["### 変更内容"];
59
+ switch (changeset.lane) {
60
+ case "tokens":
61
+ lines.push(generateTokensTable(changeset.items));
62
+ break;
63
+ case "text":
64
+ lines.push(generateTextTable(changeset.items));
65
+ break;
66
+ case "components":
67
+ lines.push(generateComponentsTable(changeset.items));
68
+ break;
69
+ }
70
+ return lines.join("\n");
71
+ }
72
+ function generateTokensTable(items) {
73
+ const rows = items.map((item) => {
74
+ const before = item.before ?? "_(new)_";
75
+ const after = item.after ?? "_(deleted)_";
76
+ return `| \`${item.tokenKey}\` | ${item.changeType} | ${escapeCell(before)} | ${escapeCell(after)} |`;
77
+ });
78
+ return [
79
+ "| Key | Type | Before | After |",
80
+ "|-----|------|--------|-------|",
81
+ ...rows,
82
+ ].join("\n");
83
+ }
84
+ function generateTextTable(items) {
85
+ const rows = items.map((item) => {
86
+ const before = item.before ?? "_(new)_";
87
+ const after = item.after ?? "_(deleted)_";
88
+ return `| \`${item.i18nKey}\` | ${item.changeType} | ${escapeCell(before)} | ${escapeCell(after)} |`;
89
+ });
90
+ return [
91
+ "| Key | Type | Before | After |",
92
+ "|-----|------|--------|-------|",
93
+ ...rows,
94
+ ].join("\n");
95
+ }
96
+ function generateComponentsTable(items) {
97
+ const rows = items.map((item) => {
98
+ const changes = describeVariantDiff(item);
99
+ return `| \`${item.componentId}\` | ${item.changeType} | ${escapeCell(changes)} |`;
100
+ });
101
+ return [
102
+ "| Component | Type | Changes |",
103
+ "|-----------|------|---------|",
104
+ ...rows,
105
+ ].join("\n");
106
+ }
107
+ function describeVariantDiff(item) {
108
+ const parts = [];
109
+ for (const [prop, values] of Object.entries(item.variantProps.added)) {
110
+ parts.push(`+${prop}: [${values.join(", ")}]`);
111
+ }
112
+ for (const [prop, values] of Object.entries(item.variantProps.removed)) {
113
+ parts.push(`-${prop}: [${values.join(", ")}]`);
114
+ }
115
+ for (const [oldName, newName] of Object.entries(item.variantProps.renamed)) {
116
+ parts.push(`${oldName} → ${newName}`);
117
+ }
118
+ return parts.join("; ") || item.changeType;
119
+ }
120
+ function generateImpactSection(impact) {
121
+ const lines = ["### 影響範囲"];
122
+ if (impact.affectedTokens > 0) {
123
+ lines.push(`- 影響を受ける tokens: **${impact.affectedTokens}** 件`);
124
+ }
125
+ if (impact.affectedComponents > 0) {
126
+ lines.push(`- 影響を受ける components: **${impact.affectedComponents}** 件`);
127
+ }
128
+ if (impact.affectedTextNodes > 0) {
129
+ lines.push(`- 影響を受ける text nodes: **${impact.affectedTextNodes}** 件`);
130
+ }
131
+ if (impact.affectedPages.length > 0) {
132
+ lines.push(`- 影響を受けるページ: ${impact.affectedPages.join(", ")}`);
133
+ }
134
+ // No impact at all
135
+ if (impact.affectedTokens === 0 &&
136
+ impact.affectedComponents === 0 &&
137
+ impact.affectedTextNodes === 0) {
138
+ lines.push("- 影響範囲なし");
139
+ }
140
+ return lines.join("\n");
141
+ }
142
+ function generateQualitySection(changeset) {
143
+ const { checksSummary } = changeset;
144
+ const lines = ["### 品質チェック"];
145
+ if (checksSummary.errors === 0 && checksSummary.warnings === 0) {
146
+ lines.push("✅ すべてのチェックを通過");
147
+ }
148
+ else {
149
+ if (checksSummary.errors > 0) {
150
+ lines.push(`❌ エラー: ${checksSummary.errors} 件`);
151
+ }
152
+ if (checksSummary.warnings > 0) {
153
+ lines.push(`⚠️ 警告: ${checksSummary.warnings} 件`);
154
+ }
155
+ for (const check of checksSummary.checks) {
156
+ if (check.severity === "error") {
157
+ lines.push(`- ❌ ${check.message}${check.target ? ` (\`${check.target}\`)` : ""}`);
158
+ }
159
+ else if (check.severity === "warning") {
160
+ lines.push(`- ⚠️ ${check.message}${check.target ? ` (\`${check.target}\`)` : ""}`);
161
+ }
162
+ }
163
+ }
164
+ return lines.join("\n");
165
+ }
166
+ function generatePreviewSection(previewUrl) {
167
+ if (previewUrl) {
168
+ return `### Preview\n[Storybook Preview を確認](${previewUrl})`;
169
+ }
170
+ return "### Preview\n_Preview URL は CI 完了後に更新されます_";
171
+ }
172
+ // ========================================
173
+ // Formatting helpers
174
+ // ========================================
175
+ function formatLane(lane) {
176
+ switch (lane) {
177
+ case "tokens":
178
+ return "Tokens";
179
+ case "text":
180
+ return "Text";
181
+ case "components":
182
+ return "Components";
183
+ default:
184
+ return lane;
185
+ }
186
+ }
187
+ function formatDirection(direction) {
188
+ switch (direction) {
189
+ case "figma-to-code":
190
+ return "Figma → Code";
191
+ case "code-to-figma":
192
+ return "Code → Figma";
193
+ default:
194
+ return direction;
195
+ }
196
+ }
197
+ function formatClassification(classification) {
198
+ switch (classification) {
199
+ case "safe":
200
+ return "safe ✅";
201
+ case "review":
202
+ return "review ⚠️";
203
+ case "blocked":
204
+ return "blocked 🚫";
205
+ default:
206
+ return classification;
207
+ }
208
+ }
209
+ /**
210
+ * Markdown table cell のエスケープ。
211
+ * パイプ文字と改行をエスケープする。
212
+ */
213
+ function escapeCell(value) {
214
+ return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
215
+ }
216
+ // ========================================
217
+ // Gate-aware PR Body
218
+ // ========================================
219
+ /**
220
+ * Gate Report を含む PR body を生成。
221
+ * summary.md の内容をベースに、PR 向けにフォーマット。
222
+ */
223
+ function generateGatePRBody(changeSet, gateResult, options) {
224
+ const opts = {
225
+ previewUrl: options?.previewUrl ?? "",
226
+ includeImpactCard: options?.includeImpactCard ?? true,
227
+ includeClassification: options?.includeClassification ?? true,
228
+ includeQualityGate: options?.includeQualityGate ?? true,
229
+ includeActionItems: options?.includeActionItems ?? true,
230
+ maxItemsInBody: options?.maxItemsInBody ?? 20,
231
+ linkToFullReport: options?.linkToFullReport ?? true,
232
+ };
233
+ const sections = [];
234
+ // Header
235
+ const statusEmoji = gateResult.passed ? "✅ PASSED" : "❌ FAILED";
236
+ sections.push("## Stil Gate Report");
237
+ sections.push("");
238
+ sections.push(`**ChangeSet**: \`${changeSet.id}\``);
239
+ sections.push(`**Lane**: ${formatLane(changeSet.lane)} | **Direction**: ${formatDirection(changeSet.direction)}`);
240
+ sections.push(`**Gate Mode**: ${gateResult.mode} | **Status**: ${statusEmoji}`);
241
+ // Classification table
242
+ if (opts.includeClassification) {
243
+ const cls = gateResult.classification;
244
+ sections.push("");
245
+ sections.push("---");
246
+ sections.push("");
247
+ sections.push("### Classification");
248
+ sections.push("");
249
+ sections.push("| Status | Count |");
250
+ sections.push("|--------|-------|");
251
+ sections.push(`| ✅ Safe | ${cls.summary.safeCount} |`);
252
+ sections.push(`| ⚠️ Review | ${cls.summary.reviewCount} |`);
253
+ sections.push(`| ❌ Blocked | ${cls.summary.blockedCount} |`);
254
+ }
255
+ // Impact Analysis
256
+ if (opts.includeImpactCard) {
257
+ const ia = gateResult.impact.impactAnalysis;
258
+ sections.push("");
259
+ sections.push("---");
260
+ sections.push("");
261
+ sections.push("### Impact Analysis");
262
+ sections.push("");
263
+ sections.push("| Metric | Value |");
264
+ sections.push("|--------|-------|");
265
+ sections.push(`| Affected Tokens | ${ia.affectedTokens.length} |`);
266
+ sections.push(`| Affected Components | ${ia.affectedComponents.length} |`);
267
+ const totalInstances = ia.affectedComponents.reduce((sum, c) => sum + c.affectedInstances, 0);
268
+ if (totalInstances > 0) {
269
+ sections.push(`| Affected Instances | ~${totalInstances} |`);
270
+ }
271
+ if (ia.affectedFiles.length > 0) {
272
+ sections.push(`| Affected Files | ${ia.affectedFiles.length} |`);
273
+ }
274
+ const depTokens = ia.affectedTokens.reduce((sum, t) => sum + t.dependentTokens.length, 0);
275
+ if (depTokens > 0) {
276
+ sections.push(`| Dependent Tokens | ${depTokens} |`);
277
+ }
278
+ }
279
+ // Quality Gate
280
+ if (opts.includeQualityGate) {
281
+ const checks = changeSet.checksSummary;
282
+ sections.push("");
283
+ sections.push("---");
284
+ sections.push("");
285
+ sections.push("### Quality Gate");
286
+ sections.push("");
287
+ sections.push(`**Passed**: ${checks.passed} | **Warnings**: ${checks.warnings} | **Errors**: ${checks.errors}`);
288
+ if (checks.checks.length > 0) {
289
+ sections.push("");
290
+ sections.push("| Check | Result |");
291
+ sections.push("|-------|--------|");
292
+ for (const check of checks.checks) {
293
+ const icon = check.severity === "error"
294
+ ? "❌"
295
+ : check.severity === "warning"
296
+ ? "⚠️"
297
+ : "✅";
298
+ const target = check.target ? ` (\`${check.target}\`)` : "";
299
+ sections.push(`| ${check.message}${target} | ${icon} |`);
300
+ }
301
+ }
302
+ }
303
+ // Changes Detail
304
+ const items = gateResult.classification.items;
305
+ if (items.length > 0) {
306
+ sections.push("");
307
+ sections.push("---");
308
+ sections.push("");
309
+ sections.push("### Changes Detail");
310
+ sections.push("");
311
+ const shown = items.slice(0, opts.maxItemsInBody);
312
+ const lines = [];
313
+ lines.push(`<details>\n<summary>${items.length} items changed (click to expand)</summary>`);
314
+ lines.push("");
315
+ for (const item of shown) {
316
+ lines.push(`#### ${item.itemId}`);
317
+ lines.push(`- **Classification**: ${item.classification}`);
318
+ if (item.reasons.length > 0) {
319
+ lines.push(`- **Reason**: ${item.reasons.join(", ")}`);
320
+ }
321
+ lines.push("");
322
+ }
323
+ if (items.length > opts.maxItemsInBody) {
324
+ lines.push(`_...and ${items.length - opts.maxItemsInBody} more items. See full report._`);
325
+ lines.push("");
326
+ }
327
+ lines.push("</details>");
328
+ sections.push(lines.join("\n"));
329
+ }
330
+ // Action Items
331
+ if (opts.includeActionItems) {
332
+ const actionItems = buildGateActionItems(gateResult);
333
+ if (actionItems.length > 0) {
334
+ sections.push("");
335
+ sections.push("---");
336
+ sections.push("");
337
+ sections.push("### Action Items");
338
+ sections.push("");
339
+ for (const ai of actionItems) {
340
+ sections.push(`- [ ] ${ai}`);
341
+ }
342
+ }
343
+ }
344
+ // Preview
345
+ if (opts.previewUrl) {
346
+ sections.push("");
347
+ sections.push("---");
348
+ sections.push("");
349
+ sections.push("### Preview");
350
+ sections.push("");
351
+ sections.push(`[View in Storybook](${opts.previewUrl})`);
352
+ }
353
+ // Footer
354
+ sections.push("");
355
+ sections.push("---");
356
+ sections.push("");
357
+ if (opts.linkToFullReport) {
358
+ sections.push("<sub>Full report: `.stil/reports/` | Generated by [Stil Gate](https://github.com/techang/stil-systems)</sub>");
359
+ }
360
+ else {
361
+ sections.push("<sub>Generated by [Stil Gate](https://github.com/techang/stil-systems)</sub>");
362
+ }
363
+ return sections.join("\n");
364
+ }
365
+ function buildGateActionItems(gateResult) {
366
+ const items = [];
367
+ const { classification } = gateResult;
368
+ if (classification.summary.blockedCount > 0) {
369
+ items.push("Resolve blocked items before merging");
370
+ }
371
+ if (classification.summary.reviewCount > 0) {
372
+ items.push("Review and approve items marked for review");
373
+ }
374
+ for (const ci of classification.items) {
375
+ for (const fix of ci.suggestedFixes) {
376
+ items.push(`${ci.itemId}: ${fix}`);
377
+ }
378
+ }
379
+ return items;
380
+ }
381
+ // ========================================
382
+ // PR Title
383
+ // ========================================
384
+ /**
385
+ * PR title を生成。
386
+ *
387
+ * Format: "[STIL {LANE}] {summary} ({classification})"
388
+ * Max 72 chars, truncated if necessary.
389
+ */
390
+ function generatePRTitle(changeSet, gateResult) {
391
+ const lane = changeSet.lane.toUpperCase();
392
+ const cls = gateResult.classification.summary;
393
+ // Build classification suffix
394
+ const parts = [];
395
+ if (cls.safeCount > 0)
396
+ parts.push(`${cls.safeCount} safe`);
397
+ if (cls.reviewCount > 0)
398
+ parts.push(`${cls.reviewCount} review`);
399
+ if (cls.blockedCount > 0)
400
+ parts.push(`${cls.blockedCount} blocked`);
401
+ const clsSuffix = parts.length > 0 ? ` (${parts.join(", ")})` : "";
402
+ // Summary from items
403
+ const itemCount = gateResult.classification.items.length;
404
+ let summary;
405
+ if (itemCount === 1) {
406
+ summary = gateResult.classification.items[0].itemId;
407
+ }
408
+ else {
409
+ summary = `Update ${itemCount} items`;
410
+ }
411
+ // Prefix
412
+ const prefix = cls.blockedCount > 0 ? `[STIL ${lane}] ` : `[STIL ${lane}] `;
413
+ const blockedPrefix = cls.blockedCount > 0 ? "Warning: " : "";
414
+ let title = `${prefix}${blockedPrefix}${summary}${clsSuffix}`;
415
+ // Truncate to 72 chars
416
+ if (title.length > 72) {
417
+ title = title.slice(0, 69) + "...";
418
+ }
419
+ return title;
420
+ }
421
+ //# sourceMappingURL=pr-body.js.map