stylemcp 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.
- package/README.md +261 -0
- package/action/action.yml +59 -0
- package/action/package.json +24 -0
- package/action/src/index.ts +455 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +335 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/rewriter/index.d.ts +45 -0
- package/dist/rewriter/index.d.ts.map +1 -0
- package/dist/rewriter/index.js +163 -0
- package/dist/rewriter/index.js.map +1 -0
- package/dist/schema/copy-patterns.d.ts +298 -0
- package/dist/schema/copy-patterns.d.ts.map +1 -0
- package/dist/schema/copy-patterns.js +53 -0
- package/dist/schema/copy-patterns.js.map +1 -0
- package/dist/schema/cta-rules.d.ts +274 -0
- package/dist/schema/cta-rules.d.ts.map +1 -0
- package/dist/schema/cta-rules.js +45 -0
- package/dist/schema/cta-rules.js.map +1 -0
- package/dist/schema/index.d.ts +2172 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +92 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/tests.d.ts +274 -0
- package/dist/schema/tests.d.ts.map +1 -0
- package/dist/schema/tests.js +37 -0
- package/dist/schema/tests.js.map +1 -0
- package/dist/schema/tokens.d.ts +632 -0
- package/dist/schema/tokens.d.ts.map +1 -0
- package/dist/schema/tokens.js +68 -0
- package/dist/schema/tokens.js.map +1 -0
- package/dist/schema/voice.d.ts +280 -0
- package/dist/schema/voice.d.ts.map +1 -0
- package/dist/schema/voice.js +52 -0
- package/dist/schema/voice.js.map +1 -0
- package/dist/server/billing.d.ts +53 -0
- package/dist/server/billing.d.ts.map +1 -0
- package/dist/server/billing.js +216 -0
- package/dist/server/billing.js.map +1 -0
- package/dist/server/http.d.ts +5 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +470 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +480 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/middleware/auth.d.ts +15 -0
- package/dist/server/middleware/auth.d.ts.map +1 -0
- package/dist/server/middleware/auth.js +83 -0
- package/dist/server/middleware/auth.js.map +1 -0
- package/dist/utils/pack-loader.d.ts +13 -0
- package/dist/utils/pack-loader.d.ts.map +1 -0
- package/dist/utils/pack-loader.js +98 -0
- package/dist/utils/pack-loader.js.map +1 -0
- package/dist/validator/index.d.ts +21 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +60 -0
- package/dist/validator/index.js.map +1 -0
- package/dist/validator/rules/constraints.d.ts +8 -0
- package/dist/validator/rules/constraints.d.ts.map +1 -0
- package/dist/validator/rules/constraints.js +150 -0
- package/dist/validator/rules/constraints.js.map +1 -0
- package/dist/validator/rules/cta.d.ts +11 -0
- package/dist/validator/rules/cta.d.ts.map +1 -0
- package/dist/validator/rules/cta.js +113 -0
- package/dist/validator/rules/cta.js.map +1 -0
- package/dist/validator/rules/voice.d.ts +6 -0
- package/dist/validator/rules/voice.d.ts.map +1 -0
- package/dist/validator/rules/voice.js +106 -0
- package/dist/validator/rules/voice.js.map +1 -0
- package/package.json +61 -0
- package/packs/saas/copy_patterns.yaml +423 -0
- package/packs/saas/cta_rules.yaml +362 -0
- package/packs/saas/manifest.yaml +16 -0
- package/packs/saas/tests.yaml +305 -0
- package/packs/saas/tokens.json +226 -0
- package/packs/saas/voice.yaml +232 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import * as core from '@actions/core';
|
|
2
|
+
import * as github from '@actions/github';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import { join, dirname } from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import * as yaml from 'js-yaml';
|
|
8
|
+
|
|
9
|
+
// Import from main package (will be bundled)
|
|
10
|
+
import { validate, ValidationResult, Violation } from '../../src/validator/index.js';
|
|
11
|
+
import { loadPack, getPacksDirectory } from '../../src/utils/pack-loader.js';
|
|
12
|
+
import { Pack } from '../../src/schema/index.js';
|
|
13
|
+
|
|
14
|
+
interface FileResult {
|
|
15
|
+
file: string;
|
|
16
|
+
result: ValidationResult;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function run(): Promise<void> {
|
|
20
|
+
try {
|
|
21
|
+
// Get inputs
|
|
22
|
+
const packName = core.getInput('pack') || 'saas';
|
|
23
|
+
const filesPattern = core.getInput('files');
|
|
24
|
+
const directText = core.getInput('text');
|
|
25
|
+
const minScore = parseInt(core.getInput('min-score') || '70', 10);
|
|
26
|
+
const strict = core.getInput('strict') === 'true';
|
|
27
|
+
const failOnWarning = core.getInput('fail-on-warning') === 'true';
|
|
28
|
+
const contentType = core.getInput('content-type') as any;
|
|
29
|
+
const commentOnPr = core.getInput('comment-on-pr') === 'true';
|
|
30
|
+
const githubToken = core.getInput('github-token');
|
|
31
|
+
|
|
32
|
+
// Load pack
|
|
33
|
+
core.info(`Loading style pack: ${packName}`);
|
|
34
|
+
let pack: Pack;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Try loading as built-in pack first
|
|
38
|
+
const packPath = join(getPacksDirectory(), packName);
|
|
39
|
+
const result = await loadPack({ packPath });
|
|
40
|
+
pack = result.pack;
|
|
41
|
+
|
|
42
|
+
if (result.errors.length > 0) {
|
|
43
|
+
core.warning(`Pack loading warnings: ${result.errors.join(', ')}`);
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
// Try loading as custom pack path
|
|
47
|
+
const result = await loadPack({ packPath: packName });
|
|
48
|
+
pack = result.pack;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
core.info(`Loaded pack: ${pack.manifest.name} v${pack.manifest.version}`);
|
|
52
|
+
|
|
53
|
+
// Collect text to validate
|
|
54
|
+
const fileResults: FileResult[] = [];
|
|
55
|
+
let directResult: ValidationResult | null = null;
|
|
56
|
+
|
|
57
|
+
// Validate files
|
|
58
|
+
if (filesPattern) {
|
|
59
|
+
const files = await glob(filesPattern, { nodir: true });
|
|
60
|
+
core.info(`Found ${files.length} files matching pattern: ${filesPattern}`);
|
|
61
|
+
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
const content = await readFile(file, 'utf-8');
|
|
64
|
+
|
|
65
|
+
// Extract text content based on file type
|
|
66
|
+
const textContent = extractTextContent(file, content);
|
|
67
|
+
|
|
68
|
+
if (textContent) {
|
|
69
|
+
const result = validate({
|
|
70
|
+
pack,
|
|
71
|
+
text: textContent,
|
|
72
|
+
context: { type: contentType },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
fileResults.push({ file, result });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Validate direct text
|
|
81
|
+
if (directText) {
|
|
82
|
+
directResult = validate({
|
|
83
|
+
pack,
|
|
84
|
+
text: directText,
|
|
85
|
+
context: { type: contentType },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Aggregate results
|
|
90
|
+
const allResults = [
|
|
91
|
+
...fileResults.map(fr => fr.result),
|
|
92
|
+
...(directResult ? [directResult] : []),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
if (allResults.length === 0) {
|
|
96
|
+
core.warning('No text found to validate');
|
|
97
|
+
core.setOutput('score', 100);
|
|
98
|
+
core.setOutput('valid', true);
|
|
99
|
+
core.setOutput('violations', 0);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Calculate overall metrics
|
|
104
|
+
const avgScore = Math.round(
|
|
105
|
+
allResults.reduce((sum, r) => sum + r.score, 0) / allResults.length
|
|
106
|
+
);
|
|
107
|
+
const totalViolations = allResults.reduce((sum, r) => sum + r.violations.length, 0);
|
|
108
|
+
const totalErrors = allResults.reduce((sum, r) => sum + r.summary.errors, 0);
|
|
109
|
+
const totalWarnings = allResults.reduce((sum, r) => sum + r.summary.warnings, 0);
|
|
110
|
+
|
|
111
|
+
// Determine pass/fail
|
|
112
|
+
let valid = avgScore >= minScore && totalErrors === 0;
|
|
113
|
+
if (strict && totalViolations > 0) {
|
|
114
|
+
valid = false;
|
|
115
|
+
}
|
|
116
|
+
if (failOnWarning && totalWarnings > 0) {
|
|
117
|
+
valid = false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Set outputs
|
|
121
|
+
core.setOutput('score', avgScore);
|
|
122
|
+
core.setOutput('valid', valid);
|
|
123
|
+
core.setOutput('violations', totalViolations);
|
|
124
|
+
core.setOutput('report', JSON.stringify({
|
|
125
|
+
score: avgScore,
|
|
126
|
+
valid,
|
|
127
|
+
totalViolations,
|
|
128
|
+
totalErrors,
|
|
129
|
+
totalWarnings,
|
|
130
|
+
files: fileResults.map(fr => ({
|
|
131
|
+
file: fr.file,
|
|
132
|
+
score: fr.result.score,
|
|
133
|
+
violations: fr.result.violations.length,
|
|
134
|
+
})),
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
// Log summary
|
|
138
|
+
core.info('');
|
|
139
|
+
core.info('=== StyleMCP Validation Results ===');
|
|
140
|
+
core.info(`Score: ${avgScore}/100`);
|
|
141
|
+
core.info(`Violations: ${totalViolations} (${totalErrors} errors, ${totalWarnings} warnings)`);
|
|
142
|
+
core.info(`Status: ${valid ? 'PASS' : 'FAIL'}`);
|
|
143
|
+
core.info('');
|
|
144
|
+
|
|
145
|
+
// Log file results
|
|
146
|
+
if (fileResults.length > 0) {
|
|
147
|
+
core.info('File Results:');
|
|
148
|
+
for (const { file, result } of fileResults) {
|
|
149
|
+
const status = result.valid ? '✓' : '✗';
|
|
150
|
+
core.info(` ${status} ${file}: ${result.score}/100 (${result.violations.length} violations)`);
|
|
151
|
+
}
|
|
152
|
+
core.info('');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Log violations
|
|
156
|
+
if (totalViolations > 0) {
|
|
157
|
+
core.info('Violations:');
|
|
158
|
+
for (const { file, result } of fileResults) {
|
|
159
|
+
for (const violation of result.violations) {
|
|
160
|
+
const icon = violation.severity === 'error' ? '❌' : violation.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
161
|
+
core.info(` ${icon} [${file}] ${violation.message}`);
|
|
162
|
+
if (violation.text) {
|
|
163
|
+
core.info(` Text: "${violation.text}"`);
|
|
164
|
+
}
|
|
165
|
+
if (violation.suggestion) {
|
|
166
|
+
core.info(` Suggestion: ${violation.suggestion}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (directResult) {
|
|
172
|
+
for (const violation of directResult.violations) {
|
|
173
|
+
const icon = violation.severity === 'error' ? '❌' : violation.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
174
|
+
core.info(` ${icon} ${violation.message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Post PR comment if enabled
|
|
180
|
+
if (commentOnPr && github.context.payload.pull_request && githubToken) {
|
|
181
|
+
await postPrComment(githubToken, {
|
|
182
|
+
score: avgScore,
|
|
183
|
+
valid,
|
|
184
|
+
totalViolations,
|
|
185
|
+
totalErrors,
|
|
186
|
+
totalWarnings,
|
|
187
|
+
fileResults,
|
|
188
|
+
directResult,
|
|
189
|
+
packName: pack.manifest.name,
|
|
190
|
+
packVersion: pack.manifest.version,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Fail the action if validation failed
|
|
195
|
+
if (!valid) {
|
|
196
|
+
core.setFailed(`StyleMCP validation failed. Score: ${avgScore}/100, Violations: ${totalViolations}`);
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
core.setFailed(error instanceof Error ? error.message : 'Unknown error');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Extract text content from files based on type
|
|
205
|
+
*/
|
|
206
|
+
function extractTextContent(file: string, content: string): string | null {
|
|
207
|
+
const ext = file.split('.').pop()?.toLowerCase();
|
|
208
|
+
|
|
209
|
+
switch (ext) {
|
|
210
|
+
case 'md':
|
|
211
|
+
case 'mdx':
|
|
212
|
+
// Return markdown content as-is
|
|
213
|
+
return content;
|
|
214
|
+
|
|
215
|
+
case 'json':
|
|
216
|
+
// Try to extract string values
|
|
217
|
+
try {
|
|
218
|
+
const obj = JSON.parse(content);
|
|
219
|
+
return extractStringsFromObject(obj).join('\n');
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case 'yaml':
|
|
225
|
+
case 'yml':
|
|
226
|
+
// Try to extract string values
|
|
227
|
+
try {
|
|
228
|
+
const obj = yaml.load(content);
|
|
229
|
+
return extractStringsFromObject(obj).join('\n');
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
case 'tsx':
|
|
235
|
+
case 'jsx':
|
|
236
|
+
// Extract string literals from JSX
|
|
237
|
+
return extractJsxStrings(content);
|
|
238
|
+
|
|
239
|
+
case 'ts':
|
|
240
|
+
case 'js':
|
|
241
|
+
// Extract string literals
|
|
242
|
+
return extractJsStrings(content);
|
|
243
|
+
|
|
244
|
+
case 'txt':
|
|
245
|
+
return content;
|
|
246
|
+
|
|
247
|
+
default:
|
|
248
|
+
// Try to extract any quoted strings
|
|
249
|
+
return extractQuotedStrings(content);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Extract string values from an object recursively
|
|
255
|
+
*/
|
|
256
|
+
function extractStringsFromObject(obj: any, strings: string[] = []): string[] {
|
|
257
|
+
if (typeof obj === 'string' && obj.length > 3) {
|
|
258
|
+
strings.push(obj);
|
|
259
|
+
} else if (Array.isArray(obj)) {
|
|
260
|
+
for (const item of obj) {
|
|
261
|
+
extractStringsFromObject(item, strings);
|
|
262
|
+
}
|
|
263
|
+
} else if (obj && typeof obj === 'object') {
|
|
264
|
+
for (const value of Object.values(obj)) {
|
|
265
|
+
extractStringsFromObject(value, strings);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return strings;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Extract string literals from JSX/TSX
|
|
273
|
+
*/
|
|
274
|
+
function extractJsxStrings(content: string): string {
|
|
275
|
+
const strings: string[] = [];
|
|
276
|
+
|
|
277
|
+
// Match JSX text content between tags
|
|
278
|
+
const jsxTextRegex = />([^<>{]+)</g;
|
|
279
|
+
let match;
|
|
280
|
+
while ((match = jsxTextRegex.exec(content)) !== null) {
|
|
281
|
+
const text = match[1].trim();
|
|
282
|
+
if (text.length > 3 && !text.startsWith('{')) {
|
|
283
|
+
strings.push(text);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Match string literals in props
|
|
288
|
+
const propRegex = /=["']([^"']+)["']/g;
|
|
289
|
+
while ((match = propRegex.exec(content)) !== null) {
|
|
290
|
+
const text = match[1].trim();
|
|
291
|
+
if (text.length > 3) {
|
|
292
|
+
strings.push(text);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return strings.join('\n');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Extract string literals from JS/TS
|
|
301
|
+
*/
|
|
302
|
+
function extractJsStrings(content: string): string {
|
|
303
|
+
const strings: string[] = [];
|
|
304
|
+
|
|
305
|
+
// Match template literals
|
|
306
|
+
const templateRegex = /`([^`]+)`/g;
|
|
307
|
+
let match;
|
|
308
|
+
while ((match = templateRegex.exec(content)) !== null) {
|
|
309
|
+
const text = match[1].trim();
|
|
310
|
+
if (text.length > 10 && !text.includes('${')) {
|
|
311
|
+
strings.push(text);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Match regular string literals (longer ones likely to be user-facing)
|
|
316
|
+
const stringRegex = /["']([^"'\n]{20,})["']/g;
|
|
317
|
+
while ((match = stringRegex.exec(content)) !== null) {
|
|
318
|
+
strings.push(match[1]);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return strings.join('\n');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Extract any quoted strings
|
|
326
|
+
*/
|
|
327
|
+
function extractQuotedStrings(content: string): string {
|
|
328
|
+
const strings: string[] = [];
|
|
329
|
+
const regex = /["']([^"'\n]{10,})["']/g;
|
|
330
|
+
let match;
|
|
331
|
+
while ((match = regex.exec(content)) !== null) {
|
|
332
|
+
strings.push(match[1]);
|
|
333
|
+
}
|
|
334
|
+
return strings.join('\n');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Post a comment on the PR with validation results
|
|
339
|
+
*/
|
|
340
|
+
async function postPrComment(
|
|
341
|
+
token: string,
|
|
342
|
+
data: {
|
|
343
|
+
score: number;
|
|
344
|
+
valid: boolean;
|
|
345
|
+
totalViolations: number;
|
|
346
|
+
totalErrors: number;
|
|
347
|
+
totalWarnings: number;
|
|
348
|
+
fileResults: FileResult[];
|
|
349
|
+
directResult: ValidationResult | null;
|
|
350
|
+
packName: string;
|
|
351
|
+
packVersion: string;
|
|
352
|
+
}
|
|
353
|
+
): Promise<void> {
|
|
354
|
+
const octokit = github.getOctokit(token);
|
|
355
|
+
const { owner, repo } = github.context.repo;
|
|
356
|
+
const pullNumber = github.context.payload.pull_request!.number;
|
|
357
|
+
|
|
358
|
+
const statusIcon = data.valid ? '✅' : '❌';
|
|
359
|
+
const scoreColor = data.score >= 80 ? '🟢' : data.score >= 60 ? '🟡' : '🔴';
|
|
360
|
+
|
|
361
|
+
let body = `## ${statusIcon} StyleMCP Validation
|
|
362
|
+
|
|
363
|
+
${scoreColor} **Score: ${data.score}/100**
|
|
364
|
+
|
|
365
|
+
| Metric | Count |
|
|
366
|
+
|--------|-------|
|
|
367
|
+
| Total Violations | ${data.totalViolations} |
|
|
368
|
+
| Errors | ${data.totalErrors} |
|
|
369
|
+
| Warnings | ${data.totalWarnings} |
|
|
370
|
+
|
|
371
|
+
`;
|
|
372
|
+
|
|
373
|
+
if (data.fileResults.length > 0) {
|
|
374
|
+
body += `### Files Checked
|
|
375
|
+
|
|
376
|
+
| File | Score | Violations |
|
|
377
|
+
|------|-------|------------|
|
|
378
|
+
`;
|
|
379
|
+
for (const { file, result } of data.fileResults) {
|
|
380
|
+
const icon = result.valid ? '✓' : '✗';
|
|
381
|
+
body += `| ${icon} ${file} | ${result.score} | ${result.violations.length} |\n`;
|
|
382
|
+
}
|
|
383
|
+
body += '\n';
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (data.totalViolations > 0) {
|
|
387
|
+
body += `### Violations
|
|
388
|
+
|
|
389
|
+
`;
|
|
390
|
+
const allViolations: { file?: string; violation: Violation }[] = [];
|
|
391
|
+
|
|
392
|
+
for (const { file, result } of data.fileResults) {
|
|
393
|
+
for (const violation of result.violations) {
|
|
394
|
+
allViolations.push({ file, violation });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (data.directResult) {
|
|
399
|
+
for (const violation of data.directResult.violations) {
|
|
400
|
+
allViolations.push({ violation });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Show top 10 violations
|
|
405
|
+
for (const { file, violation } of allViolations.slice(0, 10)) {
|
|
406
|
+
const icon = violation.severity === 'error' ? '🔴' : violation.severity === 'warning' ? '🟡' : '🔵';
|
|
407
|
+
body += `${icon} **${violation.message}**\n`;
|
|
408
|
+
if (file) {
|
|
409
|
+
body += ` 📁 ${file}\n`;
|
|
410
|
+
}
|
|
411
|
+
if (violation.text) {
|
|
412
|
+
body += ` > "${violation.text}"\n`;
|
|
413
|
+
}
|
|
414
|
+
if (violation.suggestion) {
|
|
415
|
+
body += ` 💡 ${violation.suggestion}\n`;
|
|
416
|
+
}
|
|
417
|
+
body += '\n';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (allViolations.length > 10) {
|
|
421
|
+
body += `\n_...and ${allViolations.length - 10} more violations_\n`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
body += `\n---\n_Validated with [StyleMCP](https://stylemcp.com) pack: ${data.packName} v${data.packVersion}_`;
|
|
426
|
+
|
|
427
|
+
// Check for existing comment to update
|
|
428
|
+
const comments = await octokit.rest.issues.listComments({
|
|
429
|
+
owner,
|
|
430
|
+
repo,
|
|
431
|
+
issue_number: pullNumber,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const existingComment = comments.data.find(
|
|
435
|
+
comment => comment.body?.includes('## ') && comment.body?.includes('StyleMCP Validation')
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
if (existingComment) {
|
|
439
|
+
await octokit.rest.issues.updateComment({
|
|
440
|
+
owner,
|
|
441
|
+
repo,
|
|
442
|
+
comment_id: existingComment.id,
|
|
443
|
+
body,
|
|
444
|
+
});
|
|
445
|
+
} else {
|
|
446
|
+
await octokit.rest.issues.createComment({
|
|
447
|
+
owner,
|
|
448
|
+
repo,
|
|
449
|
+
issue_number: pullNumber,
|
|
450
|
+
body,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
run();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|