Implements a complete dynamic tool loading system that allows the playground to discover and load tools from the TPMJS registry at runtime. **Architecture:** - Search tool package (@tpmjs/search-registry) - Searches registry for tools - Search API endpoint (/api/tools/search) - Text-based search with scoring - Pre-flight tool loading - Automatically searches and loads tools on every message - Railway executor service (Deno) - Loads tools from esm.sh via HTTP imports - Dynamic tool loader - Calls Railway to load and execute tools remotely **Key Components:** 1. Railway Executor (apps/railway-executor/) - Deno-based service that natively supports HTTP imports - Endpoints: /load-and-describe, /execute-tool, /cache/stats, /cache/clear - Deploys to Railway with deno run --allow-net --allow-env server.ts 2. Search Tool Package (packages/tools/search-registry/) - AI SDK v6 tool for searching TPMJS registry - Uses jsonSchema + inputSchema pattern - Searches /api/tools/search endpoint 3. Search API (apps/web/src/app/api/tools/search/) - Text-based search with composite scoring - Scores: text relevance + quality boost + download boost - Returns tool metadata with importUrl for dynamic loading 4. Dynamic Tool Loader (apps/playground/src/lib/dynamic-tool-loader.ts) - Calls Railway service to load tools from esm.sh - Creates tool wrappers that execute remotely - Process-level module cache + per-conversation tracking 5. Pre-flight Loading (apps/playground/src/app/api/chat/route.ts) - Automatically searches for tools on every user message - Loads top 5 matching tools before agent processes request - Merges with static tools for seamless experience **Technical Decisions:** - Deno over Node.js: Native HTTP import support without flags - Remote execution: Tools run in Railway sandbox, not Vercel - Pre-flight loading: Better UX than two-turn search pattern - Text search: BM25 had dependency issues, simple scoring works well **Environment Variables:** - RAILWAY_SERVICE_URL: https://endearing-commitment-production.up.railway.app 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|---|---|---|
| .changeset | ||
| .claude/commands | ||
| .github/workflows | ||
| .vscode | ||
| apps | ||
| packages | ||
| scripts | ||
| services/sandbox-executor | ||
| supabase | ||
| .dependency-cruiser.js | ||
| .gitignore | ||
| .npmrc | ||
| .nvmrc | ||
| 2025-BEST-PRACTICES.md | ||
| ai-sdk-v6.md | ||
| API_ROUTES_TIMEOUT_INVESTIGATION.md | ||
| biome.json | ||
| CLAUDE.md | ||
| convert-label-tests.py | ||
| DEPLOYMENT.md | ||
| DYNAMIC_IMPORT_ISSUE.md | ||
| DYNAMIC_TOOL_LOADING_PRD.md | ||
| HOW_TO_PUBLISH_A_TOOL.md | ||
| IMPLEMENTATION_CHECKLIST.md | ||
| IMPLEMENTATION_STATUS.md | ||
| knip.json | ||
| lefthook.yml | ||
| manual-tools.ts | ||
| MANUAL_TOOLS.md | ||
| NPM_MIRROR.md | ||
| OPENAI_SCHEMA_ERROR.md | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| QUALITY-GATES.md | ||
| RAILWAY_DEPLOYMENT_NOTE.md | ||
| RAILWAY_DYNAMIC_TOOL_LOADER.md | ||
| README.md | ||
| reset.d.ts | ||
| STREAMING_EMPTY_RESPONSE.md | ||
| sync-hello.ts | ||
| sync-manual-tools.ts | ||
| test-db.mjs | ||
| test-schema.ts | ||
| tsconfig.json | ||
| turbo.json | ||
| USECHAT_INPUT_UNDEFINED.md | ||
| vercel.json | ||
| VERCEL_FIX_CHECKLIST.md | ||
TPMJS Monorepo
Tool Package Manager for AI Agents - A Turborepo monorepo with strict TypeScript, Next.js 16, and best practices.
Structure
apps/
web/ - Next.js 16 App Router application
packages/
config/ - Shared configurations (Biome, ESLint, Tailwind, TypeScript)
ui/ - React component library (.ts-only, no barrels)
utils/ - Utility functions
types/ - Shared TypeScript types
env/ - Zod environment schema loader
test/ - Vitest shared configuration
mocks/ - MSW mock server
storybook/ - Storybook documentation
Getting Started
Prerequisites
- Node.js >= 22 (LTS)
- pnpm >= 8
- nvm (recommended for Node version management)
Installation
pnpm install
Development
# Run all apps in development mode
pnpm dev
# Run specific app
pnpm --filter @tpmjs/web dev
pnpm --filter @tpmjs/storybook dev
Building
# Build all packages and apps
pnpm build
# Build specific package
pnpm --filter @tpmjs/ui build
Testing
# Run all tests
pnpm test
# Run tests in watch mode with UI
pnpm test:ui
Linting & Formatting
# Lint all packages
pnpm lint
# Format all files
pnpm format
# Check formatting
pnpm format:check
Quality Gates
# Check architecture/dependency rules
pnpm check-architecture
# Find unused code and dependencies
pnpm find-deadcode
# Check type coverage
pnpm type-coverage
See QUALITY-GATES.md for details.
Component Usage
Components are imported directly without barrel exports:
import { Button } from '@tpmjs/ui/Button/Button';
import { Card, CardHeader } from '@tpmjs/ui/Card/Card';
Important: All UI components use .ts extension (not .tsx) and use createElement instead of JSX.
Publishing Workflow
1. Create Changesets
After making changes to publishable packages:
pnpm changeset
Follow the prompts to describe your changes and select which packages are affected.
2. Version Packages
When ready to release:
pnpm changeset:version
This updates package versions and generates CHANGELOGs.
3. Publish to npm
pnpm changeset:publish
This builds and publishes all packages with changesets to npm.
4. Push to GitHub
git push --follow-tags
Published Packages
@tpmjs/ui- React component library@tpmjs/utils- Utility functions@tpmjs/types- TypeScript types@tpmjs/env- Environment schema loader
Deployment
The project is configured to only deploy to Vercel when all CI checks pass. This ensures production always has high-quality, tested code.
CI Checks:
- Linting & formatting
- Type checking
- Tests
- Production build
- Architecture validation
- Dead code detection
See DEPLOYMENT.md for full configuration details.
Module Boundaries
ESLint enforces module boundaries:
- Apps can import from published packages only
- Packages cannot import from apps
- No barrel exports (
index.ts) allowed - Direct imports required:
@tpmjs/ui/Button/Button
Architecture Decisions
Why .ts-only Components?
Using .ts instead of .tsx for React components:
- Enforces explicit
createElementcalls - Makes React's runtime nature more visible
- Prevents JSX spreading anti-patterns
- Better for code generation and tooling
Why No Barrel Exports?
- Clearer dependency graphs
- Better tree-shaking
- Explicit imports show what's actually used
- Prevents circular dependencies
Why Biome + ESLint?
- Biome: Fast formatting and basic linting
- ESLint: Semantic rules (module boundaries, TypeScript strictness)
- Each tool focuses on what it does best
Scripts Reference
dev- Start development serversbuild- Build all packagestest- Run teststest:ui- Run tests with UIlint- Lint codeformat- Format code with Biomeformat:check- Check formattingtype-check- TypeScript type checkingtype-coverage- Check type coverage (no implicit any)check-architecture- Validate dependency rulesfind-deadcode- Find unused code/dependenciesclean- Remove build artifactschangeset- Create a changesetchangeset:version- Version packageschangeset:publish- Publish to npm
License
MIT