- Built by developers, for developers. Open source and free to use.
+ Built for developers shipping agent interfaces. Open source and free to use.
diff --git a/apps/playground/src/app/layout.tsx b/apps/playground/src/app/layout.tsx
index c6df792..886cf49 100644
--- a/apps/playground/src/app/layout.tsx
+++ b/apps/playground/src/app/layout.tsx
@@ -1,10 +1,7 @@
import type { Metadata } from "next"
-import { Inter } from "next/font/google"
import "./globals.css"
import { ToastProvider } from "@/components/ui/toast"
-const inter = Inter({ subsets: ["latin"] })
-
export const metadata: Metadata = {
title: "Agent Patterns - Playground",
description: "Interactive playground for Agent Patterns",
@@ -17,7 +14,7 @@ export default function RootLayout({
}) {
return (
-
+
{children}
diff --git a/apps/playground/src/components/launch-demos.tsx b/apps/playground/src/components/launch-demos.tsx
new file mode 100644
index 0000000..ddaea59
--- /dev/null
+++ b/apps/playground/src/components/launch-demos.tsx
@@ -0,0 +1,120 @@
+const demos = [
+ {
+ title: "Analytics dashboard",
+ promise: "KPI cards, charts, table rows, and AI insights in one copy-paste surface.",
+ patterns: ["MetricCard", "Chart", "DataTable", "InsightsList"],
+ preview: [
+ { label: "Revenue", value: "$128.4K", meta: "+18%" },
+ { label: "Pipeline", value: "$2.4M", meta: "42 deals" },
+ { label: "Expansion", value: "31%", meta: "high confidence" },
+ ],
+ code: `const dashboard = {
+ metrics: revenueMetrics,
+ chart: pipelineTrend,
+ table: accountRows,
+ insights: aiGeneratedInsights,
+}`,
+ },
+ {
+ title: "Agent inbox / task queue",
+ promise: "Turn agent work into an ops surface: triage, owners, status, timeline.",
+ patterns: ["KanbanBoard", "DetailCard", "Timeline", "AgentForm"],
+ preview: [
+ { label: "New", value: "7", meta: "needs triage" },
+ { label: "Running", value: "3", meta: "agent active" },
+ { label: "Blocked", value: "1", meta: "human review" },
+ ],
+ code: `const task = {
+ title: "Review failed webhook",
+ priority: "urgent",
+ owner: "ops-agent",
+ nextAction: "assign_human",
+}`,
+ },
+ {
+ title: "AI chat with artifacts + actions",
+ promise: "Chat is not enough. Render generated SQL, decisions, and risky actions beside it.",
+ patterns: ["ChatMessage", "CodeBlock", "ConfirmDialog", "CommandPalette"],
+ preview: [
+ { label: "Artifact", value: "SQL", meta: "copyable" },
+ { label: "Action", value: "Run", meta: "confirmed" },
+ { label: "Trace", value: "4 steps", meta: "visible" },
+ ],
+ code: `const artifact = {
+ type: "sql",
+ file: "churn-risk.sql",
+ action: "run_after_confirm",
+}`,
+ },
+]
+
+export function LaunchDemos() {
+ return (
+
+
+
+
+ Stealable examples
+
+
+ Start from a real agent UI, not a blank canvas.
+
+
+ Three launch-ready compositions show how the 15 patterns combine into dashboards,
+ ops queues, and chat surfaces developers can copy into React apps.
+
+
+
+
+ {demos.map((demo) => (
+
+
+
+ {demo.title}
+
+
{demo.promise}
+
+
+
+ {demo.preview.map((item) => (
+
+
+
+ {item.label}
+
+
{item.value}
+
+
+ {item.meta}
+
+
+ ))}
+
+
+
+ {demo.patterns.map((pattern) => (
+
+ {pattern}
+
+ ))}
+
+
+
+ {demo.code}
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/docs/LAUNCH_KIT.md b/docs/LAUNCH_KIT.md
new file mode 100644
index 0000000..61ca980
--- /dev/null
+++ b/docs/LAUNCH_KIT.md
@@ -0,0 +1,40 @@
+# Launch Kit
+
+Agent Patterns is a proof-of-work OSS artifact, not a startup launch. The goal is to make the idea easy to understand, easy to steal from, and easy to cite on a resume.
+
+## One-line positioning
+
+> shadcn-style UI patterns that agents can actually use correctly.
+
+## Short description
+
+15 copy-paste agent UI patterns for React apps: dashboards, task queues, chat artifacts, forms, tables, timelines, and Zod schemas that keep LLM-generated props valid.
+
+## Resume bullet
+
+Built Agent Patterns, an open-source React pattern library for agentic UIs with 15 copy-paste components, Zod schemas, CLI tooling, and prompt packs for reliable LLM-generated interfaces.
+
+## 45-second demo script
+
+1. Open with the wedge: βAgents are good at text, bad at UI glue. Agent Patterns gives them constrained, copy-paste UI patterns.β
+2. Show the landing page and the three stealable examples: analytics dashboard, agent inbox, chat with artifacts/actions.
+3. Open a pattern directory and show the component, schema, example, and README living together.
+4. Run the local checks: `corepack pnpm test -- --run` and `corepack pnpm build`.
+5. Close with the portfolio line: βThis is not a startup. It is proof I can turn an AI-native product idea into a shippable OSS artifact.β
+
+## Launch loop
+
+- Record a 45-second screen capture of the landing page + examples + one pattern directory.
+- Post a short X/LinkedIn launch note with the one-line wedge and repo link.
+- Add the resume bullet under Projects / Open Source.
+- Use the examples as screenshots in portfolio/resume material.
+
+## Launch copy draft
+
+Built Agent Patterns: shadcn-style UI patterns that agents can actually use correctly.
+
+It is 15 copy-paste React patterns for agentic apps: analytics dashboards, task queues, chat artifacts, forms, tables, timelines, and more.
+
+Each pattern ships with the component, example, README, and Zod schema so an LLM has a constrained interface instead of hallucinating props.
+
+Not a startup. Just a clean OSS proof-of-work artifact.
diff --git a/docs/STEALABLE_EXAMPLES.md b/docs/STEALABLE_EXAMPLES.md
new file mode 100644
index 0000000..49a6250
--- /dev/null
+++ b/docs/STEALABLE_EXAMPLES.md
@@ -0,0 +1,175 @@
+# Stealable Agent UI Examples
+
+These examples are intentionally small enough to paste into a React app, then expand with your own data and actions.
+
+## 1. Analytics Dashboard
+
+Use this when an agent needs to explain metrics, highlight risks, and hand off a table of records.
+
+```tsx
+import { MetricCard } from "@/patterns/metric-card/component"
+import { Chart } from "@/patterns/chart/component"
+import { DataTable, type Column } from "@/patterns/data-table/component"
+import { InsightsList } from "@/patterns/insights-list/component"
+
+type AccountRow = Record
& {
+ account: string
+ plan: string
+ mrr: number
+ risk: string
+}
+
+const columns: Column[] = [
+ { key: "account", header: "Account", sortable: true },
+ { key: "plan", header: "Plan", sortable: true },
+ { key: "mrr", header: "MRR", accessor: (row) => `$${row.mrr.toLocaleString()}` },
+ { key: "risk", header: "Risk" },
+]
+
+export function AnalyticsDashboardExample() {
+ return (
+
+ )
+}
+```
+
+## 2. Agent Inbox / Task Queue
+
+Use this when background agents create work that humans need to triage, approve, or unblock.
+
+```tsx
+import { KanbanBoard } from "@/patterns/kanban-board/component"
+import { DetailCard } from "@/patterns/detail-card/component"
+import { Timeline } from "@/patterns/timeline/component"
+
+export function AgentInboxExample() {
+ return (
+
+
{
+ console.log({ cardId, fromColumn, toColumn })
+ }}
+ />
+
+
+
+
+
+
+ )
+}
+```
+
+## 3. AI Chat With Artifacts + Actions
+
+Use this when the chat assistant produces durable artifacts and high-risk actions.
+
+```tsx
+import { useState } from "react"
+import { ChatMessage } from "@/patterns/chat-message/component"
+import { CodeBlock } from "@/patterns/code-block/component"
+import { ConfirmDialog } from "@/patterns/confirm-dialog/component"
+
+export function ChatArtifactsExample() {
+ const [confirmOpen, setConfirmOpen] = useState(false)
+
+ return (
+
+
+ setConfirmOpen(true) }]}
+ />
+
+ 0.82\norder by churn_score desc;`}
+ showLineNumbers
+ copyable
+ />
+
+ setConfirmOpen(false)}
+ onCancel={() => setConfirmOpen(false)}
+ />
+
+ )
+}
+```
diff --git a/package.json b/package.json
index 0e1a289..3b47b2b 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,13 @@
"private": true,
"description": "Open-source, copy-paste pattern library for LLM-generated UIs",
"scripts": {
- "build": "pnpm -r build",
- "dev": "pnpm -r --parallel dev",
- "test": "vitest",
- "test:coverage": "vitest --coverage",
+ "build": "corepack pnpm -r build",
+ "dev": "corepack pnpm -r --parallel dev",
+ "test": "NODE_ENV=test vitest",
+ "test:coverage": "NODE_ENV=test vitest --coverage",
"lint": "eslint .",
"typecheck": "tsc --noEmit --strict",
- "clean": "pnpm -r clean && rm -rf node_modules"
+ "clean": "corepack pnpm -r clean && rm -rf node_modules"
},
"devDependencies": {
"@testing-library/react": "^14.1.2",
@@ -26,8 +26,8 @@
"vitest": "^1.2.0"
},
"engines": {
- "node": ">=18.0.0",
- "pnpm": ">=8.0.0"
- }
+ "node": ">=20.0.0",
+ "pnpm": ">=9.0.0"
+ },
+ "packageManager": "pnpm@9.15.9"
}
-
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 3caefa4..6d95033 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -11,8 +11,7 @@
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
- "clean": "rm -rf dist",
- "prepare": "tsc"
+ "clean": "rm -rf dist"
},
"dependencies": {
"commander": "^11.1.0",
diff --git a/packages/cli/src/commands/audit.ts b/packages/cli/src/commands/audit.ts
index 21b8293..cdaca9e 100644
--- a/packages/cli/src/commands/audit.ts
+++ b/packages/cli/src/commands/audit.ts
@@ -159,7 +159,7 @@ export function printAuditReport(report: AuditReport, verbose: boolean = false):
: report.overall.status === "partial"
? chalk.yellow
: report.overall.status === "needs-improvement"
- ? chalk.orange
+ ? chalk.hex("#f97316")
: chalk.red
console.log(chalk.bold("Overall Compliance:"))
@@ -210,7 +210,7 @@ export function printAuditReport(report: AuditReport, verbose: boolean = false):
: pattern.overallStatus === "partial"
? chalk.yellow
: pattern.overallStatus === "needs-improvement"
- ? chalk.orange
+ ? chalk.hex("#f97316")
: chalk.red
console.log(chalk.bold(` ${pattern.pattern}:`))
@@ -256,7 +256,7 @@ export function printAuditReport(report: AuditReport, verbose: boolean = false):
: pattern.overallStatus === "partial"
? chalk.yellow
: pattern.overallStatus === "needs-improvement"
- ? chalk.orange
+ ? chalk.hex("#f97316")
: chalk.red
const statusIcon =
@@ -290,7 +290,7 @@ export function printAuditReport(report: AuditReport, verbose: boolean = false):
function getComplianceColor(compliance: number): (text: string) => string {
if (compliance >= 90) return chalk.green
if (compliance >= 70) return chalk.yellow
- if (compliance >= 50) return chalk.orange
+ if (compliance >= 50) return chalk.hex("#f97316")
return chalk.red
}
diff --git a/patterns/agent-form/component.tsx b/patterns/agent-form/component.tsx
index fdb1710..aaa2371 100644
--- a/patterns/agent-form/component.tsx
+++ b/patterns/agent-form/component.tsx
@@ -10,7 +10,7 @@ export interface FormField {
required?: boolean
options?: { label: string; value: string }[]
defaultValue?: string | number | boolean
- validation?: z.ZodType
+ validation?: z.ZodType
description?: string
}
@@ -20,7 +20,7 @@ export interface AgentFormProps extends Omit) => void | Promise
submitLabel?: string
- schema?: z.ZodObject
+ schema?: z.ZodObject
showValidationErrors?: boolean
}
diff --git a/patterns/chart/component.tsx b/patterns/chart/component.tsx
index 75549c7..84d169f 100644
--- a/patterns/chart/component.tsx
+++ b/patterns/chart/component.tsx
@@ -33,6 +33,16 @@ export interface ChartProps extends React.HTMLAttributes {
colors?: string[]
}
+interface TooltipPayload {
+ name?: React.ReactNode
+ value?: React.ReactNode
+}
+
+interface CustomTooltipProps {
+ active?: boolean
+ payload?: TooltipPayload[]
+}
+
const DEFAULT_COLORS = [
"hsl(var(--primary))",
"hsl(var(--chart-2))",
@@ -66,13 +76,14 @@ export const Chart = React.forwardRef(
}))
// Custom tooltip
- const CustomTooltip = ({ active, payload }: any) => {
- if (active && payload && payload.length) {
+ const CustomTooltip = ({ active, payload }: CustomTooltipProps) => {
+ const firstItem = payload?.[0]
+ if (active && firstItem) {
return (
-
{payload[0].name}
+
{firstItem.name}
- Value: {payload[0].value}
+ Value: {firstItem.value}
)
diff --git a/patterns/code-block/component.tsx b/patterns/code-block/component.tsx
index 8e0dfb3..d41fe44 100644
--- a/patterns/code-block/component.tsx
+++ b/patterns/code-block/component.tsx
@@ -39,7 +39,7 @@ export function CodeBlock({
setTimeout(() => setCopied(false), 2000)
}
- const languageColors = {
+ const languageColors: Record = {
typescript: "text-blue-400",
javascript: "text-yellow-400",
python: "text-green-400",
@@ -49,7 +49,7 @@ export function CodeBlock({
default: "text-purple-400",
}
- const languageColor = (languageColors as any)[language] || languageColors.default
+ const languageColor = languageColors[language] || languageColors.default
return (
diff --git a/patterns/detail-card/component.tsx b/patterns/detail-card/component.tsx
index ae4b4f8..d20573c 100644
--- a/patterns/detail-card/component.tsx
+++ b/patterns/detail-card/component.tsx
@@ -18,7 +18,7 @@ export interface DetailCardProps extends React.HTMLAttributes {
fields: DetailField[]
actions?: React.ReactNode
editable?: boolean
- onEdit?: (fields: Record) => void
+ onEdit?: (fields: Record) => void
loading?: boolean
}
@@ -38,7 +38,7 @@ export const DetailCard = React.forwardRef(
ref
) => {
const [isEditing, setIsEditing] = React.useState(false)
- const [editValues, setEditValues] = React.useState>({})
+ const [editValues, setEditValues] = React.useState>({})
const [copiedField, setCopiedField] = React.useState(null)
const handleCopy = async (text: string, label: string) => {
@@ -53,7 +53,7 @@ export const DetailCard = React.forwardRef(
const handleEditStart = () => {
// Initialize edit values with current field values
- const initialValues: Record = {}
+ const initialValues: Record = {}
fields.forEach((field) => {
if (typeof field.value === "string" || typeof field.value === "number") {
initialValues[field.label] = field.value
diff --git a/vitest.config.ts b/vitest.config.ts
index c58aa96..6d9ed12 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -5,6 +5,7 @@ export default defineConfig({
test: {
globals: true,
environment: "jsdom",
+ setupFiles: ["./vitest.setup.ts"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
diff --git a/vitest.setup.ts b/vitest.setup.ts
new file mode 100644
index 0000000..b9e7622
--- /dev/null
+++ b/vitest.setup.ts
@@ -0,0 +1 @@
+import "@testing-library/jest-dom/vitest"