cult-film-curtis

Cult Film Curtis - Lucid Agent

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cult-film-curtis" with this command: npx skills add daydreamsai/skills-market/daydreamsai-skills-market-cult-film-curtis

Cult Film Curtis - Lucid Agent

Movie-loving connoisseur that provides cult film recommendations for micropayments using TMDB API.

Prerequisites

  • Bun runtime

  • TMDB API key (free)

Environment Variables

export EVM_PRIVATE_KEY="0x..." export PAYMENTS_RECEIVABLE_ADDRESS="0x..." export FACILITATOR_URL="https://x402.org/facilitator" export NETWORK="base" export TMDB_API_KEY="your_tmdb_api_key"

Implementation

package.json

{ "name": "cult-film-curtis", "type": "module", "scripts": { "dev": "bun run --hot src/index.ts", "start": "bun run src/index.ts" }, "dependencies": { "@lucid-agents/core": "latest", "@lucid-agents/http": "latest", "@lucid-agents/hono": "latest", "@lucid-agents/payments": "latest", "hono": "^4.0.0", "zod": "^4.0.0" } }

src/index.ts

import { createAgent } from '@lucid-agents/core'; import { http } from '@lucid-agents/http'; import { createAgentApp } from '@lucid-agents/hono'; import { payments, paymentsFromEnv } from '@lucid-agents/payments'; import { z } from 'zod';

const TMDB_API_KEY = process.env.TMDB_API_KEY; if (!TMDB_API_KEY) { throw new Error('TMDB_API_KEY environment variable is required'); } const TMDB_BASE = 'https://api.themoviedb.org/3';

// Cult film keyword IDs from TMDB const CULT_KEYWORDS = [9717, 12339, 156218]; // cult-film, midnight-movie, b-movie

interface TMDBMovie { id: number; title: string; release_date: string; overview: string; vote_average: number; poster_path: string | null; genre_ids: number[]; }

// Cache to avoid rate limits const cache = new Map<string, { data: any; timestamp: number }>(); const CACHE_TTL = 300_000; // 5 minutes

async function fetchWithCache<T>(key: string, fetcher: () => Promise<T>): Promise<T> { const cached = cache.get(key); if (cached && Date.now() - cached.timestamp < CACHE_TTL) { return cached.data; } const data = await fetcher(); cache.set(key, { data, timestamp: Date.now() }); return data; }

async function fetchCultFilms(page = 1): Promise<TMDBMovie[]> { try { const keywordId = CULT_KEYWORDS[Math.floor(Math.random() * CULT_KEYWORDS.length)]; const url = ${TMDB_BASE}/discover/movie?api_key=${TMDB_API_KEY}&#x26;with_keywords=${keywordId}&#x26;sort_by=vote_count.desc&#x26;page=${page}; const res = await fetch(url); if (!res.ok) { console.error(TMDB discover failed: ${res.status} ${res.statusText}); return []; } const data = await res.json(); return data.results || []; } catch (err) { console.error('fetchCultFilms error:', err); return []; } }

async function fetchMovieDetails(movieId: number): Promise<Record<string, any> | null> { try { const url = ${TMDB_BASE}/movie/${movieId}?api_key=${TMDB_API_KEY}&#x26;append_to_response=credits,keywords; const res = await fetch(url); if (!res.ok) { console.error(TMDB movie details failed: ${res.status} ${res.statusText}); return null; } return await res.json(); } catch (err) { console.error('fetchMovieDetails error:', err); return null; } }

async function searchCultFilms(query: string): Promise<TMDBMovie[]> { try { const url = ${TMDB_BASE}/search/movie?api_key=${TMDB_API_KEY}&#x26;query=${encodeURIComponent(query)}; const res = await fetch(url); if (!res.ok) { console.error(TMDB search failed: ${res.status} ${res.statusText}); return []; } const data = await res.json(); return data.results || []; } catch (err) { console.error('searchCultFilms error:', err); return []; } }

// Create agent with extensions const agent = await createAgent({ name: 'cult-film-curtis', version: '1.0.0', description: 'Cult film recommendations for micropayments', }) .use(http()) .use(payments({ config: paymentsFromEnv() })) .build();

const { app, addEntrypoint } = await createAgentApp(agent);

// FREE: Health check addEntrypoint({ key: 'health', description: 'Health check', input: z.object({}), price: { amount: 0 }, handler: async () => ({ output: { status: 'healthy', timestamp: new Date().toISOString(), tmdbConnected: !!TMDB_API_KEY, }, }), });

// FREE: Overview of what Curtis offers addEntrypoint({ key: 'overview', description: 'Free overview of available recommendations', input: z.object({}), price: { amount: 0 }, handler: async () => { const sample = await fetchWithCache('sample', () => fetchCultFilms(1)); return { output: { endpoints: ['recommend', 'details', 'search'], dataSource: 'TMDB', sampleTitles: sample.slice(0, 5).map((m) => m.title), fetchedAt: new Date().toISOString(), }, }; }, });

// PAID: Get recommendation ($0.01) addEntrypoint({ key: 'recommend', description: 'Get a cult film recommendation', input: z.object({ mood: z.string().optional().describe('Genre or mood preference'), page: z.number().min(1).max(10).optional().describe('Result page (1-10)'), }), price: { amount: 10000 }, // 0.01 USDC handler: async (ctx) => { const page = ctx.input.page || Math.ceil(Math.random() * 5); const films = await fetchWithCache(films:${page}, () => fetchCultFilms(page));

if (films.length === 0) {
  return { output: { error: 'No films available', fetchedAt: new Date().toISOString() } };
}

let selection = films;
if (ctx.input.mood) {
  const mood = ctx.input.mood.toLowerCase();
  selection = films.filter(
    (f) => f.overview.toLowerCase().includes(mood) || f.title.toLowerCase().includes(mood)
  );
  if (selection.length === 0) selection = films;
}

const film = selection[Math.floor(Math.random() * selection.length)];
return {
  output: {
    id: film.id,
    title: film.title,
    year: film.release_date?.split('-')[0],
    rating: film.vote_average,
    synopsis: film.overview,
    poster: film.poster_path ? `https://image.tmdb.org/t/p/w500${film.poster_path}` : null,
    source: 'tmdb',
    fetchedAt: new Date().toISOString(),
  },
};

}, });

// PAID: Detailed film info ($0.005) addEntrypoint({ key: 'details', description: 'Get detailed information about a specific film', input: z.object({ movieId: z.number().describe('TMDB movie ID'), }), price: { amount: 5000 }, // 0.005 USDC handler: async (ctx) => { const details = await fetchWithCache(details:${ctx.input.movieId}, () => fetchMovieDetails(ctx.input.movieId) );

if (!details) {
  return { output: { error: 'Movie not found', movieId: ctx.input.movieId, fetchedAt: new Date().toISOString() } };
}

return {
  output: {
    id: details.id,
    title: details.title,
    year: details.release_date?.split('-')[0],
    runtime: details.runtime,
    rating: details.vote_average,
    synopsis: details.overview,
    genres: details.genres?.map((g: any) => g.name) || [],
    director: details.credits?.crew?.find((c: any) => c.job === 'Director')?.name,
    cast: details.credits?.cast?.slice(0, 5).map((c: any) => c.name) || [],
    keywords: details.keywords?.keywords?.map((k: any) => k.name) || [],
    poster: details.poster_path ? `https://image.tmdb.org/t/p/w500${details.poster_path}` : null,
    imdbId: details.imdb_id,
    source: 'tmdb',
    fetchedAt: new Date().toISOString(),
  },
};

}, });

// PAID: Search films ($0.002) addEntrypoint({ key: 'search', description: 'Search for cult films by title', input: z.object({ query: z.string().min(2).describe('Search query'), }), price: { amount: 2000 }, // 0.002 USDC handler: async (ctx) => { const results = await searchCultFilms(ctx.input.query); return { output: { query: ctx.input.query, count: results.length, films: results.slice(0, 10).map((f) => ({ id: f.id, title: f.title, year: f.release_date?.split('-')[0], rating: f.vote_average, })), fetchedAt: new Date().toISOString(), }, }; }, });

const port = Number(process.env.PORT ?? 3000); console.log(Cult Film Curtis running on port ${port});

export default { port, fetch: app.fetch };

Pricing

Endpoint Price Description

/health

Free Health check

/overview

Free Sample titles, discover endpoints

/recommend

0.01 USDC Random cult film recommendation

/details

0.005 USDC Full movie details with cast/crew

/search

0.002 USDC Search by title

Testing

bun install && bun run dev

Free endpoints

curl http://localhost:3000/health curl http://localhost:3000/entrypoints/overview/invoke -X POST -H "Content-Type: application/json" -d '{}'

Paid endpoint (returns 402 without payment)

curl http://localhost:3000/entrypoints/recommend/invoke -X POST
-H "Content-Type: application/json"
-d '{"mood": "horror"}'

Deployment

railway login && railway init railway variables set TMDB_API_KEY="..." EVM_PRIVATE_KEY="0x..."
PAYMENTS_RECEIVABLE_ADDRESS="0x..." FACILITATOR_URL="https://x402.org/facilitator" NETWORK="base" railway up && railway domain

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

lucid-agents-sdk

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

paid-agent

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

lucid-agent-creator

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

lucid-agent-editor

No summary provided by upstream source.

Repository SourceNeeds Review