content: update tutorial slides with grounded TPMJS content

- WelcomeSlide: "The missing layer between npm and AI agents"
- ProblemSlide: "npm has 2 million packages. Which ones work?"
- SolutionSlide: Registry that extracts schemas, scores quality, checks health
- HowItWorksSlide: Automated pipeline flow
- DiscoverySlide: Schema extraction from sandbox
- IntegrationSlide: Quality scoring algorithm
- QualitySlide: Health checks (import + execution)
- ToolDetailSlide: What we store (inputSchema, returnSchema, envKeys, tier)
- GetStartedSlide: CTAs with accurate descriptions

All content now grounded in actual codebase functionality.
Includes TPMJS_TALK.md as source of truth document.
This commit is contained in:
Ajax Davis 2025-12-29 11:40:39 +10:00
parent b279be2a25
commit 70d0c5ba94
10 changed files with 532 additions and 121 deletions

391
TPMJS_TALK.md Normal file
View file

@ -0,0 +1,391 @@
# TPMJS: The Missing Layer Between "LLMs Can Call Tools" and "Which Tool, Exactly?"
---
## The Setup
You're building an AI agent. It needs to do things in the world—scrape a webpage, send an email, query a database, generate an image. These capabilities come from **tools**.
The problem isn't that tools don't exist. They do. Thousands of them. The problem is:
- **You can't find them.** npm has 2 million packages. Which ones are AI-callable tools? Which ones actually work?
- **You can't trust them.** No schema. No examples. README says "AI-ready" but the function signature is `(opts: any) => Promise<any>`.
- **You can't compare them.** Three packages do "web scraping." Which one handles JavaScript rendering? Which one returns structured data? Which one is maintained?
Discovery is the bottleneck. Not capability—discovery.
---
## What TPMJS Actually Is
TPMJS is infrastructure. Specifically:
1. **A registry** that indexes npm packages designed for AI tool use
2. **A metadata extraction pipeline** that pulls schemas directly from code
3. **A quality scoring system** that ranks tools by completeness and adoption
4. **A health monitoring system** that verifies tools actually work
5. **A playground** where you can test tools before integrating them
It's not magic. It's plumbing. Good plumbing.
---
## How It Works (The Technical Reality)
### Discovery: Finding Tools in the Wild
TPMJS runs three automated sync jobs:
**1. npm Changes Feed (every 2 minutes)**
```
npm registry → /_changes endpoint → filter for tpmjs-tool keyword → process
```
This catches new packages and updates in near-real-time. We track sequence numbers so we never reprocess.
**2. Keyword Search (every 15 minutes)**
```
npm search "tpmjs-tool" → up to 250 results → validate → ingest
```
Backup mechanism. Catches anything the changes feed missed.
**3. Metrics Sync (hourly)**
```
for each package → fetch download stats → recalculate quality scores → update health status
```
Keeps the registry fresh.
### The Publisher Contract
To get indexed, a package needs two things:
```json
{
"name": "@acme/my-tool",
"keywords": ["tpmjs-tool"],
"tpmjs": {
"category": "web-scraping",
"description": "Scrapes URLs and returns structured markdown"
}
}
```
That's the minimum. Category + description. Everything else is either optional or auto-extracted.
**Categories are fixed** (12 total): web-scraping, data-processing, file-operations, communication, database, api-integration, image-processing, text-analysis, automation, ai-ml, security, monitoring.
Why fixed? Because agents need to filter. "Give me all database tools" has to mean something.
### Schema Extraction: The Hard Part
Here's what makes TPMJS different from a glorified npm search.
When we ingest a package, we don't just read the README. We **execute it in a sandbox** and extract the actual schema:
```
1. Spin up isolated executor (Railway)
2. npm install the package
3. Import and inspect exports
4. Extract JSON Schema from TypeScript types
5. Store schema in database
```
The result:
```json
{
"name": "scrapeUrl",
"inputSchema": {
"type": "object",
"properties": {
"url": { "type": "string", "format": "uri" },
"waitForSelector": { "type": "string" },
"timeout": { "type": "number", "default": 30000 }
},
"required": ["url"]
}
}
```
This isn't documentation. This is **extracted from the actual function signature**. It's ground truth.
If the author provides a schema in the `tpmjs` field, we use that. If not, we extract it. Either way, every tool in the registry has a schema.
### Quality Scoring: Ranking What Matters
Every tool gets a score from 0.00 to 1.00:
```typescript
// Base score from metadata completeness
const tierScore = tier === 'rich' ? 0.6 : 0.4;
// Adoption signals
const downloadsScore = Math.min(0.2, Math.log10(downloads + 1) / 15);
const starsScore = Math.min(0.1, Math.log10(githubStars + 1) / 10);
// Metadata richness bonus
let richnessScore = 0;
if (hasParameters) richnessScore += 0.04;
if (hasReturns) richnessScore += 0.03;
if (hasEnvVars) richnessScore += 0.03;
```
**Tier** is binary:
- **Minimal**: Just category + description (40% base)
- **Rich**: Has parameters, returns, env vars, or framework tags (60% base)
The formula is deliberately simple. We're not trying to be clever. We're trying to surface tools that are well-documented and actually used.
### Health Checks: Does It Actually Work?
Two checks, run during sync and periodically:
**1. Import Health**
```
Can we require() this package without it exploding?
```
You'd be surprised how many npm packages fail this.
**2. Execution Health**
```
Can we call the main function with minimal parameters without throwing?
```
Not a full test suite. Just "does it run at all?"
Results: `HEALTHY`, `BROKEN`, or `UNKNOWN`.
Broken tools still appear in the registry (with a warning). We don't hide them—we label them.
---
## The Data Model
Here's what we actually store:
### Package (npm package level)
```
npmPackageName (unique)
npmVersion, npmDescription, npmRepository, npmLicense
npmKeywords[], npmReadme, npmAuthor
category (enum)
tier ('minimal' | 'rich')
discoveryMethod ('changes-feed' | 'keyword')
npmDownloadsLastMonth, githubStars
frameworks[] (vercel-ai, langchain, etc.)
env[] (required environment variables)
```
### Tool (individual callable within a package)
```
packageId (FK)
name (export name: "scrapeUrl", "default", etc.)
description
inputSchema (JSON Schema)
schemaSource ('extracted' | 'author')
qualityScore (0.00-1.00)
importHealth, executionHealth (HEALTHY | BROKEN | UNKNOWN)
toolDiscoverySource ('auto' | 'manual')
```
One package can have multiple tools. `@acme/web-tools` might export `scrapeUrl`, `screenshotPage`, and `extractLinks`. Each is a separate tool with its own schema and health status.
### Simulation (playground execution)
```
toolId
userPrompt (what the user asked)
parameters (JSON, what was passed to the tool)
status (pending | running | success | error | timeout)
executionTimeMs, output, error
model, agentSteps
```
We track every playground execution. Not for surveillance—for debugging and improving the system.
---
## The API
### Search & Discovery
```
GET /api/tools
?q=scrape
&category=web-scraping
&importHealth=HEALTHY
&executionHealth=HEALTHY
&limit=20
&offset=0
→ Returns tools sorted by quality score
```
```
GET /api/tools/search
?q=I need to extract text from PDFs
→ BM25-ranked semantic search
```
### Execution
```
POST /api/tools/execute/{toolId}
{
"prompt": "Scrape the homepage of Hacker News",
"parameters": { "url": "https://news.ycombinator.com" }
}
→ Server-Sent Events stream with:
- Agent reasoning steps
- Tool call results
- Final output
```
Rate limited: 10 requests per IP per hour. We're not a free compute platform.
### Schema Operations
```
POST /api/tools/extract-schema
{ "packageName": "@acme/my-tool", "toolName": "scrapeUrl" }
→ Forces re-extraction of schema from source
```
---
## The Playground
A Next.js app where you can:
1. **Browse tools** by category, health status, quality score
2. **Inspect schemas** before you commit to anything
3. **Test execution** with an AI agent
4. **See real responses** with actual latency and token usage
It's not a demo. It's a debugging tool. "Does this tool do what I think it does?" Answer that question in 30 seconds instead of 30 minutes.
---
## What This Enables
### For Engineers Building Agents
Before TPMJS:
```
1. Search npm for "web scraper"
2. Get 500 results
3. Click through 20 of them
4. Read READMEs that say "easy to use!"
5. npm install three of them
6. Write test code for each
7. Find out two are broken
8. Pick the one that works
9. Hope it keeps working
```
After TPMJS:
```
1. Search tpmjs.com for "web scraper"
2. Filter by HEALTHY status
3. Sort by quality score
4. Click top result
5. See exact input schema
6. Test in playground
7. Integrate
```
### For Tool Authors
Before TPMJS:
```
Publish to npm → hope someone finds it → no visibility into usage
```
After TPMJS:
```
Publish to npm with tpmjs-tool keyword → indexed within 2 minutes →
schema auto-extracted → quality scored → discoverable by search →
execution stats tracked
```
Your tool becomes findable. Not just by humans grepping npm, but by agents querying the registry API.
### For Agents (Yes, Really)
Agents can query TPMJS at runtime:
```typescript
const tools = await fetch('https://tpmjs.com/api/tools?' + new URLSearchParams({
q: 'send email',
executionHealth: 'HEALTHY',
limit: '5'
})).then(r => r.json());
// Agent now has 5 working email tools with full schemas
// It can pick the best one for this specific task
```
This is the endgame. Not humans browsing a registry—agents dynamically selecting tools based on capability, health, and fit.
---
## What TPMJS Is Not
**Not a package manager.** We don't host packages. npm does that. We index and enrich.
**Not an execution platform.** The playground runs tools for testing. Production execution is your responsibility.
**Not a security guarantee.** We check if tools work. We don't audit them for malice. Same rules as npm: don't run untrusted code.
**Not magic.** We're not using AI to understand what tools do. We're extracting schemas and running health checks. Boring, reliable, debuggable.
---
## The Technical Stack
- **Database**: PostgreSQL via Prisma
- **Web**: Next.js 16 (App Router)
- **Deployment**: Vercel (web) + Railway (sandbox executor)
- **Sync**: Vercel Cron + GitHub Actions backup
- **AI**: Vercel AI SDK for playground execution
- **Monorepo**: Turborepo + pnpm
Key internal packages:
- `@tpmjs/npm-client` — npm registry integration
- `@tpmjs/package-executor` — sandbox execution client
- `@tpmjs/types` — schema validation and migration
- `@tpmjs/db` — Prisma client and models
---
## Current State
- **~100 tools indexed** (and growing with every npm publish)
- **12 categories** covering most agent use cases
- **Automated sync** running 24/7
- **Health checks** on every tool
- **Schema extraction** working for TypeScript and JavaScript
- **Playground** functional for testing
---
## The Pitch (Finally)
Tools are the API surface of AI agents. The ecosystem is a mess. TPMJS is the index.
We don't compete with npm—we sit on top of it. We don't replace tool authors—we make them discoverable. We don't build agents—we give agents a way to find their tools.
Discovery is the bottleneck. We're fixing discovery.
---
## Try It
- **Browse**: https://tpmjs.com/tool-search
- **Playground**: https://tpmjs.com/playground
- **Publish**: Add `tpmjs-tool` keyword + `tpmjs` field to your package.json
- **API**: `GET https://tpmjs.com/api/tools`
---
*Tools are inevitable. Discovery chaos isn't.*

View file

@ -24,55 +24,54 @@ export function DiscoverySlide(): React.ReactElement {
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }} transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8" className="text-6xl mb-8"
> >
🔍
</motion.div> </motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Two Users, One Surface</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Schema Extraction</h2>
<p className="text-xl text-white/40 mb-12"> <p className="text-xl text-white/40 mb-12">
Same registry. <span className="text-cyan-400 font-semibold">Different clients.</span> The hard part.{' '}
<span className="text-cyan-400 font-semibold">We run your code in a sandbox.</span>
</p> </p>
{/* Two user types */} {/* Code block showing extraction */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-8 max-w-4xl mx-auto"> <motion.div
<motion.div initial={{ opacity: 0, y: 20 }}
initial={{ opacity: 0, x: -30 }} animate={{ opacity: 1, y: 0 }}
animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.5 }}
transition={{ delay: 0.5 }} className="max-w-2xl mx-auto p-6 rounded-xl bg-[#1e1e2e] text-left font-mono text-sm"
className="p-6 rounded-2xl bg-white/5 border border-white/10 text-left" >
> <div className="text-white/40 mb-3">
<div className="text-4xl mb-4">👨💻</div> {/* comment */}
<div className="text-2xl font-bold text-cyan-400 mb-3">Engineers browsing</div> {'// We extract this from your actual code:'}
<ul className="space-y-2 text-white/60"> </div>
<li>&quot;Show me the best extraction tools&quot;</li> <div className="text-purple-400">inputSchema: {'{'}</div>
<li>&quot;I need one that supports screenshots&quot;</li> <div className="text-white/80 pl-4">
<li>&quot;I need maintained + documented&quot;</li> type: <span className="text-emerald-400">&quot;object&quot;</span>,
</ul> </div>
</motion.div> <div className="text-white/80 pl-4">properties: {'{'}</div>
<div className="text-white/80 pl-8">
<motion.div url: {'{'} type: <span className="text-emerald-400">&quot;string&quot;</span>, format:{' '}
initial={{ opacity: 0, x: 30 }} <span className="text-emerald-400">&quot;uri&quot;</span> {'}'}
animate={{ opacity: 1, x: 0 }} </div>
transition={{ delay: 0.7 }} <div className="text-white/80 pl-8">
className="p-6 rounded-2xl bg-white/5 border border-white/10 text-left" timeout: {'{'} type: <span className="text-emerald-400">&quot;number&quot;</span>,
> default: <span className="text-cyan-400">30000</span> {'}'}
<div className="text-4xl mb-4">🤖</div> </div>
<div className="text-2xl font-bold text-purple-400 mb-3">Agents selecting</div> <div className="text-white/80 pl-4">{'}'}</div>
<ul className="space-y-2 text-white/60"> <div className="text-purple-400">{'}'}</div>
<li>&quot;I need to scrape a URL into markdown&quot;</li> </motion.div>
<li>&quot;I need to summarize a PDF with citations&quot;</li>
<li>&quot;I need a company research tool&quot;</li>
</ul>
</motion.div>
</div>
{/* Key insight */} {/* Key insight */}
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 1.2 }} transition={{ delay: 1.2 }}
className="mt-12 text-white/40 text-lg" className="mt-8 text-white/40 text-lg"
> >
Results are <span className="text-emerald-400 font-semibold">tool-shaped</span>, not just package-shaped. Not documentation.{' '}
<span className="text-emerald-400 font-semibold">
Ground truth from TypeScript types.
</span>
</motion.div> </motion.div>
</motion.div> </motion.div>
</div> </div>

View file

@ -6,21 +6,21 @@ const links = [
{ {
label: 'Browse Tools', label: 'Browse Tools',
href: 'https://tpmjs.com/tool-search', href: 'https://tpmjs.com/tool-search',
description: 'Use as a discovery portal', description: 'Search by name, category, quality',
icon: '🔍', icon: '🔍',
gradient: 'from-cyan-500 to-blue-500', gradient: 'from-cyan-500 to-blue-500',
}, },
{ {
label: 'Publish a Tool', label: 'Publish a Tool',
href: 'https://tpmjs.com/docs/publishing', href: 'https://tpmjs.com/docs/publishing',
description: 'Clean metadata + examples', description: 'Add tpmjs-tool keyword to npm',
icon: '📦', icon: '📦',
gradient: 'from-purple-500 to-pink-500', gradient: 'from-purple-500 to-pink-500',
}, },
{ {
label: 'The Playground', label: 'The Playground',
href: 'https://tpmjs.com/playground', href: 'https://tpmjs.com/playground',
description: 'Try tools before adopting', description: 'Run tools in browser sandbox',
icon: '🎮', icon: '🎮',
gradient: 'from-emerald-500 to-teal-500', gradient: 'from-emerald-500 to-teal-500',
}, },
@ -61,8 +61,10 @@ export function GetStartedSlide(): React.ReactElement {
🚀 🚀
</motion.div> </motion.div>
<h2 className="text-5xl md:text-7xl font-bold text-white mb-4">Try It</h2> <h2 className="text-5xl md:text-7xl font-bold text-white mb-4">Get Started</h2>
<p className="text-xl md:text-2xl text-white/40 mb-16">Discovery chaos is optional</p> <p className="text-xl md:text-2xl text-white/40 mb-16">
Indexed automatically. Updated every 2 minutes.
</p>
{/* CTA buttons */} {/* CTA buttons */}
<div className="flex flex-col md:flex-row items-center justify-center gap-6"> <div className="flex flex-col md:flex-row items-center justify-center gap-6">
@ -100,7 +102,13 @@ export function GetStartedSlide(): React.ReactElement {
initial={{ x: -10 }} initial={{ x: -10 }}
whileHover={{ x: 0 }} whileHover={{ x: 0 }}
> >
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
aria-hidden="true"
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path <path
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
@ -120,7 +128,7 @@ export function GetStartedSlide(): React.ReactElement {
transition={{ delay: 1.2 }} transition={{ delay: 1.2 }}
className="mt-16 text-white/30 text-sm" className="mt-16 text-white/30 text-sm"
> >
Tools are inevitable. Discovery chaos isn&apos;t. The missing layer between npm and AI agents.
</motion.div> </motion.div>
</motion.div> </motion.div>
</div> </div>

View file

@ -3,10 +3,10 @@
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
const steps = [ const steps = [
{ icon: '📝', label: 'What it does', description: 'Description, category, tags' }, { icon: '📦', label: 'npm publish', description: 'Add tpmjs-tool keyword' },
{ icon: '⚙️', label: 'How to call it', description: 'Inputs, schema, examples' }, { icon: '🔄', label: 'Auto-sync', description: 'Indexed in ~2 minutes' },
{ icon: '🔑', label: 'What it needs', description: 'Env vars, auth hints' }, { icon: '⚙️', label: 'Schema extracted', description: 'From actual code' },
{ icon: '📊', label: 'Signals', description: 'Downloads, health, freshness' }, { icon: '✅', label: 'Health checked', description: 'Import + execution' },
]; ];
export function HowItWorksSlide(): React.ReactElement { export function HowItWorksSlide(): React.ReactElement {
@ -21,13 +21,13 @@ export function HowItWorksSlide(): React.ReactElement {
transition={{ duration: 0.8 }} transition={{ duration: 0.8 }}
className="relative z-10 max-w-5xl w-full" className="relative z-10 max-w-5xl w-full"
> >
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">What TPMJS Stores</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-4">How It Works</h2>
<p className="text-xl text-white/40 mb-16">For each tool, we expose the useful stuff</p> <p className="text-xl text-white/40 mb-16">Automated pipeline. No manual curation.</p>
{/* Flow diagram */} {/* Flow diagram */}
<div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-0"> <div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-0">
{steps.map((step, index) => ( {steps.map((step, index) => (
<div key={index} className="flex items-center"> <div key={step.label} className="flex items-center">
{/* Step card */} {/* Step card */}
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
@ -61,6 +61,7 @@ export function HowItWorksSlide(): React.ReactElement {
> >
<div className="w-12 h-px bg-gradient-to-r from-cyan-500/50 to-purple-500/50" /> <div className="w-12 h-px bg-gradient-to-r from-cyan-500/50 to-purple-500/50" />
<svg <svg
aria-hidden="true"
className="w-4 h-4 text-purple-500/50 -ml-1" className="w-4 h-4 text-purple-500/50 -ml-1"
fill="currentColor" fill="currentColor"
viewBox="0 0 20 20" viewBox="0 0 20 20"

View file

@ -4,14 +4,13 @@ import { motion } from 'framer-motion';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
const codeLines = [ const codeLines = [
{ text: '// Faster tool evaluation', delay: 0.5 }, { text: '// Quality score: 0.00 - 1.00', delay: 0.5 },
{ text: 'const tool = await tpmjs.find({', delay: 0.8 }, { text: "const tier = tier === 'rich' ? 0.6 : 0.4;", delay: 0.8 },
{ text: " capability: 'web-scraping',", delay: 1.1 }, { text: 'const downloads = Math.min(0.2, log10(d));', delay: 1.1 },
{ text: " minQuality: 0.8", delay: 1.4 }, { text: 'const stars = Math.min(0.1, log10(s));', delay: 1.4 },
{ text: '});', delay: 1.7 }, { text: '', delay: 1.7 },
{ text: '', delay: 2.0 }, { text: '// Rich = has params, returns, env', delay: 2.0 },
{ text: '// Schema + examples included', delay: 2.2 }, { text: 'const score = tier + downloads + stars;', delay: 2.3 },
{ text: 'console.log(tool.schema, tool.examples);', delay: 2.5 },
]; ];
function TypewriterLine({ text, delay }: { text: string; delay: number }) { function TypewriterLine({ text, delay }: { text: string; delay: number }) {
@ -40,18 +39,15 @@ function TypewriterLine({ text, delay }: { text: string; delay: number }) {
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [text, delay]); }, [text, delay]);
// Syntax highlighting // Simple text display without syntax highlighting to avoid dangerouslySetInnerHTML
const highlightedText = displayText const isComment = displayText.startsWith('//');
.replace(/(import|from|const|await)/g, '<span class="text-purple-400">$1</span>')
.replace(/('.*?')/g, '<span class="text-emerald-400">$1</span>')
.replace(/(\/\/.*)/g, '<span class="text-white/30">$1</span>')
.replace(/(\{|\}|\(|\))/g, '<span class="text-yellow-300">$1</span>');
return ( return (
<div <div
className="font-mono text-sm md:text-base text-white/80 leading-relaxed" className={`font-mono text-sm md:text-base leading-relaxed ${isComment ? 'text-white/30' : 'text-white/80'}`}
dangerouslySetInnerHTML={{ __html: highlightedText || '&nbsp;' }} >
/> {displayText || '\u00A0'}
</div>
); );
} }
@ -73,11 +69,11 @@ export function IntegrationSlide(): React.ReactElement {
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }} transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
className="text-6xl mb-8" className="text-6xl mb-8"
> >
🔌 📊
</motion.div> </motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Immediate Benefits</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Quality Scoring</h2>
<p className="text-xl text-white/40 mb-12">Even if you do nothing else</p> <p className="text-xl text-white/40 mb-12">Deliberately simple. Not trying to be clever.</p>
{/* Code block */} {/* Code block */}
<motion.div <motion.div
@ -96,8 +92,8 @@ export function IntegrationSlide(): React.ReactElement {
{/* Code content */} {/* Code content */}
<div className="bg-[#1e1e2e] p-6 rounded-b-xl text-left overflow-x-auto"> <div className="bg-[#1e1e2e] p-6 rounded-b-xl text-left overflow-x-auto">
{codeLines.map((line, index) => ( {codeLines.map((line) => (
<TypewriterLine key={index} text={line.text} delay={line.delay} /> <TypewriterLine key={line.text || line.delay} text={line.text} delay={line.delay} />
))} ))}
<motion.span <motion.span
animate={{ opacity: [1, 0, 1] }} animate={{ opacity: [1, 0, 1] }}
@ -107,19 +103,19 @@ export function IntegrationSlide(): React.ReactElement {
</div> </div>
</motion.div> </motion.div>
{/* Benefits badges */} {/* Score factors */}
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 3 }} transition={{ delay: 3 }}
className="mt-8 flex flex-wrap justify-center gap-3" className="mt-8 flex flex-wrap justify-center gap-3"
> >
{['Less spelunking', 'Schemas + examples', 'Shared vocabulary', 'Tool visibility'].map((benefit) => ( {['Tier: 40-60%', 'Downloads: 0-20%', 'Stars: 0-10%', 'Richness: 0-10%'].map((factor) => (
<span <span
key={benefit} key={factor}
className="px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400/70 text-sm" className="px-3 py-1 rounded-full bg-cyan-500/10 border border-cyan-500/20 text-cyan-400/70 text-sm font-mono"
> >
{benefit} {factor}
</span> </span>
))} ))}
</motion.div> </motion.div>

View file

@ -17,9 +17,9 @@ export function ProblemSlide(): React.ReactElement {
return ( return (
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center"> <div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
{/* Floating chaotic icons */} {/* Floating chaotic icons */}
{floatingIcons.map((item, index) => ( {floatingIcons.map((item) => (
<motion.div <motion.div
key={index} key={item.icon}
className="absolute text-4xl md:text-5xl opacity-20" className="absolute text-4xl md:text-5xl opacity-20"
initial={{ opacity: 0, scale: 0 }} initial={{ opacity: 0, scale: 0 }}
animate={{ animate={{
@ -73,13 +73,13 @@ export function ProblemSlide(): React.ReactElement {
😵 😵
</motion.div> </motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">Tool Sprawl</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-6">The Problem</h2>
<p className="text-xl md:text-2xl text-white/60 leading-relaxed max-w-2xl"> <p className="text-xl md:text-2xl text-white/60 leading-relaxed max-w-2xl">
In modern agent stacks, tools are the{' '} npm has <span className="text-red-400 font-semibold">2 million packages</span>.
<span className="text-red-400 font-semibold">real product surface area</span>.
<br /> <br />
But discovery is the <span className="text-orange-400 font-semibold">bottleneck</span>. Which ones are AI-callable tools? Which ones{' '}
<span className="text-orange-400 font-semibold">actually work</span>?
</p> </p>
<motion.div <motion.div
@ -88,17 +88,19 @@ export function ProblemSlide(): React.ReactElement {
transition={{ delay: 1 }} transition={{ delay: 1 }}
className="mt-12 flex flex-wrap justify-center gap-4" className="mt-12 flex flex-wrap justify-center gap-4"
> >
{['Dozens of packages', 'Missing schemas', 'Vague READMEs', 'Unknown status'].map((word, i) => ( {["Can't find them", "Can't trust them", "Can't compare them", 'No schemas'].map(
<motion.span (word, i) => (
key={word} <motion.span
initial={{ opacity: 0, y: 20 }} key={word}
animate={{ opacity: 1, y: 0 }} initial={{ opacity: 0, y: 20 }}
transition={{ delay: 1.2 + i * 0.1 }} animate={{ opacity: 1, y: 0 }}
className="px-4 py-2 rounded-full border border-red-500/30 text-red-400/60 text-sm font-mono" transition={{ delay: 1.2 + i * 0.1 }}
> className="px-4 py-2 rounded-full border border-red-500/30 text-red-400/60 text-sm font-mono"
{word} >
</motion.span> {word}
))} </motion.span>
)
)}
</motion.div> </motion.div>
</motion.div> </motion.div>
</div> </div>

View file

@ -28,7 +28,7 @@ function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number })
return ( return (
<div className="relative w-32 h-32"> <div className="relative w-32 h-32">
<svg className="w-full h-full transform -rotate-90"> <svg aria-hidden="true" className="w-full h-full transform -rotate-90">
{/* Background circle */} {/* Background circle */}
<circle cx="64" cy="64" r="45" fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="8" /> <circle cx="64" cy="64" r="45" fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="8" />
{/* Progress circle */} {/* Progress circle */}
@ -58,10 +58,10 @@ function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number })
} }
const qualityFactors = [ const qualityFactors = [
{ name: 'Quality Signals', icon: '📊', score: 95, color: 'cyan', desc: 'Docs, schema, adoption' }, { name: 'Import Health', icon: '📦', score: 95, color: 'cyan', desc: 'Can we require() it?' },
{ name: 'Health Checks', icon: '💚', score: 88, color: 'emerald', desc: 'Imports, exports, runs' }, { name: 'Execution Health', icon: '▶️', score: 88, color: 'emerald', desc: 'Does it run at all?' },
{ name: 'The Playground', icon: '🎮', score: 92, color: 'purple', desc: 'Try before you adopt' }, { name: 'HEALTHY', icon: '✅', score: 100, color: 'emerald', desc: 'Passed both checks' },
{ name: 'Remote Execution', icon: '☁️', score: 85, color: 'yellow', desc: 'Optional sandbox' }, { name: 'BROKEN', icon: '❌', score: 0, color: 'yellow', desc: 'Labeled, not hidden' },
]; ];
export function QualitySlide(): React.ReactElement { export function QualitySlide(): React.ReactElement {
@ -85,8 +85,10 @@ export function QualitySlide(): React.ReactElement {
</motion.div> </motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">The Multipliers</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Health Checks</h2>
<p className="text-xl text-white/40 mb-12">Extra leverage when you&apos;re ready</p> <p className="text-xl text-white/40 mb-12">
We don&apos;t hide broken tools. We <span className="text-yellow-400">label them</span>.
</p>
{/* Main gauge */} {/* Main gauge */}
<motion.div <motion.div

View file

@ -29,14 +29,14 @@ export function SolutionSlide(): React.ReactElement {
</motion.div> </motion.div>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-6">TPMJS</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-6">What TPMJS Is</h2>
<p className="text-xl md:text-2xl text-white/60 leading-relaxed mb-12 max-w-2xl"> <p className="text-xl md:text-2xl text-white/60 leading-relaxed mb-12 max-w-2xl">
A registry that indexes AI-tool packages and exposes{' '} A <span className="text-cyan-400 font-semibold">registry</span> that indexes npm packages
<span className="text-cyan-400 font-semibold">normalized, enriched information</span> for AI tool use.
<br /> <br />
so agents and engineers can{' '} <span className="text-emerald-400 font-semibold">Extracts schemas from code</span>. Scores
<span className="text-emerald-400 font-semibold">discover the right tool faster</span>. quality. Checks health.
</p> </p>
{/* Organized grid of icons */} {/* Organized grid of icons */}
@ -46,9 +46,9 @@ export function SolutionSlide(): React.ReactElement {
transition={{ delay: 0.5 }} transition={{ delay: 0.5 }}
className="grid grid-cols-4 md:grid-cols-6 gap-4 max-w-lg mx-auto" className="grid grid-cols-4 md:grid-cols-6 gap-4 max-w-lg mx-auto"
> >
{gridItems.map((item, index) => ( {gridItems.map((item) => (
<motion.div <motion.div
key={index} key={item.icon}
initial={{ opacity: 0, scale: 0, y: 20 }} initial={{ opacity: 0, scale: 0, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }} animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{ transition={{
@ -70,7 +70,7 @@ export function SolutionSlide(): React.ReactElement {
transition={{ delay: 1.5 }} transition={{ delay: 1.5 }}
className="mt-12 flex flex-wrap justify-center gap-4" className="mt-12 flex flex-wrap justify-center gap-4"
> >
{['Catalog', 'Metadata layer', 'Search surface', 'Consistent contract'].map((word, i) => ( {['Registry', 'Schema extraction', 'Quality scoring', 'Health checks'].map((word, i) => (
<motion.span <motion.span
key={word} key={word}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}

View file

@ -14,8 +14,10 @@ export function ToolDetailSlide(): React.ReactElement {
transition={{ duration: 0.8 }} transition={{ duration: 0.8 }}
className="relative z-10 max-w-4xl w-full" className="relative z-10 max-w-4xl w-full"
> >
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Tool-Shaped Results</h2> <h2 className="text-5xl md:text-6xl font-bold text-white mb-4">What We Store</h2>
<p className="text-xl text-white/40 mb-12">Remove guesswork before you integrate</p> <p className="text-xl text-white/40 mb-12">
Everything an agent needs to <span className="text-cyan-400">call a tool correctly</span>
</p>
{/* Mock tool card */} {/* Mock tool card */}
<motion.div <motion.div
@ -91,15 +93,18 @@ export function ToolDetailSlide(): React.ReactElement {
transition={{ delay: 1 }} transition={{ delay: 1 }}
className="p-4 rounded-lg bg-black/30 font-mono text-sm" className="p-4 rounded-lg bg-black/30 font-mono text-sm"
> >
<div className="text-white/40 mb-2">// Input Schema</div> <div className="text-white/40 mb-2">{'// Extracted from actual code'}</div>
<div className="text-cyan-400"> <div className="text-purple-400">
query: <span className="text-white/60">string</span> inputSchema: <span className="text-white/60">JSON Schema</span>
</div> </div>
<div className="text-cyan-400"> <div className="text-purple-400">
limit?: <span className="text-white/60">number</span> returnSchema: <span className="text-white/60">JSON Schema</span>
</div> </div>
<div className="text-cyan-400"> <div className="text-purple-400">
domains?: <span className="text-white/60">string[]</span> envKeys: <span className="text-white/60">[&quot;API_KEY&quot;]</span>
</div>
<div className="text-purple-400">
tier: <span className="text-emerald-400">&quot;rich&quot;</span>
</div> </div>
</motion.div> </motion.div>
</motion.div> </motion.div>

View file

@ -56,7 +56,8 @@ export function WelcomeSlide(): React.ReactElement {
transition={{ duration: 0.8, delay: 0.5 }} transition={{ duration: 0.8, delay: 0.5 }}
className="mt-6 text-2xl md:text-3xl text-white/60 font-light max-w-2xl" className="mt-6 text-2xl md:text-3xl text-white/60 font-light max-w-2xl"
> >
Tool Discovery for AI Agents The missing layer between &quot;LLMs can call tools&quot; and &quot;which tool,
exactly?&quot;
</motion.p> </motion.p>
{/* Decorative line */} {/* Decorative line */}
@ -79,7 +80,13 @@ export function WelcomeSlide(): React.ReactElement {
transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }} transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }}
className="text-white/40" className="text-white/40"
> >
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
aria-hidden="true"
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path <path
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"