Add generate command with provider-agnostic LLM support#8
Open
utsengar wants to merge 8 commits into
Open
Conversation
Adds `htmlbin generate --prompt <text>` which calls any OpenAI-compatible LLM endpoint, extracts the resulting HTML, and publishes it — returning the same URL output as `htmlbin publish`. Users supply the endpoint and model via env vars (HTMLBIN_LLM_BASE_URL, HTMLBIN_LLM_MODEL, HTMLBIN_LLM_API_KEY), keeping the CLI free of any vendor SDK dependency. - src/llm/provider.ts: resolves LLM config from env vars; no named providers, no hardcoded URLs or model defaults to go stale - src/llm/complete.ts: raw fetch against /chat/completions with timeout, structured error parsing, and markdown fence stripping - src/errors.ts: adds no_llm_provider (exit 9) and llm_error (exit 8) - src/bin.ts: registers the generate command; writes to a tmpfile, delegates to the active backend's publish method, then cleans up - test/llm.test.ts: unit tests for provider resolution and exit codes - test/e2e/smoke.test.ts: adds generate to the --help surface check
The generate command now builds on HTMLBin's existing quality surface instead of bypassing it: - Pattern auto-detection: prompt is matched against installed pattern triggers (project > global > bundled precedence). The matching pattern's body becomes the LLM system prompt, giving the model the same content checklist, layout directions, and quality constraints that the patterns system was designed to enforce. - Explicit override: --pattern <name> pins a specific pattern. - context field: the generation prompt is passed as context to the cloud publish call so it travels with the drop. - backend.ts: adds context? to PublishOpts; cloud backend forwards it to api.publish(). - src/llm/pattern-resolve.ts: loads and merges patterns from all three sources, matches triggers, throws not_found on unknown --pattern. - Tests updated to cover pattern auto-detection and explicit lookup.
Extracts three shared helpers so the option list, opts mapping, and result emission live in one place: - addPublishOptions(cmd): registers all publish-related flags on any command — publish and generate both call this - buildPublishOpts(file, cmdOpts, extra): converts cmd opts into PublishOpts; accepts extra fields (context) for generate - emitDropResult(r, backend): shared emit for both commands GenerateCmdOpts now extends PublishCmdOpts with only prompt/pattern/data. Adding a new publish flag in future requires changing one line.
addPublishOptions now only contains backend-agnostic flags: title, description, slug, metadata, upsert. The --pr, --repo, --branch, --project flags are backend configuration and stay directly on the publish command where they were before. generate does not inherit them.
--pr, --repo, --branch, --project were leaking backend implementation details into the public CLI surface across publish, list, delete, and url commands. Backend config belongs in the config file and env vars, not as per-command flags. - PublishOpts: drops pr field; backends auto-detect from $GITHUB_REF - makeBackend: now only accepts SetupCmdOpts overrides; all other commands use config values directly - publish, list, delete, url: no backend-specific flags - setup: keeps --repo, --branch, --project (one-time config, correct) - resolvePrNumber hint updated: directs to --slug or $GITHUB_REF
- Drop `explicit` param from resolvePrNumber; all call sites already passed `{}`
- Drop `cwd` and `env` params from resolvePattern/allPatterns; they were never populated
- Drop `source` field from ResolvedPattern; only existed for a single debug log line
- Fix extractHtml to lowercase `<html` check (was already lowercasing `<!doctype`)
- Guard generate command to cloud-only backend (matches how update is constrained)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
htmlbin generate --prompt <text>— generates an HTML page via any OpenAI-compatible LLM and publishes it, returning the same URL output ashtmlbin publish.The command is built on top of HTMLBin's existing infrastructure rather than bypassing it:
--pattern <name>pins a specific pattern explicitly.contextto the cloud publish call so it's stored alongside the drop.fetchagainst any OpenAI-compatible/chat/completionsendpoint. No vendor SDK, nothing to go stale.generateis constrained to the cloud backend (same asupdate). gh-pages and cloudflare require PR context and don't carry thecontextfield.Configuration
Three env vars, fully user-controlled — no hardcoded URLs or model defaults:
Usage
New files
src/llm/provider.ts— resolves LLM config from env varssrc/llm/complete.ts— fetch call with timeout, error parsing, fence stripping, and pattern injection into system promptsrc/llm/pattern-resolve.ts— loads patterns from all three sources, matches triggers, throwsnot_foundon unknown--patternChanges to existing files
src/backend.ts— addscontext?toPublishOpts; removespr?(was never used — slug is the right override)src/backends/cloud.ts— forwardscontexttoapi.publish()src/backends/gh-pages.ts/src/backends/cloudflare.ts— PR number now comes from CI env only; no explicit overridesrc/errors.ts— addsno_llm_provider(exit 9) andllm_error(exit 8)src/bin.ts— registersgeneratewith--prompt,--pattern,--data, and all standard publish flags via shared helpers; removes backend-specific flags (--pr,--repo,--branch,--project) from publish/list/delete/url — they belong onsetuponlysrc/gh/repo.ts—resolvePrNumbersimplified:explicitparam removed (dead code once--prflag was dropped)src/llm/pattern-resolve.ts—cwd/envparams removed (never populated by any call site);sourcefield removed fromResolvedPattern(only fed one debug log line)src/llm/complete.ts—extractHtmlnow lowercases before the<htmlcheck (was already lowercasing<!doctype)Test plan
npm run build && npm testpasses (183 tests, 0 failures)generate --helpshows the command and all flagspr-explainerpattern (stderr showspattern: pr-explainer)--pattern pr-explainerpins the pattern explicitly--pattern no-such-patternexits 4 withnot_found--datafile not found exits 4{ url, slug, backend }generate --to gh-pagesexits withinvalid_arg