@astralkit/mcp 1.4.0 → 1.6.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.
@@ -0,0 +1,161 @@
1
+ // Per-screen-type UX blueprints served by the get_screen_blueprint tool.
2
+ // Each blueprint = structure + must-haves + anti-patterns + the four-state
3
+ // requirement + which AstralKit pieces to reach for (categories, ready-to-run
4
+ // search_components queries, and polish_ui archetypes). This is the screen-aware
5
+ // companion to get_build_standards (cross-cutting rules) — it tells the agent
6
+ // how THIS kind of screen should be built and what to pull from the library.
7
+ //
8
+ // Source: docs/CODE_ARCHITECTURE_GUIDE.md, docs/empty-states-guide.md,
9
+ // docs/error-handing.md, the radix-research bank, and the real category slugs in
10
+ // src/types/component-filters.ts + src/lib/components/taxonomy.ts.
11
+ const DASHBOARD = `# Screen Blueprint: Dashboard
12
+
13
+ ## Structure (inverted pyramid)
14
+ Global KPIs first (top-left — F/Z scan), then charts/trends, then tabular detail. Persistent app shell: sidebar + top bar around the content canvas.
15
+
16
+ ## Must-haves
17
+ - KPI cards carry name + value + trend + comparison period + timestamp. A number without context is decoration.
18
+ - Keep it scannable: aim <= 5 charts per view and <= 7 data points per chart.
19
+ - Filters are visible and persistent (chips with one-click clear), not buried in a menu.
20
+ - Every data widget honors the four-state contract (skeleton matching the widget shape -> empty -> error -> data). Isolate each widget in its own error boundary so one failed chart never blanks the dashboard.
21
+
22
+ ## Anti-patterns
23
+ 3D charts; pie charts with >5 segments; a wall of equal-weight tiles (no hierarchy); a spinner for the whole page instead of per-widget skeletons; the "Ghost" (blank widget when a query returns nothing).
24
+
25
+ ## AstralKit pieces
26
+ - Categories: sidebars, top-bars, data-display.
27
+ - search_components: "analytics dashboard", "stats overview", "data table".
28
+ - polish_ui archetypes: stat-tile, table, sidebar, top-bar, section-header.`;
29
+ const NAV = `# Screen Blueprint: Navigation (top bar, mega-menu, mobile menu)
30
+
31
+ ## Structure
32
+ Top bar: brand left, primary links center/left, actions (search, account, CTA) right. Mega-menu for grouped destinations. A mobile menu is REQUIRED, not optional.
33
+
34
+ ## Must-haves
35
+ - Desktop links hidden behind a hamburger on mobile: nav links 'hidden md:flex', hamburger 'md:hidden'. The mobile menu must actually exist and be reachable — desktop-only navs are the most common responsiveness bug.
36
+ - Mobile menu = a Radix Dialog used as a side sheet (w-[85vw] max-w-sm): traps focus, closes on Escape AND on route change.
37
+ - Mega-menu collapses to a stacked accordion (<details> or Radix Accordion) inside the mobile drawer.
38
+ - Mark the current page (aria-current="page"); keyboard-operable; visible focus rings.
39
+ - Sticky bars: don't trap scroll; ensure overlay menus sit above content (z-index) and over the themed surface.
40
+
41
+ ## Anti-patterns
42
+ Desktop-only nav (no mobile menu); a custom click-dropdown for the mobile menu instead of a focus-trapping Dialog; mega-menu that overflows the viewport on mobile; low-contrast active state.
43
+
44
+ ## AstralKit pieces
45
+ - Categories: top-bars, sidebars, overlays.
46
+ - search_components: "navigation bar", "mega menu", "mobile menu".
47
+ - polish_ui archetypes: top-bar, sidebar-nav.`;
48
+ const PRICING = `# Screen Blueprint: Pricing
49
+
50
+ ## Structure
51
+ Tier cards in a row (3 is typical), one tier visually emphasized as "most popular", a clear CTA per tier, optional comparison table below, FAQ + final CTA to close.
52
+
53
+ ## Must-haves
54
+ - Strong CTA hierarchy: the recommended tier's button is the primary; others are secondary/outline. Don't make all three equal.
55
+ - Show what each tier includes with check/x rows; keep value (not just price) legible.
56
+ - Billing toggle (monthly/annual) updates prices in place; annual savings called out.
57
+ - Mobile: cards reflow to a single column; the comparison table becomes per-tier stacked cards or a horizontal-scroll with a sticky first column.
58
+ - Trust elements near the CTA (guarantee, social proof, logos via search_logos).
59
+
60
+ ## Anti-patterns
61
+ Equal-weight CTAs (no clear recommendation); a comparison table that horizontally overflows on mobile with no affordance; prices that don't update with the billing toggle; pricing on a dark hero with low-contrast text.
62
+
63
+ ## AstralKit pieces
64
+ - Categories: pricing, footers.
65
+ - search_components: "pricing tiers", "pricing comparison".
66
+ - polish_ui archetypes: pricing card via card, section-header, footer, hero.`;
67
+ const MARKETING = `# Screen Blueprint: Marketing / Landing
68
+
69
+ ## Structure
70
+ Hero (headline + subhead + primary CTA + visual) -> social proof / logo strip -> feature sections (alternating) -> testimonials -> pricing teaser -> FAQ -> final CTA -> footer.
71
+
72
+ ## Must-haves
73
+ - One dominant CTA repeated down the page; secondary CTA is lower-emphasis.
74
+ - 3x+ size hierarchy on the hero headline; restrained weights; 60-30-10 color restraint with the accent reserved for CTAs.
75
+ - Real content, not lorem/placeholder; real logos via search_logos.
76
+ - Sections are responsive (stack on mobile); images use next/image with sizes; honor prefers-reduced-motion on any scroll/entrance animation.
77
+
78
+ ## Anti-patterns
79
+ Multiple competing CTAs of equal weight; a hero that buries the value prop; stock-illustration soup; animation that ignores reduced-motion.
80
+
81
+ ## AstralKit pieces
82
+ - Categories: footers, pricing (teaser).
83
+ - search_components: "hero section", "feature section", "testimonials", "footer".
84
+ - polish_ui archetypes: hero, section-header, footer (flagship — copy the full treatment).`;
85
+ const AUTH = `# Screen Blueprint: Auth (sign in / sign up / reset)
86
+
87
+ ## Structure
88
+ Focused single-column card, centered, minimal chrome (no full app nav). Brand at top, the form, a primary submit, secondary links (forgot password, switch sign-in/up), optional social sign-in.
89
+
90
+ ## Must-haves
91
+ - Inline validation with aria-invalid + aria-describedby; on submit failure, focus the first error field and preserve input. Validation errors are inline, NOT toasts.
92
+ - Distinct, calm error states for wrong credentials / expired session ("Please sign in again") — never a raw error.
93
+ - Submit button shows a loading state and prevents double-submit.
94
+ - Social sign-in buttons use real brand logos (search_logos), 48px targets, accessible labels.
95
+ - After login, return the user to their intended destination.
96
+
97
+ ## Anti-patterns
98
+ Errors shown as toasts instead of inline; no loading state (double-submits); raw auth error text; tiny touch targets; password field with no show/hide.
99
+
100
+ ## AstralKit pieces
101
+ - Categories: forms, overlays.
102
+ - search_components: "login form", "sign up form".
103
+ - polish_ui archetypes: form (flagship), form-field, button.`;
104
+ const ONBOARDING = `# Screen Blueprint: Onboarding
105
+
106
+ ## Structure
107
+ A short stepped flow (progress indicator) or a first-use empty state that guides the very first action. Reduce choices to one clear next step per screen.
108
+
109
+ ## Must-haves
110
+ - Frame the first-use state as opportunity ("Ready to begin / your dashboard is ready for its first item"), not absence ("No data").
111
+ - Reduce first-move anxiety: "you can edit or delete this later", offer a template, keep stakes low.
112
+ - A visible progress sense (checklist or step dots: Account created -> Verify -> Create first project <- you are here).
113
+ - One primary CTA per step; outcome-focused label ("Start organizing", not "Create project"). Optional secondary (start from template).
114
+ - Skippable where reasonable; remembers where the user left off.
115
+
116
+ ## Anti-patterns
117
+ Dumping every feature at once; a blank dashboard with no guidance; multiple equal CTAs; a flow with no progress sense or no skip.
118
+
119
+ ## AstralKit pieces
120
+ - Categories: forms, overlays, utility.
121
+ - search_components: "onboarding", "stepper", "empty state".
122
+ - polish_ui archetypes: form, progress, empty-state, section-header.`;
123
+ const SETTINGS = `# Screen Blueprint: Settings
124
+
125
+ ## Structure
126
+ Sectioned page (account, security, billing, notifications, danger zone) with a sub-nav (left rail or tabs) and one section visible at a time. Each section is a small, self-contained form.
127
+
128
+ ## Must-haves
129
+ - Group related fields; clear section headers; generous vertical rhythm.
130
+ - Dirty/save state: a save button that enables only on change, shows saving/saved feedback, and surfaces errors inline (not a toast for field errors). Destructive actions (delete account) sit in a clearly separated danger zone behind a confirm Dialog.
131
+ - Optimistic or clearly-pending toggles; success confirmation for saved changes (toast OK for global success).
132
+ - Each form field follows the form-field rules (label above font-medium text-ak-sm, 48px control, helper/error below, focus ring).
133
+
134
+ ## Anti-patterns
135
+ One giant form with no sectioning; destructive actions next to normal ones with no confirm; no dirty/save feedback; field errors as toasts.
136
+
137
+ ## AstralKit pieces
138
+ - Categories: forms, sidebars, overlays.
139
+ - search_components: "settings form", "account settings", "tabs".
140
+ - polish_ui archetypes: form, form-field, sidebar-nav, modal.`;
141
+ export const SCREEN_BLUEPRINTS = {
142
+ dashboard: DASHBOARD,
143
+ nav: NAV,
144
+ pricing: PRICING,
145
+ marketing: MARKETING,
146
+ auth: AUTH,
147
+ onboarding: ONBOARDING,
148
+ settings: SETTINGS,
149
+ };
150
+ export const SCREEN_TYPES = Object.keys(SCREEN_BLUEPRINTS);
151
+ /** Build the get_screen_blueprint response for a screen type. */
152
+ export function buildScreenBlueprint(screenType) {
153
+ const bp = SCREEN_BLUEPRINTS[screenType];
154
+ if (bp) {
155
+ return (bp +
156
+ `\n\n---\nEvery data view here owes the four-state contract (get_build_standards topic "state-lifecycle"). ` +
157
+ `Pull the named pieces with search_components, preview with get_preview, adapt with get_component (mode:recipe), then validate_code.`);
158
+ }
159
+ return (`No blueprint for "${screenType}". Available screen types: ${SCREEN_TYPES.join(', ')}.`);
160
+ }
161
+ //# sourceMappingURL=screens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screens.js","sourceRoot":"","sources":["../../src/data/screens.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,2EAA2E;AAC3E,8EAA8E;AAC9E,iFAAiF;AACjF,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,uEAAuE;AACvE,iFAAiF;AACjF,mEAAmE;AAEnE,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;4EAiB0D,CAAC;AAE7E,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;8CAkBkC,CAAC;AAE/C,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;6EAkB6D,CAAC;AAE9E,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;2FAiByE,CAAC;AAE5F,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;6DAkBgD,CAAC;AAE9D,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;qEAkBkD,CAAC;AAEtE,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;8DAiB6C,CAAC;AAE/D,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,SAAS,EAAE,SAAS;IACpB,GAAG,EAAE,GAAG;IACR,OAAO,EAAE,OAAO;IAChB,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,IAAI;IACV,UAAU,EAAE,UAAU;IACtB,QAAQ,EAAE,QAAQ;CACnB,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AAE3D,iEAAiE;AACjE,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,MAAM,EAAE,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,EAAE,EAAE,CAAC;QACP,OAAO,CACL,EAAE;YACF,4GAA4G;YAC5G,qIAAqI,CACtI,CAAC;IACJ,CAAC;IACD,OAAO,CACL,qBAAqB,UAAU,8BAA8B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACxF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ export interface ReviewIssue {
2
+ rule: string;
3
+ severity: 'high' | 'medium';
4
+ detail: string;
5
+ fix: string;
6
+ }
7
+ export interface ReviewInput {
8
+ code?: string;
9
+ files?: Record<string, string>;
10
+ screenType?: string;
11
+ }
12
+ export interface ReviewResult {
13
+ valid: boolean;
14
+ issueCount: number;
15
+ issues: ReviewIssue[];
16
+ summary: string;
17
+ }
18
+ export declare function reviewApp(input: ReviewInput): ReviewResult;
19
+ //# sourceMappingURL=review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAyJD,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA2C1D"}
package/dist/review.js ADDED
@@ -0,0 +1,182 @@
1
+ // review_app — a STRUCTURAL audit (the architectural sibling of validate_code's
2
+ // visual audit). It flags high-signal "messy app" smells: a missing four-state
3
+ // contract, swallowed errors, atomic-design/separation-of-concerns violations,
4
+ // missing mobile navigation, and Radix a11y gaps. Heuristic, not a compiler —
5
+ // deliberately conservative (only flags what it is confident about) so agents
6
+ // trust it. Rules mirror the named anti-patterns in docs/empty-states-guide.md,
7
+ // docs/error-handing.md, and docs/CODE_ARCHITECTURE_GUIDE.md.
8
+ const FILE_SIZE_LIMIT = 300;
9
+ // Strip line/block comments so we don't flag commented-out code.
10
+ function decomment(code) {
11
+ return code.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/[^\n]*/g, '$1');
12
+ }
13
+ function countLines(s) {
14
+ return s.split('\n').length;
15
+ }
16
+ // ── Checks that work on any code snippet ──────────────────────────────────────
17
+ function snippetChecks(raw, where) {
18
+ const issues = [];
19
+ const code = decomment(raw);
20
+ const at = where ? ` (${where})` : '';
21
+ // --- State contract: the "Ghost" (render nothing when empty) ---
22
+ // `{items.length > 0 && <JSX>}` or `{items.length && <JSX>}` with no empty branch.
23
+ const ghost = /\{\s*[\w.]+\.length\s*(?:>\s*0\s*)?&&\s*[(<]/.test(code);
24
+ const hasEmptyHandling = /length\s*===\s*0|length\s*<\s*1|!\w+\??\.length|\bEmptyState\b|isEmpty|no[\s-]?results/i.test(code);
25
+ if (ghost && !hasEmptyHandling) {
26
+ issues.push({
27
+ rule: 'ghost-render',
28
+ severity: 'high',
29
+ detail: `Renders a list only when it has items, with no empty branch${at} — users see blank space and assume it's broken (the "Ghost" anti-pattern).`,
30
+ fix: 'Add an explicit empty branch: `if (!data?.length) return <EmptyState .../>` before rendering the list. See get_build_standards topic "state-lifecycle".',
31
+ });
32
+ }
33
+ // --- State contract: the "Naked Empty" ---
34
+ if (/return\s*\(?\s*<\w+[^>]*>\s*(No data|Nothing here|No results|Empty|None)\b/i.test(code)) {
35
+ issues.push({
36
+ rule: 'naked-empty-state',
37
+ severity: 'medium',
38
+ detail: `A bare "No data"-style empty state${at} with no icon, explanation, or action.`,
39
+ fix: 'Use a real empty state (icon + title + description + CTA) — reuse atoms/empty-state.tsx. See get_build_standards topic "empty-states".',
40
+ });
41
+ }
42
+ // --- State contract: a data view with no loading/empty handling ---
43
+ const isDataView = /\buseQuery\s*\(|\buseSWR\s*\(/.test(code);
44
+ const handlesLoadingOrEmpty = /isLoading|isPending|isFetching|Skeleton|EmptyState|length\s*===\s*0|!\w+\??\.length/.test(code);
45
+ if (isDataView && !handlesLoadingOrEmpty) {
46
+ issues.push({
47
+ rule: 'incomplete-states',
48
+ severity: 'medium',
49
+ detail: `Fetches data${at} but appears to render no loading skeleton or empty state.`,
50
+ fix: 'Handle all four states: loading (skeleton) -> empty -> error -> data. See get_build_standards topic "state-lifecycle".',
51
+ });
52
+ }
53
+ // --- Error handling: empty catch ---
54
+ if (/catch\s*\([^)]*\)\s*\{\s*\}/.test(code)) {
55
+ issues.push({
56
+ rule: 'empty-catch',
57
+ severity: 'high',
58
+ detail: `An empty catch block${at} silently swallows errors.`,
59
+ fix: 'Every catch must log AND update the UI (set an error state / show a recovery toast). See get_build_standards topic "error-handling".',
60
+ });
61
+ }
62
+ // --- Error handling: alert() ---
63
+ if (/(?:^|[^.\w])alert\s*\(/.test(code)) {
64
+ issues.push({
65
+ rule: 'alert-used',
66
+ severity: 'high',
67
+ detail: `Uses alert()${at} for user feedback.`,
68
+ fix: 'Replace alert() with a toast (react-hot-toast / Sonner / Radix Toast). Error toasts should not auto-dismiss.',
69
+ });
70
+ }
71
+ // --- Error handling: raw error rendered to the UI (the "Confuser") ---
72
+ if (/\{\s*\w*[eE]rr\w*\.message\s*\}/.test(code) || /Error\s*\d{3}\b/.test(code) || /ECONNREFUSED|ETIMEDOUT/.test(code)) {
73
+ issues.push({
74
+ rule: 'raw-error-shown',
75
+ severity: 'medium',
76
+ detail: `A raw error message or error code is shown to the user${at}.`,
77
+ fix: 'Show a plain-language title + recovery action; log the technical detail. Never surface stack traces / status codes / errno to users. See get_build_standards topic "error-handling".',
78
+ });
79
+ }
80
+ // --- Responsive: a nav with no mobile menu ---
81
+ const looksLikeNav = /<nav[\s>]|role=["']navigation["']|<header[\s>]/.test(code);
82
+ const hasDesktopLinks = /hidden\s+md:|md:flex|md:items-center/.test(code);
83
+ const hasMobileMenu = /md:hidden|Sheet|Drawer|Dialog|hamburger|mobile[-\s]?menu|MobileMenu|\bList\b.*weight|aria-label=["'][^"']*menu/i.test(code);
84
+ if (looksLikeNav && hasDesktopLinks && !hasMobileMenu) {
85
+ issues.push({
86
+ rule: 'missing-mobile-menu',
87
+ severity: 'high',
88
+ detail: `Navigation${at} has desktop links but appears to have no mobile menu (no hamburger / Dialog sheet / md:hidden toggle).`,
89
+ fix: 'Add a hamburger (md:hidden) that opens a Radix Dialog used as a side sheet (w-[85vw] max-w-sm). See get_build_standards topic "responsive" and get_screen_blueprint("nav").',
90
+ });
91
+ }
92
+ // --- Radix: Dialog/AlertDialog content without a Title (a11y) ---
93
+ const hasDialogContent = /DialogContent|Dialog\.Content|AlertDialogContent|AlertDialog\.Content/.test(code);
94
+ const hasDialogTitle = /DialogTitle|Dialog\.Title|AlertDialogTitle|AlertDialog\.Title/.test(code);
95
+ if (hasDialogContent && !hasDialogTitle) {
96
+ issues.push({
97
+ rule: 'dialog-without-title',
98
+ severity: 'medium',
99
+ detail: `A Radix Dialog/AlertDialog${at} has content but no Title — screen readers announce nothing and Radix warns.`,
100
+ fix: 'Add a Title (wrap in a visually-hidden span if there is no visible heading) and a description / aria-describedby. See get_build_standards topic "radix".',
101
+ });
102
+ }
103
+ return issues;
104
+ }
105
+ // ── Path-aware checks (only available when `files` is provided) ───────────────
106
+ function fileChecks(path, content) {
107
+ const issues = [];
108
+ const lines = countLines(content);
109
+ const lower = path.toLowerCase();
110
+ if (lines > FILE_SIZE_LIMIT) {
111
+ issues.push({
112
+ rule: 'file-too-large',
113
+ severity: 'medium',
114
+ detail: `${path} is ${lines} lines (limit ${FILE_SIZE_LIMIT}).`,
115
+ fix: 'Split it: extract sub-components, a custom hook, or utilities. See get_build_standards topic "architecture" (file-size limits) and "refactoring".',
116
+ });
117
+ }
118
+ const code = decomment(content);
119
+ const isAtomOrMolecule = /[\\/](atoms|molecules)[\\/]/.test(lower);
120
+ const hasSideEffects = /\bfetch\s*\(|\buseQuery\s*\(|\buseSWR\s*\(|await\s+prisma|['"]use server['"]/.test(code);
121
+ if (isAtomOrMolecule && hasSideEffects) {
122
+ issues.push({
123
+ rule: 'atom-with-side-effects',
124
+ severity: 'high',
125
+ detail: `${path} is an atom/molecule but fetches data or runs a server action — it should be presentational (data via props).`,
126
+ fix: 'Move data fetching to a hook/container; keep this component dumb. See get_build_standards topic "architecture" (atomic design + presentational vs container).',
127
+ });
128
+ }
129
+ const isRouteFile = /[\\/]page\.tsx$/.test(lower);
130
+ const hasBusinessLogic = /\buseState\s*\(|\buseEffect\s*\(|\buseReducer\s*\(/.test(code);
131
+ if (isRouteFile && hasBusinessLogic && lines > 60) {
132
+ issues.push({
133
+ rule: 'fat-page',
134
+ severity: 'medium',
135
+ detail: `${path} (a route file) holds component state/logic and is ${lines} lines.`,
136
+ fix: 'Keep page.tsx thin (wire data + compose). Move state/logic into a hook and the UI into an organism. See get_build_standards topic "architecture".',
137
+ });
138
+ }
139
+ return issues;
140
+ }
141
+ export function reviewApp(input) {
142
+ const issues = [];
143
+ if (input.files && Object.keys(input.files).length > 0) {
144
+ const routePaths = new Set();
145
+ for (const [path, content] of Object.entries(input.files)) {
146
+ issues.push(...fileChecks(path, content));
147
+ issues.push(...snippetChecks(content, path));
148
+ const lower = path.toLowerCase();
149
+ const m = lower.match(/(.*[\\/])(page|layout)\.tsx$/);
150
+ if (m)
151
+ routePaths.add(m[1]);
152
+ }
153
+ // Route hygiene: a route folder with page.tsx should ship error.tsx + loading.tsx.
154
+ for (const dir of routePaths) {
155
+ const has = (name) => Object.keys(input.files).some(p => p.toLowerCase() === `${dir}${name}`);
156
+ const missing = ['error.tsx', 'loading.tsx'].filter(n => !has(n));
157
+ if (missing.length) {
158
+ issues.push({
159
+ rule: 'route-missing-boundaries',
160
+ severity: 'medium',
161
+ detail: `Route ${dir} is missing ${missing.join(' + ')}.`,
162
+ fix: 'Every route should ship error.tsx (recovery UI) and loading.tsx (skeleton). See get_build_standards topic "error-handling". (Note: only the files you passed were checked.)',
163
+ });
164
+ }
165
+ }
166
+ }
167
+ else if (input.code) {
168
+ issues.push(...snippetChecks(input.code, ''));
169
+ }
170
+ const screenNote = input.screenType
171
+ ? ` For screen-specific must-haves, also see get_screen_blueprint("${input.screenType}").`
172
+ : '';
173
+ return {
174
+ valid: issues.length === 0,
175
+ issueCount: issues.length,
176
+ issues,
177
+ summary: issues.length === 0
178
+ ? `No structural issues detected. NOTE: review_app is a heuristic structural audit, not a compiler or a substitute for review — it checks for common "messy app" smells (missing states, swallowed errors, layering, mobile nav, Radix a11y), not correctness or compilation.${screenNote}`
179
+ : `Found ${issues.length} structural issue${issues.length === 1 ? '' : 's'}. Apply each fix; pair with validate_code for visual/token issues. review_app is heuristic — confirm each finding in context.${screenNote}`,
180
+ };
181
+ }
182
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AA23BA,wBAAsB,WAAW,kBA2BhC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAwhCA,wBAAsB,WAAW,kBA2BhC"}