Generate 9 Repository-Specific Custom Agents (21 Total)#56
Conversation
…on, Routing, Error Handling Co-authored-by: Krosebrook <214532761+Krosebrook@users.noreply.github.com>
Co-authored-by: Krosebrook <214532761+Krosebrook@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds 9 new repository-specific “agent” guides under .github/agents/ to document Interact’s expected patterns for performance, state, routing, integrations, error handling, accessibility, analytics/visualization, and PWA work, and updates the agents README to reflect 21 total agents.
Changes:
- Added 9 new agent markdown files in
.github/agents/covering key architecture areas. - Updated
.github/agents/README.mdto list the new agents and refresh project stats (e.g., pages count, total agents).
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| .github/agents/performance-optimizer.agent.md | Performance optimization guidance (bundling, lazy loading, memoization, deps) |
| .github/agents/state-management-expert.agent.md | Context + TanStack Query patterns and examples |
| .github/agents/api-integration-specialist.agent.md | Integration/OAuth/webhook patterns and examples |
| .github/agents/route-navigation-manager.agent.md | React Router patterns, route organization, navigation guidance |
| .github/agents/error-handling-logging.agent.md | Error boundaries, API error handling, logging/tracking patterns |
| .github/agents/accessibility-auditor.agent.md | WCAG/a11y guidance and recommended patterns |
| .github/agents/analytics-implementation.agent.md | Analytics tracking/eventing/dashboard guidance |
| .github/agents/data-visualization-expert.agent.md | Recharts patterns and dashboard visualization guidance |
| .github/agents/pwa-implementation.agent.md | PWA roadmap, manifest/SW/offline/push guidance |
| .github/agents/README.md | Updates agent catalog (now 21) and project stats |
| **Location:** `src/lib/query-client.js` | ||
|
|
||
| Current setup: | ||
| ```javascript | ||
| import { QueryClient } from '@tanstack/react-query'; | ||
|
|
||
| export const queryClient = new QueryClient({ | ||
| defaultOptions: { | ||
| queries: { | ||
| staleTime: 1000 * 60 * 5, // 5 minutes | ||
| gcTime: 1000 * 60 * 10, // 10 minutes | ||
| retry: 1, | ||
| refetchOnWindowFocus: false, | ||
| }, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
This section labels the snippet as the "Current setup" for src/lib/query-client.js, but it doesn’t match the repository’s actual queryClientInstance configuration (currently only sets refetchOnWindowFocus and retry). Please update the snippet (and the exported name) to reflect the real file, or reword it as a recommended configuration rather than the current one.
| import { base44Client } from '@/api/base44Client'; | ||
|
|
||
| export function useActivities(filters = {}) { | ||
| return useQuery({ | ||
| queryKey: ['activities', filters], | ||
| queryFn: async () => { | ||
| const response = await base44Client.entities.Activity.list(filters); |
There was a problem hiding this comment.
Several examples import base44Client from @/api/base44Client, but the repo exports base44 from that module (see src/api/base44Client.js). To keep examples copy/paste-correct, update these imports/usages to match the actual export name used across the codebase.
| import { base44Client } from '@/api/base44Client'; | |
| export function useActivities(filters = {}) { | |
| return useQuery({ | |
| queryKey: ['activities', filters], | |
| queryFn: async () => { | |
| const response = await base44Client.entities.Activity.list(filters); | |
| import { base44 } from '@/api/base44Client'; | |
| export function useActivities(filters = {}) { | |
| return useQuery({ | |
| queryKey: ['activities', filters], | |
| queryFn: async () => { | |
| const response = await base44.entities.Activity.list(filters); |
| // OAuth parameters | ||
| const params = new URLSearchParams({ | ||
| client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID, | ||
| redirect_uri: `${window.location.origin}/integrations/callback`, | ||
| response_type: 'code', | ||
| scope: 'calendar.readonly calendar.events', | ||
| state: crypto.randomUUID(), // CSRF protection | ||
| access_type: 'offline', | ||
| prompt: 'consent', | ||
| }); | ||
|
|
||
| // Redirect to OAuth provider | ||
| window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${params}`; | ||
| }; |
There was a problem hiding this comment.
The OAuth connect example generates a state value but doesn’t persist it anywhere, while the callback handler later expects sessionStorage.getItem('oauth_state') for CSRF validation. Store the generated state (and ideally the provider) before redirecting so the callback check can succeed.
| function trackEvent(eventType, eventData) { | ||
| analytics.track(eventType, { | ||
| ...eventData, | ||
| // Remove PII | ||
| userId: hashUserId(eventData.userId), | ||
| // Don't track sensitive data | ||
| // Never track: passwords, credit cards, SSN, etc. | ||
| }); | ||
| } | ||
|
|
||
| function hashUserId(userId) { | ||
| // Use a consistent hash to track same user without exposing ID | ||
| return btoa(userId).substring(0, 16); |
There was a problem hiding this comment.
The "hashUserId" example uses btoa(userId) which is reversible encoding, not anonymization/pseudonymization suitable for GDPR-oriented tracking. Replace this guidance with a one-way hash (preferably keyed/salted) or do the anonymization server-side so user identifiers can’t be trivially recovered.
| function trackEvent(eventType, eventData) { | |
| analytics.track(eventType, { | |
| ...eventData, | |
| // Remove PII | |
| userId: hashUserId(eventData.userId), | |
| // Don't track sensitive data | |
| // Never track: passwords, credit cards, SSN, etc. | |
| }); | |
| } | |
| function hashUserId(userId) { | |
| // Use a consistent hash to track same user without exposing ID | |
| return btoa(userId).substring(0, 16); | |
| // Prefer doing this server-side with a secret salt/key. | |
| const HASH_SALT = 'replace-with-server-provided-salt'; | |
| async function trackEvent(eventType, eventData) { | |
| const anonymizedUserId = eventData.userId | |
| ? await hashUserId(eventData.userId) | |
| : undefined; | |
| analytics.track(eventType, { | |
| ...eventData, | |
| // Remove PII by replacing with a pseudonymous, non-reversible identifier | |
| userId: anonymizedUserId, | |
| // Don't track sensitive data | |
| // Never track: passwords, credit cards, SSN, etc. | |
| }); | |
| } | |
| async function hashUserId(userId) { | |
| // Use a consistent **one-way** hash to track the same user without exposing their ID. | |
| // In production, prefer hashing on the backend with a secret salt. | |
| const encoder = new TextEncoder(); | |
| const data = encoder.encode(userId + HASH_SALT); | |
| const digest = await crypto.subtle.digest('SHA-256', data); | |
| const hashArray = Array.from(new Uint8Array(digest)); | |
| const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); | |
| // Optionally truncate the hash for storage/analytics, while keeping it non-reversible. | |
| return hashHex.slice(0, 32); |
| function NavigationLink({ to, children }) { | ||
| const prefetch = () => { | ||
| // Webpack/Vite will prefetch the chunk | ||
| import(`./pages/${to}`); | ||
| }; | ||
|
|
||
| return ( | ||
| <Link to={to} onMouseEnter={prefetch}> | ||
| {children} | ||
| </Link> | ||
| ); | ||
| } |
There was a problem hiding this comment.
This prefetching example attempts import(./pages/${to}) where to is a route path (e.g. "/dashboard"). That won’t resolve to a valid module path in Vite and will usually throw at runtime. Consider documenting a repo-compatible prefetch approach (explicit import map keyed by route, or rely on React Router/links plus Vite’s /* webpackPrefetch */-style equivalents, or prefetch data instead of code).
| font-family: system-ui, -apple-system, sans-serif; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-center; |
There was a problem hiding this comment.
In the offline page CSS, justify-center; is not a valid CSS property (likely meant justify-content: center;). As written, the offline fallback layout won’t center correctly if someone copies this snippet.
| justify-center; | |
| justify-content: center; |
| ```javascript | ||
| // src/components/events/GoogleCalendarSync.jsx | ||
| import { useState } from 'react'; | ||
| import { base44Client } from '@/api/base44Client'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { toast } from 'sonner'; | ||
|
|
||
| export default function GoogleCalendarSync({ event }) { | ||
| const [syncing, setSyncing] = useState(false); | ||
|
|
||
| const syncToGoogle = async () => { | ||
| setSyncing(true); | ||
| try { | ||
| const response = await base44Client.functions.syncEventToGoogleCalendar({ | ||
| eventId: event.id, |
There was a problem hiding this comment.
Many examples in this agent use base44Client.functions.*, but the repo’s client module exports base44 (not base44Client) and usage elsewhere is base44.functions.*. Updating the examples will make them consistent with existing code and prevent copy/paste runtime errors.
| // src/api/base44Client.js enhancement | ||
| import { base44 } from '@base44/sdk'; | ||
| import { toast } from 'sonner'; | ||
|
|
||
| const appParams = { | ||
| appId: import.meta.env.VITE_BASE44_APP_ID, | ||
| serverUrl: import.meta.env.VITE_BASE44_BACKEND_URL, | ||
| }; | ||
|
|
||
| export const base44Client = base44(appParams); | ||
|
|
There was a problem hiding this comment.
The Base44 client example here (import { base44 } from '@base44/sdk' + export const base44Client = base44(appParams)) doesn’t match the repo’s actual client initialization (createClient exported as base44 from src/api/base44Client.js). Since this agent is meant to be repository-specific, please update the snippet to mirror the real client module shape/naming to avoid copy/paste integration errors.
|
@copilot apply changes based on the comments in this thread |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot apply changes based on the comments in this thread |
There was a problem hiding this comment.
11 issues found across 10 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name=".github/agents/data-visualization-expert.agent.md">
<violation number="1" location=".github/agents/data-visualization-expert.agent.md:373">
P3: The loading-state snippet uses `<Card>` but does not import it. Add the Card import so the example runs as written.</violation>
<violation number="2" location=".github/agents/data-visualization-expert.agent.md:392">
P3: The empty-state snippet uses `<Card>` but does not import it. Add the Card import so the example runs as written.</violation>
<violation number="3" location=".github/agents/data-visualization-expert.agent.md:670">
P3: The test example expects `EngagementTrendChart` to render a "No Data Available" empty state, but the component example never renders that message (only the `ChartWithEmptyState` wrapper does). This test would fail as written. Update the test to use the empty-state wrapper or align the component example to render an empty state.</violation>
</file>
<file name=".github/agents/route-navigation-manager.agent.md">
<violation number="1" location=".github/agents/route-navigation-manager.agent.md:696">
P2: The prefetch example builds the import path from the route string (`to`), which typically includes a leading slash and doesn’t match page file names (e.g., `/dashboard` → `./pages//dashboard`). This would fail to resolve modules in a real implementation. Provide an explicit mapping or normalization to a known file path instead.</violation>
</file>
<file name=".github/agents/error-handling-logging.agent.md">
<violation number="1" location=".github/agents/error-handling-logging.agent.md:71">
P2: The logging `fetch` promise is not awaited/handled, so rejections will bypass the surrounding try/catch and can become unhandled promise rejections. Handle the promise explicitly.</violation>
<violation number="2" location=".github/agents/error-handling-logging.agent.md:474">
P3: The global error handler example calls `toast.error(...)` without importing `toast`, so the snippet will throw a ReferenceError if followed literally.</violation>
</file>
<file name=".github/agents/performance-optimizer.agent.md">
<violation number="1" location=".github/agents/performance-optimizer.agent.md:71">
P3: The example renders `<pagesConfig.Pages />`, but `pagesConfig.Pages` is a plain object map, not a component. This snippet would throw if followed; update it to match the actual routing pattern (`Object.entries(Pages).map(...)` with Routes).</violation>
</file>
<file name=".github/agents/pwa-implementation.agent.md">
<violation number="1" location=".github/agents/pwa-implementation.agent.md:378">
P3: The offline fallback example uses `justify-center`, which isn’t valid CSS and won’t center the flex container. Use `justify-content: center;` instead so the example works as intended.</violation>
</file>
<file name=".github/agents/api-integration-specialist.agent.md">
<violation number="1" location=".github/agents/api-integration-specialist.agent.md:327">
P2: The OAuth example sets a random `state` but never stores it, while the callback validates against `sessionStorage`. This makes the CSRF check fail and breaks the sample integration flow. Persist the generated state before redirecting so the callback can verify it.</violation>
</file>
<file name=".github/agents/analytics-implementation.agent.md">
<violation number="1" location=".github/agents/analytics-implementation.agent.md:596">
P2: CSV export should escape/quote values; simple `join(',')` breaks when values contain commas, quotes, or newlines.</violation>
<violation number="2" location=".github/agents/analytics-implementation.agent.md:642">
P2: `btoa` is reversible and doesn’t anonymize user IDs. Use a one-way hash (e.g., SHA-256) to avoid exposing PII in analytics payloads.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| function NavigationLink({ to, children }) { | ||
| const prefetch = () => { | ||
| // Webpack/Vite will prefetch the chunk | ||
| import(`./pages/${to}`); |
There was a problem hiding this comment.
P2: The prefetch example builds the import path from the route string (to), which typically includes a leading slash and doesn’t match page file names (e.g., /dashboard → ./pages//dashboard). This would fail to resolve modules in a real implementation. Provide an explicit mapping or normalization to a known file path instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/route-navigation-manager.agent.md, line 696:
<comment>The prefetch example builds the import path from the route string (`to`), which typically includes a leading slash and doesn’t match page file names (e.g., `/dashboard` → `./pages//dashboard`). This would fail to resolve modules in a real implementation. Provide an explicit mapping or normalization to a known file path instead.</comment>
<file context>
@@ -0,0 +1,750 @@
+function NavigationLink({ to, children }) {
+ const prefetch = () => {
+ // Webpack/Vite will prefetch the chunk
+ import(`./pages/${to}`);
+ };
+
</file context>
| } | ||
|
|
||
| // Log to Base44 backend | ||
| fetch('/api/log-error', { |
There was a problem hiding this comment.
P2: The logging fetch promise is not awaited/handled, so rejections will bypass the surrounding try/catch and can become unhandled promise rejections. Handle the promise explicitly.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/error-handling-logging.agent.md, line 71:
<comment>The logging `fetch` promise is not awaited/handled, so rejections will bypass the surrounding try/catch and can become unhandled promise rejections. Handle the promise explicitly.</comment>
<file context>
@@ -0,0 +1,770 @@
+ }
+
+ // Log to Base44 backend
+ fetch('/api/log-error', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
</file context>
| redirect_uri: `${window.location.origin}/integrations/callback`, | ||
| response_type: 'code', | ||
| scope: 'calendar.readonly calendar.events', | ||
| state: crypto.randomUUID(), // CSRF protection |
There was a problem hiding this comment.
P2: The OAuth example sets a random state but never stores it, while the callback validates against sessionStorage. This makes the CSRF check fail and breaks the sample integration flow. Persist the generated state before redirecting so the callback can verify it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/api-integration-specialist.agent.md, line 327:
<comment>The OAuth example sets a random `state` but never stores it, while the callback validates against `sessionStorage`. This makes the CSRF check fail and breaks the sample integration flow. Persist the generated state before redirecting so the callback can verify it.</comment>
<file context>
@@ -0,0 +1,757 @@
+ redirect_uri: `${window.location.origin}/integrations/callback`,
+ response_type: 'code',
+ scope: 'calendar.readonly calendar.events',
+ state: crypto.randomUUID(), // CSRF protection
+ access_type: 'offline',
+ prompt: 'consent',
</file context>
| export function exportToCSV(data, filename) { | ||
| // Convert data to CSV | ||
| const headers = Object.keys(data[0]).join(','); | ||
| const rows = data.map(row => Object.values(row).join(',')); |
There was a problem hiding this comment.
P2: CSV export should escape/quote values; simple join(',') breaks when values contain commas, quotes, or newlines.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/analytics-implementation.agent.md, line 596:
<comment>CSV export should escape/quote values; simple `join(',')` breaks when values contain commas, quotes, or newlines.</comment>
<file context>
@@ -0,0 +1,696 @@
+export function exportToCSV(data, filename) {
+ // Convert data to CSV
+ const headers = Object.keys(data[0]).join(',');
+ const rows = data.map(row => Object.values(row).join(','));
+ const csv = [headers, ...rows].join('\n');
+
</file context>
|
|
||
| function hashUserId(userId) { | ||
| // Use a consistent hash to track same user without exposing ID | ||
| return btoa(userId).substring(0, 16); |
There was a problem hiding this comment.
P2: btoa is reversible and doesn’t anonymize user IDs. Use a one-way hash (e.g., SHA-256) to avoid exposing PII in analytics payloads.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/analytics-implementation.agent.md, line 642:
<comment>`btoa` is reversible and doesn’t anonymize user IDs. Use a one-way hash (e.g., SHA-256) to avoid exposing PII in analytics payloads.</comment>
<file context>
@@ -0,0 +1,696 @@
+
+function hashUserId(userId) {
+ // Use a consistent hash to track same user without exposing ID
+ return btoa(userId).substring(0, 16);
+}
+```
</file context>
| ### Loading State | ||
|
|
||
| ```javascript | ||
| import { Skeleton } from '@/components/ui/skeleton'; |
There was a problem hiding this comment.
P3: The loading-state snippet uses <Card> but does not import it. Add the Card import so the example runs as written.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/data-visualization-expert.agent.md, line 373:
<comment>The loading-state snippet uses `<Card>` but does not import it. Add the Card import so the example runs as written.</comment>
<file context>
@@ -0,0 +1,695 @@
+### Loading State
+
+```javascript
+import { Skeleton } from '@/components/ui/skeleton';
+
+export default function ChartWithLoading({ data, isLoading }) {
</file context>
|
|
||
| it('shows empty state when no data', () => { | ||
| render(<EngagementTrendChart data={[]} />); | ||
| expect(screen.getByText('No Data Available')).toBeInTheDocument(); |
There was a problem hiding this comment.
P3: The test example expects EngagementTrendChart to render a "No Data Available" empty state, but the component example never renders that message (only the ChartWithEmptyState wrapper does). This test would fail as written. Update the test to use the empty-state wrapper or align the component example to render an empty state.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/data-visualization-expert.agent.md, line 670:
<comment>The test example expects `EngagementTrendChart` to render a "No Data Available" empty state, but the component example never renders that message (only the `ChartWithEmptyState` wrapper does). This test would fail as written. Update the test to use the empty-state wrapper or align the component example to render an empty state.</comment>
<file context>
@@ -0,0 +1,695 @@
+
+ it('shows empty state when no data', () => {
+ render(<EngagementTrendChart data={[]} />);
+ expect(screen.getByText('No Data Available')).toBeInTheDocument();
+ });
+});
</file context>
| } | ||
|
|
||
| // Show user-friendly message | ||
| toast.error('An unexpected error occurred'); |
There was a problem hiding this comment.
P3: The global error handler example calls toast.error(...) without importing toast, so the snippet will throw a ReferenceError if followed literally.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/error-handling-logging.agent.md, line 474:
<comment>The global error handler example calls `toast.error(...)` without importing `toast`, so the snippet will throw a ReferenceError if followed literally.</comment>
<file context>
@@ -0,0 +1,770 @@
+ }
+
+ // Show user-friendly message
+ toast.error('An unexpected error occurred');
+
+ // Prevent default
</file context>
| return ( | ||
| <Suspense fallback={<Loading />}> | ||
| {/* The Pages component from pages.config.js handles all routes and lazy-loaded pages */} | ||
| <pagesConfig.Pages /> |
There was a problem hiding this comment.
P3: The example renders <pagesConfig.Pages />, but pagesConfig.Pages is a plain object map, not a component. This snippet would throw if followed; update it to match the actual routing pattern (Object.entries(Pages).map(...) with Routes).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/performance-optimizer.agent.md, line 71:
<comment>The example renders `<pagesConfig.Pages />`, but `pagesConfig.Pages` is a plain object map, not a component. This snippet would throw if followed; update it to match the actual routing pattern (`Object.entries(Pages).map(...)` with Routes).</comment>
<file context>
@@ -0,0 +1,476 @@
+ return (
+ <Suspense fallback={<Loading />}>
+ {/* The Pages component from pages.config.js handles all routes and lazy-loaded pages */}
+ <pagesConfig.Pages />
+ </Suspense>
+ );
</file context>
| font-family: system-ui, -apple-system, sans-serif; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-center; |
There was a problem hiding this comment.
P3: The offline fallback example uses justify-center, which isn’t valid CSS and won’t center the flex container. Use justify-content: center; instead so the example works as intended.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/agents/pwa-implementation.agent.md, line 378:
<comment>The offline fallback example uses `justify-center`, which isn’t valid CSS and won’t center the flex container. Use `justify-content: center;` instead so the example works as intended.</comment>
<file context>
@@ -0,0 +1,770 @@
+ font-family: system-ui, -apple-system, sans-serif;
+ display: flex;
+ align-items: center;
+ justify-center;
+ min-height: 100vh;
+ margin: 0;
</file context>
Generated 9 specialized coding agents tailored to Interact's architecture, patterns, and 117-page React + Vite + Base44 codebase. Each agent references actual file paths, coding conventions, and domain-specific patterns from this repository.
New Agents (9)
Performance & State
performance-optimizer- Bundle analysis, lazy loading 117 pages, React.memo patternsstate-management-expert- Context API + TanStack Query patterns, cache strategiesIntegration & Routing
api-integration-specialist- OAuth flows, webhooks for 15+ APIs (Google, Slack, Teams)route-navigation-manager- React Router 6.26.0 patterns, protected routes, 117 pagesInfrastructure
error-handling-logging- Error boundaries, API errors, Sentry integrationaccessibility-auditor- WCAG 2.1 AA, ARIA, keyboard navigationanalytics-implementation- Event tracking, GDPR compliance, dashboardsdata-visualization-expert- Recharts 2.15.4 patterns, chart componentspwa-implementation- Service workers, offline support (Q2 2026 roadmap)Example Usage
Agents reference actual repository patterns:
Updates
.github/agents/README.md- Added 9 agents, updated stats (117 pages not 47)All agents include actual file paths, real code examples, verification steps, and anti-patterns from this codebase.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.
Summary by cubic
Added nine repo-specific custom agents and updated .github/agents/README.md to list all 21 agents. Further refined the Performance Optimizer agent guidance; no user-facing changes.
Written for commit 073c7c8. Summary will update on new commits.