Consolidate webapp skills @W-21338965@ (#118)

* Consolidate webapp skills into three focused skills

Merge 10 granular webapp skills into 3 consolidated skills:
- generating-webapp-ui: pages, components, layout, navigation, design
- generating-webapp-metadata: scaffolding, bundles, CSP, deployment
- generating-webapp-features: feature CLI (search, describe, install)

Keep standalone: implementing-webapp-file-upload,
implementing-webapp-agentforce-conversation-client (renamed from managing-),
using-webapp-salesforce-data.

Remove data visualization skill (not helpful).
Remove system-level instructions (a4drules refs, shell commands).
Incorporate Anthropic frontend-design skill content into UI skill.

* Fix frontmatter name to match directory for agentforce skill

---------

Co-authored-by: Hemant Singh Bisht <hsinghbisht@salesforce.com>
This commit is contained in:
k-j-kim 2026-03-27 10:04:44 -07:00 committed by GitHub
parent 675e38f41f
commit f39ac26c0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 348 additions and 1827 deletions

View File

@ -1,72 +0,0 @@
---
name: building-webapp-data-visualization
description: "Adds data visualization components (charts, stat cards, KPI metrics) to React pages using Recharts. Use when the user asks to add a chart, graph, donut chart, pie chart, bar chart, stat card, KPI metric, dashboard visualization, or analytics component to the web application."
---
# Data Visualization
## When to Use
Use this skill when:
- Adding charts (donut, pie, bar, line, area) to a dashboard or analytics page
- Displaying KPI/metric stat cards with trend indicators
- Building a dashboard layout with mixed chart types and summary cards
---
## Step 1 — Determine the visualization type
Identify what the user needs:
- **Donut / pie chart** — categorical breakdown (e.g. issue types, status distribution)
- **Bar chart** — comparison across categories or time periods
- **Line / area chart** — trends over time
- **Stat card** — single KPI metric with optional trend indicator
- **Combined dashboard** — stat cards + one or more charts
If unclear, ask:
> "What data should the chart display, and would a donut chart, bar chart, line chart, or stat cards work best?"
---
## Step 2 — Install dependencies
All chart types in this skill use **recharts**. Install once from the web app directory:
```bash
npm install recharts
```
Recharts is built on D3 and provides declarative React components. No additional CSS is needed.
---
## Step 3 — Choose implementation path
Read the corresponding guide:
- **Bar chart** — read `implementation/bar-line-chart.md` (categorical data)
- **Line / area chart** — read `implementation/bar-line-chart.md` (time-series data)
- **Donut / pie chart** — read `implementation/donut-chart.md`
- **Stat card with trend** — read `implementation/stat-card.md`
- **Dashboard layout** — read `implementation/dashboard-layout.md`
---
## Verification
Before completing:
1. Chart renders with correct data and colors.
2. Chart is responsive (resizes with container).
3. Legend labels match the data categories.
4. Stat card trends display correct positive/negative indicators.
5. Run from the web app directory:
```bash
cd force-app/main/default/webapplications/<appName> && npm run lint && npm run build
```
- **Lint:** MUST result in 0 errors.
- **Build:** MUST succeed.

View File

@ -1,316 +0,0 @@
# Bar & Line / Area Chart — Implementation Guide
Requires **recharts** (install from the web app directory; see SKILL.md Step 2).
---
## Data shapes
### Time-series (line / area chart)
Use when data represents a trend over time or ordered sequence.
```ts
interface TimeSeriesDataPoint {
x: string; // date or label on the x-axis
y: number; // numeric value
}
```
Map raw fields to this shape: e.g. `date``x`, `revenue``y`.
### Categorical (bar chart)
Use when data compares discrete categories.
```ts
interface CategoricalDataPoint {
name: string; // category label
value: number; // numeric value
}
```
Map raw fields to this shape: e.g. `product``name`, `sales``value`.
### How to decide
| Signal | Type |
|--------|------|
| "over time", "trend", date-like keys | Time-series → line chart |
| "by category", "by X", label-like keys | Categorical → bar chart |
---
## Theme colors
Pick a theme based on the data's sentiment:
| Theme | Stroke / Fill | When to use |
|-------|---------------|-------------|
| `green` | `#22c55e` | Growth, gain, positive trend |
| `red` | `#ef4444` | Decline, loss, negative trend |
| `neutral` | `#6366f1` | Default or mixed data |
Define colors as constants — do not use inline hex values.
```ts
const THEME_COLORS = {
red: "#ef4444",
green: "#22c55e",
neutral: "#6366f1",
} as const;
type ChartTheme = keyof typeof THEME_COLORS;
```
---
## Line chart component
Create at `components/LineChart.tsx` (or colocate with the page):
```tsx
import React from "react";
import {
LineChart as RechartsLineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from "recharts";
const THEME_COLORS = {
red: "#ef4444",
green: "#22c55e",
neutral: "#6366f1",
} as const;
type ChartTheme = keyof typeof THEME_COLORS;
interface TimeSeriesDataPoint {
x: string;
y: number;
}
interface TimeSeriesChartProps {
data: TimeSeriesDataPoint[];
theme?: ChartTheme;
title?: string;
className?: string;
}
export function TimeSeriesChart({
data,
theme = "neutral",
title,
className = "",
}: TimeSeriesChartProps) {
if (data.length === 0) {
return <p className="text-muted-foreground text-center py-8">No data to display</p>;
}
const color = THEME_COLORS[theme];
return (
<div className={className}>
{title && (
<h3 className="text-sm font-medium text-primary mb-2 uppercase tracking-wide">
{title}
</h3>
)}
<ResponsiveContainer width="100%" height={300}>
<RechartsLineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="x" />
<YAxis />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="y" stroke={color} strokeWidth={2} dot={false} />
</RechartsLineChart>
</ResponsiveContainer>
</div>
);
}
```
---
## Bar chart component
Create at `components/BarChart.tsx` (or colocate with the page):
```tsx
import React from "react";
import {
BarChart as RechartsBarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from "recharts";
const THEME_COLORS = {
red: "#ef4444",
green: "#22c55e",
neutral: "#6366f1",
} as const;
type ChartTheme = keyof typeof THEME_COLORS;
interface CategoricalDataPoint {
name: string;
value: number;
}
interface CategoricalChartProps {
data: CategoricalDataPoint[];
theme?: ChartTheme;
title?: string;
className?: string;
}
export function CategoricalChart({
data,
theme = "neutral",
title,
className = "",
}: CategoricalChartProps) {
if (data.length === 0) {
return <p className="text-muted-foreground text-center py-8">No data to display</p>;
}
const color = THEME_COLORS[theme];
return (
<div className={className}>
{title && (
<h3 className="text-sm font-medium text-primary mb-2 uppercase tracking-wide">
{title}
</h3>
)}
<ResponsiveContainer width="100%" height={300}>
<RechartsBarChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="value" fill={color} radius={[4, 4, 0, 0]} />
</RechartsBarChart>
</ResponsiveContainer>
</div>
);
}
```
---
## Area chart variant
For a filled area chart (useful for volume-over-time), swap `Line` for `Area`:
```tsx
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts";
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="x" />
<YAxis />
<Tooltip />
<Area type="monotone" dataKey="y" stroke={color} fill={color} fillOpacity={0.2} />
</AreaChart>
</ResponsiveContainer>
```
---
## Chart container wrapper
Wrap any chart in a styled card for consistent spacing:
```tsx
import { Card } from "@/components/ui/card";
interface ChartContainerProps {
children: React.ReactNode;
className?: string;
}
export function ChartContainer({ children, className = "" }: ChartContainerProps) {
return (
<Card className={`p-4 border-gray-200 shadow-sm ${className}`}>
{children}
</Card>
);
}
```
Usage:
```tsx
<ChartContainer>
<TimeSeriesChart data={monthlyData} theme="green" title="Monthly Revenue" />
</ChartContainer>
```
---
## Preparing raw data
Map API responses to the expected shape before passing to the chart:
```tsx
const timeSeriesData = useMemo(
() => apiRecords.map((r) => ({ x: r.date, y: r.revenue })),
[apiRecords],
);
const categoricalData = useMemo(
() => apiRecords.map((r) => ({ name: r.product, value: r.sales })),
[apiRecords],
);
```
---
## Key Recharts concepts
| Component | Purpose |
|-----------|---------|
| `ResponsiveContainer` | Wraps chart to fill parent width |
| `CartesianGrid` | Background grid lines |
| `XAxis` / `YAxis` | Axis labels; `dataKey` maps to the data field |
| `Tooltip` | Hover info |
| `Legend` | Series labels |
| `Line` | Line series; `type="monotone"` for smooth curves |
| `Bar` | Bar series; `radius` rounds top corners |
| `Area` | Filled area; `fillOpacity` controls transparency |
---
## Accessibility
- Always include a text legend (not just colors).
- Chart should be wrapped in a section with a visible heading.
- For critical data, provide a text summary or table alternative.
- Use sufficient color contrast between the chart stroke/fill and background.
- Consider `prefers-reduced-motion` for chart animations.
---
## Common mistakes
| Mistake | Fix |
|---------|-----|
| Missing `ResponsiveContainer` | Chart won't resize; always wrap |
| Fixed width/height on chart | Let `ResponsiveContainer` control sizing |
| No empty-data handling | Show "No data" message when `data.length === 0` |
| Inline colors | Extract to `THEME_COLORS` constant |
| Using raw Recharts for every chart type | Use `DonutChart` (see `donut-chart.md`) for pie/donut |

View File

@ -1,189 +0,0 @@
# Dashboard Layout — Implementation Guide
## Anatomy of a dashboard page
A typical dashboard combines stat cards, charts, and data tables:
```
┌────────────────────────────────────────────────────┐
│ Search / global action bar │
├──────────┬──────────┬──────────────────────────────┤
│ Stat 1 │ Stat 2 │ Stat 3 │
├──────────┴──────────┴──────┬───────────────────────┤
│ │ │
│ Data table / list │ Donut chart │
│ (70% width) │ (30% width) │
│ │ │
└────────────────────────────┴───────────────────────┘
```
---
## Layout implementation
```tsx
import { PageContainer } from "@/components/layout/PageContainer";
import { StatCard } from "@/components/StatCard";
import { DonutChart } from "@/components/DonutChart";
export default function Dashboard() {
return (
<PageContainer>
<div className="max-w-7xl mx-auto space-y-6">
{/* Search bar */}
<div>{/* global search component */}</div>
{/* Main content: 70/30 split */}
<div className="grid grid-cols-1 lg:grid-cols-[70%_30%] gap-6">
<div className="space-y-6">
{/* Stat cards row */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<StatCard title="Metric A" value={42} />
<StatCard title="Metric B" value={18} />
<StatCard title="Metric C" value={7} />
</div>
{/* Data table */}
<div>{/* table component */}</div>
</div>
{/* Sidebar chart */}
<div>
<DonutChart title="Distribution" data={chartData} />
</div>
</div>
</div>
</PageContainer>
);
}
```
---
## Responsive behavior
| Breakpoint | Layout |
|------------|--------|
| Mobile (`< 768px`) | Single column, everything stacked |
| Tablet (`md`) | Stat cards in 3-col grid, rest stacked |
| Desktop (`lg`) | 70/30 split for table + chart |
Key Tailwind classes:
```
grid grid-cols-1 lg:grid-cols-[70%_30%] gap-6
grid grid-cols-1 md:grid-cols-3 gap-6
```
---
## Loading state
Show a full-page loading state while dashboard data is being fetched:
```tsx
if (loading) {
return (
<PageContainer>
<div className="flex items-center justify-center min-h-[400px]">
<p className="text-muted-foreground">Loading dashboard…</p>
</div>
</PageContainer>
);
}
```
Or use a skeleton layout:
```tsx
if (loading) {
return (
<PageContainer>
<div className="max-w-7xl mx-auto space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[1, 2, 3].map((i) => (
<div key={i} className="h-28 animate-pulse rounded-xl bg-muted" />
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-[70%_30%] gap-6">
<div className="h-64 animate-pulse rounded-xl bg-muted" />
<div className="h-64 animate-pulse rounded-xl bg-muted" />
</div>
</div>
</PageContainer>
);
}
```
---
## Data fetching pattern
Use `useEffect` with cancellation for dashboard metrics:
```ts
const [metrics, setMetrics] = useState<Metrics | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
(async () => {
try {
setLoading(true);
const data = await fetchDashboardMetrics();
if (!cancelled) setMetrics(data);
} catch (error) {
if (!cancelled) console.error("Error loading metrics:", error);
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => { cancelled = true; };
}, []);
```
---
## Combining multiple data sources
Dashboards often aggregate data from several APIs. Load them in parallel:
```ts
const [metrics, setMetrics] = useState<Metrics | null>(null);
const [requests, setRequests] = useState<Request[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
Promise.all([fetchMetrics(), fetchRecentRequests()])
.then(([metricsData, requestsData]) => {
if (!cancelled) {
setMetrics(metricsData);
setRequests(requestsData);
}
})
.catch((err) => {
if (!cancelled) console.error(err);
})
.finally(() => {
if (!cancelled) setLoading(false);
});
return () => { cancelled = true; };
}, []);
```
---
## PageContainer wrapper
A simple wrapper for consistent page padding:
```tsx
interface PageContainerProps {
children: React.ReactNode;
}
export function PageContainer({ children }: PageContainerProps) {
return <div className="p-6">{children}</div>;
}
```

View File

@ -1,181 +0,0 @@
# Donut / Pie Chart — Implementation Guide
Requires **recharts** (install from the web app directory; see SKILL.md Step 2).
---
## Data structure
Charts expect an array of objects with `name`, `value`, and `color`:
```ts
interface ChartData {
name: string;
value: number;
color: string;
}
```
---
## Donut chart component
Create at `components/DonutChart.tsx`:
```tsx
import React from "react";
import { PieChart, Pie, Cell, ResponsiveContainer } from "recharts";
import { Card } from "@/components/ui/card";
interface ChartData {
name: string;
value: number;
color: string;
}
interface DonutChartProps {
title: string;
data: ChartData[];
}
export const DonutChart: React.FC<DonutChartProps> = ({ title, data }) => {
const total = data.reduce((sum, item) => sum + item.value, 0);
const mainPercentage = total > 0 ? Math.round((data[0]?.value / total) * 100) : 0;
return (
<Card className="p-4 border-gray-200 shadow-sm flex flex-col">
<h3 className="text-sm font-medium text-primary mb-2 uppercase tracking-wide">
{title}
</h3>
<div className="relative flex items-center justify-center">
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
innerRadius={70}
outerRadius={110}
paddingAngle={2}
dataKey="value"
>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
{/* Center label */}
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-center">
<div className="text-5xl font-bold text-primary">{mainPercentage}%</div>
</div>
</div>
</div>
{/* Legend */}
<div className="mt-6 grid grid-cols-2 gap-3">
{data.map((item, index) => (
<div key={index} className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }} />
<span className="text-sm text-gray-700">{item.name}</span>
</div>
))}
</div>
</Card>
);
};
```
---
## Key Recharts concepts
| Component | Purpose |
|-----------|---------|
| `ResponsiveContainer` | Wraps chart to make it fill its parent's width |
| `PieChart` | Chart container for pie/donut |
| `Pie` | The data ring; `innerRadius` > 0 makes it a donut |
| `Cell` | Individual segment; accepts `fill` color |
| `paddingAngle` | Gap between segments (degrees) |
### Donut vs Pie
| Property | Donut | Pie |
|----------|-------|-----|
| `innerRadius` | `> 0` (e.g. `70`) | `0` |
| Center label | Yes, positioned absolutely | Not typical |
---
## Preparing chart data from raw records
Transform API data into the `ChartData[]` format before passing to the chart:
```tsx
const CATEGORIES = ["Plumbing", "HVAC", "Electrical"] as const;
const OTHER_LABEL = "Other";
const COLORS = ["#7C3AED", "#EC4899", "#14B8A6", "#06B6D4"];
const chartData = useMemo(() => {
const counts: Record<string, number> = {};
CATEGORIES.forEach((c) => (counts[c] = 0));
counts[OTHER_LABEL] = 0;
records.forEach((record) => {
const type = record.category;
if (CATEGORIES.includes(type as (typeof CATEGORIES)[number])) {
counts[type]++;
} else {
counts[OTHER_LABEL]++;
}
});
return [
...CATEGORIES.map((name, i) => ({ name, value: counts[name], color: COLORS[i] })),
{ name: OTHER_LABEL, value: counts[OTHER_LABEL], color: COLORS[CATEGORIES.length] },
];
}, [records]);
```
---
## Color palette recommendations
| Use case | Colors |
|----------|--------|
| Categorical (4 items) | `#7C3AED` `#EC4899` `#14B8A6` `#06B6D4` |
| Status (3 items) | `#22C55E` `#F59E0B` `#EF4444` (green/amber/red) |
| Sequential | Use opacity variants of one hue: `#7C3AED` at 100%, 75%, 50%, 25% |
Keep chart colors consistent with the app's design system. Define them as constants, not inline values.
---
## Other chart types
For **bar charts** and **line / area charts**, see `bar-line-chart.md` in this directory.
---
## Accessibility
- Always include a text legend (not just colors).
- Chart should be wrapped in a section with a visible heading.
- For critical data, provide a text summary or table alternative.
- Use sufficient color contrast between segments.
- Consider `prefers-reduced-motion` for chart animations.
---
## Common mistakes
| Mistake | Fix |
|---------|-----|
| Missing `ResponsiveContainer` | Chart won't resize; always wrap in `ResponsiveContainer` |
| Fixed width/height on `PieChart` | Let `ResponsiveContainer` control sizing |
| No legend | Add a grid legend below the chart |
| Inline colors | Extract to constants for consistency |
| No fallback for empty data | Show "No data" message when `data` is empty |

View File

@ -1,150 +0,0 @@
# Stat Card — Implementation Guide
## What is a stat card
A stat card displays a single KPI metric with an optional trend indicator. Used on dashboards to show at-a-glance numbers like "Total Properties: 42 (+10%)".
---
## Component interface
```ts
interface StatCardProps {
title: string;
value: number | string;
trend?: {
value: number;
isPositive: boolean;
};
subtitle?: string;
onClick?: () => void;
}
```
---
## StatCard component
Create at `components/StatCard.tsx`:
```tsx
import React from "react";
import { Card } from "@/components/ui/card";
import { TrendingUp, TrendingDown } from "lucide-react";
interface StatCardProps {
title: string;
value: number | string;
trend?: {
value: number;
isPositive: boolean;
};
subtitle?: string;
onClick?: () => void;
}
export const StatCard: React.FC<StatCardProps> = ({ title, value, trend, subtitle, onClick }) => {
return (
<Card
className={`p-4 border-gray-200 shadow-sm relative ${
onClick ? "cursor-pointer hover:shadow-lg transition-shadow" : ""
}`}
onClick={onClick}
>
<div className="space-y-1">
<p className="text-sm font-medium text-muted-foreground uppercase tracking-wide">{title}</p>
<div className="flex items-baseline gap-3">
<p className="text-4xl font-bold text-primary">{value}</p>
{trend && (
<span
className={`inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-sm font-medium ${
trend.isPositive
? "bg-emerald-100 text-emerald-800"
: "bg-pink-100 text-pink-800"
}`}
>
{trend.isPositive ? (
<TrendingUp className="w-4 h-4" />
) : (
<TrendingDown className="w-4 h-4" />
)}
{Math.abs(trend.value)}%
</span>
)}
</div>
{subtitle && <p className="text-sm text-muted-foreground mt-1">{subtitle}</p>}
</div>
</Card>
);
};
```
This version uses Lucide icons (`TrendingUp`/`TrendingDown`) instead of custom SVGs for portability across projects.
---
## Layout: stat card grid
Display stat cards in a responsive grid:
```tsx
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<StatCard
title="Total Properties"
value={metrics.totalProperties}
trend={{ value: 10, isPositive: true }}
subtitle="Last month total 38"
/>
<StatCard
title="Units Available"
value={metrics.unitsAvailable}
trend={{ value: 5, isPositive: false }}
subtitle="Last month total 12/42"
/>
<StatCard
title="Occupied Units"
value={metrics.occupiedUnits}
trend={{ value: 8, isPositive: true }}
subtitle="Last month total 27"
/>
</div>
```
---
## Computing trend values
Calculate trends from current vs previous period:
```ts
const trends = useMemo(() => {
const previousTotal = metrics.totalProperties - Math.round(metrics.totalProperties * 0.1);
const trendPercent = previousTotal > 0
? Math.round(((metrics.totalProperties - previousTotal) / previousTotal) * 100)
: 0;
return {
value: Math.abs(trendPercent),
isPositive: trendPercent >= 0,
};
}, [metrics]);
```
---
## Trend badge color conventions
| Trend | Background | Text | Meaning |
|-------|------------|------|---------|
| Positive (up) | `bg-emerald-100` | `text-emerald-800` | Growth, improvement |
| Negative (down) | `bg-pink-100` | `text-pink-800` | Decline, concern |
| Neutral | `bg-gray-100` | `text-gray-600` | No change |
---
## Accessibility
- Card uses `cursor-pointer` and `hover:shadow-lg` only when `onClick` is provided.
- Trend icons have implicit meaning from color + direction icon.
- Stat values use large, bold text for visibility.
- Title uses `uppercase tracking-wide` for visual hierarchy without heading tags (appropriate in a card grid).

View File

@ -1,96 +0,0 @@
---
name: building-webapp-react-components
description: "Use when editing any React code in the web application — creating or modifying components, pages, layout, headers, footers, or any TSX/JSX files. Follow this skill for add component, add page, header/footer, and general React UI implementation patterns (shadcn UI and Tailwind CSS)."
---
# React Web App (Components, Pages, Layout)
Use this skill whenever you are editing React/TSX code in the web app (creating or modifying components, pages, header/footer, or layout).
## Step 1 — Identify the type of component
Determine which of these three categories the request falls into, then follow the corresponding section below:
- **Page** — user wants a new routed page (e.g. "add a contacts page", "create a dashboard page", "add a settings section")
- **Header / Footer** — user wants a site-wide header, footer, nav bar, or page footer that appears on every page
- **Component** — everything else: a widget, card, table, form, dialog, or other UI element placed within an existing page
If it is not immediately clear from the user's message, ask:
> "Are you looking to add a new page, a site-wide header or footer, or a component within an existing page?"
Then follow the matching section.
---
## Clarifying Questions
Ask **one question at a time** and wait for the response before asking the next. Stop when you have enough to build accurately — do not guess or assume.
### For a Page
1. **What is the name and purpose of the page?** (e.g., Contacts, Dashboard, Settings)
2. **What URL path should it use?** (e.g., `/contacts`, `/dashboard`) — or derive from the page name?
3. **Should the page appear in the navigation menu?**
4. **Who can access it?** Public, authenticated users only (`PrivateRoute`), or unauthenticated only (e.g., login — `AuthenticationRoute`)?
5. **What content or sections should the page include?** (list, form, table, detail view, etc.)
6. **Does it need to fetch any data?** If so, from where?
### For a Header / Footer
1. **Header, footer, or both?**
2. **What should the header contain?** (logo/app name, nav links, user avatar, CTA button, etc.)
3. **What should the footer contain?** (copyright text, links, social icons, etc.)
4. **Should the header be sticky (fixed to top while scrolling)?**
5. **Is there a logo or brand name to display?** (or placeholder?)
6. **Any specific color scheme or style direction?** (dark background, branded primary color, minimal, etc.)
7. **Should navigation links appear in the header?** If so, which pages?
### For a Component
1. **What should the component do?** (display data, accept input, trigger an action, etc.)
2. **What page or location should it appear on?**
3. **Is this shared/reusable across pages, or specific to one feature?** (determines file location)
4. **What data or props does it need?** (static content, props, fetched data)
5. **Does it need internal state?** (loading, toggle, form state, etc.)
6. **Are there any specific shadcn components to use?** (Card, Table, Dialog, Form, etc.)
7. **Should it appear in a specific layout position?** (full-width, sidebar, inline, etc.)
---
## Implementation
Once you have identified the type and gathered answers to the clarifying questions, read and follow the corresponding implementation guide:
- **Page** — read `implementation/page.md` and follow the instructions there.
- **Header / Footer** — read `implementation/header-footer.md` and follow the instructions there.
- **Component** — read `implementation/component.md` and follow the instructions there.
---
## TypeScript Standards
- **Never use `any`** — use proper types, generics, or `unknown` with type guards.
- **Event handlers:** `(event: React.FormEvent<HTMLFormElement>): void`
- **State:** `useState<User | null>(null)` — always provide the type parameter.
- **No unsafe assertions** (`obj as User`). Use type guards:
```typescript
function isUser(obj: unknown): obj is User {
return typeof obj === 'object' && obj !== null && typeof (obj as User).id === 'string';
}
```
---
## Verification (MANDATORY)
Before completing, run from the web app directory `force-app/main/default/webapplications/<appName>/`:
```bash
cd force-app/main/default/webapplications/<appName> && npm run lint && npm run build
```
- **Lint:** MUST result in 0 errors.
- **Build:** MUST succeed (includes TypeScript check).
If either fails, fix the errors and re-run. Do not leave the session with failing quality gates.

View File

@ -1,90 +0,0 @@
---
name: configuring-webapp-csp-trusted-sites
description: "Creates Salesforce CSP Trusted Site metadata when adding external domains. Use when the user adds an external API, CDN, image host, font provider, map tile server, or any third-party URL that the web application needs to load resources from — or when a browser console shows a CSP violation error."
---
# CSP Trusted Sites
## When to Use
Use this skill whenever the application references a new external domain that is not already registered as a CSP Trusted Site. This includes:
- Adding images from a new CDN (Unsplash, Pexels, Cloudinary, etc.)
- Loading fonts from an external provider (Google Fonts, Adobe Fonts)
- Calling a third-party API (Open-Meteo, Nominatim, Mapbox, etc.)
- Loading map tiles from a tile server (OpenStreetMap, Mapbox)
- Embedding iframes from external services (YouTube, Vimeo)
- Loading external stylesheets or scripts
Salesforce enforces Content Security Policy (CSP) headers on all web applications. Any external domain not registered as a CSP Trusted Site will be blocked by the browser, causing images to not load, API calls to fail, or fonts to be missing.
**Reference:** [Salesforce CspTrustedSite Object Reference](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_csptrustedsite.htm)
---
## Step 1 — Identify external domains
Scan the code for any URLs pointing to external domains. Common patterns:
- `fetch("https://api.example.com/...")` — API calls
- `<img src="https://images.example.com/..." />` — images
- `<link href="https://fonts.example.com/..." />` — stylesheets
- `url="https://tiles.example.com/{z}/{x}/{y}.png"` — map tiles
- `@import url("https://cdn.example.com/...")` — CSS imports
Extract the **origin** (scheme + host) from each URL. For example:
- `https://api.open-meteo.com/v1/forecast?lat=...``https://api.open-meteo.com`
- `https://images.unsplash.com/photo-123?w=800``https://images.unsplash.com`
---
## Step 2 — Check existing CSP Trusted Sites
Before creating a new file, check if the domain already has a CSP Trusted Site:
```bash
ls force-app/main/default/cspTrustedSites/
```
If the domain is already registered, no action is needed.
---
## Step 3 — Determine the CSP directive(s)
Map the resource type to the correct CSP `isApplicableTo*Src` fields. Read `implementation/metadata-format.md` for the full reference.
Quick reference:
| Resource type | CSP directive field(s) to set `true` |
|--------------|--------------------------------------|
| Images (img, background-image) | `isApplicableToImgSrc` |
| API calls (fetch, XMLHttpRequest) | `isApplicableToConnectSrc` |
| Fonts (.woff, .woff2, .ttf) | `isApplicableToFontSrc` |
| Stylesheets (CSS) | `isApplicableToStyleSrc` |
| Video / audio | `isApplicableToMediaSrc` |
| Iframes | `isApplicableToFrameSrc` |
**Always also set `isApplicableToConnectSrc` to `true`** — most resources also require connect-src for preflight/redirect handling.
---
## Step 4 — Create the metadata file
Read `implementation/metadata-format.md` and follow the instructions to create the `.cspTrustedSite-meta.xml` file.
---
## Step 5 — Verify
1. Confirm the file is valid XML and matches the expected schema.
2. Confirm the file is placed in `force-app/main/default/cspTrustedSites/`.
3. Confirm only the necessary `isApplicableTo*Src` fields are set to `true`.
4. Run from the web app directory:
```bash
cd force-app/main/default/webapplications/<appName> && npm run lint && npm run build
```
- **Lint:** MUST result in 0 errors.
- **Build:** MUST succeed.

View File

@ -1,158 +0,0 @@
---
name: configuring-webapp-metadata
description: "Use this skill when configuring web application metadata structure, webapplication.json, or bundle organization. Covers WebApplication bundle layout, meta XML, build output directory, and webapplication.json settings."
---
# WebApplication Requirements
## Bundle Rules
- A WebApplication bundle must live under `webapplications/<AppName>/`
- The bundle must contain `<AppName>.webapplication-meta.xml`
- The metadata filename must exactly match the folder name
- A build output directory must exist and contain at least one file
- Default build output directory: `dist/`
- If `webapplication.json.outputDir` is set, it overrides `dist/`
Valid example:
```text
webapplications/
MyApp/
MyApp.webapplication-meta.xml
webapplication.json
dist/
index.html
```
## Metadata XML
Required fields:
- `masterLabel`
- `version` (max 20 chars)
- `isActive` (boolean)
Optional fields:
- `description` (max 255 chars)
## webapplication.json
`webapplication.json` is optional.
Allowed top-level keys only:
- `outputDir`
- `routing`
- `headers`
### File Constraints
- Must be valid UTF-8 JSON
- Max size: 100 KB
- Root must be a non-empty object
- Never allow `{}`, arrays, or primitives as the root
### Path Safety
Applies to:
- `outputDir`
- `routing.fallback`
Reject:
- backslashes
- leading `/` or `\`
- `..` segments
- null or control characters
- globs: `*`, `?`, `**`
- `%`
All resolved paths must stay within the application bundle.
### outputDir
- Must be a non-empty string
- Must reference a subdirectory only
- Reject `.` and `./`
- The directory must exist in the bundle
- The directory must contain at least one file
### routing
- If present, must be a non-empty object
- Allowed keys only:
- `rewrites`
- `redirects`
- `fallback`
- `trailingSlash`
- `fileBasedRouting`
#### routing.trailingSlash
- Must be one of: `"always"`, `"never"`, `"auto"`
#### routing.fileBasedRouting
- Must be a boolean
#### routing.fallback
- Must be a non-empty string
- Must satisfy Path Safety rules
- Target file must exist
#### routing.rewrites
- Must be a non-empty array
- Each item must be a non-empty object
- Allowed keys: `route`, `rewrite`
- `rewrite` must be a non-empty string
- `route`, if present, must be a non-empty string
Example:
```json
{
"routing": {
"rewrites": [
{ "route": "/app/:path*", "rewrite": "/index.html" }
]
}
}
```
#### routing.redirects
- Must be a non-empty array
- Each item must be a non-empty object
- Allowed keys: `route`, `redirect`, `statusCode`
- `redirect` must be a non-empty string
- `route`, if present, must be a non-empty string
- `statusCode`, if present, must be one of: `301`, `302`, `307`, `308`
Example:
```json
{
"routing": {
"redirects": [
{ "route": "/old-page", "redirect": "/new-page", "statusCode": 301 }
]
}
}
```
### headers
- If present, must be a non-empty array
- Each item must be a non-empty object
- Allowed keys: `source`, `headers`
- `headers` must be a non-empty array
Each header entry must contain:
- `key`: non-empty string
- `value`: non-empty string
Example:
```json
{
"headers": [
{
"source": "/assets/**",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
}
]
}
```
## Never Suggest
- `{}` as the JSON root
- `"routing": {}`
- empty arrays
- empty array items such as `[{}]`
- `"outputDir": "."`
- `"outputDir": "./"`

View File

@ -1,138 +0,0 @@
---
name: creating-webapp
description: "Use this skill when creating or setting up a new SFDX React web application. Covers first steps, npm install, skills-first protocol, deployment order, and core web app rules."
---
# First Steps (MUST FOLLOW)
**Always run `npm install` before doing anything else** when working in a web app directory (e.g. `force-app/main/default/webapplications/<appName>/` or a dist app path). Dependencies must be installed before running `npm run dev`, `npm run build`, `npm run lint`, or any other script. If `node_modules` is missing or stale, commands will fail.
# Skills-First (MUST FOLLOW)
**Before writing any code or running any command**, search for relevant skills (`SKILL.md` files) that cover your task. Read the full skill and follow its instructions. Skills live in `.a4drules/skills/` and `feature/*/skills/`.
- Do not write custom scripts or complex bash commands for a workflow already covered by a loaded skill.
- Only proceed with manual execution after confirming no relevant skill exists.
# Deployment Order (MUST FOLLOW)
**Metadata deployments must complete before fetching GraphQL schema or running codegen.** The schema reflects the current org state; custom objects and fields appear only after metadata is deployed. Running schema fetch or codegen too early produces incomplete or incorrect types.
**Invoke the `deploying-to-salesforce` skill** (`.a4drules/skills/deploying-to-salesforce/`) whenever the task involves:
- Deploying metadata (objects, permission sets, layouts)
- Fetching GraphQL schema (`npm run graphql:schema`)
- Running GraphQL codegen (`npm run graphql:codegen`)
- Generating deploy/setup commands or syncing with the org
The skill enforces the correct sequence: **deploy metadata → assign permset → schema fetch → codegen**.
**Critical rules:**
- Do **not** run `npm run graphql:schema` before metadata (objects, permission sets) is deployed — the schema will not include custom objects/fields.
- Do **not** skip schema refetch after any metadata deployment — re-run `npm run graphql:schema` and `npm run graphql:codegen` from the webapp dir.
# Web App Generation
## Before `sf webapp generate`
**Webapp name (`-n`):** Must be **alphanumerical only**—no spaces, hyphens, underscores, or special characters. Use only letters (AZ, az) and digits (09). Example: `CoffeeBoutique` not `Coffee Boutique`.
```bash
sf webapp generate -n MyWebApp -t reactbasic
```
Do not use `create-react-app`, Vite, or other generic scaffolds; use `sf webapp generate` so the app is SFDX-aware.
## After Generation (MANDATORY)
After generating or when touching an existing app:
1. **Replace all default boilerplate** — "React App", "Vite + React", default `<title>`, placeholder text in shell. Use the actual app name.
2. **Populate the home page** — Never leave it as default template. Add real content: landing section, banners, hero, navigation to features.
3. **Update navigation and placeholders** — See [Navigation & Layout section](#navigation--layout-mandatory) below.
# Navigation & Layout (MANDATORY)
Agents consistently miss these. **You must not leave them default.**
## appLayout.tsx is the Source of Truth
- **Build navigation into the app layout** (`appLayout.tsx`). The layout must include nav (header, sidebar, or both) so every page shares the same shell.
- Path: `force-app/main/default/webapplications/<appName>/src/appLayout.tsx`
## When Making UI Changes
**When making any change** that affects navigation, header, footer, sidebar, theme, or overall layout:
1. **You MUST edit `src/appLayout.tsx`** (the layout used by `routes.tsx`).
2. Do not only edit pages/components and leave `appLayout.tsx` unchanged.
3. Before finishing: confirm you opened and modified `appLayout.tsx`. If you did not, the task is incomplete.
## Navigation Menu (Critical)
- **Always edit the navigation menu** in `appLayout.tsx`. Replace default nav items and labels with **app-specific** links and names.
- Do **not** leave template items (e.g. "Home", "About", generic placeholder links).
- Use real routes and labels matching the app (e.g. "Dashboard", "Products", "Orders").
**Check before finishing:** Did I change the nav items and labels to match this app?
## Placeholder Name & Design (Critical)
- **Replace the placeholder app name** everywhere: header, nav brand/logo, footer, `<title>` in `index.html`, any "Welcome to…" text.
- **Replace placeholder design** in the shell: default header/footer styling, generic branding.
**Check before finishing:** Is the app name and shell design still the template default? If yes, update it.
## Where to Edit
| What | Where |
| ------------------- | -------------------------------------------------------------------- |
| Layout/nav/branding | `force-app/main/default/webapplications/<appName>/src/appLayout.tsx` |
| Document title | `force-app/main/default/webapplications/<appName>/index.html` |
| Root page content | Component at root route (often `Home` in `routes.tsx`) |
# React & TypeScript Constraints
## Routing (React Router)
Use a **single** router package. When using `createBrowserRouter` / `RouterProvider`, all imports MUST come from **`react-router`** — not `react-router-dom`.
## Component Library + Styling
- **shadcn/ui** for components: `import { Button } from '@/components/ui/button';`
- **Tailwind CSS** utility classes
## URL & Path Handling
Apps run behind dynamic base paths. Router navigation (`<Link to>`, `navigate()`) prefer absolute paths (`/x`). Non-router attributes (`<img src>`) use dot-relative (`./x`) to resolve against `<base>`. Prefer Vite `import` for static assets.
## Module Restrictions
React apps must NOT import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, invoke the **using-salesforce-data** skill.
# Frontend Aesthetics
**Avoid AI slop.** Make creative, distinctive frontends:
- **Typography:** Avoid Inter, Roboto, Arial, Space Grotesk as defaults. Choose distinctive fonts.
- **Color:** Use cohesive color with sharp accents via CSS variables. Avoid purple-on-white clichés.
- **Motion:** Use high-impact motion (e.g. staggered reveals).
- **Depth:** Add atmosphere/depth in backgrounds.
# Shell Command Safety (MUST FOLLOW)
**Never use complex `node -e` one-liners** for file edits or multi-line transforms. They break in Zsh due to `!` history expansion and backtick interpolation. Use a temporary `.js` file, `sed`/`awk`, `jq`, or IDE file-editing tools instead.
# Development Cycle
- Execute tasks continuously until all planned items complete in the current iteration.
- Maintain a running checklist and proceed sequentially.
## Stop Conditions
Only stop when:
- All checklist items are completed and quality gates pass, or
- A blocking error cannot be resolved after reasonable remediation, or
- The user explicitly asks to pause.

View File

@ -1,226 +0,0 @@
---
name: deploying-webapp-to-salesforce
description: "Enforces the correct order for deploying metadata, assigning permission sets, and fetching GraphQL schema. Use for ANY deployment to a Salesforce org — webapps, LWC, Aura, Apex, metadata, schema fetch, or org sync. Codifies setup-cli.mjs."
---
# Deploying to Salesforce
Guidance for AI agents deploying metadata to a Salesforce org or syncing with the org. **The order of operations is critical.** This skill codifies the exact sequence from `scripts/setup-cli.mjs` and documents **every Salesforce interaction**.
## When to Use
Invoke this skill whenever the task involves:
- Deploying metadata (objects, permission sets, layouts, Apex, web applications)
- Generating deploy commands or setup instructions
- Fetching the GraphQL schema (`npm run graphql:schema`)
- Running GraphQL codegen (`npm run graphql:codegen`)
- Full org setup (login, deploy, permset, data, schema, build)
- Any manual step that touches the Salesforce org
## Canonical Sequence (from setup-cli.mjs)
Execute steps in this **exact order**. Steps marked **(SF)** perform a Salesforce API or CLI interaction.
### Step 1: Login — org authentication
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Check if org is connected | **(SF)** | `sf org display --target-org <alias> --json` |
| If not connected: authenticate | **(SF)** | `sf org login web --alias <alias>` |
- **Run when:** Org is not connected. **Omit when:** Org is already authenticated (check via `sf org display`).
- All subsequent steps require an authenticated org.
### Step 2: Webapp build — pre-deploy (required for entity deployment)
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Install dependencies | No | `npm install` (in each webapp dir) |
| Build web app | No | `npm run build` (in each webapp dir) |
- Produces `dist/` so `sf project deploy start` can deploy web application entities. Run **before** deploy when deploying web apps.
- **Run when:** Deploying web apps AND (`dist/` does not exist OR webapp source has changed since last build). **Omit when:** Not deploying, or `dist/` is current and no source changes.
### Step 3: Deploy metadata
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Deploy metadata | **(SF)** | See below |
**Check for a manifest (package.xml) first.** Only use it if present:
- **If `manifest/package.xml` (or `package.xml`) exists:** Deploy using the manifest:
```bash
sf project deploy start --manifest manifest/package.xml --target-org <alias>
```
- **If no manifest exists:** Deploy all metadata from the project (packageDirectories in sfdx-project.json):
```bash
sf project deploy start --target-org <alias>
```
Do not assume a manifest exists. Check the project root and common locations (e.g., `manifest/`, `config/`) before choosing the deploy command.
- Deploys objects, layouts, permission sets, Apex classes, web applications, and all other metadata.
- **Must complete successfully before schema fetch** — the schema reflects org state; custom objects/fields appear only after deployment.
- **Run when:** Metadata has changed since last deploy, or never deployed. **Omit when:** No metadata changes and deploy has already run successfully.
### Step 4: Post-deployment configuration — assign permissions and configure
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Assign permission set or group | **(SF)** | `sf org assign permset --name <name> --target-org <alias>` (works for both permsets and permset groups) |
| Assign profile to user | **(SF)** | `sf data update record` or at user creation |
| Other post-deploy config | **(SF)** | Varies (e.g., named credentials, connected apps, custom settings) |
**Example commands:**
```bash
# Permission set (assigns to default user of target org)
sf org assign permset --name Property_Management_Access --target-org myorg
# Permission set group (same command; pass the group name)
sf org assign permset --name My_Permset_Group --target-org myorg
# Assign to a specific user
sf org assign permset --name Property_Management_Access --target-org myorg --on-behalf-of user@example.com
# Profile — update existing user's profile (requires ProfileId and User Id)
sf data update record --sobject User --record-id <userId> --values "ProfileId=<profileId>" --target-org myorg
# Profile — get ProfileId first
sf data query --query "SELECT Id, Name FROM Profile WHERE Name='Standard User'" --target-org myorg
```
- **Deploying does not mean assigning.** Even after permission sets, permission set groups, and profiles are deployed to the org, they must be explicitly assigned or configured for users. Deployment makes them available; assignment/configuration grants access.
- **Permission sets** — Assign to users so they have access to custom objects and fields. Required for GraphQL introspection to return the correct schema.
- **Permission set groups** — Assign to users when using grouped permission sets.
- **Profiles** — Ensure users have the correct profile; profile assignment may be done at user creation or via Setup.
- **Other post-deploy configuration** — Named credentials, connected apps, custom settings, flow activation, and any metadata that requires manual configuration after deploy.
- All of the above must exist in the org (deployed in Step 3).
- **Run when:** Any permission set, permission set group, profile, or other post-deploy config was deployed or changed and not yet assigned/configured. **Omit when:** All required assignments and configuration are already in place.
**Proactive behavior:** After a successful deploy, discover permission sets in the project (e.g., `force-app/main/default/permissionsets/*.permissionset-meta.xml`) and assign each one. Extract the API name from the filename (e.g., `Property_Management_Access.permissionset-meta.xml``Property_Management_Access`). If the user has not explicitly requested a full setup, you may ask: "Do you want me to assign the permission sets to your org?" — but do not skip this step silently; either run it or ask.
### Step 5: Data — prepare and import (optional)
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Prepare unique fields | No | `node scripts/prepare-import-unique-fields.js --data-dir <dataDir>` |
| Clean existing records | **(SF)** | `sf apex run --target-org <alias> --file <apex>` (per sobject, reverse plan order) |
| Import records | **(SF)** | `sf apex run --target-org <alias> --file <apex>` (per batch) |
- Only runs if `data/data-plan.json` and `data/` exist.
- Delete runs in reverse plan order (children before parents).
- Import uses Anonymous Apex with `Database.DMLOptions.duplicateRuleHeader.allowSave = true`.
- **Run when:** Data plan exists AND (never imported OR data files or plan changed). **Omit when:** No data plan, or data already imported and unchanged.
**Proactive behavior:** If `data/data-plan.json` and `data/` exist, you MUST ask the user: "Do you want me to import the sample data now? This will prepare unique fields, optionally clean existing records, and import data. Reply yes to proceed." Do not skip this step silently; either run it (after confirmation) or ask. Never import or clean without explicit user confirmation.
### Step 6: GraphQL schema and codegen
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Install webapp deps | No | `npm install` (in webapp dir) |
| Set default org | **(SF)** | `sf config set target-org <alias> --global` |
| Fetch schema (introspection) | **(SF)** | `npm run graphql:schema` (from webapp dir) |
| Generate types | No | `npm run graphql:codegen` (from webapp dir) |
- `graphql:schema` performs GraphQL introspection against the org — a **Salesforce API call**.
- Schema is written to `schema.graphql` at the SFDX project root.
- Codegen reads the schema file locally; no Salesforce interaction.
- **Run when:** Schema does not exist, OR metadata was deployed/changed since last schema fetch, OR permissions or post-deploy config was assigned since last schema fetch. **Omit when:** Schema exists and is current relative to org state.
### Step 7: Webapp build (if not done in Step 2)
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Build web app | No | `npm run build` (in webapp dir) |
- **Run when:** Build is needed (e.g., for dev server or deploy) AND (`dist/` does not exist OR webapp source has changed since last build). **Omit when:** `dist/` is current and no build needed.
### Step 8: Dev server (optional)
| Action | Salesforce interaction? | Command |
|--------|-------------------------|---------|
| Launch dev server | No | `npm run dev` (in webapp dir) |
- **Run when:** User requests to launch the dev server. **Omit when:** Not requested.
## Summary: All Salesforce Interactions (in order)
1. `sf org display` — check org connection
2. `sf org login web` — authenticate (if needed)
3. `sf project deploy start` — deploy metadata
4. `sf org assign permset` (permsets and permset groups) / profile assignment / other post-deploy config — assign permissions and configure
5. `sf apex run` — delete existing data (if data plan)
6. `sf apex run` — import data (if data plan)
7. `sf config set target-org` — set default org for schema
8. `npm run graphql:schema` — GraphQL introspection (Salesforce API)
## Post-Deploy Checklist (MUST NOT SKIP)
After **every successful metadata deploy**, the agent MUST address these before considering the task complete:
1. **Permission sets** — Discover `force-app/main/default/permissionsets/*.permissionset-meta.xml`, extract API names, and assign each via `sf org assign permset --name <name> --target-org <alias>`. If unsure, ask: "Do you want me to assign the permission sets (e.g., Property_Management_Access, Tenant_Maintenance_Access) to your org?"
2. **Data import** — If `data/data-plan.json` exists, ask: "Do you want me to import the sample data now? Reply yes to proceed." Do not import without confirmation.
3. **Schema refetch** — Run `npm run graphql:schema` and `npm run graphql:codegen` from the webapp dir (required after deploy).
Do not silently skip permission set assignment or data import. Either run them or ask the user.
## Agent Decision Criteria
Evaluate each step before running it. Ask:
- **Has this step ever run before?** If not, it is likely needed.
- **Have changes been made that require this step?** (e.g., metadata edits → deploy; deploy → schema refetch)
- **Is the current state sufficient?** (e.g., org connected, schema exists and is current, permissions and post-deploy config assigned)
Do not rely on CLI flags. Decide based on project state and what has changed.
## Evaluation: When Each Step Touches Salesforce
| Step | Salesforce interaction | Run when |
|------|------------------------|----------|
| 1. Login | Yes | Org not connected |
| 2. Webapp build | No | Deploying web apps AND dist missing or source changed |
| 3. Deploy | Yes | Metadata changed or never deployed |
| 4. Post-deploy config | Yes | Permission sets, permset groups, profiles, or other config deployed/changed and not assigned |
| 5. Data | Yes | Data plan exists AND (never imported OR data changed) |
| 6. GraphQL | Yes (schema only) | Schema missing or metadata/permissions changed since last fetch |
| 7. Webapp build | No | Build needed AND dist missing or source changed |
| 8. Dev | No | User requests dev server |
**Critical rule:** Steps 3 (deploy) and 4 (post-deploy config) must complete **before** step 6 (graphql:schema). The schema reflects the org state; running introspection too early yields an incomplete schema.
## Schema Refetch Rule (CRITICAL)
**After any metadata deployment**, you MUST re-run schema fetch and codegen:
- New custom objects
- New custom fields
- New or updated permission sets
- Any change to metadata that affects the GraphQL schema
```bash
# From webapp dir (force-app/main/default/webapplications/<appName>/)
npm run graphql:schema
npm run graphql:codegen
```
Do **not** assume the existing `schema.graphql` is current after metadata changes.
## One-Command Setup (Reference)
The project includes `scripts/setup-cli.mjs` which runs this sequence in batch. Use it when the user wants a full setup; otherwise, follow this skill and run only the steps that are needed based on current state.
## Prohibited Actions
- **Do not** run `npm run graphql:schema` before metadata is deployed — the schema will not include custom objects/fields
- **Do not** skip schema refetch after deploying new metadata — types and queries will be out of sync
- **Do not** assign permissions or configure before deploying — permission sets, permset groups, and profiles must exist in the org first
- **Do not** run GraphQL introspection before assigning permissions — the user may lack FLS for custom fields
## Related Skills
- **using-salesforce-data** — Full data access workflow (GraphQL queries/mutations, REST APIs, schema exploration, webapp integration)

View File

@ -0,0 +1,45 @@
---
name: generating-webapp-features
description: "Search and install pre-built features into Salesforce React web applications — authentication, shadcn, search, navigation, GraphQL, Agentforce AI, and more. Use whenever searching for or installing features. Always check for an existing feature before building from scratch. Triggers on: install feature, add authentication, add shadcn, add feature, search features, list features."
---
# Web Application Features
## Installing Pre-built Features
Always check for an existing feature before building something from scratch. The features CLI installs pre-built, tested packages into Salesforce webapps — from foundational UI libraries (shadcn/ui) to full-stack capabilities (authentication, search, navigation, GraphQL, Agentforce AI).
### Workflow
1. **Search project code first** — check `src/` for existing implementations before installing anything. Scope searches to `src/` to avoid matching `node_modules/` or `dist/`.
2. **Search available features** — use `npx @salesforce/webapps-features-experimental list` with `--search <query>` to filter by keyword. Use `--verbose` for full descriptions.
3. **Describe a feature** — use `npx @salesforce/webapps-features-experimental describe <feature>` to see components, dependencies, copy operations, and example files.
4. **Install** — use `npx @salesforce/webapps-features-experimental install <feature> --webapp-dir <name>`. Key options:
- `--dry-run` to preview changes
- `--yes` for non-interactive mode (skips conflicts)
- `--on-conflict error` to detect conflicts, then `--conflict-resolution <file>` to resolve them
If no matching feature is found, ask the user before building a custom implementation — a relevant feature may exist under a different name.
### Conflict Handling
In non-interactive environments, use the two-pass approach: first run with `--on-conflict error` to detect conflicts, then create a resolution JSON file (`{ "path": "skip" | "overwrite" }`) and re-run with `--conflict-resolution`.
### Post-install: Integrating Example Files
Features may include `__example__` files showing integration patterns. For each:
1. Read the example file to understand the pattern
2. Read the target file (shown in `describe` output)
3. Apply the pattern from the example into the target
4. Delete the example file after successful integration
### Hint Placeholders
Some copy paths use `<descriptive-name>` placeholders (e.g., `<desired-page-with-search-input>`) that the CLI does not resolve. After installation, rename or relocate these files to the intended target, or integrate their patterns into an existing file.

View File

@ -0,0 +1,180 @@
---
name: generating-webapp-metadata
description: "Scaffold new Salesforce web applications and configure/deploy their metadata — sf webapp generate, WebApplication bundles (meta XML, webapplication.json with routing/headers/outputDir), CSP Trusted Sites for external domains, and the full deployment sequence (org auth, build, deploy, permset assign, data import, GraphQL schema fetch, codegen). Use whenever creating a new webapp, setting up webapp metadata structure, adding external domains that need CSP registration, deploying to a Salesforce org, assigning permission sets, fetching GraphQL schema, or running codegen. Triggers on: create webapp, new app, sf webapp generate, deploy, metadata, webapplication.json, CSP, trusted site, permission set, schema fetch, codegen, org setup, bundle configuration, meta XML, routing config, external domain."
---
# Web Application Metadata
## Scaffolding a New Webapp
Use `sf webapp generate` to create new apps — not create-react-app, Vite, or other generic scaffolds.
**Webapp name (`-n`):** Alphanumerical only — no spaces, hyphens, underscores, or special characters. Example: `CoffeeBoutique` (not `Coffee Boutique`).
After generation:
1. Replace all default boilerplate — "React App", "Vite + React", default `<title>`, placeholder text
2. Populate the home page with real content (landing section, banners, hero, navigation)
3. Update navigation and placeholders (see the `generating-webapp-ui` skill)
Always install dependencies before running any scripts in the webapp directory.
---
## WebApplication Bundle
A WebApplication bundle lives under `webapplications/<AppName>/` and must contain:
- `<AppName>.webapplication-meta.xml` — filename must exactly match the folder name
- A build output directory (default: `dist/`) with at least one file
### Meta XML
Required fields: `masterLabel`, `version` (max 20 chars), `isActive` (boolean).
Optional: `description` (max 255 chars).
### webapplication.json
Optional file. Allowed top-level keys: `outputDir`, `routing`, `headers`.
**Constraints:**
- Valid UTF-8 JSON, max 100 KB
- Root must be a non-empty object (never `{}`, arrays, or primitives)
**Path safety** (applies to `outputDir` and `routing.fallback`): Reject backslashes, leading `/` or `\`, `..` segments, null/control characters, globs (`*`, `?`, `**`), and `%`. All resolved paths must stay within the bundle.
#### outputDir
Non-empty string referencing a subdirectory (not `.` or `./`). Directory must exist and contain at least one file.
#### routing
If present, must be a non-empty object. Allowed keys: `rewrites`, `redirects`, `fallback`, `trailingSlash`, `fileBasedRouting`.
- **trailingSlash**: `"always"`, `"never"`, or `"auto"`
- **fileBasedRouting**: boolean
- **fallback**: non-empty string satisfying path safety; target file must exist
- **rewrites**: non-empty array of `{ route?, rewrite }` objects — e.g., `{ "route": "/app/:path*", "rewrite": "/index.html" }`
- **redirects**: non-empty array of `{ route?, redirect, statusCode? }` objects — statusCode must be 301, 302, 307, or 308
#### headers
Non-empty array of `{ source, headers: [{ key, value }] }` objects.
**Example:**
```json
{
"routing": {
"rewrites": [{ "route": "/app/:path*", "rewrite": "/index.html" }],
"trailingSlash": "never"
},
"headers": [
{
"source": "/assets/**",
"headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
}
]
}
```
**Never suggest:** `{}` as root, empty `"routing": {}`, empty arrays, `[{}]`, `"outputDir": "."`, `"outputDir": "./"`.
---
## CSP Trusted Sites
Salesforce enforces Content Security Policy headers. Any external domain not registered as a CSP Trusted Site will be blocked (images won't load, API calls fail, fonts missing).
### When to Create
Whenever the app references a new external domain: CDN images, external fonts, third-party APIs, map tiles, iframes, external stylesheets.
### Steps
1. **Identify external domains** — extract the origin (scheme + host) from each external URL in the code
2. **Check existing registrations** — look in `force-app/main/default/cspTrustedSites/`
3. **Map resource type to CSP directive:**
| Resource Type | Directive Field |
|--------------|----------------|
| Images | `isApplicableToImgSrc` |
| API calls (fetch, XHR) | `isApplicableToConnectSrc` |
| Fonts | `isApplicableToFontSrc` |
| Stylesheets | `isApplicableToStyleSrc` |
| Video / audio | `isApplicableToMediaSrc` |
| Iframes | `isApplicableToFrameSrc` |
Always also set `isApplicableToConnectSrc` to `true` for preflight/redirect handling.
4. **Create the metadata file** — follow `implementation/csp-metadata-format.md` for the `.cspTrustedSite-meta.xml` format. Place in `force-app/main/default/cspTrustedSites/`.
---
## Deployment Sequence
The order of operations is critical when deploying to a Salesforce org. This sequence reflects the canonical flow.
### Step 1: Org Authentication
Check if the org is connected. If not, authenticate. All subsequent steps require an authenticated org.
### Step 2: Pre-deploy Webapp Build
Install dependencies and build the webapp to produce `dist/`. Required before deploying web application entities.
Run when: deploying web apps and `dist/` is missing or source has changed.
### Step 3: Deploy Metadata
Check for a manifest (`manifest/package.xml` or `package.xml`) first. If present, deploy using the manifest. If not, deploy all metadata from the project.
Deploys objects, layouts, permission sets, Apex classes, web applications, and all other metadata. Must complete before schema fetch — the schema reflects org state.
### Step 4: Post-deploy Configuration
Deploying does not mean assigning. After deployment:
- **Permission sets / groups** — assign to users so they have access to custom objects and fields. Required for GraphQL introspection to return the correct schema.
- **Profiles** — ensure users have the correct profile.
- **Other config** — named credentials, connected apps, custom settings, flow activation.
Proactive behavior: after a successful deploy, discover permission sets in `force-app/main/default/permissionsets/` and assign each one (or ask the user).
### Step 5: Data Import (optional)
Only if `data/data-plan.json` exists. Delete runs in reverse plan order (children before parents). Import uses Anonymous Apex with duplicate rule save enabled.
Always ask the user before importing or cleaning data.
### Step 6: GraphQL Schema and Codegen
1. Set default org
2. Fetch schema (GraphQL introspection) — writes `schema.graphql` at project root
3. Generate types (codegen reads schema locally)
Run when: schema missing, or metadata/permissions changed since last fetch.
### Step 7: Final Webapp Build
Build the webapp if not already done in Step 2.
### Summary: Interaction Order
1. Check/authenticate org
2. Build webapp (if deploying web apps)
3. Deploy metadata
4. Assign permissions and configure
5. Import data (if data plan exists, with user confirmation)
6. Fetch GraphQL schema and run codegen
7. Build webapp (if needed)
### Critical Rules
- Deploy metadata **before** fetching schema — custom objects/fields appear only after deployment
- Assign permissions **before** schema fetch — the user may lack FLS for custom fields
- Re-run schema fetch and codegen **after every metadata deployment** that changes objects, fields, or permissions
- Never skip permission set assignment or data import silently — either run them or ask the user
### Post-deploy Checklist
After every successful metadata deploy:
1. Discover and assign permission sets (or ask the user)
2. If `data/data-plan.json` exists, ask the user about data import
3. Re-run schema fetch and codegen from the webapp directory

View File

@ -0,0 +1,122 @@
---
name: generating-webapp-ui
description: "Build and modify React UI for Salesforce web applications — pages, components, layout, navigation, and headers/footers. Use whenever creating or editing TSX/JSX files or making visual/layout changes. Triggers on: add page, add component, header, footer, navigation, layout, styling, Tailwind, shadcn, React component, appLayout."
---
# Web Application UI
## Identify the Task
Determine which category the request falls into:
| Category | Examples | Implementation Guide |
|----------|----------|---------------------|
| **Page** | New routed page (contacts, dashboard, settings) | `implementation/page.md` |
| **Header / Footer** | Site-wide nav bar, footer, branding | `implementation/header-footer.md` |
| **Component** | Widget, card, table, form, dialog | `implementation/component.md` |
---
## Layout and Navigation
`appLayout.tsx` is the source of truth for navigation and layout. Every page shares this shell.
When making any change that affects navigation, header, footer, sidebar, theme, or layout:
1. Edit `src/appLayout.tsx` — the layout used by `routes.tsx`
2. Replace all default/template nav items and labels with app-specific links and names
3. Replace placeholder app name everywhere: header, nav brand, footer, `<title>` in `index.html`
Before finishing, confirm: Did I update `appLayout.tsx` with real nav items and branding?
| What | Where |
|------|-------|
| Layout, nav, branding | `src/appLayout.tsx` |
| Document title | `index.html` |
| Root page content | Component at root route in `routes.tsx` |
---
## React and TypeScript Standards
### Routing
Use a single router package. With `createBrowserRouter` / `RouterProvider`, all imports must come from `react-router` (not `react-router-dom`).
### Component Library and Styling
- **shadcn/ui** for components: `import { Button } from '@/components/ui/button';`
- **Tailwind CSS** utility classes
### URL and Path Handling
Apps run behind dynamic base paths. Router navigation (`<Link to>`, `navigate()`) uses absolute paths (`/x`). Non-router attributes (`<img src>`) use dot-relative (`./x`). Prefer Vite `import` for static assets.
### TypeScript
- Never use `any` — use proper types, generics, or `unknown` with type guards
- Event handlers: `(event: React.FormEvent<HTMLFormElement>): void`
- State: `useState<User | null>(null)` — always provide the type parameter
- No unsafe assertions (`obj as User`) — use type guards instead
### Module Restrictions
React apps must not import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, use the `using-webapp-salesforce-data` skill.
---
## Design Thinking
Before coding, commit to a bold aesthetic direction:
- **Purpose:** What problem does this interface solve? Who uses it?
- **Tone:** Pick a clear direction — brutally minimal, maximalist, retro-futuristic, organic, luxury, playful, editorial, brutalist, art deco, soft/pastel, industrial. Use these as inspiration but design one true to the context.
- **Differentiation:** What makes this unforgettable? What's the one thing someone will remember?
Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work — the key is intentionality, not intensity.
---
## Frontend Aesthetics
- **Typography:** Choose distinctive, characterful fonts. Pair a display font with a refined body font. Never default to Inter, Roboto, Arial, Space Grotesk, or system fonts.
- **Color:** Commit to a cohesive palette using CSS variables. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Avoid cliched purple gradients on white.
- **Motion:** Focus on high-impact moments — one well-orchestrated page load with staggered reveals (`animation-delay`) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. Prefer CSS-only solutions; use Motion library for React when available.
- **Spatial Composition:** Unexpected layouts — asymmetry, overlap, diagonal flow, grid-breaking elements. Generous negative space OR controlled density.
- **Backgrounds & Depth:** Create atmosphere rather than defaulting to solid colors. Gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, grain overlays.
Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate animations and effects. Minimalist designs need restraint, precision, and careful spacing/typography. No two designs should look the same — vary themes, fonts, and aesthetics across generations.
---
## Clarifying Questions
Ask one question at a time and stop when you have enough context.
### For a Page
1. Name and purpose?
2. URL path?
3. Should it appear in navigation?
4. Access control? (public, authenticated via `PrivateRoute`, or unauthenticated via `AuthenticationRoute`)
5. Content sections? (list, form, table, detail view)
6. Data fetching needs?
### For a Header / Footer
1. Header, footer, or both?
2. Contents? (logo, nav links, user avatar, copyright, social icons)
3. Sticky header?
4. Color scheme or style direction?
### For a Component
1. What should it do?
2. Which page does it belong to?
3. Shared/reusable or specific to one feature?
4. Data or props needed?
5. Internal state? (loading, toggle, form state)
6. Specific shadcn components to use?
---
## Verification
Before completing, run lint and build from the webapp directory. Lint must result in 0 errors and build must succeed.

View File

@ -1,5 +1,5 @@
---
name: managing-webapp-agentforce-conversation-client
name: implementing-webapp-agentforce-conversation-client
description: "Adds or modifies AgentforceConversationClient in React apps (.tsx or .jsx files). Use when user says \"add chat widget\", \"embed agentforce\", \"add agent\", \"add chatbot\", \"integrate conversational AI\", or asks to change colors, dimensions, styling, or configure agentId, width, height, inline mode, or styleTokens for travel agent, HR agent, employee agent, or any Salesforce agent chat."
metadata:
author: ACC Components

View File

@ -1,210 +0,0 @@
---
name: installing-webapp-features
description: "Search, describe, and install pre-built UI features (authentication, shadcn components, navigation, search, GraphQL, Agentforce AI) into Salesforce webapps. Use this when the user wants to add functionality to a webapp, or when determining what salesforce-provided features are available — whether prompted by the user or on your own initiative. Always check for an existing feature before building from scratch."
---
# webapps-features-experimental CLI — Agent Reference
**Always check for an existing feature before building something yourself.** This CLI installs pre-built, tested feature packages into Salesforce webapps. Features range from foundational UI component libraries (shadcn/ui with Button, Card, Input, Table, etc.) to full-stack application capabilities like authentication (login, registration, password flows, session management, and Apex backend classes), global search, navigation menus, GraphQL integrations, and Agentforce AI conversation UIs. Each feature ships as a complete implementation — including React components, context providers, route guards, and any required Salesforce server-side code — that already handles platform-specific concerns like Salesforce API integration, session management, and SFDX metadata structure. Building these from scratch is error-prone and unnecessary when a feature exists. **If no existing feature is found, ask the user before proceeding with a custom implementation — a relevant feature may exist under a different name or keyword.**
```
npx @salesforce/webapps-features-experimental <command> [options]
```
## Workflow: Search Project → Search Features → Describe → Install
**MANDATORY**: When the user asks to add ANY webapp functionality, follow this entire workflow. Do not skip steps.
### 1. Search existing project code
Before installing anything, check whether the functionality already exists in the **project source code** (not dependencies).
- **Always scope searches to `src/`** to avoid matching files in `node_modules/`, `dist/`, or `build/` output
- Use Glob with a scoped path: e.g., `src/**/Button.tsx`, `src/**/*auth*.tsx`
- Use Grep with the `path` parameter set to the `src/` directory, or use `glob: "*.{ts,tsx}"` to restrict file types
- Check common directories: `src/components/`, `src/lib/`, `src/pages/`, `src/hooks/`
- **Never** search from the project root without a path or glob filter — this will crawl `node_modules` and produce massive, unhelpful output
**If existing code is found** — read the files, present them to the user, and ask if they want to reuse or extend what's there. If yes, use the existing code and stop. If no, proceed to step 2.
**If nothing is found** — proceed to step 2.
### 2. Search available features
```bash
npx @salesforce/webapps-features-experimental list [options]
```
Options:
- `-v, --verbose` — Show full descriptions, packages, and dependencies
- `--search <query>` — Filter features by keyword (ranked by relevance)
```bash
npx @salesforce/webapps-features-experimental list
npx @salesforce/webapps-features-experimental list --search "auth"
npx @salesforce/webapps-features-experimental list --search "button"
```
**If no matching feature is found** — ask the user before proceeding with a custom implementation. A relevant feature may exist under a different name or keyword.
### 3. Describe a feature
```bash
npx @salesforce/webapps-features-experimental describe <feature>
```
Shows description, package name, dependencies, components, copy operations, and example files.
```bash
npx @salesforce/webapps-features-experimental describe authentication
npx @salesforce/webapps-features-experimental describe shadcn
```
### 4. Install a feature
```bash
npx @salesforce/webapps-features-experimental install <feature> --webapp-dir <path> [options]
```
Resolves the feature name to an npm package, installs it and its dependencies (including transitive feature dependencies like `shadcn`), copies source files into your project, and reports any `__example__` files that require manual integration.
Options:
- `--webapp-dir <name>` (required) — Webapp name, resolves to `<sfdx-source>/webapplications/<name>`
- `--sfdx-source <path>` (default: `force-app/main/default`) — SFDX source directory
- `--dry-run` (default: `false`) — Preview changes without writing files
- `-v, --verbose` (default: `false`) — Enable verbose logging
- `-y, --yes` (default: `false`) — Skip all prompts (auto-skip conflicts)
- `--on-conflict <mode>` (default: `prompt`) — `prompt`, `error`, `skip`, or `overwrite`
- `--conflict-resolution <file>` — Path to JSON file with per-file resolutions
```bash
# Install authentication (also installs shadcn dependency)
npx @salesforce/webapps-features-experimental install authentication \
--webapp-dir mywebapp
# Dry run to preview changes
npx @salesforce/webapps-features-experimental install shadcn \
--webapp-dir mywebapp \
--dry-run
# Non-interactive install (skip all file conflicts)
npx @salesforce/webapps-features-experimental install authentication \
--webapp-dir mywebapp \
--yes
```
## Conflict Handling
Since you are running in a non-interactive environment, you cannot use `--on-conflict prompt` directly. When conflicts are likely (e.g. installing into an existing project), you have two options:
**Option A — Let the user resolve conflicts interactively.** Suggest the user run the install command themselves with `--on-conflict prompt` so they can decide per-file.
**Option B — Two-pass automated resolution:**
```bash
# Pass 1: detect conflicts
npx @salesforce/webapps-features-experimental install authentication \
--webapp-dir mywebapp \
--on-conflict error
# The CLI will exit with an error listing every conflicting file path.
# Pass 2: create a resolution file and re-run
echo '{ "src/styles/global.css": "overwrite", "src/lib/utils.ts": "skip" }' > resolutions.json
npx @salesforce/webapps-features-experimental install authentication \
--webapp-dir mywebapp \
--conflict-resolution resolutions.json
```
Resolution values per file: `"skip"` (keep existing) or `"overwrite"` (replace). When unsure how to resolve a conflict, ask the user rather than guessing.
## Hint Placeholders in Copy Paths
Some copy operations use **hint placeholders** in the `"to"` path — descriptive segments like `<desired-page-with-search-input>` that are NOT resolved by the CLI. These are guidance for the user or LLM to choose an appropriate destination.
**How they work:** The file is copied with the literal placeholder name (e.g., `src/pages/<desired-page-with-search-input>.tsx`). After installation, you should:
1. Read the copied file to understand its purpose
2. Rename or relocate it to the intended target (e.g., `src/pages/Home.tsx`)
3. Or integrate its patterns into an existing file, then delete it
**How to identify them:** Hint placeholders use `<descriptive-name>` syntax but are NOT one of the system placeholders (`<sfdxSource>`, `<webappDir>`, `<webapp>`). They always appear in the middle or end of a path, never as the leading segment.
**Example from features.json:**
```json
{
"to": "<webappDir>/src/pages/<desired-page-with-search-input>.tsx",
"description": "Example home page showing GlobalSearchInput integration",
"integrationTarget": "src/pages/Home.tsx"
}
```
The `integrationTarget` field tells you the suggested destination. Use your judgment — if the user already has a different page where search should go, integrate there instead.
**When `integrationTarget` itself is a placeholder:** Some features use a hint placeholder in the `integrationTarget` value (e.g., `"integrationTarget": "src/<path-to-desired-page-with-search-input>.tsx"`). This means there is no single default target — the user must decide which existing file to integrate into. When you encounter this:
1. Ask the user which page or file they want to integrate the feature into
2. Read the `__example__` file to understand the integration pattern
3. Read the user's chosen target file
4. Apply the pattern from the example into the target file
## Post Installation: Integrating **example** Files
Features may include `__example__` files (e.g., `__example__auth-app.tsx`) showing integration patterns.
**The describe command shows**:
- Which **example** files will be copied
- Target file to integrate into (e.g., `src/app.tsx`)
- What the example demonstrates
### How to Integrate Example Files (CRITICAL FOR LLMs)
⚠️ **ONLY USE Read AND Edit TOOLS - NO BASH COMMANDS** ⚠️
**DO NOT DO THIS**:
- ❌ `git status` or any git commands
- ❌ `ls`, `cat`, `sed`, `awk`, or ANY bash file commands
- ❌ Chaining bash commands to read multiple files
- ❌ Using bash to check directories or file existence
**DO THIS INSTEAD**:
- ✅ Use Read tool with `file_path` parameter to read each file
- ✅ Use Edit tool with `file_path`, `old_string`, `new_string` to modify files
- ✅ That's it! Just Read and Edit tools.
**Integration steps**:
1. **Read each example file** (use Read tool)
- Example: Read tool with `file_path: "force-app/main/default/webapplications/mywebapp/src/__example__auth-app.tsx"`
- Note the imports and patterns to integrate
2. **Read each target file** (use Read tool)
- Example: Read tool with `file_path: "force-app/main/default/webapplications/mywebapp/src/app.tsx"`
- Understand where the new code should go
3. **Edit each target file** (use Edit tool)
- Add imports from the example
- Add or modify code following the example's patterns
- Preserve existing functionality
4. **Delete the example file after successful integration** (use Bash tool)
- Example: `rm force-app/main/default/webapplications/mywebapp/src/__example__authentication-routes.tsx`
- Only delete after you have successfully integrated the pattern
- This keeps the codebase clean and removes temporary example files
## Troubleshooting
**Directory not found**: Check paths are correct, use absolute or correct relative paths
**Feature not found**: Use `npx @salesforce/webapps-features-experimental list` to see available feature names
**Conflicts in error mode**: Follow CLI instructions to create resolution file
**Need help?**: Run `npx @salesforce/webapps-features-experimental --help` to see all commands and options