Skip to content

davidmovas/postulator-overview

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 

Repository files navigation

Postulator

AI-powered desktop workstation for generating and publishing WordPress content at scale — multi-provider LLMs, local-first storage, and direct site sync.

Hero feature

Status Go Wails Next.js SQLite OpenAI Anthropic Gemini


Contents


The Problem

Content teams running multiple WordPress sites face three pain points that SaaS products do not solve. Privacy — their briefs, drafts, prompts, and API keys live on someone else's server, next to someone else's data. Flexibility — they are locked into whichever model the SaaS chose and pay a markup on every token, with no real control over model, mode, or cost. Security — a SaaS endpoint is a public attack surface, and for users in restricted regions or sensitive verticals the existence of any outbound signal is itself a risk.

Postulator is a local-first desktop workstation that puts the user in control of the whole loop: their own API keys, their own database, their own model choice, and an application-level proxy layer that can route every external call through Tor, SOCKS, or HTTP proxy — making the workstation effectively invisible on the network. The product ships as a single native Windows binary; no server, no cloud, no telemetry.


Overview

Postulator is a Wails v2 desktop application built for professionals who produce content at scale for one or more WordPress sites. The user imports a site's sitemap (CSV, JSON, or Excel), defines topics and categories, builds a prompt library, picks an AI provider (OpenAI, Anthropic, or Google Gemini), and generates articles in bulk — with internal SEO linking, per-provider token and cost tracking, and direct publication to the target WordPress site over the REST API. Everything is stored locally in a SQLite database under the platform's XDG data directory.

The product was built and delivered under contract to a specific client as a turnkey workstation. It is this portfolio's first end-to-end polished desktop product and the first one where UI/UX was the product — the core value is a rich, multi-screen flow for building complex contextual inputs per site, which is the hardest thing the app does.

Technically it is interesting for three reasons: a unified abstraction over three different AI vendor SDKs, a pure-Go SQLite stack that packages trivially into a single Windows binary with no CGO or bundled native libraries, and a first-class application-level proxy layer that treats network invisibility as a core feature rather than an afterthought.


Architecture

Postulator is built as a modular Go backend hosted inside a Wails v2 window, with a Next.js 15 / React 19 static-exported frontend embedded directly into the Go binary via embed.FS. Every handler exposed to the frontend is an auto-generated Wails binding — the React side calls articles.Generate(...) as if it were a local function, and the Wails runtime marshals the call across the JS ↔ Go boundary.

Inside the Go process, the same modular-monolith discipline I use on the server side applies here: domains are isolated packages under internal/domain, infrastructure adapters sit behind interfaces under internal/infra, and everything is wired through Uber Fx with lifecycle hooks tied directly into Wails' OnStartup and OnShutdown events. The DI graph lights up when the window opens and tears down cleanly when the user quits from the system tray.

The architecture is explicitly local-first. All persistent state — articles, prompts, site configurations, AI usage history, migrations, settings — lives in a single SQLite file under the platform's XDG data directory, accessed through a pure-Go SQLite driver (ncruces/go-sqlite3, compiled from SQLite's WASM build and run on tetratelabs/wazero). There is no CGO, which means the whole application cross-compiles and packages into one Windows executable with no external DLLs or native dependencies. Schema evolution is handled by pressly/goose migrations bundled into the binary and run on startup.

Every outbound network call — AI provider APIs, WordPress REST calls, update checks — flows through a single application-level proxy layer (internal/infra/proxy) that supports direct, HTTP proxy, SOCKS5, and Tor. The proxy is configured per environment, and the rest of the code does not know or care: adapters call the proxy-aware HTTP client and the right transport is chosen at runtime. API keys for the three AI providers are never written to the SQLite database — they live in the operating system's secure secret storage via internal/infra/secret.

System Overview

A zoomed-out view of the whole product in one frame: the user, the Wails window with the embedded Next.js frontend, the Go backend running in the same process, the application-level proxy transport, the local-first storage, and the only two kinds of external calls the app ever makes. The proxy sits deliberately on the path to every external dependency — there is no second exit from the process.

%%{init: {'theme':'dark', 'themeVariables': {'fontSize':'14px', 'fontFamily':'Inter, system-ui, sans-serif'}}}%%
flowchart LR
    User([User])

    subgraph Window["Desktop Window — Wails v2"]
        direction TB
        UI[Next.js 15 · React 19<br/>embedded static export]
        Tray[System Tray<br/>Show · Hide · Quit]
    end

    subgraph Backend["Go Backend · single process"]
        direction TB
        Core[Handlers · Domain · Infrastructure<br/>wired via Uber Fx]
    end

    Proxy[[Proxy Transport<br/>direct · HTTP · SOCKS5 · Tor]]

    subgraph Local["Local-First Storage"]
        direction TB
        DB[(SQLite<br/>pure-Go · wazero<br/>goose migrations)]
        Keys[(OS Keychain<br/>API keys)]
    end

    subgraph Ext["External Services"]
        direction TB
        AI[AI Providers<br/>OpenAI · Anthropic · Gemini]
        WP[Client WordPress Sites<br/>REST API]
    end

    User ==> UI
    User <-.-> Tray
    UI <==> Core
    Core --> DB
    Core --> Keys
    Core --> Proxy
    Proxy --> AI
    Proxy --> WP

    classDef user fill:#0f172a,stroke:#64748b,color:#e2e8f0,stroke-width:2px
    classDef window fill:#1e3a8a,stroke:#60a5fa,color:#dbeafe,stroke-width:2px
    classDef backend fill:#14532d,stroke:#4ade80,color:#dcfce7,stroke-width:2px
    classDef proxy fill:#713f12,stroke:#facc15,color:#fef9c3,stroke-width:3px
    classDef storage fill:#7f1d1d,stroke:#f87171,color:#fee2e2,stroke-width:2px
    classDef external fill:#4a044e,stroke:#d946ef,color:#fae8ff,stroke-width:2px

    class User user
    class UI,Tray window
    class Core backend
    class Proxy proxy
    class DB,Keys storage
    class AI,WP external
Loading

Backend Internals — Inside the Go Process

Zooming into the Go process. Every call from the frontend follows the same top-down path: Wails binding → handler → domain service → infrastructure adapter, and from there out to SQLite, the OS keychain, or — through the proxy transport — to an AI vendor or a WordPress site. Uber Fx wires the whole graph at startup and tears it down cleanly on window close; dashed arrows mark the DI boundary.

%%{init: {'theme':'dark', 'themeVariables': {'fontSize':'14px', 'fontFamily':'Inter, system-ui, sans-serif'}}}%%
flowchart TB
    Bridge[[Wails JS ↔ Go Bindings<br/>auto-generated · typed contracts]]

    subgraph Handlers["Handlers — internal/handlers"]
        direction LR
        H1[articles · prompts<br/>categories · topics]
        H2[sites · sitemaps<br/>linking · healthcheck]
        H3[providers · ai_usage<br/>stats · jobs]
        H4[importer · media · dialogs<br/>settings · proxy]
    end

    subgraph Domain["Domain Services — internal/domain"]
        direction LR
        D1[articles · prompts<br/>categories · topics]
        D2[sites · sitemap<br/>linking · healthcheck]
        D3[providers · aiusage<br/>stats · jobs]
        D4[settings · proxy<br/>deletion · entities]
    end

    subgraph Infra["Infrastructure — internal/infra"]
        direction LR
        I1[ai<br/>provider registry]
        I2[database<br/>SQLite + dbx]
        I3[wp<br/>WordPress REST]
        I4[importer · events<br/>notification · window]
        I5[secret<br/>OS keychain]
    end

    Proxy[[Application Proxy Transport<br/>direct · HTTP · SOCKS5 · Tor]]

    subgraph Storage["Local-First Storage"]
        direction LR
        DB[(SQLite · wazero<br/>goose migrations)]
        Keys[(OS Keychain · API keys)]
    end

    subgraph Ext["External Services"]
        direction LR
        AI[AI Providers<br/>OpenAI · Anthropic · Gemini]
        WP[Client WordPress Sites<br/>REST API]
    end

    DI[[Uber Fx · Dependency Injection<br/>lifecycle · OnStartup / OnShutdown]]

    Bridge --> Handlers
    Handlers --> Domain
    Domain --> Infra
    Infra --> Storage
    Infra --> Proxy
    Proxy --> Ext
    DI -. provides .-> Handlers
    DI -. provides .-> Domain
    DI -. provides .-> Infra

    classDef bridge fill:#581c87,stroke:#c084fc,color:#f3e8ff,stroke-width:3px
    classDef handler fill:#134e4a,stroke:#2dd4bf,color:#ccfbf1,stroke-width:2px
    classDef domain fill:#14532d,stroke:#4ade80,color:#dcfce7,stroke-width:2px
    classDef infra fill:#7c2d12,stroke:#fb923c,color:#ffedd5,stroke-width:2px
    classDef proxy fill:#713f12,stroke:#facc15,color:#fef9c3,stroke-width:3px
    classDef storage fill:#7f1d1d,stroke:#f87171,color:#fee2e2,stroke-width:2px
    classDef external fill:#4a044e,stroke:#d946ef,color:#fae8ff,stroke-width:2px
    classDef di fill:#581c87,stroke:#c084fc,color:#f3e8ff,stroke-width:3px

    class Bridge bridge
    class H1,H2,H3,H4 handler
    class D1,D2,D3,D4 domain
    class I1,I2,I3,I4,I5 infra
    class Proxy proxy
    class DB,Keys storage
    class AI,WP external
    class DI di
Loading

Signal Flow — End-to-End

A concrete trace of what happens when the user clicks "Generate Article" on a topic. Every horizontal column is one architectural layer; every arrow is a real call across the boundary, with pseudocode-style labels showing what is actually being invoked.

%%{init: {'theme':'dark', 'themeVariables': {'fontSize':'14px', 'fontFamily':'Inter, system-ui, sans-serif'}}}%%
sequenceDiagram
    participant U as User
    participant UI as React / TipTap
    participant W as Wails Binding
    participant H as articles.Handler
    participant S as articles.Service
    participant P as ai.ProviderRegistry
    participant PX as proxy Transport
    participant AI as OpenAI / Anthropic / Gemini
    participant D as SQLite (dbx)

    U->>UI: click "Generate Article"<br/>topic, site, prompt, provider
    UI->>W: articles.Generate(dto)
    W->>H: Handler.Generate(ctx, dto)
    H->>S: Service.GenerateArticle(cmd)
    S->>D: load prompt template + site context
    D-->>S: PromptTemplate · SiteContext
    S->>S: build unified completion request
    S->>P: registry.ProviderFor(cmd.Provider)
    P-->>S: Provider impl (OpenAI · Anthropic · Gemini)
    S->>PX: Complete(msgs, opts)
    PX->>AI: HTTPS via direct / SOCKS / Tor
    AI-->>PX: streamed completion + usage
    PX-->>S: UnifiedCompletion{text, tokens, cost}
    S->>D: save Article draft
    S->>D: record AIUsage event
    D-->>S: Article{ID, ...}
    S-->>H: ArticleDTO
    H-->>W: ArticleDTO
    W-->>UI: typed Article object
    UI->>U: render draft in TipTap editor<br/>toast "article generated"
Loading

The arrows going right are commands, the arrows going back are responses, and the round trip touches every layer exactly once. The signal never skips the proxy transport — even a health check against a WordPress site goes through the same path.


Domain Modules

The Go backend is organized into sixteen domain modules under internal/domain, each exposed to the Next.js frontend through a paired handler in internal/handlers:

Group Modules
Content Creation articles, prompts, categories, topics
Site Integration sites, sitemap, linking, healthcheck
AI Layer providers, aiusage
Operations jobs, stats, deletion
Platform settings, proxy, entities

Each module follows the same internal shape — domain types and interfaces in internal/domain/<name>, HTTP-equivalent handlers in internal/handlers/<name>.go, and infrastructure adapters in internal/infra/<area>. Everything is resolved through a single Uber Fx container with startup and shutdown hooks wired directly into the Wails window lifecycle.


Key Features

  • Multi-provider AI abstraction — OpenAI, Anthropic, and Google Gemini sit behind a single provider registry with a unified request / response contract. The user picks their provider per run, per topic, or as a default, and swapping vendors never touches domain code.
  • Local-first storage — every byte of state (articles, prompts, sites, sitemap data, usage history, settings) lives in a single SQLite file under the platform's XDG data directory. No server, no cloud, no telemetry. The app works fully offline aside from the AI and WordPress calls the user explicitly makes.
  • Pure-Go SQLite with no CGO — SQLite is run through ncruces/go-sqlite3 on top of tetratelabs/wazero, meaning the Windows build has zero native dependencies. The whole application cross-compiles and ships as one binary with no DLLs, no installer surgery, no CGO toolchain.
  • Goose migrations bundled in the binary — schema evolution for the local SQLite is handled by pressly/goose migrations embedded at build time and applied on startup, so an updated Postulator build safely upgrades an existing client database.
  • Application-level proxy layer — every outbound call (AI providers, WordPress REST) flows through a single proxy transport that supports direct, HTTP, SOCKS5, and Tor. The workstation can be made effectively invisible on the network, which is a first-class product requirement, not an afterthought.
  • OS-level secret storage for API keys — AI provider credentials never touch the SQLite database. They are stored in the operating system's secure secret store via internal/infra/secret, so a leaked database file reveals no keys.
  • Direct WordPress REST integration — Postulator connects directly to the client's WordPress sites over the REST API, performs health checks, pulls site structure, and publishes generated articles straight into the CMS without intermediate servers.
  • Bulk sitemap import — sitemap data can be imported from CSV, JSON, or Excel, drag-and-dropped into the window; the importer streams and normalizes the file and builds the per-site context that later drives topic, category, and linking decisions.
  • Internal linking engine with graph visualization — the linking domain builds SEO-aware internal links between articles, and the frontend renders the result as an interactive graph using xyflow + dagre, so the user can see and edit the structure visually.
  • Prompt library with a Monaco code editor — reusable prompt templates are authored in a Monaco editor (the same engine VS Code uses) with full syntax affordances, so the user can iterate on prompts as first-class artifacts rather than one-off strings.
  • Per-provider AI usage and cost tracking — every completion records token counts and cost against the chosen provider, and the stats domain aggregates usage over time so the user sees exactly what each article and each run actually cost.
  • Desktop-native integration — system-tray presence with Show / Hide / Quit, single-instance lock so a second launch refocuses the existing window, drag-and-drop file handling, native OS notifications via beeep, and full window lifecycle wired to Uber Fx OnStartup / OnShutdown hooks.
  • Rich multi-surface UI — TipTap for article editing, Monaco for prompt authoring, xyflow for the linking graph, TanStack Table for bulk operations, and a cohesive shadcn-ui + Radix design system across every screen — all statically exported from Next.js 15 and embedded in the Go binary.

Tech Stack

Layer Technology
Backend Language Go 1.25
Desktop Framework Wails v2 (Go ↔ web bindings, embedded frontend, system-tray)
Dependency Injection Uber Fx — with lifecycle hooks tied into Wails OnStartup / OnShutdown
Database SQLite via ncruces/go-sqlite3 (pure-Go, tetratelabs/wazero runtime, no CGO)
Query Builder Masterminds Squirrel
Migrations Pressly Goose v3 (embedded in the binary)
Logging Zerolog
AI Providers OpenAI v3, Anthropic Go SDK, Google Generative AI Go SDK
Networking go-resty/resty/v2, custom proxy transport (direct · HTTP · SOCKS5 · Tor)
System Integration getlantern/systray, gen2brain/beeep (notifications), adrg/xdg (paths), OS keychain for secrets
Excel / Import xuri/excelize, streaming CSV / JSON parsers
JSON Schema invopop/jsonschema — for LLM structured outputs
Frontend Framework Next.js 15 (static export) + Turbopack, React 19, TypeScript
Styling / Components Tailwind v4, shadcn-ui + Radix UI, Framer Motion, Sonner
Rich Editing TipTap (article editor) + Monaco (prompt editor)
Graph Visualization @xyflow/react + dagre (internal linking graph)
Data UI TanStack Table, Recharts
Packaging Wails build → single native Windows executable, zero CGO, embedded migrations and frontend

Showcases

Screens and feature walkthroughs, ordered roughly along the user journey — from first opening the app, through configuring providers and sites, to generating and publishing articles.

In the screenshots below, blurred regions contain the client's personal or operational data (API keys, site URLs, article titles, internal categories). The blur is deliberate and only affects those regions — the surrounding UI is live.

AI Providers

Register any supported AI provider — OpenAI, Anthropic, or Google Gemini — choose a model from the set Postulator supports, and paste your own API key. The key never touches the SQLite database: it goes straight into the operating system's secure keychain, so a copied data file reveals nothing about the user's credentials. The first shot shows the providers list; the second is the same flow from inside the provider-creation modal.

AI Providers

AI Providers


Prompt Library

The prompt library is where reusable instructions for the AI live. On first launch the user already finds a curated set of Built-in templates shipped with the app — worked examples for every use case (articles, linking, sitemap generation, and more). They can be studied, duplicated, and used as starting points for the user's own prompts.

Prompts

Users don't write raw system and user messages. They write instructions describing who the AI is and what it does, and the prompt's category (article, linking, sitemap, etc.) decides which context fields appear below — tone, language, target word count, and so on. Defaults are set once on the prompt and can be overridden per run. Because the shape and count of the context fields is driven by the chosen category, every prompt lands exactly in the pipeline stage it was built for and can never be misused.

Prompts

Prompts


Sites Management

The user's pool of connected WordPress sites and everything that touches a single site — credentials, health, per-site dashboard, and the site-level health history.

Sites pool — the main surface. The user's full pool of connected WordPress sites in one place: add, remove, edit credentials, run health probes, and drill into any single site in one click. Every column is live data pulled from the site's REST API.

Sites

Add-site modal. Paste a WordPress Application Password, the publishing user the articles should be posted under, the site URL, and a friendly display name for the local pool. No OAuth dance, no plugin install — Postulator authenticates against the stock WordPress REST API.

Sites

Per-site dashboard. Everything about one site in one place: status, aggregated stats, last activity, and quick navigation into that site's articles, jobs, categories, topics, and sitemap. This is the default landing surface once the user picks which site they want to work on.

Sites

Site health history. Every health probe feeds a timeline chart that the user can scope to any period; the table underneath lists response time, status code, payload, error text, and timestamp for every probe, with full-text search for drilling into a specific incident.

Sites


Sitemap

The sitemap module is the heart of the app. A Postulator sitemap is a full graph of a WordPress site where every node is a page (not an article) — title, slug, description, keywords, status, parent, and children. Everything downstream (linking, content generation, article jobs) is driven by the sitemap tree.

Four ways to create a sitemap. The entry modal. Manual starts an empty tree with just the root home node and leaves everything else to the user. Import loads an existing sitemap from CSV, JSON, or Excel. AI Generate picks a prompt from the library and lets the AI draft a complete page architecture from scratch. Scan crawls the user's own connected site and mirrors its live structure — hierarchy, parent/child links, and per-page status — into a fresh sitemap.

Sitemap

Import mode in action. The user has chosen a local JSON file. The modal ships with inline format documentation for each supported format, so the user can see exactly how Postulator expects a CSV / JSON / Excel file to be structured before building the import.

Sitemap

The sitemap editor canvas — the heart of the app. Built on React Flow. Every sitemap interaction lives here: panning, zooming, node creation, reshaping, bulk selection, a persistent quick-action toolbar, a status-color legend for page states, and contextual tooltips on every affordance.

Sitemap

Command palette. Every editor action and every shortcut in one searchable surface, so the entire canvas is navigable from the keyboard without touching the mouse.

Sitemap

Node editor. Set the page title, URL slug, an optional description, and the list of target keywords (addable and removable inline). From the same modal the user can delete the node — cascading to its children — or spawn a new child node with one click. Every field is auto-saved.

Sitemap

Bulk node creation. One textarea, one row per node — slug Title — and a hotkey to materialize the whole batch at once. A fast path for hand-building a subtree from a prepared outline.

Sitemap

AI-powered node generation. Pick a provider and a library prompt, feed in raw material (titles, keywords), optionally pass in the existing sitemap hierarchy for context, and optionally cap the max depth of the generated tree (0 = no limit). The AI returns a proposed architecture — pages, slugs, parent/child layout — ready to drop into the canvas.

Sitemap

Linking mode — the editor's second hat. The graph is the same but the toolset is different: instead of building pages, the user is wiring internal links between them. Inspect every incoming and outgoing link for a page, author new ones by hand, or hand the job to the AI — give it a prompt and the per-page context, and it proposes a link graph that matches topical relevance and hierarchy, with configurable caps on incoming and outgoing counts per page.

Sitemap

AI content generation. The step that actually fills the pages with articles. Pick a provider, pick a library prompt, and the AI walks the hierarchy — all eligible nodes, or just the current selection — and generates content for each. Linking can be applied in one of two strategies: pre-built uses the links authored in Linking mode and drops them verbatim as text is written; post-pass runs content generation first, then a second AI pass over the finished corpus — slower, but the links land with full awareness of what was actually written instead of only what the sitemap planned.

Sitemap

The second half of the generation modal — parameters that didn't fit in one viewport. Treat this and the previous shot as a single screen split across two captures.

Sitemap

The hero flow — AI generation running live against a sitemap. The job descends the tree in carefully batched passes instead of generating every page in one request, for two reasons: to stay inside each provider's context window, and to guarantee that parent pages exist before their children do — a child page cannot reference a parent that has not been generated yet. Progress and per-node status stream back into the canvas in real time. (For the recording I ran this against example.com because I didn't have a live client site on hand, so every node ends with a red error badge — generation itself worked, there was simply nowhere for the finished pages to be published.)

Sitemap


Article Generation

The article layer sits on top of the sitemap. In Postulator a piece of content is an Article (deliberately not a WordPress "post") — a first-class entity with its own lifecycle, stats, publication status, and edit history. Articles can be authored manually, generated one at a time from a library prompt, or produced in bulk by a background job.

All articles for the current site. The full article table: inline editing, per-article stats, publication-status toggling, and bulk actions across the whole table. This is where the user sees everything that has been generated or authored for a site, in one place.

Articles

Manual article editor. A full authoring surface for when the user wants to skip AI generation and write an article by hand. Rich text or raw HTML, live Preview, media upload, categories and tags, publication-status control, and a basic SEO probe that shows how the article will look in a search-result listing.

Articles

The automation surface — background article jobs. This is where bulk generation is orchestrated. By the time the user reaches this modal, the upstream rules are already in place: the prompt library for tone, language, and category-aware context fields; the topic list as first-class SEO-optimized titles; and the site as the destination. Here the user picks a configured provider, chooses how the job should consume topics, and sets the schedule.

Two topic strategies. Unique guarantees that once a topic has been used, the job remembers it and never reuses it — so a job that runs forever still produces strictly unique titles. Reuse with Variation feeds the same topic back in round-robin but asks the AI to generate variations on the theme rather than the literal title, letting a single topic seed dozens of non-overlapping articles.

Four run modes. Run once manually, run once at a scheduled future time, run on a recurring interval, or run on specific days of the week at specific times. The two recurring modes accept an optional jitter that spreads publication timestamps so a batch of generated articles doesn't all land on the wall clock at once.

Validation toggle. When enabled, generated articles land in WordPress in Draft status instead of being published immediately, so a human reviews before anything goes live.

Articles


Settings

Application settings. Dashboard auto-refresh interval (or fully off), and global site health-checking as an app-wide feature: enable it, set the interval, and wire up OS-native notifications. Once enabled, any site that has health-check turned on participates — and the moment a probe catches a site that is down or returning errors, the user gets alerted at the desktop level, not just inside the app.

Settings

Proxy settings — where network invisibility is switched on. Toggle the proxy layer on or off, register one or many SOCKS5 endpoints, and pick a routing mode: Single funnels every outbound call through whichever registered endpoint is currently healthy, Multi distributes requests across the whole registered pool. If the user has the Tor Browser running, Postulator auto-discovers its local proxy and routes traffic through the Tor network transparently — no manual address configuration. The anonymity check button runs a live probe and reports which exits the traffic is currently leaving through, how long the circuit has been up, and whether the chain is actually working end-to-end.

Settings


What I Learned

  • Shipping a polished desktop product end-to-end — this was the first desktop project I took all the way from an empty directory to a turnkey delivery. Packaging a Wails app forces you to care about things server work never makes you think about: the window lifecycle, system-tray UX, single-instance locking, OS-level notifications, secret storage via the keychain, XDG paths, and cross-compile constraints. None of that work is flashy, but skipping any of it breaks the user's trust in the product the first time they close and reopen it.
  • A rich multi-surface UI was the product — this was my first project where the interaction surface was not a thin admin screen on top of server logic, but the actual value proposition. Building a coherent context-authoring flow across TipTap, Monaco, xyflow, TanStack Table, and dozens of shadcn screens taught me where state belongs, when to lift it, and how much state shape matters when the user is composing complex inputs (site context, prompt templates, linking rules) across many screens in one session.
  • A unified contract over three AI vendor SDKs — OpenAI, Anthropic, and Google Gemini each have their own message schema, streaming format, structured-output mechanism, and error model. Designing a single provider interface that is thin enough to stay honest but rich enough to be useful meant sitting between the three SDKs for a while and carefully deciding what to normalize, what to pass through, and where to let the caller see the native shape anyway.

Status

Completed contract project, delivered to the client and running in their content workflow. The application is packaged as a single native Windows executable with embedded migrations and frontend; schema upgrades are handled transparently on the next launch, and no external hosting or infrastructure is required on the client's side.


Built by David Movsesian

About

AI-powered desktop workstation for WordPress content at scale — multi-provider LLMs, local-first SQLite, Tor/SOCKS proxy, direct WordPress sync. Wails v2 · Go · Next.js.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors