New Feature·Pushed May 1, 2026·M
Git history now generates editorial stories via LLM
The gitpulse GitHub Action now reads local commit history, asks an LLM to write a journalistic story for each commit, and outputs Story JSON. Works with any OpenAI-compatible endpoint.
Gitpulse now turns commit history into editorial content automatically. The action walks the git log on the default branch, sends each commit's metadata to an OpenAI-compatible LLM, and writes structured Story JSON to the site content directory.
The pipeline processes commits from the last 30 days by default (configurable via [[code]]GITPULSE_BOOTSTRAP_DAYS[[/code]]), calling out to the LLM for a headline, standfirst, and body text. Each story includes the SHA, author, commit URL, and classification—all marked as direct-push for this initial version.
The GitHub Actions workflow now accepts provider-neutral inputs: [[code]]ai-model[[/code]], [[code]]ai-base-url[[/code]], and [[code]]ai-temperature[[/code]]. Switching from OpenAI to Groq, OpenRouter, or MiniMax requires only updating the base URL. The same [[code]]OPENAI_API_KEY[[/code]] secret works across all OpenAI-compatible providers.
Currently every commit gets classified as direct-push. Pull request classification via GraphQL associatedPullRequests is planned for Phase 2.1.
In the [[code]]@gitpulse/action[[/code]] package.
Technical description
This PR introduces Phase 2 of the gitpulse GitHub Action: a complete pipeline that transforms raw git commits into editorial stories using a large language model.
**The Pipeline**
The orchestration lives in [[code ref=6]]main[[/code]]. It loads configuration, determines the default branch via [[code ref=3]]walkCommits[[/code]], calculates a date cutoff (N days ago), then iterates each commit—calling the LLM and writing the result.
The git layer uses git log with a custom format string and ASCII record/field separators. Each commit record is parsed and augmented with diff stats (files changed, insertions, deletions) parsed from [[code]]git show --shortstat[[/code]]. No external Git library is used—only [[code]]execSync[[/code]].
The LLM layer wraps LangChain's [[code ref=4]]createSummarizer[[/code]] with structured output via a zod schema. The schema enforces constraints: headlines 8-120 characters, no trailing period, no emoji; body 80-1200 characters of plain prose. The system prompt specifies a "beat reporter" voice—dry, precise, no marketing speak. Trivial commits (typo fixes, dependency bumps) are acknowledged plainly rather than dramatized.
Stories are rendered by [[code ref=5]]buildStoryFromCommit[[/code]] into a [[code ref=1]]Story[[/code]] object and written as pretty-printed JSON to [[code]]site/src/content/stories/{commit-sha}.json[[/code]].
**Configuration**
[[code ref=2]]loadConfig[[/code]] reads environment variables with sensible defaults:
- [[code]]OPENAI_API_KEY[[/code]] (required)
- [[code]]AI_MODEL[[/code]] defaults to gpt-4o-mini
- [[code]]AI_BASE_URL[[/code]] is optional—empty means use OpenAI directly; setting it redirects to any OpenAI-compatible endpoint
- [[code]]AI_TEMPERATURE[[/code]] defaults to 0.7
- [[code]]GITPULSE_BOOTSTRAP_DAYS[[/code]] defaults to 30
- [[code]]GITPULSE_LIMIT[[/code]] for testing small batches
The workflow file was updated to use provider-neutral input names matching the env vars (ai-model, ai-base-url, ai-temperature).
**Scope Limitations**
All commits are currently classified as [[code]]"direct-push"[[/code]]. The PR classification via GitHub's GraphQL associatedPullRequests is explicitly deferred to Phase 2.1.
````mermaid
graph LR
A[git log] --> B[walkCommits]
B --> C[for each commit]
C --> D[createSummarizer]
D --> E[LLM with zod schema]
E --> F[Story JSON]
F --> G[site/src/content/stories/]
````
**Files at a Glance**
- [[code]]action/src/types.ts[[/code]] — [[code ref=1]]Story[[/code]], [[code]]CommitRecord[[/code]], [[code]]AISummary[[/code]] interfaces
- [[code]]action/src/config.ts[[/code]] — [[code ref=2]]loadConfig[[/code]] with env-driven runtime configuration
- [[code]]action/src/git.ts[[/code]] — [[code ref=3]]walkCommits[[/code]] using git log format strings and diff stat parsing
- [[code]]action/src/llm.ts[[/code]] — [[code ref=4]]createSummarizer[[/code]] with LangChain ChatOpenAI and zod structured output
- [[code]]action/src/render.ts[[/code]] — [[code ref=5]]buildStoryFromCommit[[/code]] and [[code]]writeStory[[/code]
- [[code]]action/src/index.ts[[/code]] — [[code ref=6]]main[[/code]] orchestration entry point
- [[code]].github/workflows/analyze.yml[[/code]] — provider-neutral workflow inputs (ai-model, ai-base-url, ai-temperature)
- [[code]]action/package.json[[/code]] — added [[code]]analyze[[/code]] script
- [[code]]action/tsconfig.json[[/code]] — added [[code]]noEmit[[/code]] and [[code]]allowImportingTsExtensions[[/code]] for tsx support
Categories
- New Feature (75%) — Core purpose: adds a new local-git-to-LLM-to-story pipeline that generates editorial content from commit history
- Configuration (25%) — Workflow inputs renamed to provider-neutral names; new environment-driven config layer added