Skip to main content

GitRepository

Interface for working with Git repositories.

Constructor

GitRepository(path: Path, *, prefer_gitpython: bool = True)
path
Path
required
Path to the Git repository directory.
prefer_gitpython
bool
default:"true"
Prefer using GitPython when available, with automatic fallback to the git CLI.

Methods

iter_commits
Iterator[CommitInfo]
Iterate over commits in a given range.
commits = repo.iter_commits(commit_range)
for commit in commits:
    print(commit.subject)
Parameters:
  • commit_range: CommitRange - Range of commits to fetch
Returns: Iterator of CommitInfo objects
list_tags
List[TagInfo]
List all tags in the repository, sorted by date (newest first).
tags = repo.list_tags()
latest_tag = tags[0] if tags else None
Returns: List of TagInfo objects
get_github_slug
Optional[Tuple[str, str]]
Extract GitHub owner/repo from remote URL.
slug = repo.get_github_slug()
if slug:
    owner, repo_name = slug
    print(f"{owner}/{repo_name}")
Returns: Tuple of (owner, repo) or None

Example

from pathlib import Path
from helixcommit.git_client import GitRepository, CommitRange

# Initialize repository
repo = GitRepository(Path("."))

# Get commits
commits = list(repo.iter_commits(
    CommitRange(since="v1.0.0", until="v2.0.0")
))

# List tags
tags = repo.list_tags()
for tag in tags:
    print(f"{tag.name}: {tag.tagged_date}")

CommitRange

Define a range of commits to process.

Constructor

CommitRange(
    since: Optional[str] = None,
    until: Optional[str] = None,
    include_merges: bool = True,
    max_count: Optional[int] = None
)
since
str
Starting ref (tag, commit SHA, or branch). Commits after this ref are included.
until
str
default:"HEAD"
Ending ref. Commits up to and including this ref are included.
include_merges
bool
default:"true"
Whether to include merge commits in the output.
max_count
int
Maximum number of commits to process.

Example

from helixcommit.git_client import CommitRange

# Between tags
range1 = CommitRange(since="v1.0.0", until="v2.0.0")

# Unreleased changes
range2 = CommitRange(since="v1.0.0", until="HEAD")

# With filtering
range3 = CommitRange(
    since="v1.0.0",
    until="HEAD",
    include_merges=False,
    max_count=100
)

ChangelogBuilder

Build structured changelogs from commits.

Constructor

ChangelogBuilder(
    summarizer: Optional[BaseSummarizer] = None,
    section_order: Optional[Sequence[str]] = None,
    include_scopes: bool = True,
    dedupe_prs: bool = True,
    summary_body_limit: int = 1600,
)
summarizer
BaseSummarizer
AI summarizer for generating enhanced descriptions. Pass None to disable AI.
section_order
List[str]
Custom order for changelog sections. Uses default order if not specified.
include_scopes
bool
default:"true"
Whether to include commit scopes in descriptions.
dedupe_prs
bool
default:"true"
Whether to deduplicate pull requests.

Methods

build
Changelog
Build a changelog from commits.
changelog = builder.build(
    version="v2.0.0",
    release_date=datetime.now(),
    commits=commits,
    commit_prs={},
    pr_index={},
)
Parameters:
  • version: Optional[str] - Version number
  • release_date: datetime - Release date
  • commits: Sequence[CommitInfo] - List of commits
  • commit_prs: Dict[str, List[PullRequestInfo]] - Commit to PRs mapping
  • pr_index: Dict[int, PullRequestInfo] - PR number to PR info mapping
Returns: Changelog object

Example

from datetime import datetime
from helixcommit.changelog import ChangelogBuilder

# Basic usage
builder = ChangelogBuilder(include_scopes=True)
changelog = builder.build(
    version="v2.0.0",
    release_date=datetime.now(),
    commits=commits,
    commit_prs={},
    pr_index={},
)

# With AI summarization
from helixcommit.summarizer import PromptEngineeredSummarizer

summarizer = PromptEngineeredSummarizer(
    api_key="sk-...",
    model="gpt-4o-mini",
)
builder = ChangelogBuilder(summarizer=summarizer)
changelog = builder.build(
    version="v2.0.0",
    release_date=datetime.now(),
    commits=commits,
    commit_prs={},
    pr_index={},
)

Formatters

Render changelogs in different formats.

Markdown

from helixcommit.formatters.markdown import render_markdown

markdown_output = render_markdown(changelog)
Path("RELEASE_NOTES.md").write_text(markdown_output)

HTML

from helixcommit.formatters.html import render_html

html_output = render_html(changelog)
Path("changelog.html").write_text(html_output)

Text

from helixcommit.formatters.text import render_text

text_output = render_text(changelog)
print(text_output)

GitHubClient

Interface with GitHub’s API.

Constructor

GitHubClient(settings: GitHubSettings)
settings
GitHubSettings
required
GitHub configuration including owner, repo, and token.

Methods

get_pull_request
Optional[PullRequestInfo]
Fetch pull request by number.
pr = client.get_pull_request(123)
if pr:
    print(f"PR #{pr.number}: {pr.title}")
Parameters:
  • number: int - Pull request number
Returns: PullRequestInfo or None
find_pull_requests_by_commit
List[PullRequestInfo]
Find pull requests associated with a commit.
prs = client.find_pull_requests_by_commit("abc123def")
for pr in prs:
    print(f"PR #{pr.number}")
Parameters:
  • sha: str - Commit SHA
Returns: List of PullRequestInfo
close
None
Close the GitHub client and release resources.
client.close()

Example

from helixcommit.github_client import GitHubClient, GitHubSettings

# Initialize client
settings = GitHubSettings(
    owner="username",
    repo="repository",
    token="ghp_..."  # Optional
)
client = GitHubClient(settings)

try:
    # Fetch PR
    pr = client.get_pull_request(123)
    
    # Find PRs by commit
    prs = client.find_pull_requests_by_commit("abc123")
finally:
    client.close()

PromptEngineeredSummarizer

AI-powered summarization of commits.

Constructor

PromptEngineeredSummarizer(
    api_key: str,
    model: str = "gpt-4o-mini",
    base_url: Optional[str] = None,
    cache_path: Optional[Path] = None,
    domain_scope: Optional[str] = None,
    expert_roles: Optional[List[str]] = None,
    rag_backend: str = "simple"
)
api_key
str
required
OpenAI or OpenRouter API key.
model
str
default:"gpt-4o-mini"
AI model to use for summarization.
base_url
str
Custom API base URL (for OpenRouter: https://openrouter.ai/api/v1).
cache_path
Path
Path to cache file for storing summaries.
domain_scope
str
Domain context for the AI (e.g., “software release notes”).
expert_roles
List[str]
Expert perspectives to consider (e.g., [“Product Manager”, “Tech Lead”]).
rag_backend
str
default:"simple"
RAG backend: “simple” (keyword) or “chroma” (vector).

Example

from pathlib import Path
from helixcommit.summarizer import PromptEngineeredSummarizer

# OpenAI
summarizer = PromptEngineeredSummarizer(
    api_key="sk-...",
    model="gpt-4o-mini",
    cache_path=Path(".cache/summaries.json"),
    domain_scope="software release notes",
    expert_roles=["Product Manager", "Tech Lead", "QA Engineer"]
)

# OpenRouter
summarizer = PromptEngineeredSummarizer(
    api_key="sk-or-...",
    model="meta-llama/llama-3.1-8b-instruct:free",
    base_url="https://openrouter.ai/api/v1",
    cache_path=Path(".cache/summaries.json")
)

# Use with ChangelogBuilder
builder = ChangelogBuilder(summarizer=summarizer)

Data models

CommitInfo

@dataclass
class CommitInfo:
    sha: str
    subject: str
    body: str
    author_name: str
    author_email: str
    authored_date: datetime
    committed_date: datetime
    is_merge: bool = False
    pr_number: Optional[int] = None
    labels: List[str] = field(default_factory=list)
    diff: Optional[str] = None

    @property
    def message(self) -> str: ...

Changelog

@dataclass
class Changelog:
    version: Optional[str]
    date: Optional[datetime]
    sections: List[ChangelogSection]
    metadata: Dict[str, Any]

ChangelogSection

@dataclass
class ChangelogSection:
    title: str
    items: List[ChangeItem] = field(default_factory=list)

    def extend(self, changes: Iterable[ChangeItem]) -> None: ...

PullRequestInfo

@dataclass
class PullRequestInfo:
    number: int
    title: str
    url: str
    author: Optional[str]
    merged_at: Optional[datetime]
    body: Optional[str] = None
    labels: List[str] = field(default_factory=list)
    assignees: List[str] = field(default_factory=list)

Complete example

from datetime import datetime
from pathlib import Path
from typing import Optional

from helixcommit.changelog import ChangelogBuilder
from helixcommit.formatters.markdown import render_markdown
from helixcommit.formatters.html import render_html
from helixcommit.git_client import CommitRange, GitRepository
from helixcommit.github_client import GitHubClient, GitHubSettings
from helixcommit.summarizer import PromptEngineeredSummarizer

def generate_release_notes(
    repo_path: Path,
    since: str,
    until: str,
    github_token: Optional[str] = None,
    openai_key: Optional[str] = None
) -> str:
    """Generate comprehensive release notes."""
    
    # Initialize repository
    repo = GitRepository(repo_path)
    
    # Get commits
    commit_range = CommitRange(since=since, until=until, include_merges=False)
    commits = list(repo.iter_commits(commit_range))
    
    # GitHub enrichment
    pr_index = {}
    commit_prs = {}
    if github_token:
        slug = repo.get_github_slug()
        if slug:
            settings = GitHubSettings(
                owner=slug[0],
                repo=slug[1],
                token=github_token
            )
            client = GitHubClient(settings)
            try:
                for commit in commits:
                    if commit.pr_number:
                        pr = client.get_pull_request(commit.pr_number)
                        if pr:
                            pr_index[commit.pr_number] = pr
            finally:
                client.close()
    
    # AI summarization
    summarizer = None
    if openai_key:
        summarizer = PromptEngineeredSummarizer(
            api_key=openai_key,
            model="gpt-4o-mini",
            cache_path=repo_path / ".cache" / "summaries.json"
        )
    
    # Build changelog
    builder = ChangelogBuilder(
        summarizer=summarizer,
        include_scopes=True,
        dedupe_prs=True,
    )
    
    changelog = builder.build(
        version=until,
        release_date=datetime.now(),
        commits=commits,
        commit_prs=commit_prs,
        pr_index=pr_index,
    )
    
    # Render output
    markdown = render_markdown(changelog)
    html = render_html(changelog)
    
    # Save files
    (repo_path / "RELEASE_NOTES.md").write_text(markdown)
    (repo_path / "release.html").write_text(html)
    
    return markdown

# Usage
if __name__ == "__main__":
    notes = generate_release_notes(
        repo_path=Path("."),
        since="v1.0.0",
        until="v2.0.0",
        github_token="ghp_...",
        openai_key="sk-..."
    )
    print(notes)

Next steps

Git client

Detailed Git client documentation

Changelog builder

Changelog building API

Formatters

Output formatting reference

Examples

See Python API in action