diff --git a/apps/tutorial/eslint.config.mjs b/apps/tutorial/eslint.config.mjs
new file mode 100644
index 0000000..f2ac8a1
--- /dev/null
+++ b/apps/tutorial/eslint.config.mjs
@@ -0,0 +1,16 @@
+import reactConfig from '@tpmjs/eslint-config/react.js';
+
+export default [
+ {
+ ignores: [
+ '.next/**',
+ '.turbo/**',
+ 'node_modules/**',
+ '*.config.js',
+ '*.config.ts',
+ 'next-env.d.ts',
+ 'eslint.config.mjs',
+ ],
+ },
+ ...reactConfig,
+];
diff --git a/apps/tutorial/src/components/slides/DiscoverySlide.tsx b/apps/tutorial/src/components/slides/DiscoverySlide.tsx
index dd035ca..7592751 100644
--- a/apps/tutorial/src/components/slides/DiscoverySlide.tsx
+++ b/apps/tutorial/src/components/slides/DiscoverySlide.tsx
@@ -2,76 +2,91 @@
import { motion } from 'framer-motion';
+const searchSchema = [
+ ' query: z.string(),',
+ ' category?: z.enum([...]),',
+ ' limit?: z.number().max(20),',
+];
+
+const responseExample = [
+ { id: 'open', line: '{' },
+ { id: 'tools', line: ' "tools": [{' },
+ { id: 'toolId', line: ' "toolId": "@tpmjs/markdown::formatTable",' },
+ { id: 'desc', line: ' "description": "Format markdown tables",' },
+ { id: 'schema', line: ' "inputSchema": { ... },' },
+ { id: 'score', line: ' "qualityScore": 0.92' },
+ { id: 'closeArr', line: ' }]' },
+ { id: 'close', line: '}' },
+];
+
export function DiscoverySlide(): React.ReactElement {
return (
- {/* Background pulse */}
-
+
-
- ⚙️
-
-
- Schema Extraction
-
- The hard part.{' '}
- We run your code in a sandbox.
+
searchTpmjsToolsTool
+
+ Agent describes what it needs. Registry returns matches.
- {/* Code block showing extraction */}
-
-
- {/* comment */}
- {'// We extract this from your actual code:'}
-
- inputSchema: {'{'}
-
- type: "object" ,
-
- properties: {'{'}
-
- url: {'{'} type: "string" , format:{' '}
- "uri" {'}'}
-
-
- timeout: {'{'} type: "number" ,
- default: 30000 {'}'}
-
- {'}'}
- {'}'}
-
+
+ {/* Input */}
+
+ inputSchema
+
+
{'z.object({'}
+ {searchSchema.map((line) => (
+
+ {line}
+
+ ))}
+
{'})'}
+
+
+
+ {/* Output */}
+
+ returns
+
+ {responseExample.map((item) => (
+
+ {item.line}
+
+ ))}
+
+
+
- {/* Key insight */}
- Not documentation.{' '}
-
- Ground truth from TypeScript types.
-
+ BM25 search over 200+ tools. Ranked by relevance and quality score.
diff --git a/apps/tutorial/src/components/slides/GetStartedSlide.tsx b/apps/tutorial/src/components/slides/GetStartedSlide.tsx
index 49844ca..3fcb200 100644
--- a/apps/tutorial/src/components/slides/GetStartedSlide.tsx
+++ b/apps/tutorial/src/components/slides/GetStartedSlide.tsx
@@ -2,46 +2,31 @@
import { motion } from 'framer-motion';
-const links = [
- {
- label: 'Browse Tools',
- href: 'https://tpmjs.com/tool-search',
- description: 'Search by name, category, quality',
- icon: '🔍',
- gradient: 'from-cyan-500 to-blue-500',
- },
- {
- label: 'Publish a Tool',
- href: 'https://tpmjs.com/docs/publishing',
- description: 'Add tpmjs-tool keyword to npm',
- icon: '📦',
- gradient: 'from-purple-500 to-pink-500',
- },
- {
- label: 'The Playground',
- href: 'https://tpmjs.com/playground',
- description: 'Run tools in browser sandbox',
- icon: '🎮',
- gradient: 'from-emerald-500 to-teal-500',
- },
+const packageJsonLines = [
+ { id: 'open', line: '{' },
+ { id: 'name', line: ' "name": "@your-org/your-tools",' },
+ { id: 'keywords', line: ' "keywords": ["tpmjs-tool"],' },
+ { id: 'tpmjs', line: ' "tpmjs": {' },
+ { id: 'tools', line: ' "tools": [{' },
+ { id: 'toolName', line: ' "name": "yourTool",' },
+ { id: 'desc', line: ' "description": "Does something useful"' },
+ { id: 'closeArr', line: ' }]' },
+ { id: 'closeTpmjs', line: ' }' },
+ { id: 'close', line: '}' },
+];
+
+const steps = [
+ { step: '1', text: 'Add tpmjs-tool keyword', color: 'cyan' },
+ { step: '2', text: 'Define tpmjs field with tool metadata', color: 'purple' },
+ { step: '3', text: 'npm publish', color: 'emerald' },
];
export function GetStartedSlide(): React.ReactElement {
return (
- {/* Celebratory background */}
-
@@ -52,83 +37,107 @@ export function GetStartedSlide(): React.ReactElement {
transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full"
>
-
- 🚀
-
-
-
Get Started
-
- Indexed automatically. Updated every 2 minutes.
+
Publish Your Tool
+
+ Add keyword. Define metadata. Publish. Indexed in 2 minutes.
- {/* CTA buttons */}
-
- {links.map((link, index) => (
-
-
-
{link.icon}
-
-
{link.label}
-
{link.description}
-
-
-
- {/* Hover arrow */}
-
-
+ {/* package.json */}
+
+ package.json
+
+ {packageJsonLines.map((item) => (
+
-
-
-
-
- ))}
+ {item.line}
+
+ ))}
+
+
+
+ {/* Steps */}
+
+ three steps
+
+ {steps.map((item, i) => (
+
+
+ {item.step}
+
+ {item.text}
+
+ ))}
+
+
+
+ npm publish
+ That's it. We find you.
+
+
- {/* Footer */}
- The missing layer between npm and AI agents.
+
+ Browse Registry
+
+
+ Try Playground
+
diff --git a/apps/tutorial/src/components/slides/HowItWorksSlide.tsx b/apps/tutorial/src/components/slides/HowItWorksSlide.tsx
index d07bccd..d6bcec3 100644
--- a/apps/tutorial/src/components/slides/HowItWorksSlide.tsx
+++ b/apps/tutorial/src/components/slides/HowItWorksSlide.tsx
@@ -2,89 +2,73 @@
import { motion } from 'framer-motion';
-const steps = [
- { icon: '📦', label: 'npm publish', description: 'Add tpmjs-tool keyword' },
- { icon: '🔄', label: 'Auto-sync', description: 'Indexed in ~2 minutes' },
- { icon: '⚙️', label: 'Schema extracted', description: 'From actual code' },
- { icon: '✅', label: 'Health checked', description: 'Import + execution' },
+const tools = [
+ {
+ name: 'searchTpmjsToolsTool',
+ package: '@tpmjs/search-registry',
+ description: 'Agent searches the registry by query, category, or context',
+ color: 'cyan',
+ },
+ {
+ name: 'registryExecuteTool',
+ package: '@tpmjs/registry-execute',
+ description: 'Agent executes a discovered tool by ID in a sandbox',
+ color: 'purple',
+ },
];
export function HowItWorksSlide(): React.ReactElement {
return (
- {/* Background gradient */}
-
+
- How It Works
- Automated pipeline. No manual curation.
+ Two Meta-Tools
+ Tools that discover and execute other tools
- {/* Flow diagram */}
-
- {steps.map((step, index) => (
-
- {/* Step card */}
-
+ {tools.map((tool, index) => (
+
+
-
- {step.icon}
-
-
{step.label}
-
{step.description}
-
-
- {/* Arrow */}
- {index < steps.length - 1 && (
-
-
-
-
-
-
- )}
-
+ {tool.package}
+
+
+ {tool.name}
+
+
{tool.description}
+
))}
- {/* Animated highlight line */}
+ initial={{ opacity: 0 }}
+ animate={{ opacity: 1 }}
+ transition={{ delay: 1 }}
+ className="mt-10 text-white/40 text-sm font-mono"
+ >
+ Both are standard AI SDK tools. Pass them to generateText like any other.
+
);
diff --git a/apps/tutorial/src/components/slides/IntegrationSlide.tsx b/apps/tutorial/src/components/slides/IntegrationSlide.tsx
index f284cf9..b861cd7 100644
--- a/apps/tutorial/src/components/slides/IntegrationSlide.tsx
+++ b/apps/tutorial/src/components/slides/IntegrationSlide.tsx
@@ -1,123 +1,100 @@
'use client';
import { motion } from 'framer-motion';
-import { useEffect, useState } from 'react';
-const codeLines = [
- { text: '// Quality score: 0.00 - 1.00', delay: 0.5 },
- { text: "const tier = tier === 'rich' ? 0.6 : 0.4;", delay: 0.8 },
- { text: 'const downloads = Math.min(0.2, log10(d));', delay: 1.1 },
- { text: 'const stars = Math.min(0.1, log10(s));', delay: 1.4 },
- { text: '', delay: 1.7 },
- { text: '// Rich = has params, returns, env', delay: 2.0 },
- { text: 'const score = tier + downloads + stars;', delay: 2.3 },
+const executeSchema = [
+ ' toolId: z.string(),',
+ ' params: z.record(z.unknown()),',
+ ' env?: z.record(z.string()),',
];
-function TypewriterLine({ text, delay }: { text: string; delay: number }) {
- const [displayText, setDisplayText] = useState('');
-
- useEffect(() => {
- if (!text) {
- setDisplayText('');
- return;
- }
-
- const timeout = setTimeout(() => {
- let i = 0;
- const interval = setInterval(() => {
- if (i <= text.length) {
- setDisplayText(text.slice(0, i));
- i++;
- } else {
- clearInterval(interval);
- }
- }, 30);
-
- return () => clearInterval(interval);
- }, delay * 1000);
-
- return () => clearTimeout(timeout);
- }, [text, delay]);
-
- // Simple text display without syntax highlighting to avoid dangerouslySetInnerHTML
- const isComment = displayText.startsWith('//');
-
- return (
-
- {displayText || '\u00A0'}
-
- );
-}
+const flowSteps = [
+ { step: '1', text: 'Fetch tool metadata from registry', color: 'cyan' },
+ { step: '2', text: 'Import package from esm.sh', color: 'purple' },
+ { step: '3', text: 'Execute in isolated sandbox', color: 'emerald' },
+ { step: '4', text: 'Return result to agent', color: 'yellow' },
+];
export function IntegrationSlide(): React.ReactElement {
return (
- {/* Background */}
-
+
-
- 📊
-
+ registryExecuteTool
+
+ Run any tool from the registry. No install. No bundle.
+
- Quality Scoring
- Deliberately simple. Not trying to be clever.
+
+ {/* Input */}
+
+ inputSchema
+
+
{'z.object({'}
+ {executeSchema.map((line) => (
+
+ {line}
+
+ ))}
+
{'})'}
+
+
- {/* Code block */}
-
- {/* Window chrome */}
-
+ {/* Flow */}
+
+ execution flow
+
+ {flowSteps.map((item, i) => (
+
+
+ {item.step}
+
+ {item.text}
+
+ ))}
+
+
+
- {/* Code content */}
-
- {codeLines.map((line) => (
-
- ))}
-
-
-
-
- {/* Score factors */}
- {['Tier: 40-60%', 'Downloads: 0-20%', 'Stars: 0-10%', 'Richness: 0-10%'].map((factor) => (
-
- {factor}
-
- ))}
+ Sandbox runs on Railway. 5s timeout. Isolated per execution.
diff --git a/apps/tutorial/src/components/slides/ProblemSlide.tsx b/apps/tutorial/src/components/slides/ProblemSlide.tsx
index 0f4e5ba..6e4cdd5 100644
--- a/apps/tutorial/src/components/slides/ProblemSlide.tsx
+++ b/apps/tutorial/src/components/slides/ProblemSlide.tsx
@@ -2,103 +2,85 @@
import { motion } from 'framer-motion';
-const floatingIcons = [
- { icon: '🔧', x: '15%', y: '20%', delay: 0 },
- { icon: '⚙️', x: '75%', y: '15%', delay: 0.2 },
- { icon: '🔌', x: '85%', y: '60%', delay: 0.4 },
- { icon: '📦', x: '10%', y: '70%', delay: 0.6 },
- { icon: '🛠️', x: '60%', y: '80%', delay: 0.8 },
- { icon: '🔗', x: '25%', y: '45%', delay: 1 },
- { icon: '📡', x: '80%', y: '35%', delay: 1.2 },
- { icon: '💾', x: '40%', y: '25%', delay: 1.4 },
+const codeLines = [
+ "import { weatherTool } from './tools/weather';",
+ "import { searchTool } from './tools/search';",
+ "import { calendarTool } from './tools/calendar';",
+ "import { emailTool } from './tools/email';",
+ '// ... 47 more imports',
+ '',
+ 'const result = await generateText({',
+ " model: openai('gpt-4-turbo'),",
+ ' tools: { weather, search, calendar, email, ... },',
+ '});',
];
export function ProblemSlide(): React.ReactElement {
return (
- {/* Floating chaotic icons */}
- {floatingIcons.map((item) => (
-
- {item.icon}
-
- ))}
+
- {/* Red warning gradient */}
-
-
- {/* Content */}
-
- 😵
-
-
- The Problem
-
-
- npm has 2 million packages .
-
- Which ones are AI-callable tools? Which ones{' '}
- actually work ?
+
AI SDK Tools Are Static
+
+ You import them. You bundle them. They're compiled in.
+ {/* Code block */}
+
+
+
+ {codeLines.map((line, i) => (
+
+ {line || '\u00A0'}
+
+ ))}
+
+
+
- {["Can't find them", "Can't trust them", "Can't compare them", 'No schemas'].map(
- (word, i) => (
- (
+
- {word}
-
+ {text}
+
)
)}
diff --git a/apps/tutorial/src/components/slides/QualitySlide.tsx b/apps/tutorial/src/components/slides/QualitySlide.tsx
index e671548..8f71873 100644
--- a/apps/tutorial/src/components/slides/QualitySlide.tsx
+++ b/apps/tutorial/src/components/slides/QualitySlide.tsx
@@ -1,137 +1,106 @@
'use client';
-import { animate, motion, useMotionValue } from 'framer-motion';
-import { useEffect, useState } from 'react';
+import { motion } from 'framer-motion';
-function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number }) {
- const progress = useMotionValue(0);
- const [displayValue, setDisplayValue] = useState(0);
-
- useEffect(() => {
- const timeout = setTimeout(() => {
- animate(progress, value, {
- duration: 1.5,
- ease: 'easeOut',
- });
- }, delay * 1000);
-
- return () => clearTimeout(timeout);
- }, [progress, value, delay]);
-
- useEffect(() => {
- const unsubscribe = progress.on('change', (v) => setDisplayValue(Math.round(v)));
- return unsubscribe;
- }, [progress]);
-
- const circumference = 2 * Math.PI * 45;
- const strokeDashoffset = circumference - (displayValue / 100) * circumference;
-
- return (
-
-
- {/* Background circle */}
-
- {/* Progress circle */}
-
-
-
-
-
-
-
-
-
- {displayValue}%
-
-
- );
-}
+const registryData = [
+ { field: 'toolId', value: '"@tpmjs/markdown::formatTable"', color: 'cyan' },
+ { field: 'description', value: '"Format markdown tables"', color: 'white' },
+ { field: 'inputSchema', value: '{ type: "object", ... }', color: 'purple' },
+ { field: 'returnSchema', value: '{ type: "string" }', color: 'purple' },
+ { field: 'envKeys', value: '[]', color: 'yellow' },
+ { field: 'qualityScore', value: '0.92', color: 'emerald' },
+];
const qualityFactors = [
- { name: 'Import Health', icon: '📦', score: 95, color: 'cyan', desc: 'Can we require() it?' },
- { name: 'Execution Health', icon: '▶️', score: 88, color: 'emerald', desc: 'Does it run at all?' },
- { name: 'HEALTHY', icon: '✅', score: 100, color: 'emerald', desc: 'Passed both checks' },
- { name: 'BROKEN', icon: '❌', score: 0, color: 'yellow', desc: 'Labeled, not hidden' },
+ { name: 'Schema Completeness', desc: 'Input/output types defined' },
+ { name: 'npm Downloads', desc: 'Community adoption signal' },
+ { name: 'Health Status', desc: 'Can it import and execute?' },
];
export function QualitySlide(): React.ReactElement {
return (
- {/* Background */}
-
- ⭐
-
-
- Health Checks
-
- We don't hide broken tools. We label them .
+
What Gets Indexed
+
+ Schemas extracted. Quality scored. Health checked.
- {/* Main gauge */}
-
-
-
+
+ {/* Registry entry */}
+
+ registry entry
+
+
{'{'}
+ {registryData.map((item) => (
+
+ {item.field}:
+
+ {item.value}
+
+
+ ))}
+
{'}'}
+
+
- {/* Quality factors */}
-
- {qualityFactors.map((factor, index) => (
-
- {factor.icon}
- {factor.name}
- {factor.desc}
-
+ {/* Quality factors */}
+
+ qualityScore factors
+
+ {qualityFactors.map((factor, i) => (
-
-
- ))}
+ key={factor.name}
+ initial={{ opacity: 0, x: 10 }}
+ animate={{ opacity: 1, x: 0 }}
+ transition={{ delay: 0.7 + i * 0.1 }}
+ className="p-3 rounded-lg bg-white/5 border border-white/10"
+ >
+
{factor.name}
+
{factor.desc}
+
+ ))}
+
+
+
+
+ Synced from npm every 2 minutes. 200+ tools indexed.
+
);
diff --git a/apps/tutorial/src/components/slides/SolutionSlide.tsx b/apps/tutorial/src/components/slides/SolutionSlide.tsx
index 8837ca7..8035f8c 100644
--- a/apps/tutorial/src/components/slides/SolutionSlide.tsx
+++ b/apps/tutorial/src/components/slides/SolutionSlide.tsx
@@ -2,84 +2,82 @@
import { motion } from 'framer-motion';
-const gridItems = Array.from({ length: 12 }, (_, i) => ({
- icon: ['🔧', '⚙️', '📦', '🔌', '🛠️', '📡', '💾', '🔗', '🎯', '⚡', '🌐', '🔐'][i],
- delay: i * 0.05,
-}));
+const codeLines = [
+ "import { searchTpmjsToolsTool } from '@tpmjs/search-registry';",
+ "import { registryExecuteTool } from '@tpmjs/registry-execute';",
+ '',
+ 'const result = await generateText({',
+ " model: openai('gpt-4-turbo'),",
+ ' tools: { search: searchTpmjsToolsTool, execute: registryExecuteTool },',
+ " prompt: 'Format this markdown table for me...',",
+ '});',
+];
export function SolutionSlide(): React.ReactElement {
return (
- {/* Green success gradient */}
- {/* Content */}
+
+ What If Tools Were Dynamic?
+
+ Two imports. Infinite tools at runtime.
+
+ {/* Code block */}
- ✨
-
-
- What TPMJS Is
-
-
- A registry that indexes npm packages
- for AI tool use.
-
- Extracts schemas from code . Scores
- quality. Checks health.
-
-
- {/* Organized grid of icons */}
-
- {gridItems.map((item) => (
-
- {item.icon}
-
- ))}
+
+
+ {codeLines.map((line, i) => (
+
+ {line || '\u00A0'}
+
+ ))}
+
- {['Registry', 'Schema extraction', 'Quality scoring', 'Health checks'].map((word, i) => (
- (
+
- {word}
-
+ {text}
+
))}
diff --git a/apps/tutorial/src/components/slides/ToolDetailSlide.tsx b/apps/tutorial/src/components/slides/ToolDetailSlide.tsx
index a3f8bd9..0881da1 100644
--- a/apps/tutorial/src/components/slides/ToolDetailSlide.tsx
+++ b/apps/tutorial/src/components/slides/ToolDetailSlide.tsx
@@ -2,111 +2,107 @@
import { motion } from 'framer-motion';
+const roleStyles: Record
= {
+ user: 'bg-white/10 text-white/60',
+ agent: 'bg-cyan-500/20 text-cyan-400',
+ registry: 'bg-emerald-500/20 text-emerald-400',
+ sandbox: 'bg-yellow-500/20 text-yellow-400',
+};
+
+const colorStyles: Record = {
+ white: 'text-white/70',
+ cyan: 'text-cyan-400',
+ purple: 'text-purple-400',
+ emerald: 'text-emerald-400',
+ yellow: 'text-yellow-400',
+};
+
+const conversation = [
+ {
+ id: 'user-ask',
+ role: 'user',
+ content: '"Format this CSV as a markdown table"',
+ color: 'white',
+ },
+ {
+ id: 'agent-search',
+ role: 'agent',
+ content: 'searchTpmjsToolsTool({ query: "markdown table" })',
+ color: 'cyan',
+ },
+ {
+ id: 'registry-found',
+ role: 'registry',
+ content: '→ Found: @tpmjs/markdown::formatTable',
+ color: 'emerald',
+ },
+ {
+ id: 'agent-execute',
+ role: 'agent',
+ content: 'registryExecuteTool({ toolId: "...", params: {...} })',
+ color: 'purple',
+ },
+ {
+ id: 'sandbox-result',
+ role: 'sandbox',
+ content: '→ Executed in 120ms',
+ color: 'yellow',
+ },
+ {
+ id: 'agent-respond',
+ role: 'agent',
+ content: '"Here\'s your formatted table: ..."',
+ color: 'white',
+ },
+];
+
export function ToolDetailSlide(): React.ReactElement {
return (
- {/* Background */}
- What We Store
-
- Everything an agent needs to call a tool correctly
+
Full Agent Flow
+
+ User asks. Agent searches. Tool executes. Done.
- {/* Mock tool card */}
-
- {/* Header */}
-
-
-
-
- 🔍
-
-
-
webSearch
- @tpmjs/web-tools
-
-
-
+
+ {conversation.map((msg, i) => (
- Verified
+
+ {msg.role}
+
+
+ {msg.content}
+
+ ))}
+
+
+
+
+ The agent never imported formatTable .
+ It discovered and executed it at runtime.
-
- {/* Description */}
-
- Search the web and return structured results. Supports query filtering, result limits,
- and domain restrictions.
-
-
- {/* Metadata grid */}
-
-
-
-
-
-
- {/* Schema preview */}
-
- {'// Extracted from actual code'}
-
- inputSchema: JSON Schema
-
-
- returnSchema: JSON Schema
-
-
- envKeys: ["API_KEY"]
-
-
- tier: "rich"
-
-
diff --git a/apps/tutorial/src/components/slides/WelcomeSlide.tsx b/apps/tutorial/src/components/slides/WelcomeSlide.tsx
index 86b6188..0c6b6f7 100644
--- a/apps/tutorial/src/components/slides/WelcomeSlide.tsx
+++ b/apps/tutorial/src/components/slides/WelcomeSlide.tsx
@@ -4,76 +4,53 @@ import { motion } from 'framer-motion';
export function WelcomeSlide(): React.ReactElement {
return (
-
- {/* Animated background orbs */}
-
-
-
-
+
+ {/* Subtle gradient background */}
+
- {/* Logo / Title */}
+ {/* Main content */}
-
-
- TPMJS
-
-
-
-
- {/* Tagline */}
-
- The missing layer between "LLMs can call tools" and "which tool,
- exactly?"
-
+
+
+ TPMJS
+
+
- {/* Decorative line */}
-
+
+ A Tool Registry for the AI SDK
+
- {/* CTA hint */}
+
+ Dynamic tool discovery and execution for agents
+
+
+
+ {/* Press to continue */}
-
Press any key to begin
+
Press any key
);