@ryuenn3123/agentic-senior-core 3.0.10 → 3.0.12
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/.agent-context/prompts/bootstrap-design.md +7 -1
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.cursorrules +1 -1
- package/.windsurfrules +1 -1
- package/lib/cli/commands/init.mjs +2 -0
- package/lib/cli/commands/upgrade.mjs +5 -0
- package/lib/cli/compiler.mjs +9 -0
- package/lib/cli/constants.mjs +1 -0
- package/lib/cli/detector.mjs +422 -90
- package/lib/cli/project-scaffolder.mjs +122 -8
- package/package.json +1 -2
- package/scripts/frontend-usability-audit.mjs +4 -1
- package/scripts/mcp-server.mjs +200 -118
- package/scripts/ui-design-judge.mjs +21 -86
- package/scripts/validate.mjs +6 -1
- package/.agent-context/state/ui-design-judge-report.json +0 -25
|
@@ -6,30 +6,14 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Advisory-first UI design contract judge.
|
|
8
8
|
*
|
|
9
|
+
* Repo-internal workflow audit; no user-facing runtime modes.
|
|
9
10
|
* Compares changed UI diffs against docs/design-intent.json and docs/DESIGN.md.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* node scripts/ui-design-judge.mjs
|
|
14
|
-
* node scripts/ui-design-judge.mjs --dry-run
|
|
15
|
-
* node scripts/ui-design-judge.mjs --strict
|
|
16
|
-
*
|
|
17
|
-
* Environment variables:
|
|
18
|
-
* OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY
|
|
19
|
-
* UI_DESIGN_JUDGE_MODEL Override model for this script
|
|
20
|
-
* LLM_JUDGE_MODEL Shared fallback model override
|
|
21
|
-
* UI_DESIGN_JUDGE_MAX_DIFF_CHARS Max diff chars to send (default: 12000)
|
|
22
|
-
* UI_DESIGN_JUDGE_OUTPUT_PATH Machine-readable report output path
|
|
23
|
-
* UI_DESIGN_JUDGE_EMIT_JSON false disables report file emission
|
|
24
|
-
* UI_DESIGN_JUDGE_MOCK_RESPONSE Test-only raw LLM response body
|
|
25
|
-
* UI_DESIGN_JUDGE_CHANGED_FILES Optional comma/newline-separated changed file override
|
|
26
|
-
* PR_DIFF Inject diff directly
|
|
27
|
-
* GITHUB_BASE_SHA / GITHUB_HEAD_SHA
|
|
28
|
-
* CI_MERGE_REQUEST_DIFF_BASE_SHA / CI_COMMIT_SHA
|
|
11
|
+
* Runs only in advisory mode for this repository workflow.
|
|
12
|
+
* Emits JSON to stdout for release-gate and CI consumption.
|
|
29
13
|
*/
|
|
30
14
|
|
|
31
15
|
import { execSync } from 'node:child_process';
|
|
32
|
-
import { existsSync, readFileSync
|
|
16
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
33
17
|
import { resolve, dirname, extname } from 'node:path';
|
|
34
18
|
import { fileURLToPath } from 'node:url';
|
|
35
19
|
|
|
@@ -39,13 +23,7 @@ const REPOSITORY_ROOT = resolve(__dirname, '..');
|
|
|
39
23
|
|
|
40
24
|
const DESIGN_INTENT_PATH = resolve(REPOSITORY_ROOT, 'docs', 'design-intent.json');
|
|
41
25
|
const DESIGN_GUIDE_PATH = resolve(REPOSITORY_ROOT, 'docs', 'DESIGN.md');
|
|
42
|
-
const
|
|
43
|
-
const MACHINE_REPORT_PATH = process.env.UI_DESIGN_JUDGE_OUTPUT_PATH || DEFAULT_MACHINE_REPORT_PATH;
|
|
44
|
-
const SHOULD_EMIT_MACHINE_REPORT = process.env.UI_DESIGN_JUDGE_EMIT_JSON !== 'false';
|
|
45
|
-
const MAX_DIFF_CHARS = parseInt(process.env.UI_DESIGN_JUDGE_MAX_DIFF_CHARS ?? '12000', 10);
|
|
46
|
-
const IS_DRY_RUN = process.argv.includes('--dry-run');
|
|
47
|
-
const IS_STRICT_MODE = process.argv.includes('--strict');
|
|
48
|
-
const IS_ADVISORY_MODE = !IS_STRICT_MODE;
|
|
26
|
+
const MAX_DIFF_CHARS = 12000;
|
|
49
27
|
const UI_FILE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.vue', '.css', '.scss', '.sass']);
|
|
50
28
|
|
|
51
29
|
/**
|
|
@@ -64,7 +42,7 @@ const UI_FILE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.vue', '.css'
|
|
|
64
42
|
* generatedAt: string,
|
|
65
43
|
* auditName: string,
|
|
66
44
|
* schemaVersion: string,
|
|
67
|
-
* mode: 'advisory'
|
|
45
|
+
* mode: 'advisory',
|
|
68
46
|
* advisoryOnly: boolean,
|
|
69
47
|
* passed: boolean,
|
|
70
48
|
* skipped: boolean,
|
|
@@ -177,13 +155,6 @@ function collectPullRequestDiff() {
|
|
|
177
155
|
}
|
|
178
156
|
|
|
179
157
|
function collectChangedFiles() {
|
|
180
|
-
if (process.env.UI_DESIGN_JUDGE_CHANGED_FILES) {
|
|
181
|
-
return process.env.UI_DESIGN_JUDGE_CHANGED_FILES
|
|
182
|
-
.split(/[\r\n,]+/u)
|
|
183
|
-
.map((filePath) => filePath.trim())
|
|
184
|
-
.filter(Boolean);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
158
|
if (process.env.PR_DIFF) {
|
|
188
159
|
const filePathSet = new Set();
|
|
189
160
|
for (const diffHeaderMatch of process.env.PR_DIFF.matchAll(/^diff --git a\/(.+?) b\/(.+)$/gm)) {
|
|
@@ -256,7 +227,7 @@ function loadDesignGuide() {
|
|
|
256
227
|
return readFileSync(DESIGN_GUIDE_PATH, 'utf8');
|
|
257
228
|
}
|
|
258
229
|
|
|
259
|
-
function buildSystemPrompt(
|
|
230
|
+
function buildSystemPrompt() {
|
|
260
231
|
return [
|
|
261
232
|
'You are a Principal UI/UX Design Reviewer.',
|
|
262
233
|
'Compare the changed UI code against the provided design contract.',
|
|
@@ -264,9 +235,10 @@ function buildSystemPrompt(modeLabel) {
|
|
|
264
235
|
'Treat docs/DESIGN.md as explanatory context, not a generic style guide.',
|
|
265
236
|
'Do not reward generic SaaS defaults or popular template patterns.',
|
|
266
237
|
'Do not penalize originality when the implementation still aligns with the contract.',
|
|
238
|
+
'Purposeful motion is allowed and can improve quality. Only flag motion when it drifts from the contract, ignores reduced-motion expectations, or adds avoidable performance/accessibility risk.',
|
|
267
239
|
'Only flag drift when there is a clear mismatch with the contract, accessibility non-negotiables, or cross-viewport adaptation rules.',
|
|
268
|
-
|
|
269
|
-
'Focus on color intent, typographic hierarchy, responsive re-layout, interaction behavior, and genericity drift.',
|
|
240
|
+
'This audit always runs in advisory mode for this repository workflow.',
|
|
241
|
+
'Focus on color intent, typographic hierarchy, responsive re-layout, purposeful motion, component morphology across states, interaction behavior, and genericity drift.',
|
|
270
242
|
'Return ONLY one JSON object on a single line prefixed with JSON_VERDICT:.',
|
|
271
243
|
'Schema:',
|
|
272
244
|
'{"alignmentScore": number|null, "notes": string[], "findings": [{"area": string, "severity": "high|medium|low", "problem": string, "evidence": string, "recommendation": string, "blockingRecommended": boolean}]}',
|
|
@@ -297,12 +269,12 @@ function buildUserMessage(designIntentContent, designGuideContent, diffContent,
|
|
|
297
269
|
truncatedDiff.trim() || '(no UI diff)',
|
|
298
270
|
'```',
|
|
299
271
|
'',
|
|
300
|
-
'Judge alignment to the contract. Avoid aesthetic bias toward generic web trends.',
|
|
272
|
+
'Judge alignment to the contract. Avoid aesthetic bias toward generic web trends or toward motionless/static outputs.',
|
|
301
273
|
].join('\n');
|
|
302
274
|
}
|
|
303
275
|
|
|
304
276
|
async function callOpenAiProvider(systemPrompt, userMessage) {
|
|
305
|
-
const selectedModel = process.env.
|
|
277
|
+
const selectedModel = process.env.LLM_JUDGE_MODEL ?? 'gpt-4o-mini';
|
|
306
278
|
const apiResponse = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
307
279
|
method: 'POST',
|
|
308
280
|
headers: {
|
|
@@ -330,7 +302,7 @@ async function callOpenAiProvider(systemPrompt, userMessage) {
|
|
|
330
302
|
}
|
|
331
303
|
|
|
332
304
|
async function callAnthropicProvider(systemPrompt, userMessage) {
|
|
333
|
-
const selectedModel = process.env.
|
|
305
|
+
const selectedModel = process.env.LLM_JUDGE_MODEL ?? 'claude-3-5-haiku-latest';
|
|
334
306
|
const apiResponse = await fetch('https://api.anthropic.com/v1/messages', {
|
|
335
307
|
method: 'POST',
|
|
336
308
|
headers: {
|
|
@@ -356,7 +328,7 @@ async function callAnthropicProvider(systemPrompt, userMessage) {
|
|
|
356
328
|
}
|
|
357
329
|
|
|
358
330
|
async function callGeminiProvider(systemPrompt, userMessage) {
|
|
359
|
-
const selectedModel = process.env.
|
|
331
|
+
const selectedModel = process.env.LLM_JUDGE_MODEL ?? 'gemini-2.0-flash';
|
|
360
332
|
const apiKey = process.env.GEMINI_API_KEY ?? '';
|
|
361
333
|
const endpointUrl = `https://generativelanguage.googleapis.com/v1beta/models/${selectedModel}:generateContent?key=${apiKey}`;
|
|
362
334
|
|
|
@@ -445,8 +417,8 @@ function buildReport(partialReport) {
|
|
|
445
417
|
generatedAt: new Date().toISOString(),
|
|
446
418
|
auditName: 'ui-design-judge',
|
|
447
419
|
schemaVersion: '1.0',
|
|
448
|
-
mode:
|
|
449
|
-
advisoryOnly:
|
|
420
|
+
mode: 'advisory',
|
|
421
|
+
advisoryOnly: true,
|
|
450
422
|
passed: true,
|
|
451
423
|
skipped: false,
|
|
452
424
|
skipReason: null,
|
|
@@ -468,10 +440,6 @@ function buildReport(partialReport) {
|
|
|
468
440
|
}
|
|
469
441
|
|
|
470
442
|
function emitMachineReadableReport(machineReportPayload) {
|
|
471
|
-
if (SHOULD_EMIT_MACHINE_REPORT) {
|
|
472
|
-
writeFileSync(MACHINE_REPORT_PATH, `${JSON.stringify(machineReportPayload, null, 2)}\n`, 'utf8');
|
|
473
|
-
}
|
|
474
|
-
|
|
475
443
|
console.log(JSON.stringify(machineReportPayload, null, 2));
|
|
476
444
|
}
|
|
477
445
|
|
|
@@ -508,28 +476,9 @@ async function main() {
|
|
|
508
476
|
return;
|
|
509
477
|
}
|
|
510
478
|
|
|
511
|
-
const systemPrompt = buildSystemPrompt(
|
|
479
|
+
const systemPrompt = buildSystemPrompt();
|
|
512
480
|
const userMessage = buildUserMessage(designIntentContent, designGuideContent, rawDiff, changedUiFiles);
|
|
513
481
|
|
|
514
|
-
if (IS_DRY_RUN) {
|
|
515
|
-
emitMachineReadableReport(buildReport({
|
|
516
|
-
provider: 'dry-run',
|
|
517
|
-
contractPresent: true,
|
|
518
|
-
summary: {
|
|
519
|
-
changedUiFileCount: changedUiFiles.length,
|
|
520
|
-
alignmentScore: null,
|
|
521
|
-
driftCount: 0,
|
|
522
|
-
blockingCandidateCount: 0,
|
|
523
|
-
},
|
|
524
|
-
notes: [
|
|
525
|
-
'Dry run enabled. No LLM provider call was made.',
|
|
526
|
-
`System prompt chars: ${systemPrompt.length}`,
|
|
527
|
-
`User message chars: ${userMessage.length}`,
|
|
528
|
-
],
|
|
529
|
-
}));
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
482
|
const selectedProvider = selectAvailableProvider();
|
|
534
483
|
if (!selectedProvider) {
|
|
535
484
|
emitMachineReadableReport(buildReport({
|
|
@@ -541,7 +490,7 @@ async function main() {
|
|
|
541
490
|
driftCount: 0,
|
|
542
491
|
blockingCandidateCount: 0,
|
|
543
492
|
},
|
|
544
|
-
notes: ['No LLM provider configured. UI design judge skipped provider review and
|
|
493
|
+
notes: ['No LLM provider configured. UI design judge skipped provider review and stayed advisory.'],
|
|
545
494
|
}));
|
|
546
495
|
return;
|
|
547
496
|
}
|
|
@@ -565,13 +514,8 @@ async function main() {
|
|
|
565
514
|
blockingCandidateCount: 0,
|
|
566
515
|
},
|
|
567
516
|
notes: [`Provider call failed: ${providerErrorMessage}`],
|
|
568
|
-
passed:
|
|
517
|
+
passed: true,
|
|
569
518
|
}));
|
|
570
|
-
|
|
571
|
-
if (IS_STRICT_MODE) {
|
|
572
|
-
process.exit(1);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
519
|
return;
|
|
576
520
|
}
|
|
577
521
|
|
|
@@ -582,12 +526,11 @@ async function main() {
|
|
|
582
526
|
const notes = Array.isArray(verdict?.notes)
|
|
583
527
|
? verdict.notes.map((note) => String(note))
|
|
584
528
|
: [];
|
|
585
|
-
const shouldFailInStrictMode = IS_STRICT_MODE && blockingCandidateCount > 0;
|
|
586
529
|
|
|
587
530
|
const reportPayload = buildReport({
|
|
588
531
|
provider: selectedProvider.providerName,
|
|
589
532
|
contractPresent: true,
|
|
590
|
-
passed:
|
|
533
|
+
passed: true,
|
|
591
534
|
malformedVerdict: malformed,
|
|
592
535
|
summary: {
|
|
593
536
|
changedUiFileCount: changedUiFiles.length,
|
|
@@ -602,10 +545,6 @@ async function main() {
|
|
|
602
545
|
});
|
|
603
546
|
|
|
604
547
|
emitMachineReadableReport(reportPayload);
|
|
605
|
-
|
|
606
|
-
if (IS_STRICT_MODE && (malformed || shouldFailInStrictMode)) {
|
|
607
|
-
process.exit(1);
|
|
608
|
-
}
|
|
609
548
|
}
|
|
610
549
|
|
|
611
550
|
main().catch((unexpectedError) => {
|
|
@@ -616,11 +555,7 @@ main().catch((unexpectedError) => {
|
|
|
616
555
|
emitMachineReadableReport(buildReport({
|
|
617
556
|
provider: 'none',
|
|
618
557
|
providerError: true,
|
|
619
|
-
passed:
|
|
558
|
+
passed: true,
|
|
620
559
|
notes: [`Unexpected ui-design-judge failure: ${errorMessage}`],
|
|
621
560
|
}));
|
|
622
|
-
|
|
623
|
-
if (IS_STRICT_MODE) {
|
|
624
|
-
process.exit(1);
|
|
625
|
-
}
|
|
626
561
|
});
|
package/scripts/validate.mjs
CHANGED
|
@@ -285,13 +285,16 @@ const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
|
|
|
285
285
|
'Responsive Strategy and Cross-Viewport Adaptation Matrix',
|
|
286
286
|
'`colorTruth.format`',
|
|
287
287
|
'`crossViewportAdaptation.mutationRules.mobile/tablet/desktop`',
|
|
288
|
+
'`motionSystem`',
|
|
289
|
+
'`componentMorphology`',
|
|
288
290
|
],
|
|
289
291
|
},
|
|
290
292
|
{
|
|
291
293
|
path: 'scripts/ui-design-judge.mjs',
|
|
292
294
|
snippets: [
|
|
293
295
|
'Advisory-first UI design contract judge.',
|
|
294
|
-
'
|
|
296
|
+
'Repo-internal workflow audit; no user-facing runtime modes.',
|
|
297
|
+
'Runs only in advisory mode for this repository workflow.',
|
|
295
298
|
'Do not reward generic SaaS defaults or popular template patterns.',
|
|
296
299
|
'UI design judge only evaluates changed UI surfaces.',
|
|
297
300
|
],
|
|
@@ -301,6 +304,8 @@ const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
|
|
|
301
304
|
snippets: [
|
|
302
305
|
'colorTruth',
|
|
303
306
|
'crossViewportAdaptation',
|
|
307
|
+
'motionSystem',
|
|
308
|
+
'componentMorphology',
|
|
304
309
|
'requireViewportMutationRules',
|
|
305
310
|
'allowHexDerivatives',
|
|
306
311
|
],
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"generatedAt": "2026-04-20T12:33:27.082Z",
|
|
3
|
-
"auditName": "ui-design-judge",
|
|
4
|
-
"schemaVersion": "1.0",
|
|
5
|
-
"mode": "advisory",
|
|
6
|
-
"advisoryOnly": true,
|
|
7
|
-
"passed": true,
|
|
8
|
-
"skipped": true,
|
|
9
|
-
"skipReason": "Design contract is missing or unreadable. Skipping UI design judge.",
|
|
10
|
-
"provider": "none",
|
|
11
|
-
"ciProvider": "github",
|
|
12
|
-
"contractPresent": false,
|
|
13
|
-
"summary": {
|
|
14
|
-
"changedUiFileCount": 0,
|
|
15
|
-
"alignmentScore": null,
|
|
16
|
-
"driftCount": 0,
|
|
17
|
-
"blockingCandidateCount": 0
|
|
18
|
-
},
|
|
19
|
-
"malformedVerdict": false,
|
|
20
|
-
"providerError": false,
|
|
21
|
-
"findings": [],
|
|
22
|
-
"notes": [
|
|
23
|
-
"docs/design-intent.json is required for contract-aware UI judging."
|
|
24
|
-
]
|
|
25
|
-
}
|