> ## Documentation Index
> Fetch the complete documentation index at: https://lunr-f6858e75.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog builder

> Build and structure changelog data from commits

## ChangelogBuilder

The `ChangelogBuilder` class transforms raw commit data into structured changelog objects.

### Constructor

```python theme={}
ChangelogBuilder(
    summarizer: Optional[BaseSummarizer] = None,
    section_order: Optional[Sequence[str]] = None,
    include_scopes: bool = True,
    dedupe_prs: bool = True
)
```

### Parameters

<ParamField path="summarizer" type="BaseSummarizer">
  Optional AI summarizer for enhancing commit descriptions.

  ```python theme={}
  from helixcommit.summarizer import PromptEngineeredSummarizer

  summarizer = PromptEngineeredSummarizer(api_key="sk-...", model="gpt-4o-mini")
  builder = ChangelogBuilder(summarizer=summarizer)
  ```

  Pass `None` to disable AI summarization.
</ParamField>

<ParamField path="section_order" type="List[str]">
  Custom ordering for changelog sections.

  ```python theme={}
  from helixcommit.grouper import DEFAULT_ORDER

  # Use default order
  builder = ChangelogBuilder(section_order=DEFAULT_ORDER)

  # Custom order
  custom_order = [
      "breaking",
      "features",
      "fixes",
      "docs"
  ]
  builder = ChangelogBuilder(section_order=custom_order)
  ```

  **Default order:**

  1. Breaking Changes
  2. Features
  3. Bug Fixes
  4. Performance
  5. Documentation
  6. Refactoring
  7. Tests
  8. Build
  9. CI/CD
  10. Chores
  11. Style
</ParamField>

<ParamField path="include_scopes" type="bool" default="true">
  Whether to include commit scopes in output.

  ```python theme={}
  # With scopes: "**api**: Add authentication"
  builder = ChangelogBuilder(include_scopes=True)

  # Without scopes: "Add authentication"
  builder = ChangelogBuilder(include_scopes=False)
  ```
</ParamField>

<ParamField path="dedupe_prs" type="bool" default="true">
  Whether to deduplicate pull requests across commits.

  ```python theme={}
  # Deduplicate PRs (recommended)
  builder = ChangelogBuilder(dedupe_prs=True)

  # Show all PR references
  builder = ChangelogBuilder(dedupe_prs=False)
  ```
</ParamField>

***

## build() method

Generate a structured changelog from commits.

```python theme={}
changelog = builder.build(
    version: Optional[str],
    release_date: datetime,
    commits: Sequence[CommitInfo],
    commit_prs: Optional[Dict[str, List[PullRequestInfo]]] = None,
    pr_index: Optional[Dict[int, PullRequestInfo]] = None,
    stream_handler: Optional[Callable[[str, str, bool], None]] = None,
) -> Changelog
```

### Parameters

<ParamField path="version" type="str">
  Version number for the release (e.g., "v2.0.0").

  ```python theme={}
  changelog = builder.build(version="v2.0.0", ...)
  ```
</ParamField>

<ParamField path="release_date" type="datetime" required>
  Date and time of the release.

  ```python theme={}
  from datetime import datetime, timezone

  changelog = builder.build(
      release_date=datetime.now(timezone.utc),
      ...
  )
  ```
</ParamField>

<ParamField path="commits" type="List[CommitInfo]" required>
  List of commits to include in the changelog.

  ```python theme={}
  from helixcommit.git_client import GitRepository, CommitRange

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

  changelog = builder.build(commits=commits, ...)
  ```
</ParamField>

<ParamField path="commit_prs" type="Dict[str, List[PullRequestInfo]]">
  Mapping of commit SHAs to associated pull requests.

  ```python theme={}
  # Populated by GitHub client
  commit_prs = {
      "abc123": [pr_info_1],
      "def456": [pr_info_2]
  }

  changelog = builder.build(commit_prs=commit_prs, ...)
  ```
</ParamField>

<ParamField path="pr_index" type="Dict[int, PullRequestInfo]">
  Mapping of PR numbers to PR information.

  ```python theme={}
  # Populated by GitHub client
  pr_index = {
      123: pr_info_1,
      456: pr_info_2
  }

  changelog = builder.build(pr_index=pr_index, ...)
  ```
</ParamField>

<ParamField path="stream_handler" type="Callable[[str, str, bool], None]">
  Optional handler to receive incremental streaming chunks from an AI summarizer. The handler is called as `handler(identifier, chunk, final)` where `identifier` is the summary id, `chunk` is a piece of text, and `final` is a boolean set to True on the last chunk. When provided, summarization will stream chunks to the handler; final summaries are still returned in the resulting `Changelog` object.
</ParamField>

### Returns

<ResponseField name="Changelog" type="Changelog">
  Structured changelog object containing:

  * `version: Optional[str]` - Version number
  * `release_date: datetime` - Release date
  * `sections: List[ChangelogSection]` - Organized sections
  * `metadata: Dict[str, Any]` - Additional metadata

  ```python theme={}
  changelog = builder.build(...)

  print(f"Version: {changelog.version}")
  print(f"Release date: {changelog.release_date}")
  print(f"Sections: {len(changelog.sections)}")

  for section in changelog.sections:
      print(f"  {section.title}: {len(section.items)} items")
  ```
</ResponseField>

***

## Complete example

```python theme={}
from datetime import datetime
from pathlib import Path
from helixcommit.changelog import ChangelogBuilder
from helixcommit.formatters.markdown import render_markdown
from helixcommit.git_client import CommitRange, GitRepository
from helixcommit.github_client import GitHubClient, GitHubSettings

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

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

# Optional: Enrich with GitHub data
pr_index = {}
commit_prs = {}
slug = repo.get_github_slug()

if slug:
    settings = GitHubSettings(owner=slug[0], repo=slug[1], token="ghp_...")
    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()

# Build changelog
builder = ChangelogBuilder(
    include_scopes=True,
    dedupe_prs=True
)

changelog = builder.build(
    version="v2.0.0",
    release_date=datetime.now(),
    commits=commits,
    commit_prs=commit_prs,
    pr_index=pr_index
)

# Render output
markdown = render_markdown(changelog)
print(markdown)

# Access structured data
for section in changelog.sections:
    print(f"\n{section.title}")
    for item in section.items:
        print(f"  - {item.description}")
```

***

## Changelog data model

### Changelog

```python theme={}
@dataclass
class Changelog:
    version: Optional[str]
    release_date: datetime
    sections: List[ChangelogSection]
    metadata: Dict[str, Any]
```

### ChangelogSection

```python theme={}
@dataclass
class ChangelogSection:
    title: str
    items: List[ChangelogItem]
    order: int
```

### ChangelogItem

```python theme={}
@dataclass
class ChangelogItem:
    description: str
    commit_sha: str
    commit_type: str
    scope: Optional[str]
    breaking: bool
    pr_numbers: List[int]
```

***

## Next steps

<CardGroup cols={2}>
  <Card title="Git client" icon="code-branch" href="/api-reference/git-client" color="#0A6ACB">
    Learn about fetching commits
  </Card>

  <Card title="Formatters" icon="file-export" href="/api-reference/formatters" color="#F26419">
    Render changelogs in different formats
  </Card>

  <Card title="Python API" icon="python" href="/api-reference/python-api" color="#F6A02D">
    Complete API reference
  </Card>

  <Card title="Examples" icon="code" href="/examples" color="#0A6ACB">
    See practical usage examples
  </Card>
</CardGroup>
