Commit graph

45 commits

Author SHA1 Message Date
Thomas Davis
2a2f0f487d fix(railway): remove startCommand and invalid maxRetries from railway.toml
startCommand overrides Dockerfile CMD causing build failure. Removed
restartPolicyMaxRetries=-1 which is not a valid Railway value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 04:55:04 +10:00
Thomas Davis
198f9f7d1e fix: separate sync discovery from enrichment and harden Railway executor
Sync system was timing out because discovery endpoints (keyword, changes)
also ran schema extraction (~10-15s per tool). Now discovery is fast
(npm metadata + DB writes only) and a new /api/sync/enrich endpoint
handles schema extraction in time-budgeted chunks.

Railway executor was crashing without restarting due to unhandled promise
rejections, no restart policy, and no health checks. Added crash
protection, graceful shutdown, cache size limits, railway.toml with
ALWAYS restart policy, and upgraded Deno from 1.39 to 2.1.9.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 04:49:54 +10:00
Ajax Davis
48c41e8733 feat: add use cases marketing product with AI-generated content
Transform qualifying scenarios into marketing-ready use cases with:
- AI-generated titles, descriptions, ROI estimates, business value
- Persona/industry/category taxonomy for targeting
- Browseable feed with filtering and ranking
- SEO-optimized case study pages
- Daily cron job for generation and ranking

Database:
- Add Persona, Industry, Category lookup tables
- Add UseCase model with marketing content fields
- Add junction tables for personas/industries/categories
- Add SocialProof model for cached metrics

API:
- GET /api/use-cases - Global directory with filtering
- GET /api/use-cases/[id] - Individual use case details
- GET /api/public/users/[username]/collections/[slug]/use-cases
- POST /api/cron/use-cases - Nightly generation job

Frontend:
- /use-cases - Global feed with persona dropdown
- /use-cases/[slug] - SEO case study page
- /[username]/collections/[slug]/use-cases - Collection feed
- UseCasesFeed component - Sortable table component
- UseCaseCaseStudy component - Full case study layout
2026-01-20 16:17:35 +10:00
Ajax Davis
cae05f1504 fix(executor): use ephemeral cache dir instead of volume
Switch from /data volume to /tmp/deno-cache to avoid persistent permission issues.
2026-01-15 09:30:02 +10:00
Ajax Davis
44606e2b78 fix(executor): fix volume permissions on startup
Adds startup script that fixes /data volume ownership before starting Deno.
This resolves permission errors after cache clear operations.
2026-01-15 09:25:00 +10:00
Ajax Davis
045ad36d14 feat(executor): install openssh-client for SSH-based tools
Tools like exe-dev require SSH to communicate with external services.
Installs openssh-client in the Deno container.
2026-01-15 08:47:57 +10:00
Ajax Davis
c5eca8fdeb feat(executor): add --allow-run flag to Deno executor
Enables tools like exe-dev that require shell execution (SSH commands) to work in the sandbox environment.
2026-01-15 08:42:46 +10:00
Ajax Davis
2cd2b10cd0 refactor: replace exportName with name throughout codebase
- Update TpmjsToolDefinitionSchema to only use 'name' field
- Add 'sandbox' as valid category for sprites tools
- Update all package.json files to use 'name' instead of 'exportName'
- Update documentation and source files accordingly
- Add 11 new sprites tools for sandbox/code-execution
2026-01-14 14:18:36 +10:00
Ajax Davis
5c9f1a1d0a chore: update dependencies with compatibility fixes
- Update all packages to latest versions via pnpm update --latest
- Downgrade Prisma 7 to 6 (v7 requires schema migration)
- Downgrade Tailwind CSS 4 to 3 (v4 requires PostCSS migration)
- Downgrade Storybook 10 to 8 (addons not available in v10)
- Pin cheerio to 1.0.0-rc.12 via pnpm override (type exports changed)
- Fix AI SDK tool definitions: parameters -> inputSchema
- Fix cheerio types in extract-meta and table-extract tools
- Add explicit type annotations to tool execute functions
- Migrate biome config to v2.3.11 schema

All type-checks, tests, and builds pass.
2026-01-09 22:49:41 +10:00
Ajax Davis
f8740fb7ff fix: add biome-ignore comments and config overrides for lint issues
- Add ToolSearchResult interface with biome-ignore for dynamic tool types
- Add biome-ignore comments for UIMessage.parts type casting
- Add biome config overrides for complexity warnings in MessageBubble.tsx
- Add biome config override for MobileMenu.tsx a11y rule
- Add biome config override for railway-executor complexity
- Fix unused template literal in railway-executor
- All lint tasks now pass with 0 errors

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-02 01:51:46 +10:00
Ajax Davis
ae5b3e6249 fix: use z.toJSONSchema for Zod v4 schema conversion
Import zod@4 directly and use z.toJSONSchema method with fallback
to z.default.toJSONSchema for compatibility.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-29 23:29:06 +10:00
Ajax Davis
c2d244a5c9 fix: use dynamic import for Zod v4 JSON schema conversion
Static import from 'zod@4/json-schema' was failing and crashing executor.
Use dynamic import inside the schema extraction logic instead.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-29 23:25:40 +10:00
Ajax Davis
1c8b516db8 fix: add Zod v4 schema extraction support in Railway executor
- Import Zod v4's toJSONSchema from zod@4/json-schema
- Add Strategy 3: Detect Zod v4 schemas via ._zod property
- Convert Zod v4 schemas using native toJSONSchema function
- Update error message to mention both Zod v4 and v3 support

Fixes schema extraction for @tpmjs/emoji-magic and other Zod v4 tools

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-29 23:21:05 +10:00
Ajax Davis
e84eda7525 refactor: rename exportName to name across entire codebase
- Database: Migrate column export_name to name in tools table
- Prisma schema: Update Tool model to use name field
- Sync routes: Update keyword and changes sync to use name
- Railway executor: Update API endpoints to use name parameter
- API routes: Update all tool routes to use name field
- Web app: Update all pages and components
- Playground: Update tool loader and sidebar
- create-basic-tools: Update types and generators
- Scripts: Update sync and test scripts

Database migration was done via direct SQL:
  ALTER TABLE tools RENAME COLUMN export_name TO name;

The unique constraint remains on (package_id, name).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-17 17:19:21 +10:00
Ajax Davis
5fc584fc66 feat(tpmjs-spec): add auto-discovery of tools and rename exportName to name
Major changes to the TPMJS specification:

1. Auto-Discovery: The `tools` array is now optional. If omitted, TPMJS
   automatically scans package exports and registers any export with
   `description` and `execute` properties (standard AI SDK tool format).

2. Renamed `exportName` to `name` in tool definitions for cleaner spec.

3. Added `/list-exports` endpoint to Railway executor that:
   - Lists all exports from a package
   - Identifies valid AI SDK tools
   - Extracts descriptions for auto-discovered tools

4. Added `toolDiscoverySource` field to track 'auto' vs 'manual' discovery.

5. Updated tool page UI with:
   - Auto-discovery warning banner
   - Badge showing discovery source

6. Updated all documentation pages (docs, spec, publish) to reflect:
   - Optional tools array with auto-discovery
   - Use of `name` instead of `exportName`
   - Auto-extraction of schema and description

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-17 14:02:26 +10:00
Ajax Davis
00441c30db feat(railway-executor): add 2-minute TTL cache for esm.sh modules
- Add TTL-based module cache (2 minutes) for imported esm.sh packages
- Cache non-factory tools to avoid re-downloading on each request
- Factory functions are cached but always re-imported to get fresh env vars
- Automatic cleanup of expired cache entries every minute
- Enhanced /cache/stats endpoint shows TTL info and expiration times
2025-12-17 11:42:12 +10:00
Ajax Davis
feeb3cf3e8 refactor: move schema updates to executor
- Executor now updates TPM.js database directly when loading tools
- Update /api/tools/update-schema to use packageName+exportName lookup
- Remove schema update logic from HLLM proxy (no longer needed)
2025-12-15 13:07:42 +10:00
Ajax Davis
9642f0b7e7 fix: disable tool caching entirely to ensure fresh env vars
Temporary fix - always re-import tools so env vars are fresh each request
2025-12-15 12:36:26 +10:00
Ajax Davis
8a79396a0a fix: inject env vars before factory calls and skip caching factory tools
- Move env var injection to happen BEFORE cache check and factory function calls
- This ensures process.env is set when factory functions like Valyu's webSearch() read from it
- Skip caching factory-created tools since they may read env vars at creation time
- Fixes issue where Valyu tools fail with 'VALYU_API_KEY is required' even when key is provided
2025-12-15 12:34:22 +10:00
Ajax Davis
8f27d1cfa7 fix(executor): declare packageName/exportName before try block
Same issue as startTime - these variables were destructured inside the
try block but referenced in the catch block for health reporting. If
JSON parsing or any early error occurred, the catch block would crash
with 'packageName is not defined'.

Now declares them with 'unknown' defaults before try, then assigns
the actual values inside.
2025-12-12 06:14:08 +10:00
Ajax Davis
c8e590419e fix(executor): move startTime declaration before try block
The startTime variable was declared inside the try block but referenced
in the catch block, causing 'startTime is not defined' errors when
exceptions occurred before line 404 (e.g., during req.json() parsing).

Moving the declaration before the try ensures it's in scope for the
catch block's executionTimeMs calculation.
2025-12-12 05:53:47 +10:00
Ajax Davis
1ae6923d1a refactor(health): move health reporting to Railway executor
Health status is now reported from the executor - the single point where
all tools run. This ensures consistent health tracking regardless of
client (playground, direct API, etc).

- Add reportToolHealth() to Railway executor
- Report success/failure after every tool execution
- Remove health reporting from playground (executor handles it)
- Executor calls /api/tools/report-health which has all the logic
2025-12-12 05:23:27 +10:00
Ajax Davis
e09596a7fa fix(executor): pass execution context with abortSignal to tool execute()
Some AI SDK tools (like @parallel-web/ai-sdk-tools) expect execute(params, context)
where context contains { abortSignal, messages, toolCallId }. Previously we only
passed params which caused 'Cannot destructure abortSignal' errors.

Also:
- Improved playground system prompt for better tool execution
- Playground /api/tools now proxies to web app with response transformation
- Added broken-tools.md documenting tool failure categories

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 04:43:20 +10:00
Ajax Davis
31ff2fa87f fix: remove nonexistent deno.json from Dockerfile 2025-12-05 01:10:42 +10:00
Ajax Davis
ade550dcf4 fix: use manual volume at /data for Deno cache
Simplified Docker configuration to use manually mounted volume at /data
instead of complex pre-caching strategy. This approach:

- Uses ENV DENO_DIR=/data to point to manually created Railway volume
- Keeps Dockerfile simple and maintainable
- Adds --allow-read/--allow-write permissions for cache access
- Removes railway.toml volume configuration (manual setup instead)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 01:05:07 +10:00
Ajax Davis
d843d7b78d Revert "feat: add persistent Deno cache to Railway executor for faster tool loading"
This reverts commit eb8a5ff91e.
2025-12-05 01:04:39 +10:00
Ajax Davis
47577a43e5 feat: add persistent Deno cache to Railway executor for faster tool loading
- Set DENO_DIR=/app/.deno_cache to persist module cache
- Pre-cache common dependencies (zod-to-json-schema, ai, zod) during build
- Add Railway volume configuration for /app/.deno_cache
- Improve logging to show cache hits vs network downloads
- Add --allow-read and --allow-write permissions for cache access

This dramatically reduces tool loading time after the first import.
Dependencies are downloaded once and reused across all subsequent requests.

Example: ctx-zip with 200+ dependencies will only download once instead
of on every chat request.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 00:50:10 +10:00
Ajax Davis
89564af030 fix: sanitize invalid JSON schemas with type 'None' to valid object schemas
Add sanitizeJsonSchema() function to fix common schema issues:
- Replaces invalid type 'None' (common in Python tools) with 'object'
- Ensures all schemas have a valid type field
- Recursively sanitizes nested schemas in properties, items, anyOf/oneOf/allOf
- Prevents OpenAI API errors from malformed tool schemas

This fixes the error with @superagent-ai/ai-sdk guard tool which
returns type: 'None' instead of a valid JSON Schema type.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 18:02:10 +10:00
Ajax Davis
6676f01a2d Revert "feat: add Node.js compatibility layer to Railway executor"
This reverts commit 8562eb5b38.
2025-12-04 17:55:25 +10:00
Ajax Davis
ac22733cfc fix: support AI SDK jsonSchema() with .jsonSchema property
Added Strategy 2.5 to schema extraction to handle AI SDK v6's
jsonSchema() wrapper which uses `.jsonSchema` property instead
of `.schema`.

This fixes schema validation errors for @tpmjs/hello and other
packages that use jsonSchema() wrapper.

Error was: "No valid schema found" with keys ["_type", "jsonSchema", "validate"]
Fix: Check for inputSchema.jsonSchema as an object property

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 17:44:26 +10:00
Ajax Davis
dd8fceb0d5 fix: set env vars in both Deno.env and process.env for Node.js compat
The Node.js compatibility layer (npm: specifier) broke environment
variable passing because tools imported via npm: expect process.env,
not Deno.env.

Root cause: Recent commit added npm: specifier for Node compatibility,
but env injection code only set Deno.env.set(), not process.env.

Fix: Set environment variables in BOTH locations:
- Deno.env.set() for esm.sh imports
- globalThis.process.env for npm: imports

This restores functionality for tools like Firecrawl that require
API keys via environment variables.

Fixes regression from commit 8562eb5.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 17:29:42 +10:00
Ajax Davis
cf10a73e5a feat: add Node.js compatibility layer to Railway executor
Add multi-strategy import system to support Node.js packages in Deno:

1. Primary: Use npm: specifier for Node.js compatibility mode
2. Fallback: Use esm.sh with explicit esnext target

This allows packages like ai-sdk-tool-code-execution that depend on
Node.js built-ins (node:sqlite, undici) to work in the Deno runtime.

Also added deno.json with nodeModulesDir and BYONM support.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:18:38 +10:00
Ajax Davis
9b9f0f0859 feat: add factory function support to Railway tool executor
Detects and handles tools exported as factory functions that require
configuration before returning the actual AI SDK tool object.

Supports multiple factory patterns:
1. No-args factory: toolName()
2. Config object: toolName({ apiKey: 'xxx' })
3. Single-arg: toolName('api-key-value')

For config objects, tries multiple key name variations:
- Raw env vars: { VALYU_API_KEY: 'xxx' }
- Normalized apiKey: { apiKey: 'xxx' }
- Normalized key: { key: 'xxx' }

This enables dynamic loading of tools like @valyu/ai-sdk paperSearch
that use factory patterns instead of direct tool exports.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:52:47 +10:00
Ajax Davis
6e7c75dd14 fix: use function body in DefaultChatTransport to send latest env vars
PROBLEM:
DefaultChatTransport body is cloned ONCE on mount, so env vars
were always empty {} even after localStorage loaded them.

SOLUTION:
Use a function for body instead of an object. AI SDK v6 calls
body() on each request, ensuring latest env vars are sent.

Changes:
- useChat.ts: body: { env } → body: () => ({ env: buildEnvObject() })
- buildEnvObject() is called fresh on each request
- Env vars now sent correctly to /api/chat

Credit: ChatGPT for identifying the exact AI SDK v6 pattern

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:25:59 +10:00
Ajax Davis
377f90989d fix: properly pass env vars to cached tools and add extensive logging
PROBLEM:
- Tool wrappers cached env vars in closure, so cached tools used stale env
- Client env vars weren't reaching Railway executor even when provided
- No visibility into env var flow through the system

SOLUTION:
1. Store env vars per conversation in conversationEnv Map
2. Tool execute functions look up latest env from Map (not closure)
3. Chat API calls setConversationEnv() on each request
4. Added logging at every step

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:18:32 +10:00
Ajax Davis
8441b5fc70 fix: use deployed /api/tools endpoint with client-side filtering and streamline git hooks
- Change search-registry to use /api/tools instead of /api/tools/search (not deployed yet)
- Add client-side filtering for search queries since deployed API doesn't support search
- Handle both deployed (/api/tools) and local dev (/api/tools/search) response formats
- Remove lint from pre-commit hooks to speed up commits (keep format + type-check)
- Fixes 404 errors when playground tries to search tools in production

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 13:43:19 +10:00
Ajax Davis
1c949f6a11 feat: add playground sidebars with dynamic tools and env var management
Left Sidebar:
- Create /api/tools endpoint to fetch tools from registry
- Update ToolsSidebar to fetch and display tools dynamically
- Add filter input for searching tools by name/description/category
- Fix interface to use packageName/exportName from search registry

Right Sidebar:
- Create SettingsSidebar with environment variable management
- Add localStorage persistence for env vars
- Implement password masking for values
- Export useEnvVars() hook for accessing env vars

Environment Variable Forwarding:
- Update useChat hook to read and forward env vars to API
- Update chat route to extract env vars from request body
- Update dynamic-tool-loader to accept and forward env vars
- Update Railway executor to inject env vars into Deno environment
- Complete chain: localStorage → client → chat → Railway → Deno.env

Bug Fixes:
- Fix undefined property errors in tool detail page
- Add optional chaining for npmDownloadsLastMonth and qualityScore

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 11:00:20 +10:00
Ajax Davis
9aa7a1e2be feat: add Zod v3 schema support via zod-to-json-schema
- Import zod-to-json-schema from esm.sh
- Add Strategy 3: detect Zod schemas via _def property
- Convert Zod v3/v4 schemas to JSON Schema
- Update error message to mention Zod v3 support
- Fixes firecrawl-aisdk tool schema extraction
2025-12-04 09:49:39 +10:00
Ajax Davis
ec27965ab4 feat: add fallback schema extraction with Zod v4 support
- Try Zod v4 toJSONSchema() or jsonSchema() first
- Fall back to AI SDK jsonSchema.schema property
- Fail gracefully with detailed debug info
- Supports both Zod-based and jsonSchema-based tools
2025-12-04 09:42:53 +10:00
Ajax Davis
956f9fdbae debug: add logging for inputSchema structure inspection 2025-12-04 09:33:09 +10:00
Ajax Davis
bb672edf15 fix: extract and serialize JSON Schema from AI SDK v6 tools correctly
THE BREAKTHROUGH: AI SDK v6 tools use jsonSchema() which wraps plain JSON Schema objects, NOT Zod schemas. JSON Schema is fully serializable.

Changes:
1. Railway server: Extract raw JSON Schema from toolModule.inputSchema?.schema
2. Playground loader: Wrap received JSON Schema with { type: 'json_schema', schema: ... }
3. This matches AI SDK v6 format exactly - no Zod serialization needed

How it works:
- Tools define inputSchema: jsonSchema({ type: 'object', properties: {...} })
- AI SDK stores it as { type: 'json_schema', schema: {...} }
- Railway extracts the plain JSON Schema (.schema property)
- Sends it as plain JSON (fully serializable)
- Playground wraps it back in AI SDK format
- OpenAI receives valid JSON Schema for function calling

This fixes both errors:
 No more "def.shape is not a function" (not using Zod)
 No more "Invalid schema type None" (proper JSON Schema provided)

Note: Tools using Zod instead of jsonSchema() will need to migrate.
TPMJS standard: All tools MUST use jsonSchema() with plain JSON Schema.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 09:00:00 +10:00
Ajax Davis
287d9b97c9 docs: document Zod schema serialization problem for external consultation 2025-12-04 08:53:26 +10:00
Ajax Davis
b7e6a6b2dc fix: use Dockerfile instead of Nixpacks for Railway deployment
Railway is deprecating Nixpacks, so switching to a standard Dockerfile
with the official Deno image. This provides a cleaner and more maintainable
deployment configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 08:35:50 +10:00
Ajax Davis
ba893d646a fix: add nixpacks.toml to configure Deno for Railway deployment
Railway's Nixpacks builder needs explicit configuration to install Deno.
This adds nixpacks.toml to specify Deno as a required package.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 08:30:19 +10:00
Ajax Davis
d597a71eb4 feat: add dynamic tool loading system with Railway executor
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>
2025-12-04 08:25:53 +10:00