A motivational quote generator built with Bun + TypeScript and Vercel AI SDK. Automatically generates and stores inspirational quotes with AI-generated titles.
- π€ AI-powered quote generation using Gemini 2.5 Flash
- π¨ Advanced variety system - 7,200 unique combinations (20 themes Γ 12 tones Γ 10 styles Γ 3 lengths)
- π Generates both quote text and creative titles
- πΎ Dual storage: JSON + organized Markdown files
- π
Automatic organization by date (
quotes/yyyy/mm/) - π Smart duplicate handling - replaces quotes for same day
- π LangFuse observability - Optional AI tracing, metrics, and analytics
- π GitHub composite action - Reusable across workflows and repositories
- βοΈ Automated daily commits via GitHub Actions
- π― Modular, maintainable architecture
Add to your workflow:
- uses: ruchernchong/quotely@v1
with:
google-generative-ai-api-key: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}Required secrets (add to repository Settings β Secrets and variables β Actions):
GOOGLE_GENERATIVE_AI_API_KEY- Get from Google AI Studio
Optional secrets (for AI observability):
LANGFUSE_PUBLIC_KEY- Get from LangFuseLANGFUSE_SECRET_KEYLANGFUSE_HOST(defaults tohttps://cloud.langfuse.com)
1. Install dependencies:
bun install2. Configure environment:
Create a .env file:
# Required
GOOGLE_GENERATIVE_AI_API_KEY=your_key_here
# Optional (for AI observability)
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://cloud.langfuse.com3. Generate a quote:
bun run src/generate-quote.ts- Generate: Uses Vercel AI SDK with Gemini 2.5 Flash to create structured quotes (title + text)
- Randomly selects theme, tone, style, and length from variety system
- Creates unique, context-rich prompts for maximum creativity
- Prevents repetitive or generic quotes
- Check: Detects if a quote already exists for today's date
- Store/Replace:
- New quote: Adds to
quotes.jsonand creates new markdown file - Existing quote: Replaces entry in
quotes.jsonand updates markdown file (deletes all files matching date pattern first) - Files stored at
quotes/yyyy/mm/dd-title-slug.md
- New quote: Adds to
- Automate: GitHub Actions runs daily to generate and commit quotes
src/
βββ config.ts # Constants
βββ config/
β βββ quote-variety.ts # Variety system (themes, tones, styles, lengths)
βββ instrumentation.ts # LangFuse OpenTelemetry tracing setup
βββ types/quote.ts # TypeScript interfaces
βββ services/ # Core services
β βββ generator.ts # AI quote generation with variety + telemetry
β βββ json-storage.ts # JSON read/write/replace operations
β βββ markdown-storage.ts # Markdown create/delete operations
βββ generate-quote.ts # Main script with LangFuse tracing
tests/
βββ services/ # Service-layer tests (14 test cases)
βββ json-storage.test.ts # JSON storage & replacement tests
βββ markdown-storage.test.ts # Markdown storage & deletion tests
Every quote is generated with unique characteristics by randomly combining:
20 Themes: career, relationships, creativity, resilience, adventure, mindfulness, courage, wisdom, health, change, purpose, authenticity, leadership, gratitude, failure, time, solitude, passion, discipline, freedom
12 Tones: powerful, gentle, humorous, philosophical, poetic, raw, practical, provocative, warm, mysterious, rebellious, serene
10 Styles: metaphorical, storytelling, direct advice, question-based, paradoxical, contrarian, observational, comparative, imperative, reflective
3 Lengths: brief (20-40 words), standard (40-70 words), expansive (70-100 words)
Result: 7,200 possible combinations ensuring fresh, diverse quotes every day.
This project uses LangFuse for comprehensive AI observability and analytics:
What's Tracked:
- π― Every quote generation trace with full lifecycle
- π Model usage metrics (Gemini 2.5 Flash)
- π° Token consumption and cost analysis
- β±οΈ Latency and performance metrics
- π·οΈ Custom metadata (theme, tone, style, length)
- π Input/output tracking for each generation
Integration Details:
- Uses OpenTelemetry with
@langfuse/oteland@langfuse/tracing - Automatic span collection via
LangfuseSpanProcessor - Vercel AI SDK telemetry enabled with
experimental_telemetry - Proper flush handling for short-lived environments
View your traces: https://cloud.langfuse.com
Testing:
bun testTests use Bun's describe/it("should β¦") structure and interact with the filesystem through Node compatibility
imports (e.g. node:fs/promises).
Code Quality:
This project uses Biome for linting and formatting:
# Format code
bun run format
# Lint code
bun run lint
# Check and fix all issues
bun run checkGit Hooks:
This project uses Husky to enforce code quality:
- pre-commit: Automatically runs Biome on staged files
- commit-msg: Validates commit messages follow Conventional Commits format
This project is available as a reusable GitHub composite action. The action encapsulates:
- Bun runtime setup
- Dependency installation
- Quote generation with AI
- Optional automatic commits
| Input | Required | Default | Description |
|---|---|---|---|
google-generative-ai-api-key |
β Yes | - | Google Generative AI API key |
langfuse-public-key |
β No | - | LangFuse public key (optional) |
langfuse-secret-key |
β No | - | LangFuse secret key (optional) |
langfuse-host |
β No | - | LangFuse host URL (optional) |
auto-commit |
β No | 'true' |
Auto-commit and push changes |
Daily automated quotes:
name: Generate Quote
on:
schedule:
- cron: "0 0 * * *" # Daily at midnight UTC
jobs:
generate-quote:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: ruchernchong/quotely@v1
with:
google-generative-ai-api-key: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}
langfuse-public-key: ${{ secrets.LANGFUSE_PUBLIC_KEY }}
langfuse-secret-key: ${{ secrets.LANGFUSE_SECRET_KEY }}
langfuse-host: ${{ secrets.LANGFUSE_HOST }}Manual trigger without commits:
name: Generate Quote (No Commit)
on: workflow_dispatch
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruchernchong/quotely@v1
with:
google-generative-ai-api-key: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }}
auto-commit: 'false'- First run of the day: Creates new quote and commits
- Subsequent runs: Replaces existing quote and commits
- Auto-commit enabled: Each run creates a commit with message
chore: update quote [skip ci]
Additional workflows:
A separate workflow (.github/workflows/release.yml) runs on pushes to main, executing tests and semantic-release before creating releases.
Semantic-release keeps project versions, changelog entries, and GitHub releases in sync with Conventional Commits.
.github/workflows/release.ymlruns on pushes tomainafter tests succeed and executesbunx semantic-release- Releases update
CHANGELOG.md, tag the commit, and publish GitHub releases without publishing to npm - Run
bun run releaselocally to trigger the same flow (requiresGITHUB_TOKENwith repo scope when run outside CI)
Runtime & Language:
- Bun - Fast all-in-one JavaScript runtime
- TypeScript - Type safety
AI & Data:
- Vercel AI SDK - AI integration framework
- Gemini 2.5 Flash - Google's AI model
- Zod - Schema validation
Observability:
- LangFuse - AI observability and analytics
- OpenTelemetry - Distributed tracing
Utilities:
Code Quality:
- Biome - Fast formatter and linter
- Husky - Git hooks
- lint-staged - Pre-commit file linting
- commitlint - Commit message validation