Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# sigdiff

Zero-config CLI that diffs the public API surface of a TypeScript project between two git refs and outputs a structured changelog.
Detects breaking changes in TypeScript projects by diffing the actual API surface between git refs. cargo-semver-checks for TypeScript.

## Build & Test

Expand All @@ -15,15 +15,15 @@ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for full details. Also [docs/PR

```
src/
types.ts All contracts (define before implementing)
extract.ts TS Compiler API -> ApiSurface
diff.ts Two ApiSurfaces -> Change[]
classify.ts Change -> SemverLevel
format.ts ChangelogResult -> markdown or JSON
errors.ts Typed error union (SigdiffError)
git.ts Read files at git refs, temp file management
cli.ts Entry point, arg parsing, error boundary
index.ts Barrel export for programmatic API
types.ts - All contracts (define before implementing)
extract.ts - TS Compiler API -> ApiSurface
diff.ts - Two ApiSurfaces -> Change[]
classify.ts - Change -> SemverLevel
format.ts - ChangelogResult -> markdown or JSON
errors.ts - Typed error union (SigdiffError)
git.ts - Read files at git refs, temp file management
cli.ts - Entry point, arg parsing, error boundary
index.ts - Barrel export for programmatic API
```

## Design Rules
Expand All @@ -36,7 +36,7 @@ src/

## Conventions

- Zero config — no config files
- `typescript` is a peer dep, `cac` for CLI parsing — that's it
- `ExportedSymbol.signature` is a normalized stringcomparison happens on strings, not AST nodes
- Zero config, zero overhead
- `typescript` is a peer dep, `cac` for CLI parsing, 1 runtime dependency
- `ExportedSymbol.signature` is a normalized string, comparison happens on strings not AST nodes
- `ApiSurface.symbols` keyed by `filePath:name` for cross-file uniqueness
42 changes: 28 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# sigdiff

Diffs the public API surface of a TypeScript project between two git refs and outputs a structured changelog.
Detects breaking changes in TypeScript projects by diffing the actual API surface between git refs. No config, no setup, no commit conventions.

```bash
npx sigdiff v1.0.0..v2.0.0
```

## Why
## Why this exists

Commit messages are a human interpretation of a change — imprecise, incomplete, or missing entirely. `sigdiff` adds a second perspective: what the code actually exported. It catches things commit messages miss, like a signature change buried in a large PR.
Tools like `changesets` require developers to manually describe what changed. `semantic-release` relies on commit message conventions. `api-extractor` extracts the API surface but leaves semver classification to humans.

`sigdiff` skips all of that. Point it at two git refs and it tells you what changed in your exports, classified by semver level. Think [`cargo-semver-checks`](https://github.com/obi1kenobi/cargo-semver-checks) for TypeScript.

The goal here is to keep it simple and minimize overhead. No config files. No plugins. No workflow changes.

## Usage

Expand All @@ -31,19 +35,13 @@ npx sigdiff --entrypoint src/index.ts
npx sigdiff --json
```

## What it detects

Functions, arrow functions, interfaces, type aliases, enums, classes, and constants — parameters, return types, property shapes, and member names.

Changes are classified as `major`, `minor`, or `patch` per semver rules.

## Example output

```
### Breaking Changes

- Removed function `fetchLegacyData`
- `createUser` signature changed: `(name: string, email: string)` `(opts: CreateUserOpts)`
- `createUser` signature changed: `(name: string, email: string)` -> `(opts: CreateUserOpts)`

### New

Expand All @@ -53,6 +51,22 @@ Changes are classified as `major`, `minor`, or `patch` per semver rules.
Suggested version bump: major
```

## What it detects

Functions, arrow functions, interfaces, type aliases, enums, classes, and constants. Parameters, return types, property shapes, and member names.

Changes are classified as `major`, `minor`, or `patch` per semver rules.

## How it compares

| | sigdiff | changesets | semantic-release | api-extractor |
| --------------------- | --------------- | ----------------------- | -------------------- | --------------- |
| Detects API changes | Automatic (AST) | Manual (you write them) | No (commit messages) | Automatic (AST) |
| Semver classification | Automatic | Manual | Commit-based | Manual |
| Changelog output | Yes | Yes | Yes | No |
| Config required | None | Yes | Yes | Yes |
| Git ref comparison | Any ref | N/A | N/A | Baseline file |

## Programmatic API

```typescript
Expand All @@ -66,10 +80,10 @@ console.log(format(buildResult(classify(diff(before, after)))));

## Notes

- TypeScript only — no JS support (no type info to diff)
- Single-package projects only — no monorepo support yet
- Read-only — never touches your working tree
- 1 runtime dependency ([`cac`](https://github.com/cacjs/cac))
- TypeScript only. No JS support (no type info to diff).
- Single-package projects. No monorepo support yet.
- Read-only. Never touches your working tree.
- 1 runtime dependency ([`cac`](https://github.com/cacjs/cac)). ~8 KB published.

## License

Expand Down
79 changes: 27 additions & 52 deletions docs/PRODUCT.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,56 @@
# sigdiff — Changelog from code, not commits
# sigdiff

Detects breaking changes in TypeScript projects by diffing the actual API surface between git refs.

## Problem

Every existing changelog tool (conventional-changelog, changesets, release-it, auto-changelog) parses **commit messages** to generate changelogs. This is fundamentally flawed:
Existing tools require humans to correctly describe what changed:

- `changesets` requires developers to manually write changeset files and pick semver levels
- `semantic-release` relies on commit message conventions to determine version bumps
- `api-extractor` extracts the API surface but leaves semver classification to humans

- Commit messages are written by humans and are frequently wrong, vague, or missing
- A developer can write `fix: typo` and ship a breaking API change
- Teams that don't follow conventional commits get nothing
Even with good conventions and linting, commit messages are a human interpretation of a change. They can be imprecise, incomplete, or miss things entirely.

## Solution

`sigdiff` reads what the code actually did. Your exported functions, types, and interfaces _are_ your API contract. If a function signature changed, that's a breaking change — regardless of what the commit message says.
`sigdiff` reads what the code actually exported. Point it at two git refs, it diffs the public API surface using the TypeScript Compiler API and classifies every change by semver level.

```
git ref A --> TS Compiler API --> API Surface A --\
--> Diff --> Classify --> Changelog
git ref B --> TS Compiler API --> API Surface B --/
```

## Usage

```bash
# Compare last tag to HEAD
npx sigdiff

# Compare two specific refs
npx sigdiff v1.2.0..v1.3.0

# Scope to barrel export only
npx sigdiff --entrypoint src/index.ts

# Machine-readable output
npx sigdiff --json
```

## Example Output

```markdown
### Breaking Changes

- Removed function `fetchLegacyData`
- `createUser` signature changed: `function createUser(name: string)` -> `function createUser(opts: CreateUserOpts)`

### New

- Added `updateUser(id: string, patch: Partial<User>): Promise<User>`
- Added interface `CreateUserOpts`
```
Think [`cargo-semver-checks`](https://github.com/obi1kenobi/cargo-semver-checks) for TypeScript. No equivalent exists in the npm ecosystem.

## Scope

### In

- Single command: `sigdiff <ref>..<ref>` (defaults to last tag..HEAD)
- `sigdiff <ref>..<ref>` (defaults to last tag..HEAD)
- TypeScript files only (`.ts`, `.tsx`)
- Detect exports: functions, interfaces, type aliases, enums, classes, constants
- Diff: added / removed / signature-changed
- Diff: added, removed, signature-changed
- Semver classification (major/minor/patch)
- Output: markdown (default) and `--json`
- Zero configworks out of the box on any TS project
- Zero config, works on any TS project
- `--entrypoint` flag to scope to specific files
- Programmatic API: `import { extract, diff, classify, format } from 'sigdiff'`

### Out of Scope
### Out of scope

- **No commit message parsing** — that's what every other tool does
- **No release management** — use `np` or `release-it` for that
- **No monorepo support** — single-package only
- **No JavaScript support** — no type info means no meaningful signature diffing
- **No config file** — if it needs config, the defaults are wrong
- **No plugin system** — premature abstraction
- **No watch mode** — it's a point-in-time diff, not a daemon
- No commit message parsing
- No release management (use `np` or `release-it`)
- No monorepo support (single-package only)
- No JavaScript support (no type info to diff)
- No config files
- No plugin system
- No watch mode

## Design Philosophy
## Design philosophy

- Zero config. No `.sigdiffrc`, no `sigdiff.config.js`.
- Tiny dependency footprint. `typescript` as a peer dep and `cac` for CLI parsing.
- One command, one output.
- Composable — generates a changelog, doesn't manage releases. Pipe its output wherever you want.
- Adopt in 30 seconds: `npx sigdiff` and you're done.
- Zero config. Zero overhead. Run it and get output.
- 1 runtime dependency. ~8 KB published.
- Composable. Generates a changelog, doesn't manage releases. Pipe it wherever you want.
- Read-only. Never touches your working tree.
12 changes: 7 additions & 5 deletions docs/TODO.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# sigdiff TODO
# sigdiff - TODO

## Next
## Done

- [ ] README with usage examples and badges
- [ ] Publish to npm
- [x] Published to npm as `sigdiff@0.1.0`
- [x] README with comparison table and usage examples
- [x] CI (GitHub Actions: build + test + api surface check)

## Known Issues / Future

- [ ] Duplicate removals when same symbol is exported from multiple files
- [ ] `compilerOptions` param on `extract()` leaks `ts.CompilerOptions` — public API should accept plain `object`
- [ ] `compilerOptions` param on `extract()` leaks `ts.CompilerOptions` - public API should accept plain `object`
- [ ] Implement rules from [semver-ts.org](https://www.semver-ts.org/) spec
Loading