@tritard/waterbrother 0.8.30 → 0.8.32
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/package.json +1 -1
- package/src/frontend.js +53 -17
- package/src/tools.js +10 -1
package/package.json
CHANGED
package/src/frontend.js
CHANGED
|
@@ -55,30 +55,31 @@ const UNIVERSAL_FRONTEND_REMINDERS = [
|
|
|
55
55
|
"Prefer hand-authored CSS variables and layout rules over generic template utility sprawl when feasible.",
|
|
56
56
|
"Cut fake credibility elements, fake brands, fake testimonials, and filler interface chrome unless explicitly requested.",
|
|
57
57
|
"Do not add fake keyboard shortcuts, fake command palettes, or demo-only interactive chrome unless the interface truly needs them.",
|
|
58
|
-
"Avoid placeholder image services, Inter/Playfair default pairings, Tailwind CDN starter aesthetics, and generic premium-blog tropes.",
|
|
58
|
+
"Avoid placeholder image services, Font Awesome CDN chrome, Inter/Playfair default pairings, Tailwind CDN starter aesthetics, and generic premium-blog tropes.",
|
|
59
59
|
"Prefer fewer sections with stronger hierarchy over a long page full of low-value widgets."
|
|
60
60
|
];
|
|
61
61
|
|
|
62
62
|
const BENCHMARK_FRONTEND_REMINDERS = [
|
|
63
63
|
"For benchmark frontend tasks, cut generic reflective-editorial copy. Use sharper, more concrete language or neutral structural placeholders instead of atmosphere-writing.",
|
|
64
64
|
"For benchmark frontend tasks, force at least one asymmetrical or compositionally distinctive move instead of a fully balanced template layout.",
|
|
65
|
-
"Benchmark mode: treat fake issue framing, fake publication history, fake keyboard shortcuts,
|
|
65
|
+
"Benchmark mode: treat fake issue framing, fake publication history, fake keyboard shortcuts, invented named contributors, and demo-page behavior as disallowed outputs. Use neutral structural placeholders if needed.",
|
|
66
|
+
"Benchmark mode: the page should feel publishable, not like a static demo or wireframe explanation."
|
|
66
67
|
];
|
|
67
68
|
|
|
68
69
|
const BENCHMARK_SITE_TYPE_RULES = {
|
|
69
70
|
blog: [
|
|
70
71
|
"Benchmark blog mode: use neutral structural placeholders or concrete subject matter instead of publication worldbuilding or reflective-editorial atmosphere prose.",
|
|
71
|
-
"Benchmark blog mode: do not wrap the page in a generic publication shell like Journal, Featured Essay, Latest Dispatches, Print Edition, Submit Work, or similar magazine-site framing.",
|
|
72
|
+
"Benchmark blog mode: do not wrap the page in a generic publication shell like Journal, Featured Essay, Latest Dispatches, Notes, Archive, Print Edition, Submit Work, or similar magazine-site framing.",
|
|
72
73
|
"Benchmark blog mode: avoid the default editorial scaffold of hero, story list, archive rail, topics grid, and publication footer unless the user explicitly asked for a magazine-style site.",
|
|
73
|
-
"Benchmark blog mode: do not use Tailwind CDN starter theming, picsum/placehold imagery,
|
|
74
|
+
"Benchmark blog mode: do not use Tailwind CDN starter theming, Font Awesome chrome, picsum/placehold imagery, fake keyboard/search chrome, or demo-only modal article previews."
|
|
74
75
|
],
|
|
75
76
|
store: [
|
|
76
77
|
"Benchmark ecommerce mode: prioritize conversion architecture over editorial styling. The page should sell, not just look clean.",
|
|
77
78
|
"Benchmark ecommerce mode: include proof, objection handling, and trust signals that belong on a real single-product PDP.",
|
|
78
|
-
"Benchmark ecommerce mode: do not leave the main merchandising surface as a literal placeholder
|
|
79
|
-
"Benchmark ecommerce mode: do not use Tailwind CDN starter theming or placeholder product images such as picsum/placehold on the live merchandising surface.",
|
|
80
|
-
"Benchmark ecommerce mode: fake command palettes, fake app shortcuts, and unrelated theme chrome are disallowed.",
|
|
81
|
-
"Benchmark ecommerce mode: fake review counts, fake bestseller labels, and fake as-featured-in proof are disallowed."
|
|
79
|
+
"Benchmark ecommerce mode: do not leave the main merchandising surface as a literal placeholder, dashed demo box, emoji stand-in, or visual wireframe.",
|
|
80
|
+
"Benchmark ecommerce mode: do not use Tailwind CDN starter theming, Font Awesome chrome, or placeholder product images such as picsum/placehold on the live merchandising surface.",
|
|
81
|
+
"Benchmark ecommerce mode: fake command palettes, fake app shortcuts, demo-only cart gimmicks, and unrelated theme chrome are disallowed.",
|
|
82
|
+
"Benchmark ecommerce mode: fake review counts, fake bestseller labels, fake sales counters, and fake as-featured-in proof are disallowed."
|
|
82
83
|
]
|
|
83
84
|
};
|
|
84
85
|
|
|
@@ -112,7 +113,8 @@ const SITE_TYPE_RULES = {
|
|
|
112
113
|
blog: [
|
|
113
114
|
"For blogs and editorial sites, prefer structural placeholder labels over inventing fictional publication brands, named editors, or fake biographical lore.",
|
|
114
115
|
"Do not invent issue numbers, seasonal issue metadata, staff credits, or named authors/photographers unless the user explicitly asked for fictional worldbuilding.",
|
|
115
|
-
"Do not add a newsletter/signup block unless the user explicitly asked for one or the site type clearly requires capture."
|
|
116
|
+
"Do not add a newsletter/signup block unless the user explicitly asked for one or the site type clearly requires capture.",
|
|
117
|
+
"Prefer concrete topics or neutral structural labels over essay-magazine abstractions about slowness, attention, memory, or restraint."
|
|
116
118
|
],
|
|
117
119
|
landing: [
|
|
118
120
|
"For landing pages, prioritize the value proposition, proof, and one primary action over editorial atmosphere.",
|
|
@@ -178,6 +180,7 @@ const ARCHETYPE_RULES = {
|
|
|
178
180
|
"Use a commerce-first hierarchy: product image, offer, price, variants, CTA, trust, and objection handling should be visually obvious.",
|
|
179
181
|
"Favor sharp sans-serif typography with disciplined emphasis; avoid editorial-magazine drift.",
|
|
180
182
|
"The hero surface must merchandise the product, not just reserve space for a placeholder.",
|
|
183
|
+
"If no real photo is available, create a product-shaped merchandising composition with material callouts and silhouette treatment, not a dashed placeholder box or emoji stand-in.",
|
|
181
184
|
"Target type palette: assertive commerce sans with compact utility text and a restrained accent display if needed."
|
|
182
185
|
],
|
|
183
186
|
"quiet-portfolio": [
|
|
@@ -240,16 +243,20 @@ const GENERIC_EDITORIAL_LAYOUT_PATTERNS = [
|
|
|
240
243
|
const SLOP_PATTERNS = [
|
|
241
244
|
{ key: "placeholder_images", label: "placeholder image service", pattern: /\b(?:picsum\.photos|placehold\.co|placeholder\.com)\b/i, weight: 3 },
|
|
242
245
|
{ key: "tailwind_cdn", label: "Tailwind CDN starter styling", pattern: /cdn\.tailwindcss\.com/i, weight: 2 },
|
|
246
|
+
{ key: "font_awesome_cdn", label: "Font Awesome CDN chrome", pattern: /cdnjs\.cloudflare\.com\/ajax\/libs\/font-awesome|fontawesome/i, weight: 2 },
|
|
243
247
|
{ key: "inter_playfair", label: "generic Inter/Playfair premium pairing", pattern: /Inter|Playfair\s+Display/i, weight: 2 },
|
|
244
248
|
{ key: "browser_default_type", label: "browser-default serif/sans fallback typography", pattern: /\b(?:Georgia|Times New Roman|serif;|system-ui|BlinkMacSystemFont|Segoe UI|Helvetica|Arial)\b/i, weight: 2 },
|
|
245
249
|
{ key: "fake_prestige", label: "fake prestige or publication badge", pattern: /\b(?:featured in|as seen in|forbes|the atlantic|wall street journal|award-winning)\b/i, weight: 3 },
|
|
246
250
|
{ key: "fake_founder_lore", label: "fake founder or studio lore", pattern: /\b(?:founded in|est\s+20\d{2}|from the studio|published from a small studio|founder\s*&\s*essayist)\b/i, weight: 2 },
|
|
247
251
|
{ key: "newsletter_cliche", label: "generic newsletter promise copy", pattern: /\b(?:no spam, ever|respect your inbox|join the newsletter|subscribe to the journal|receive the next dispatch|quarterly letters from the edge of attention|only the essential)\b/i, weight: 2 },
|
|
248
252
|
{ key: "fake_ui_chrome", label: "fake low-value UI chrome", pattern: /\b(?:search|filterCategory|showPostModal|toggleSearch|Latest Stories|Recent Dispatches|command palette would open here|metaKey && e\.key === ['"]k['"])\b/i, weight: 2 },
|
|
253
|
+
{ key: "demo_modal_chrome", label: "demo modal or prompt/alert interaction chrome", pattern: /\b(?:prompt\(|alert\(|modal|demo only|structural preview|select an essay to read a structural preview)\b/i, weight: 2 },
|
|
254
|
+
{ key: "demo_page_language", label: "demo page explanation copy", pattern: /\b(?:static demonstration page|demonstration only|demo only|not a real store|not a real publication|structural demonstration|built as a single html file|static demo)\b/i, weight: 3 },
|
|
249
255
|
{ key: "premium_blog_trope", label: "generic premium-blog editorial trope", pattern: /\b(?:thoughtful living|slow living|curated reflections|crafted with intention|made with intention|a quiet publication|made with restraint|journal of attention|discipline of seeing|private notes made public)\b/i, weight: 2 },
|
|
250
256
|
{ key: "reflective_editorial_copy", label: "generic reflective-editorial copy trope", pattern: /\b(?:quiet architecture of attention|great thinkers understood|value of slowness|the examined life|deliberate cultivation of focus|most radical act|what we have forgotten|how everyday items carry|still matters)\b/i, weight: 2 },
|
|
251
257
|
{ key: "fictional_publication_brand", label: "invented publication/author scaffolding", pattern: /\b(?:by [A-Z][a-z]+ [A-Z][a-z]+|photography by [A-Z][a-z]+ [A-Z][a-z]+|journal of attention|vesper|lumen|dispatches|vol\.\s*\d+|winter 20\d{2}|autumn 20\d{2}|spring 20\d{2}|summer 20\d{2}|the quarterly|editorial\.)\b/i, weight: 3 },
|
|
252
|
-
{ key: "fake_social_proof", label: "fake reader count or social proof", pattern: /\b(?:\d+\s+readers joined this month|\d+\s+subscribers?|trusted by)\b/i, weight: 2 }
|
|
258
|
+
{ key: "fake_social_proof", label: "fake reader count or social proof", pattern: /\b(?:\d+\s+readers joined this month|\d+\s+subscribers?|trusted by)\b/i, weight: 2 },
|
|
259
|
+
{ key: "placeholder_surface", label: "placeholder surface or stand-in hero", pattern: /\b(?:placeholder|visual placeholder|product representation|hero placeholder|dashed|emoji stand-in|🧥|🎧)\b/i, weight: 3 }
|
|
253
260
|
];
|
|
254
261
|
|
|
255
262
|
function getBenchmarkSiteTypeRules(siteType, benchmarkMode) {
|
|
@@ -434,18 +441,30 @@ export function detectFrontendSlop({ promptText = "", assistantText = "", receip
|
|
|
434
441
|
flags.push("benchmark store left the primary merchandising surface as a placeholder");
|
|
435
442
|
score += 4;
|
|
436
443
|
}
|
|
444
|
+
if (siteType === "store" && benchmarkMode && /\b(?:placeholder|visual placeholder|product representation|dashed|emoji stand-in|🧥|🎧)\b/i.test(haystack)) {
|
|
445
|
+
flags.push("benchmark store used a placeholder hero surface instead of product-shaped merchandising");
|
|
446
|
+
score += 4;
|
|
447
|
+
}
|
|
437
448
|
if (siteType === "store" && benchmarkMode && /cdn\.tailwindcss\.com/i.test(haystack)) {
|
|
438
449
|
flags.push("benchmark store relied on Tailwind CDN starter theming");
|
|
439
450
|
score += 4;
|
|
440
451
|
}
|
|
452
|
+
if (siteType === "store" && benchmarkMode && /(?:cdnjs\.cloudflare\.com\/ajax\/libs\/font-awesome|fontawesome)/i.test(haystack)) {
|
|
453
|
+
flags.push("benchmark store relied on Font Awesome chrome");
|
|
454
|
+
score += 3;
|
|
455
|
+
}
|
|
441
456
|
if (siteType === "store" && benchmarkMode && /\b(?:picsum\.photos|placehold\.co|placeholder\.com)\b/i.test(haystack)) {
|
|
442
457
|
flags.push("benchmark store used placeholder product imagery");
|
|
443
458
|
score += 4;
|
|
444
459
|
}
|
|
445
|
-
if (siteType === "store" && benchmarkMode && /\b(?:best seller|
|
|
460
|
+
if (siteType === "store" && benchmarkMode && /\b(?:best seller|bestseller|\d[\d,]*\s+reviews|\d[\d,]*\s+\w+\s+sold\s+this\s+month|sold this month|as featured in|featured in|trusted by|studio engineers approved)\b/i.test(haystack)) {
|
|
446
461
|
flags.push("benchmark store used fake proof or badge chrome");
|
|
447
462
|
score += 4;
|
|
448
463
|
}
|
|
464
|
+
if (siteType === "store" && benchmarkMode && /\b(?:metaKey && e\.key === ['"]k['"]|command palette|keyboard shortcuts?)\b/i.test(haystack)) {
|
|
465
|
+
flags.push("benchmark store used fake keyboard or shortcut chrome");
|
|
466
|
+
score += 4;
|
|
467
|
+
}
|
|
449
468
|
if (siteType === "store" && benchmarkMode && !/\b(?:review|reviews|testimonial|rated|stars?|customers?)\b/i.test(haystack)) {
|
|
450
469
|
flags.push("benchmark store lacks social proof or review architecture");
|
|
451
470
|
score += 2;
|
|
@@ -466,14 +485,26 @@ export function detectFrontendSlop({ promptText = "", assistantText = "", receip
|
|
|
466
485
|
flags.push("benchmark blog used generic publication-shell framing");
|
|
467
486
|
score += 3;
|
|
468
487
|
}
|
|
488
|
+
if (siteType === "blog" && benchmarkMode && /\b(?:Featured Essay|In Praise of Slowness|Measure of Restraint|On Attention|Craft and Memory|Latest Dispatches|From the Archive|Notes)\b/i.test(haystack)) {
|
|
489
|
+
flags.push("benchmark blog fell back to essay-magazine placeholder language");
|
|
490
|
+
score += 3;
|
|
491
|
+
}
|
|
469
492
|
if (siteType === "blog" && benchmarkMode && /cdn\.tailwindcss\.com/i.test(haystack)) {
|
|
470
493
|
flags.push("benchmark blog relied on Tailwind CDN starter theming");
|
|
471
494
|
score += 4;
|
|
472
495
|
}
|
|
496
|
+
if (siteType === "blog" && benchmarkMode && /(?:cdnjs\.cloudflare\.com\/ajax\/libs\/font-awesome|fontawesome)/i.test(haystack)) {
|
|
497
|
+
flags.push("benchmark blog relied on Font Awesome chrome");
|
|
498
|
+
score += 3;
|
|
499
|
+
}
|
|
473
500
|
if (siteType === "blog" && benchmarkMode && /\b(?:picsum\.photos|placehold\.co|placeholder\.com)\b/i.test(haystack)) {
|
|
474
501
|
flags.push("benchmark blog used placeholder imagery");
|
|
475
502
|
score += 4;
|
|
476
503
|
}
|
|
504
|
+
if (siteType === "blog" && benchmarkMode && /\b(?:prompt\(|alert\(|toggleSearch|viewPost\(|modal|structural preview|subscribe)\b/i.test(haystack)) {
|
|
505
|
+
flags.push("benchmark blog used demo interaction chrome");
|
|
506
|
+
score += 3;
|
|
507
|
+
}
|
|
477
508
|
if (/\b(?:command palette would open here|metaKey && e\.key === ['"]k['"]|keyboard accessibility)\b/i.test(haystack)) {
|
|
478
509
|
flags.push("fake keyboard or command-palette gimmick");
|
|
479
510
|
score += 3;
|
|
@@ -492,14 +523,14 @@ export function detectFrontendSlop({ promptText = "", assistantText = "", receip
|
|
|
492
523
|
return {
|
|
493
524
|
score,
|
|
494
525
|
flags,
|
|
495
|
-
hardBlock: flags.some((flag) => /fictional publication identity|generic publication-shell framing|fake keyboard|reflective-editorial atmosphere|primary merchandising surface as a placeholder|Tailwind CDN starter theming|placeholder product imagery|placeholder imagery|fake proof or badge chrome/.test(flag)),
|
|
526
|
+
hardBlock: flags.some((flag) => /fictional publication identity|generic publication-shell framing|essay-magazine placeholder language|fake keyboard|reflective-editorial atmosphere|primary merchandising surface as a placeholder|placeholder hero surface|Tailwind CDN starter theming|Font Awesome chrome|placeholder product imagery|placeholder imagery|fake proof or badge chrome|demo interaction chrome|demo page explanation copy/.test(flag)),
|
|
496
527
|
severe: score >= 5,
|
|
497
528
|
summary: flags.length > 0 ? `frontend slop flags: ${flags.join(", ")}` : "no deterministic frontend slop flags"
|
|
498
529
|
};
|
|
499
530
|
}
|
|
500
531
|
|
|
501
532
|
export function shouldAutoReviseFrontend({ designReview = null, slop = null, revisionCount = 0 } = {}) {
|
|
502
|
-
if (revisionCount >=
|
|
533
|
+
if (revisionCount >= 3) return false;
|
|
503
534
|
if (!designReview) return false;
|
|
504
535
|
if (slop?.hardBlock) return true;
|
|
505
536
|
if (designReview.verdict === "weak") return true;
|
|
@@ -528,16 +559,21 @@ export function buildFrontendRevisionPrompt({
|
|
|
528
559
|
visualNextPass.length > 0 ? `Screenshot revision priorities:\n- ${visualNextPass.join("\n- ")}` : "",
|
|
529
560
|
"Do not add new filler sections.",
|
|
530
561
|
"Do not add fake prestige, fake testimonials, fake brands, placeholder-image services, or fictional publication/persona lore unless the user explicitly asked for that worldbuilding.",
|
|
562
|
+
"Do not use Tailwind CDN or Font Awesome CDN for benchmark pages. Use authored CSS and native SVG or plain text/icon treatment instead.",
|
|
531
563
|
"If this is a blog or editorial site, prefer structural labels and a stronger layout idea over invented magazines, authors, newsletter promises, or atmosphere-copy.",
|
|
532
564
|
"Do not use browser-default serif/sans fallback stacks as the end-state typography. Pick a deliberate type system or simplify until the typography feels intentional.",
|
|
533
565
|
"Remove fake keyboard shortcuts, fake command palettes, and demo-only interaction flourishes that do not help the page.",
|
|
566
|
+
"Remove demo toasts, alerts, console narration, and static-demo explanation copy. The page should stand on its own without explaining that it is a demo.",
|
|
534
567
|
"Cut reflective-editorial filler copy and replace it with either concrete language or neutral structural placeholders.",
|
|
535
568
|
"For benchmark blog tasks, default to neutral structural placeholder content instead of invented publication framing, issue metadata, or named contributors.",
|
|
536
|
-
"For benchmark blog tasks, do not use generic publication-shell labels like Journal, Featured Essay, Latest Dispatches, Print Edition, Submit Work, or publication-footer framing.",
|
|
537
|
-
"For benchmark blog tasks, do not use
|
|
569
|
+
"For benchmark blog tasks, do not use generic publication-shell labels like Journal, Featured Essay, Latest Dispatches, Notes, Archive, Print Edition, Submit Work, or publication-footer framing.",
|
|
570
|
+
"For benchmark blog tasks, do not use essay-magazine fallback headlines like In Praise of Slowness, On Attention, The Measure of Restraint, or similar reflective placeholders.",
|
|
571
|
+
"For benchmark blog tasks, do not use Tailwind CDN starter theming, Font Awesome chrome, picsum-style placeholder imagery, fake search/shortcut chrome, subscribe/newsletter CTA chrome, or demo modal article previews.",
|
|
538
572
|
"For benchmark store tasks, do not leave the product image area as a labeled placeholder. Use product-shaped merchandising composition, proof, and objection-handling blocks instead.",
|
|
539
|
-
"For benchmark store tasks, do not use
|
|
540
|
-
"For benchmark store tasks, do not
|
|
573
|
+
"For benchmark store tasks, do not use dashed placeholder boxes, emoji stand-ins, or wireframe hero surfaces. If needed, build a graphic product silhouette or material-driven composition instead.",
|
|
574
|
+
"For benchmark store tasks, do not use Tailwind CDN starter theming, Font Awesome chrome, picsum-style placeholder imagery, or fake command-palette behavior.",
|
|
575
|
+
"For benchmark store tasks, do not invent review counts, bestseller labels, sold-this-month counters, or as-featured-in proof unless the user explicitly requested fictional marketing chrome.",
|
|
576
|
+
slop?.hardBlock ? "Hard-fail benchmark patterns are still present. Rebuild the page from scratch if needed rather than preserving those patterns." : "",
|
|
541
577
|
"Reduce section count if needed and push one stronger asymmetrical composition instead of a sequence of balanced blocks.",
|
|
542
578
|
"Simplify the page if needed. Stronger direction with fewer elements is preferred over busier generic output.",
|
|
543
579
|
"Rewrite the weakest sections rather than making superficial tweaks."
|
package/src/tools.js
CHANGED
|
@@ -506,11 +506,20 @@ function normalizeContractArgs(args = {}) {
|
|
|
506
506
|
summary: String(args.summary || "").trim(),
|
|
507
507
|
paths: normalizePathList(args.paths || []),
|
|
508
508
|
commands: uniqueStrings(args.commands || []),
|
|
509
|
-
verification: uniqueStrings(args.verification || []),
|
|
509
|
+
verification: uniqueStrings((args.verification || []).map((command) => normalizeVerificationCommand(command))),
|
|
510
510
|
risk: ["low", "medium", "high"].includes(String(args.risk || "").toLowerCase()) ? String(args.risk).toLowerCase() : "medium"
|
|
511
511
|
};
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
+
function normalizeVerificationCommand(command) {
|
|
515
|
+
const raw = String(command || "").trim();
|
|
516
|
+
if (!raw) return raw;
|
|
517
|
+
return raw
|
|
518
|
+
.replace(/(^|[\s("'`])\/desktop(?=\/|$)/gi, `$1${shellEscape(path.join(os.homedir(), "Desktop"))}`)
|
|
519
|
+
.replace(/(^|[\s("'`])\/downloads(?=\/|$)/gi, `$1${shellEscape(path.join(os.homedir(), "Downloads"))}`)
|
|
520
|
+
.replace(/(^|[\s("'`])\/documents(?=\/|$)/gi, `$1${shellEscape(path.join(os.homedir(), "Documents"))}`);
|
|
521
|
+
}
|
|
522
|
+
|
|
514
523
|
function getTouchedPathsForTool(toolName, args = {}) {
|
|
515
524
|
if (toolName === "write_file" || toolName === "replace_in_file" || toolName === "make_directory" || toolName === "delete_path") {
|
|
516
525
|
return normalizePathList([args.path || ""]);
|