@event4u/agent-config 16.4.0 → 16.5.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +40 -191
- package/README.md +1 -1
- package/dist/agent-src/commands/design.md +1 -1
- package/dist/agent-src/guidelines/design-antipatterns.md +1 -1
- package/dist/agent-src/rules/token-budget-discipline.md +2 -2
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- package/dist/agent-src/skills/canvas-design/SKILL.md +4 -2
- package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +13 -10
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +49 -2
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
- package/dist/agent-src/skills/iconography/SKILL.md +4 -2
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
- package/dist/agent-src/skills/logo-generation/SKILL.md +12 -8
- package/dist/agent-src/skills/svg-authoring/SKILL.md +318 -0
- package/dist/agent-src/skills/wireframe/SKILL.md +5 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/commands/uiRender.js +201 -4
- package/dist/cli/commands/uiRenderAsset.js +1308 -0
- package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
- package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
- package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
- package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
- package/dist/cli/commands/uiRenderAssetTypes.js +145 -0
- package/dist/cli/main.js +47 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/{chunk-MSUL5NYP.js → chunk-4DBG5K7M.js} +1 -1
- package/dist/cli-delegate/{chunk-AED2VQGM.js → chunk-TMWYUOK4.js} +10 -1
- package/dist/cli-delegate/cmd_conformance.js +395 -214
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +47 -22
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +6 -6
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +19 -15
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +13 -9
- package/dist/hooks/dispatch.js +1073 -808
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/svg_rules.chunk.cjs +1626 -0
- package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +931 -735
- package/dist/scripts/_lib/scan_scope.js +105 -0
- package/dist/scripts/_lib/svg_dom.js +284 -0
- package/dist/scripts/_lib/svg_geometry.js +477 -0
- package/dist/scripts/_lib/svg_motion.js +179 -0
- package/dist/scripts/_lib/svg_rules.js +1337 -0
- package/dist/scripts/_lib/svg_urls.js +67 -0
- package/dist/scripts/lint_svg_asset.js +324 -0
- package/docs/guidelines/design-antipatterns.md +1 -1
- package/package.json +4 -3
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/routing-coverage-seed.json +9 -2
- package/src/scripts/_lib/svg_dom.ts +318 -0
- package/src/scripts/_lib/svg_geometry.ts +480 -0
- package/src/scripts/_lib/svg_motion.ts +208 -0
- package/src/scripts/_lib/svg_rules.ts +1453 -0
- package/src/scripts/_lib/svg_urls.ts +72 -0
- package/src/scripts/_lib/verification_command.ts +59 -0
- package/src/scripts/_lib/verify_clause.ts +27 -17
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +16 -1
- package/src/scripts/hooks/design_pass_hook.ts +157 -3
- package/src/scripts/hooks/svg_obligation.ts +497 -0
- package/src/scripts/hooks/svg_rules_loader.ts +133 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +5 -3
- package/src/scripts/lint_consolidation_lineage.ts +6 -3
- package/src/scripts/lint_design_quality.ts +60 -12
- package/src/scripts/lint_motion_authority_drift.ts +27 -0
- package/src/scripts/lint_svg_asset.ts +365 -0
|
@@ -23,14 +23,17 @@
|
|
|
23
23
|
* `b-page-capture-primitive` closed as a recorded null. This supplies the
|
|
24
24
|
* primitive, so that null's own reopening condition fires.
|
|
25
25
|
*
|
|
26
|
-
* Playwright is a devDependency (`package.json:
|
|
26
|
+
* Playwright is a devDependency (`package.json:111`) and is therefore NOT
|
|
27
27
|
* guaranteed present in a consumer install. The import is dynamic and the
|
|
28
28
|
* absence path is a clean, explained exit rather than a stack trace — and it
|
|
29
29
|
* reports `verification: unverified` instead of writing an empty manifest that
|
|
30
30
|
* would read as a successful capture of nothing.
|
|
31
31
|
*/
|
|
32
|
+
import * as crypto from 'node:crypto';
|
|
32
33
|
import * as fs from 'node:fs';
|
|
33
34
|
import * as path from 'node:path';
|
|
35
|
+
import { runAssetRender } from './uiRenderAsset.js';
|
|
36
|
+
import { scanHostHtml, scanSvg } from '../../scripts/lint_svg_asset.js';
|
|
34
37
|
/** The three viewports E3.1 names. Widths are the contract, not a preference. */
|
|
35
38
|
export const VIEWPORTS = [
|
|
36
39
|
{ name: 'desktop', width: 1440, height: 900 },
|
|
@@ -49,6 +52,82 @@ export const CAPTURED_PROPERTIES = [
|
|
|
49
52
|
'overflow-y',
|
|
50
53
|
];
|
|
51
54
|
export const RENDER_REL = path.join('agents', 'runtime', 'state', 'render');
|
|
55
|
+
/**
|
|
56
|
+
* `--profile asset` with no renderer. Distinct from 1, which this command
|
|
57
|
+
* already uses for an internal error (a missing path, a URL handed to the
|
|
58
|
+
* asset profile), because a caller has to tell "nothing rendered" from
|
|
59
|
+
* "you called it wrong" — the first is a consumer's ordinary state.
|
|
60
|
+
*/
|
|
61
|
+
export const EXIT_RENDERER_ABSENT = 3;
|
|
62
|
+
/**
|
|
63
|
+
* The families whose ERROR means the file is broken, as opposed to improvable.
|
|
64
|
+
*
|
|
65
|
+
* Finding 4 of the third completion review, reproduced by running `scanSvg`:
|
|
66
|
+
* an ordinary stroke icon with no `<title>` yields exactly one error —
|
|
67
|
+
* `svg-a11y-no-accessible-name` — so `errors` was never 0, the static-only
|
|
68
|
+
* discharge never fired, and a consumer with no Playwright was refused by a
|
|
69
|
+
* BLOCKING turn-end gate on every `.svg` they wrote, with no way out. That is
|
|
70
|
+
* the opposite of the path's stated purpose, which the obligation module's own
|
|
71
|
+
* header spells out: it exists so a Playwright-less install is not refused
|
|
72
|
+
* forever.
|
|
73
|
+
*
|
|
74
|
+
* `structure` is "this file does not parse or does not resolve" and `safety`
|
|
75
|
+
* is "this file does something it should not". Both are brokenness, and both
|
|
76
|
+
* SHOULD stop a discharge taken without a render. Accessibility, motion and
|
|
77
|
+
* geometry are readings about a file that works — they are reported by the
|
|
78
|
+
* design pass in their own right, and a missing title is not a reason to
|
|
79
|
+
* refuse a turn on a machine that could not render.
|
|
80
|
+
*/
|
|
81
|
+
const BLOCKING_FAMILIES = ['svg-structure-', 'svg-safety-'];
|
|
82
|
+
/**
|
|
83
|
+
* The static witness, bound to the bytes it read.
|
|
84
|
+
*
|
|
85
|
+
* `errors` counts `severity: 'error'` findings in the blocking families only —
|
|
86
|
+
* see `BLOCKING_FAMILIES` for which, and for the refusal that made the
|
|
87
|
+
* distinction necessary. A warning is a reading, not a refusal, and the
|
|
88
|
+
* static-only discharge in the turn-end gate is about whether the file is
|
|
89
|
+
* BROKEN. The digest is of the SAME bytes, so a later reader can tell whether
|
|
90
|
+
* this reading describes the file in front of it.
|
|
91
|
+
*/
|
|
92
|
+
export function staticWitness(absFile, delivery) {
|
|
93
|
+
// THE DIGEST IS OF THE BYTES, the scan is of the decoded text.
|
|
94
|
+
//
|
|
95
|
+
// Finding 2 of the sixth completion review: three places hash "the file"
|
|
96
|
+
// and two of them hashed the UTF-8 DECODED string while the render hashed
|
|
97
|
+
// the raw buffer. For any file the decode is not byte-exact over — a BOM,
|
|
98
|
+
// a lone surrogate, an invalid sequence replaced by U+FFFD — the two
|
|
99
|
+
// digests differ forever, so the gate never matches a render to the file
|
|
100
|
+
// it rendered and the obligation can never be discharged. One definition,
|
|
101
|
+
// and it is the one a digest normally means.
|
|
102
|
+
let bytes;
|
|
103
|
+
try {
|
|
104
|
+
bytes = fs.readFileSync(absFile);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const content = bytes.toString('utf8');
|
|
110
|
+
const digest = crypto.createHash('sha256').update(bytes).digest('hex');
|
|
111
|
+
// The DELIVERY MODE is threaded through. Finding 6 of this change's second
|
|
112
|
+
// completion review: `scanSvg` grades an undeclared mode as `inline`, the
|
|
113
|
+
// strictest, because that is the safe default for a file whose embedding
|
|
114
|
+
// nobody stated. Here the embedding WAS stated — `--delivery img` is on the
|
|
115
|
+
// same command line — so defaulting raised the safety family to `error` for
|
|
116
|
+
// an asset that cannot execute anything, and a correct `img`-delivered file
|
|
117
|
+
// was refused the static-only discharge it qualifies for.
|
|
118
|
+
// AN HTML HOST IS SCANNED AS A HOST, NOT AS A GRAPHIC.
|
|
119
|
+
//
|
|
120
|
+
// Finding 1 of the fourth completion review, reproduced on this repo's own
|
|
121
|
+
// `hosts/img-delivery-static.html` fixture: `scanSvg` over a host document
|
|
122
|
+
// reports `svg-structure-not-well-formed` for every HTML void element —
|
|
123
|
+
// `<meta>` and `<img>` never close, which is correct HTML and malformed
|
|
124
|
+
// XML — so the witness read 2 errors where `scanHostHtml` over the same
|
|
125
|
+
// bytes reports none. Under the blocking-family count those are structure
|
|
126
|
+
// errors, so a correct host document could not discharge its obligation.
|
|
127
|
+
const scan = /\.html?$/i.test(absFile) ? scanHostHtml : scanSvg;
|
|
128
|
+
const errors = scan(content, path.basename(absFile), { delivery }).filter((f) => f.severity === 'error' && BLOCKING_FAMILIES.some((fam) => f.rule.startsWith(fam))).length;
|
|
129
|
+
return { errors, digest };
|
|
130
|
+
}
|
|
52
131
|
export function slugify(target) {
|
|
53
132
|
return (target
|
|
54
133
|
.replace(/^https?:\/\//, '')
|
|
@@ -90,11 +169,20 @@ export async function runUiRender(opts) {
|
|
|
90
169
|
}
|
|
91
170
|
const slug = opts.slug ?? slugify(opts.target);
|
|
92
171
|
const outDir = path.join(projectRoot, RENDER_REL, slug);
|
|
172
|
+
const isAsset = opts.profile === 'asset';
|
|
93
173
|
let chromium;
|
|
94
174
|
try {
|
|
95
175
|
({ chromium } = await import('@playwright/test'));
|
|
96
176
|
}
|
|
97
177
|
catch {
|
|
178
|
+
// Step 2.3, and the reason the absence path sits ABOVE the profile
|
|
179
|
+
// branch rather than inside either one: both profiles reach it by
|
|
180
|
+
// construction, so there is no second absence path to keep honest.
|
|
181
|
+
// Step 2.1(i): on the ASSET profile the renderer-absent path runs the
|
|
182
|
+
// static witness itself and binds it to the same digest, so the gate's
|
|
183
|
+
// static-only discharge reads ONE file and never depends on how a
|
|
184
|
+
// separate lint run was named or stored.
|
|
185
|
+
const staticReading = isAsset && !isUrl ? staticWitness(abs, opts.delivery) : null;
|
|
98
186
|
const manifest = {
|
|
99
187
|
schema: 1,
|
|
100
188
|
generated_at: new Date().toISOString(),
|
|
@@ -104,6 +192,8 @@ export async function runUiRender(opts) {
|
|
|
104
192
|
palette: [],
|
|
105
193
|
type_families: [],
|
|
106
194
|
verification: 'unverified',
|
|
195
|
+
...(isAsset ? { profile: 'asset', reason: 'renderer-absent', goal: 'unassessed' } : {}),
|
|
196
|
+
...(staticReading ? { static: staticReading } : {}),
|
|
107
197
|
degradation_reason: '@playwright/test is not installed — it is a devDependency, so a consumer install has no browser. ' +
|
|
108
198
|
'Install it, or scope any verdict to the static checks that actually ran.',
|
|
109
199
|
};
|
|
@@ -111,11 +201,27 @@ export async function runUiRender(opts) {
|
|
|
111
201
|
process.stderr.write(`[ui:render] ${manifest.degradation_reason}\n`);
|
|
112
202
|
if (opts.json)
|
|
113
203
|
process.stdout.write(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
114
|
-
//
|
|
115
|
-
// not a failure of
|
|
116
|
-
|
|
204
|
+
// The PAGE profile keeps exit 0: an absent devDependency is a
|
|
205
|
+
// capability fact, not a failure of that command, and the manifest
|
|
206
|
+
// carries the honest state.
|
|
207
|
+
//
|
|
208
|
+
// The ASSET profile exits NON-ZERO (step 2.1(a), review claim C2). The
|
|
209
|
+
// turn-end gate's transcript path never sees an exit code, so a
|
|
210
|
+
// non-zero exit alone does not close the hole — but the record path
|
|
211
|
+
// reads one, and a bare `ui:render --profile asset` that rendered
|
|
212
|
+
// nothing must not look like a check that passed. The code is distinct
|
|
213
|
+
// from 1, which this command already uses for an internal error such as
|
|
214
|
+
// a missing path or a URL handed to the asset profile.
|
|
215
|
+
return isAsset ? EXIT_RENDERER_ABSENT : 0;
|
|
117
216
|
}
|
|
118
217
|
fs.mkdirSync(outDir, { recursive: true });
|
|
218
|
+
if (isAsset) {
|
|
219
|
+
if (isUrl) {
|
|
220
|
+
process.stderr.write('[ui:render] --profile asset reads a file on disk, not a URL\n');
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
return runAssetProfile({ ...opts, projectRoot, abs, slug, outDir }, chromium);
|
|
224
|
+
}
|
|
119
225
|
const captures = [];
|
|
120
226
|
const allStyles = [];
|
|
121
227
|
let browser = null;
|
|
@@ -199,6 +305,97 @@ export async function runUiRender(opts) {
|
|
|
199
305
|
}
|
|
200
306
|
return 0;
|
|
201
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* The asset profile. Same manifest, same output directory, same Class-A
|
|
310
|
+
* boundary — the browser is closed in a `finally` on every path including a
|
|
311
|
+
* throw, exactly as the page profile closes its own.
|
|
312
|
+
*/
|
|
313
|
+
async function runAssetProfile(opts, chromium) {
|
|
314
|
+
let browser = null;
|
|
315
|
+
try {
|
|
316
|
+
browser = await chromium.launch({ headless: true });
|
|
317
|
+
const result = await runAssetRender(browser, {
|
|
318
|
+
file: opts.abs,
|
|
319
|
+
projectRoot: opts.projectRoot,
|
|
320
|
+
outDir: opts.outDir,
|
|
321
|
+
assetClass: opts.assetClass,
|
|
322
|
+
delivery: opts.delivery,
|
|
323
|
+
thresholdsFile: opts.thresholds,
|
|
324
|
+
iconSet: opts.iconSet,
|
|
325
|
+
compareTo: opts.compareTo,
|
|
326
|
+
registration: opts.registration,
|
|
327
|
+
});
|
|
328
|
+
// Finding 8 of this change's completion review: ONE evaluation. The
|
|
329
|
+
// spread below used to call this twice — once as the truthiness guard,
|
|
330
|
+
// once for the value — so the verified path re-read the file, re-hashed
|
|
331
|
+
// it and re-ran the whole rule suite, and the two reads could disagree
|
|
332
|
+
// if the file moved between them.
|
|
333
|
+
const staticReading = staticWitness(opts.abs, opts.delivery);
|
|
334
|
+
const manifest = {
|
|
335
|
+
schema: 1,
|
|
336
|
+
generated_at: new Date().toISOString(),
|
|
337
|
+
target: opts.target,
|
|
338
|
+
slug: opts.slug,
|
|
339
|
+
captures: [],
|
|
340
|
+
palette: [],
|
|
341
|
+
type_families: [],
|
|
342
|
+
verification: result.verification,
|
|
343
|
+
profile: 'asset',
|
|
344
|
+
// Step 2.3. Written on EVERY asset render, by the only layer that
|
|
345
|
+
// could plausibly be mistaken for the one deciding it. A reader who
|
|
346
|
+
// sees `verification: verified` and no goal field has to supply the
|
|
347
|
+
// missing half themselves, which is exactly how `verified` came to
|
|
348
|
+
// be read as "the graphic is right".
|
|
349
|
+
goal: 'unassessed',
|
|
350
|
+
asset: result.reading,
|
|
351
|
+
// The static witness on the same digest, on the verified path too —
|
|
352
|
+
// the gate's digest-match discharge reads the ASSET reading, but a
|
|
353
|
+
// human comparing two manifests should not have to tell whether the
|
|
354
|
+
// static layer ran from which path produced the file.
|
|
355
|
+
...(staticReading ? { static: staticReading } : {}),
|
|
356
|
+
...(result.degradationReason ? { degradation_reason: result.degradationReason } : {}),
|
|
357
|
+
...(result.verification === 'verified' ? {} : { reason: 'render-failed' }),
|
|
358
|
+
};
|
|
359
|
+
writeManifest(opts.outDir, manifest);
|
|
360
|
+
if (opts.json)
|
|
361
|
+
process.stdout.write(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
362
|
+
else {
|
|
363
|
+
const r = result.reading;
|
|
364
|
+
process.stdout.write(`✅ ui:render --profile asset — ${r.asset_class}` +
|
|
365
|
+
`${r.asset_class_inferred ? ' (inferred)' : ''}, ${r.sizes.length} capture(s), ` +
|
|
366
|
+
`${r.findings.length} finding(s), ${r.network.aborted} request(s) aborted\n` +
|
|
367
|
+
` ${path.relative(opts.projectRoot, opts.outDir)}\n` +
|
|
368
|
+
r.findings.map((f) => ` ⚠️ ${f.rule} — ${f.detail}\n`).join(''));
|
|
369
|
+
}
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
const manifest = {
|
|
374
|
+
schema: 1,
|
|
375
|
+
generated_at: new Date().toISOString(),
|
|
376
|
+
target: opts.target,
|
|
377
|
+
slug: opts.slug,
|
|
378
|
+
captures: [],
|
|
379
|
+
palette: [],
|
|
380
|
+
type_families: [],
|
|
381
|
+
verification: 'degraded',
|
|
382
|
+
profile: 'asset',
|
|
383
|
+
// A renderer that RAN and failed, which is not the same fact as a
|
|
384
|
+
// renderer that is absent — the turn-end gate discharges an SVG
|
|
385
|
+
// obligation on the second and never on the first.
|
|
386
|
+
reason: 'render-failed',
|
|
387
|
+
goal: 'unassessed',
|
|
388
|
+
degradation_reason: `asset capture failed: ${String(err)}`,
|
|
389
|
+
};
|
|
390
|
+
writeManifest(opts.outDir, manifest);
|
|
391
|
+
process.stderr.write(`[ui:render] ${manifest.degradation_reason}\n`);
|
|
392
|
+
return 1;
|
|
393
|
+
}
|
|
394
|
+
finally {
|
|
395
|
+
// The Class-A boundary, same clause as the page profile's.
|
|
396
|
+
await browser?.close();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
202
399
|
function writeManifest(outDir, manifest) {
|
|
203
400
|
fs.mkdirSync(outDir, { recursive: true });
|
|
204
401
|
fs.writeFileSync(path.join(outDir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`);
|