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:
parent
20f5fcc4b0
commit
bda49615fb
10 changed files with 532 additions and 121 deletions
391
TPMJS_TALK.md
Normal file
391
TPMJS_TALK.md
Normal 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.*
|
||||
|
|
@ -24,55 +24,54 @@ export function DiscoverySlide(): React.ReactElement {
|
|||
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
|
||||
className="text-6xl mb-8"
|
||||
>
|
||||
🔍
|
||||
⚙️
|
||||
</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">
|
||||
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>
|
||||
|
||||
{/* Two user types */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-8 max-w-4xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="p-6 rounded-2xl bg-white/5 border border-white/10 text-left"
|
||||
>
|
||||
<div className="text-4xl mb-4">👨💻</div>
|
||||
<div className="text-2xl font-bold text-cyan-400 mb-3">Engineers browsing</div>
|
||||
<ul className="space-y-2 text-white/60">
|
||||
<li>"Show me the best extraction tools"</li>
|
||||
<li>"I need one that supports screenshots"</li>
|
||||
<li>"I need maintained + documented"</li>
|
||||
</ul>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.7 }}
|
||||
className="p-6 rounded-2xl bg-white/5 border border-white/10 text-left"
|
||||
>
|
||||
<div className="text-4xl mb-4">🤖</div>
|
||||
<div className="text-2xl font-bold text-purple-400 mb-3">Agents selecting</div>
|
||||
<ul className="space-y-2 text-white/60">
|
||||
<li>"I need to scrape a URL into markdown"</li>
|
||||
<li>"I need to summarize a PDF with citations"</li>
|
||||
<li>"I need a company research tool"</li>
|
||||
</ul>
|
||||
</motion.div>
|
||||
</div>
|
||||
{/* Code block showing extraction */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="max-w-2xl mx-auto p-6 rounded-xl bg-[#1e1e2e] text-left font-mono text-sm"
|
||||
>
|
||||
<div className="text-white/40 mb-3">
|
||||
{/* comment */}
|
||||
{'// We extract this from your actual code:'}
|
||||
</div>
|
||||
<div className="text-purple-400">inputSchema: {'{'}</div>
|
||||
<div className="text-white/80 pl-4">
|
||||
type: <span className="text-emerald-400">"object"</span>,
|
||||
</div>
|
||||
<div className="text-white/80 pl-4">properties: {'{'}</div>
|
||||
<div className="text-white/80 pl-8">
|
||||
url: {'{'} type: <span className="text-emerald-400">"string"</span>, format:{' '}
|
||||
<span className="text-emerald-400">"uri"</span> {'}'}
|
||||
</div>
|
||||
<div className="text-white/80 pl-8">
|
||||
timeout: {'{'} type: <span className="text-emerald-400">"number"</span>,
|
||||
default: <span className="text-cyan-400">30000</span> {'}'}
|
||||
</div>
|
||||
<div className="text-white/80 pl-4">{'}'}</div>
|
||||
<div className="text-purple-400">{'}'}</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Key insight */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
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>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,21 +6,21 @@ const links = [
|
|||
{
|
||||
label: 'Browse Tools',
|
||||
href: 'https://tpmjs.com/tool-search',
|
||||
description: 'Use as a discovery portal',
|
||||
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: 'Clean metadata + examples',
|
||||
description: 'Add tpmjs-tool keyword to npm',
|
||||
icon: '📦',
|
||||
gradient: 'from-purple-500 to-pink-500',
|
||||
},
|
||||
{
|
||||
label: 'The Playground',
|
||||
href: 'https://tpmjs.com/playground',
|
||||
description: 'Try tools before adopting',
|
||||
description: 'Run tools in browser sandbox',
|
||||
icon: '🎮',
|
||||
gradient: 'from-emerald-500 to-teal-500',
|
||||
},
|
||||
|
|
@ -61,8 +61,10 @@ export function GetStartedSlide(): React.ReactElement {
|
|||
🚀
|
||||
</motion.div>
|
||||
|
||||
<h2 className="text-5xl md:text-7xl font-bold text-white mb-4">Try It</h2>
|
||||
<p className="text-xl md:text-2xl text-white/40 mb-16">Discovery chaos is optional</p>
|
||||
<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">
|
||||
Indexed automatically. Updated every 2 minutes.
|
||||
</p>
|
||||
|
||||
{/* CTA buttons */}
|
||||
<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 }}
|
||||
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
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -120,7 +128,7 @@ export function GetStartedSlide(): React.ReactElement {
|
|||
transition={{ delay: 1.2 }}
|
||||
className="mt-16 text-white/30 text-sm"
|
||||
>
|
||||
Tools are inevitable. Discovery chaos isn't.
|
||||
The missing layer between npm and AI agents.
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
import { motion } from 'framer-motion';
|
||||
|
||||
const steps = [
|
||||
{ icon: '📝', label: 'What it does', description: 'Description, category, tags' },
|
||||
{ icon: '⚙️', label: 'How to call it', description: 'Inputs, schema, examples' },
|
||||
{ icon: '🔑', label: 'What it needs', description: 'Env vars, auth hints' },
|
||||
{ icon: '📊', label: 'Signals', description: 'Downloads, health, freshness' },
|
||||
{ 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' },
|
||||
];
|
||||
|
||||
export function HowItWorksSlide(): React.ReactElement {
|
||||
|
|
@ -21,13 +21,13 @@ export function HowItWorksSlide(): React.ReactElement {
|
|||
transition={{ duration: 0.8 }}
|
||||
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>
|
||||
<p className="text-xl text-white/40 mb-16">For each tool, we expose the useful stuff</p>
|
||||
<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">Automated pipeline. No manual curation.</p>
|
||||
|
||||
{/* Flow diagram */}
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-0">
|
||||
{steps.map((step, index) => (
|
||||
<div key={index} className="flex items-center">
|
||||
<div key={step.label} className="flex items-center">
|
||||
{/* Step card */}
|
||||
<motion.div
|
||||
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" />
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="w-4 h-4 text-purple-500/50 -ml-1"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ import { motion } from 'framer-motion';
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
const codeLines = [
|
||||
{ text: '// Faster tool evaluation', delay: 0.5 },
|
||||
{ text: 'const tool = await tpmjs.find({', delay: 0.8 },
|
||||
{ text: " capability: 'web-scraping',", delay: 1.1 },
|
||||
{ text: " minQuality: 0.8", delay: 1.4 },
|
||||
{ text: '});', delay: 1.7 },
|
||||
{ text: '', delay: 2.0 },
|
||||
{ text: '// Schema + examples included', delay: 2.2 },
|
||||
{ text: 'console.log(tool.schema, tool.examples);', delay: 2.5 },
|
||||
{ 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 },
|
||||
];
|
||||
|
||||
function TypewriterLine({ text, delay }: { text: string; delay: number }) {
|
||||
|
|
@ -40,18 +39,15 @@ function TypewriterLine({ text, delay }: { text: string; delay: number }) {
|
|||
return () => clearTimeout(timeout);
|
||||
}, [text, delay]);
|
||||
|
||||
// Syntax highlighting
|
||||
const highlightedText = displayText
|
||||
.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>');
|
||||
// Simple text display without syntax highlighting to avoid dangerouslySetInnerHTML
|
||||
const isComment = displayText.startsWith('//');
|
||||
|
||||
return (
|
||||
<div
|
||||
className="font-mono text-sm md:text-base text-white/80 leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedText || ' ' }}
|
||||
/>
|
||||
className={`font-mono text-sm md:text-base leading-relaxed ${isComment ? 'text-white/30' : 'text-white/80'}`}
|
||||
>
|
||||
{displayText || '\u00A0'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -73,11 +69,11 @@ export function IntegrationSlide(): React.ReactElement {
|
|||
transition={{ type: 'spring', stiffness: 200, delay: 0.2 }}
|
||||
className="text-6xl mb-8"
|
||||
>
|
||||
🔌
|
||||
📊
|
||||
</motion.div>
|
||||
|
||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">Immediate Benefits</h2>
|
||||
<p className="text-xl text-white/40 mb-12">Even if you do nothing else</p>
|
||||
<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">Deliberately simple. Not trying to be clever.</p>
|
||||
|
||||
{/* Code block */}
|
||||
<motion.div
|
||||
|
|
@ -96,8 +92,8 @@ export function IntegrationSlide(): React.ReactElement {
|
|||
|
||||
{/* Code content */}
|
||||
<div className="bg-[#1e1e2e] p-6 rounded-b-xl text-left overflow-x-auto">
|
||||
{codeLines.map((line, index) => (
|
||||
<TypewriterLine key={index} text={line.text} delay={line.delay} />
|
||||
{codeLines.map((line) => (
|
||||
<TypewriterLine key={line.text || line.delay} text={line.text} delay={line.delay} />
|
||||
))}
|
||||
<motion.span
|
||||
animate={{ opacity: [1, 0, 1] }}
|
||||
|
|
@ -107,19 +103,19 @@ export function IntegrationSlide(): React.ReactElement {
|
|||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Benefits badges */}
|
||||
{/* Score factors */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 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
|
||||
key={benefit}
|
||||
className="px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400/70 text-sm"
|
||||
key={factor}
|
||||
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>
|
||||
))}
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ export function ProblemSlide(): React.ReactElement {
|
|||
return (
|
||||
<div className="relative flex flex-col items-center justify-center h-full px-8 text-center">
|
||||
{/* Floating chaotic icons */}
|
||||
{floatingIcons.map((item, index) => (
|
||||
{floatingIcons.map((item) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
key={item.icon}
|
||||
className="absolute text-4xl md:text-5xl opacity-20"
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
animate={{
|
||||
|
|
@ -73,13 +73,13 @@ export function ProblemSlide(): React.ReactElement {
|
|||
😵
|
||||
</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">
|
||||
In modern agent stacks, tools are the{' '}
|
||||
<span className="text-red-400 font-semibold">real product surface area</span>.
|
||||
npm has <span className="text-red-400 font-semibold">2 million packages</span>.
|
||||
<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>
|
||||
|
||||
<motion.div
|
||||
|
|
@ -88,17 +88,19 @@ export function ProblemSlide(): React.ReactElement {
|
|||
transition={{ delay: 1 }}
|
||||
className="mt-12 flex flex-wrap justify-center gap-4"
|
||||
>
|
||||
{['Dozens of packages', 'Missing schemas', 'Vague READMEs', 'Unknown status'].map((word, i) => (
|
||||
<motion.span
|
||||
key={word}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
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>
|
||||
))}
|
||||
{["Can't find them", "Can't trust them", "Can't compare them", 'No schemas'].map(
|
||||
(word, i) => (
|
||||
<motion.span
|
||||
key={word}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
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>
|
||||
)
|
||||
)}
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number })
|
|||
|
||||
return (
|
||||
<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 */}
|
||||
<circle cx="64" cy="64" r="45" fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="8" />
|
||||
{/* Progress circle */}
|
||||
|
|
@ -58,10 +58,10 @@ function AnimatedGauge({ value, delay = 0 }: { value: number; delay?: number })
|
|||
}
|
||||
|
||||
const qualityFactors = [
|
||||
{ name: 'Quality Signals', icon: '📊', score: 95, color: 'cyan', desc: 'Docs, schema, adoption' },
|
||||
{ name: 'Health Checks', icon: '💚', score: 88, color: 'emerald', desc: 'Imports, exports, runs' },
|
||||
{ name: 'The Playground', icon: '🎮', score: 92, color: 'purple', desc: 'Try before you adopt' },
|
||||
{ name: 'Remote Execution', icon: '☁️', score: 85, color: 'yellow', desc: 'Optional sandbox' },
|
||||
{ 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' },
|
||||
];
|
||||
|
||||
export function QualitySlide(): React.ReactElement {
|
||||
|
|
@ -85,8 +85,10 @@ export function QualitySlide(): React.ReactElement {
|
|||
⭐
|
||||
</motion.div>
|
||||
|
||||
<h2 className="text-5xl md:text-6xl font-bold text-white mb-4">The Multipliers</h2>
|
||||
<p className="text-xl text-white/40 mb-12">Extra leverage when you're ready</p>
|
||||
<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">
|
||||
We don't hide broken tools. We <span className="text-yellow-400">label them</span>.
|
||||
</p>
|
||||
|
||||
{/* Main gauge */}
|
||||
<motion.div
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ export function SolutionSlide(): React.ReactElement {
|
|||
✨
|
||||
</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">
|
||||
A registry that indexes AI-tool packages and exposes{' '}
|
||||
<span className="text-cyan-400 font-semibold">normalized, enriched information</span>
|
||||
A <span className="text-cyan-400 font-semibold">registry</span> that indexes npm packages
|
||||
for AI tool use.
|
||||
<br />
|
||||
so agents and engineers can{' '}
|
||||
<span className="text-emerald-400 font-semibold">discover the right tool faster</span>.
|
||||
<span className="text-emerald-400 font-semibold">Extracts schemas from code</span>. Scores
|
||||
quality. Checks health.
|
||||
</p>
|
||||
|
||||
{/* Organized grid of icons */}
|
||||
|
|
@ -46,9 +46,9 @@ export function SolutionSlide(): React.ReactElement {
|
|||
transition={{ delay: 0.5 }}
|
||||
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
|
||||
key={index}
|
||||
key={item.icon}
|
||||
initial={{ opacity: 0, scale: 0, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
transition={{
|
||||
|
|
@ -70,7 +70,7 @@ export function SolutionSlide(): React.ReactElement {
|
|||
transition={{ delay: 1.5 }}
|
||||
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
|
||||
key={word}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@ export function ToolDetailSlide(): React.ReactElement {
|
|||
transition={{ duration: 0.8 }}
|
||||
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>
|
||||
<p className="text-xl text-white/40 mb-12">Remove guesswork before you integrate</p>
|
||||
<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">
|
||||
Everything an agent needs to <span className="text-cyan-400">call a tool correctly</span>
|
||||
</p>
|
||||
|
||||
{/* Mock tool card */}
|
||||
<motion.div
|
||||
|
|
@ -91,15 +93,18 @@ export function ToolDetailSlide(): React.ReactElement {
|
|||
transition={{ delay: 1 }}
|
||||
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-cyan-400">
|
||||
query: <span className="text-white/60">string</span>
|
||||
<div className="text-white/40 mb-2">{'// Extracted from actual code'}</div>
|
||||
<div className="text-purple-400">
|
||||
inputSchema: <span className="text-white/60">JSON Schema</span>
|
||||
</div>
|
||||
<div className="text-cyan-400">
|
||||
limit?: <span className="text-white/60">number</span>
|
||||
<div className="text-purple-400">
|
||||
returnSchema: <span className="text-white/60">JSON Schema</span>
|
||||
</div>
|
||||
<div className="text-cyan-400">
|
||||
domains?: <span className="text-white/60">string[]</span>
|
||||
<div className="text-purple-400">
|
||||
envKeys: <span className="text-white/60">["API_KEY"]</span>
|
||||
</div>
|
||||
<div className="text-purple-400">
|
||||
tier: <span className="text-emerald-400">"rich"</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ export function WelcomeSlide(): React.ReactElement {
|
|||
transition={{ duration: 0.8, delay: 0.5 }}
|
||||
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 "LLMs can call tools" and "which tool,
|
||||
exactly?"
|
||||
</motion.p>
|
||||
|
||||
{/* Decorative line */}
|
||||
|
|
@ -79,7 +80,13 @@ export function WelcomeSlide(): React.ReactElement {
|
|||
transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'easeInOut' }}
|
||||
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
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue