O OmniPost
Portfolio project · TypeScript · Agentic LLM systems

One source. Ten posts. Zero manual reformatting.

OmniPost is an agentic TypeScript pipeline that turns one long-form article, transcript or note into a full set of platform-native posts — X thread, LinkedIn, Instagram caption, newsletter blurb — plus a weekly content calendar exportable as .ics. The LLM drafts; code enforces every character limit, hashtag rule and split.

TypeScript 5.6 Node 20 Anthropic SDK Groq zod .ics calendar
1 → 13+
posts per source
≤ 280
code-enforced tweet limit
2 LLMs
Claude or Groq · 1 zod schema
.ics
weekly calendar export

Live demo · pre-generated bundle

Real output from npm run repurpose -- --mock on the bundled 1,300-word source. Same pipeline, same enforcement, no API key required.

— tweets — hashtags 7-day calendar
Source · — words
data/source.md

—

Loading…
H

Hooks · 3 variants

    𝕏

    X / Twitter thread

    All tweets ≤ 280 · code-enforced
    in

    LinkedIn post

    — chars
    ◉

    Instagram caption

    — chars
    Hashtags · deduped, ≤ 10
    "

    Pull quotes

      ✉

      Newsletter blurb

      — chars

      Weekly content calendar

      Code spreads posts across slots — daily for X, Mon/Wed/Fri for LinkedIn, Tue/Thu/Sat for Instagram, Thursday for newsletter. Exports as calendar.csv and calendar.ics (import into Google Calendar).

      — scheduled
      When (UTC) Platform Preview

      The pipeline

      Five pure, typed steps. LLM only writes drafts. Every hard constraint — character limits, hashtag dedup, thread numbering — lives in enforce.ts. That separation is the entire reliability story.

      1
      ingest

      Read a file or fetch a URL behind one ContentSource interface. HTML article extraction is built in.

      2
      repurpose

      Claude tool-use or Groq json_object → one zod schema. Brand voice baked into the system prompt.

      3
      enforce

      Code splits tweets ≤ 280, numbers 1/n, dedups hashtags, normalizes markdown per platform.

      4
      schedule

      Slot-based planner across N days. Emits CSV + RFC-5545 .ics for Google Calendar.

      5
      write

      JSON + human-readable Markdown to data/out/. Cached by source hash — re-runs skip the LLM.

      Engineering highlights

      LLM drafts · code enforces

      Language models cannot count characters reliably. So character limits, hashtag dedup and thread numbering all live in enforce.ts — the same pattern used in PulseReport for numbers.

      Strict structured output, two providers, one schema

      Claude path uses tools + tool_choice + input_schema; Groq path uses response_format: json_object. Both validated through the same zod schema.

      Graceful fallback

      If the LLM call fails or returns invalid JSON, a deterministic paragraph-splitter produces a usable draft and the run is marked degraded: true. The pipeline never silently breaks.

      Source-hash cache

      Each LLM result is cached at data/cache/<hash>.json keyed by source + brand voice. Re-running with the same input skips the API entirely. --force drops the cache.

      Brand voice from config

      Tone, audience, CTA, do/don't and language come from env. The voice is part of the cache key — change it and the next run regenerates.

      Offline demo

      npm run repurpose -- --mock --dry produces a full bundle from the bundled source and the seeded cache. Same code path CI runs on every push.

      Stack & CLI

      • • TypeScript 5.6 ESM strict · Node 20+ · tsx
      • • @anthropic-ai/sdk with tool-use + cache_control
      • • openai SDK pointed at Groq's OpenAI-compatible endpoint
      • • zod for env + every LLM response
      • • Native fetch + tiny HTML extractor, no headless browser
      • • RFC-5545 .ics generation, zero deps
      • • GitHub Actions CI · typecheck + offline mock run
      # offline demo (no API key)
      npm run repurpose -- --mock --dry
      
      # real run on the bundled article
      npm run repurpose
      
      # fetch and repurpose a URL
      npm run repurpose -- --url=https://example.com/article
      
      # pick platforms
      npm run repurpose -- --platforms=x,linkedin
      
      # add a 7-day content calendar
      npm run repurpose -- --schedule --days=7
      
      # bypass cache
      npm run repurpose -- --force