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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for astlog git refs
fetch-depth: 0 # full history for sigdiff git refs

- uses: actions/setup-node@v4
with:
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# astlog
# sigdiff

Zero-config CLI that diffs the public API surface of a TypeScript project between two git refs and outputs a structured changelog.

Expand All @@ -20,7 +20,7 @@ src/
diff.ts — Two ApiSurfaces -> Change[]
classify.ts — Change -> SemverLevel
format.ts — ChangelogResult -> markdown or JSON
errors.ts — Typed error union (AstlogError)
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
Expand All @@ -32,11 +32,11 @@ src/
- **Discriminated unions over optional fields.** Each `Change` variant carries exactly its data.
- **Validate at boundaries only.** CLI validates args. Internal functions trust typed inputs.
- **Each function is independently useful.** `extract`, `diff`, `classify`, `format` are composable.
- **Consistent error format.** Library code throws `AstlogException`, never calls `process.exit`.
- **Consistent error format.** Library code throws `SigdiffException`, never calls `process.exit`.

## Conventions

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

Diffs the public API surface of a TypeScript project between two git refs and outputs a structured changelog.

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

## Why

Commit messages are a human interpretation of a change — imprecise, incomplete, or missing entirely. `astlog` adds a second perspective: what the code actually exported. It catches things commit messages miss, like a signature change buried in a large PR.
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.

## Usage

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

# Compare two refs
npx astlog v1.0.0..v2.0.0
npx sigdiff v1.0.0..v2.0.0

# Scope to a specific entrypoint
npx astlog --entrypoint src/index.ts
npx sigdiff --entrypoint src/index.ts

# JSON output
npx astlog --json
npx sigdiff --json
```

## What it detects
Expand Down Expand Up @@ -51,7 +51,7 @@ Suggested version bump: major
## Programmatic API

```typescript
import { extract, diff, classify, buildResult, format } from 'astlog';
import { extract, diff, classify, buildResult, format } from 'sigdiff';

const before = extract(['src/v1/index.ts']);
const after = extract(['src/v2/index.ts']);
Expand Down
8 changes: 4 additions & 4 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ src/
classify.ts — Change[] -> ClassifiedChange[] (semver levels)
format.ts — ChangelogResult -> markdown or JSON string
types.ts — All shared contracts
errors.ts — Typed error union (AstlogError)
errors.ts — Typed error union (SigdiffError)
index.ts — Barrel export for programmatic API
```

Expand Down Expand Up @@ -64,7 +64,7 @@ interface ChangelogResult {

**Signature as string.** `ExportedSymbol.signature` is a canonical string, not raw AST nodes. Diff comparison is string equality. Signatures are normalized at extraction time (whitespace collapsed, semicolons standardized, index signatures canonicalized to `Record<>`).

**Validate at boundaries only.** `cli.ts` validates args and catches `AstlogException`. Internal functions (`diff`, `classify`, `format`) trust typed inputs — no defensive checks.
**Validate at boundaries only.** `cli.ts` validates args and catches `SigdiffException`. Internal functions (`diff`, `classify`, `format`) trust typed inputs — no defensive checks.

**Arrow function detection.** `const fn = () => ...` is classified as `"function"` kind, not `"constant"`. The extractor checks if a variable declaration's type has call signatures.

Expand All @@ -79,15 +79,15 @@ interface ChangelogResult {
## Error Handling

```typescript
type AstlogError =
type SigdiffError =
| { code: 'INVALID_REF'; message: string }
| { code: 'NO_TAGS'; message: string }
| { code: 'NO_TYPESCRIPT'; message: string }
| { code: 'PARSE_ERROR'; message: string }
| { code: 'NOT_GIT_REPO'; message: string };
```

Library code throws `AstlogException`. Only `cli.ts` calls `process.exit`.
Library code throws `SigdiffException`. Only `cli.ts` calls `process.exit`.

## Semver Classification Rules

Expand Down
20 changes: 10 additions & 10 deletions docs/PRODUCT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# astlog — Changelog from code, not commits
# sigdiff — Changelog from code, not commits

## Problem

Expand All @@ -10,7 +10,7 @@ Every existing changelog tool (conventional-changelog, changesets, release-it, a

## Solution

`astlog` 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 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.

```
git ref A --> TS Compiler API --> API Surface A --\
Expand All @@ -22,16 +22,16 @@ git ref B --> TS Compiler API --> API Surface B --/

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

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

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

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

## Example Output
Expand All @@ -52,15 +52,15 @@ npx astlog --json

### In

- Single command: `astlog <ref>..<ref>` (defaults to last tag..HEAD)
- Single command: `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
- Semver classification (major/minor/patch)
- Output: markdown (default) and `--json`
- Zero config — works out of the box on any TS project
- `--entrypoint` flag to scope to specific files
- Programmatic API: `import { extract, diff, classify, format } from 'astlog'`
- Programmatic API: `import { extract, diff, classify, format } from 'sigdiff'`

### Out of Scope

Expand All @@ -74,8 +74,8 @@ npx astlog --json

## Design Philosophy

- Zero config. No `.astlogrc`, no `astlog.config.js`.
- 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 astlog` and you're done.
- Adopt in 30 seconds: `npx sigdiff` and you're done.
2 changes: 1 addition & 1 deletion docs/TODO.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# astlog — TODO
# sigdiff — TODO

## Next

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "astlog",
"name": "sigdiff",
"version": "0.1.0",
"description": "Changelog from code, not commits. AST-based API surface diffing for TypeScript.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"astlog": "dist/cli.js"
"sigdiff": "dist/cli.js"
},
"files": [
"dist"
Expand Down Expand Up @@ -33,7 +33,7 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/andydeng/astlog"
"url": "https://github.com/1dengaroo/sigdiff"
},
"engines": {
"node": ">=18"
Expand Down
6 changes: 3 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/usr/bin/env node

import cac from 'cac';
import { AstlogException } from './errors';
import { SigdiffException } from './errors';
import { assertGitRepo, extractAtRef, resolveRefs } from './git';
import { diff } from './diff';
import { classify } from './classify';
import { buildResult, format } from './format';

const cli = cac('astlog');
const cli = cac('sigdiff');

cli
.command('[range]', 'Diff the public API surface between two git refs')
Expand All @@ -28,7 +28,7 @@ cli

process.stdout.write(output);
} catch (err) {
if (err instanceof AstlogException) {
if (err instanceof SigdiffException) {
process.stderr.write(`Error: ${err.error.message}\n`);
process.exit(1);
}
Expand Down
8 changes: 4 additions & 4 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export type AstlogError =
export type SigdiffError =
| { code: 'INVALID_REF'; message: string }
| { code: 'NO_TAGS'; message: string }
| { code: 'NO_TYPESCRIPT'; message: string }
| { code: 'PARSE_ERROR'; message: string }
| { code: 'NOT_GIT_REPO'; message: string };

export class AstlogException extends Error {
constructor(public readonly error: AstlogError) {
export class SigdiffException extends Error {
constructor(public readonly error: SigdiffError) {
super(error.message);
this.name = 'AstlogException';
this.name = 'SigdiffException';
}
}
18 changes: 9 additions & 9 deletions src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { execSync } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { AstlogException } from './errors';
import { SigdiffException } from './errors';
import { extract } from './extract';
import { ApiSurface, ExportedSymbol } from './types';

export function assertGitRepo(): void {
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
} catch {
throw new AstlogException({
throw new SigdiffException({
code: 'NOT_GIT_REPO',
message: 'Not a git repository. Run astlog from inside a git repo.'
message: 'Not a git repository. Run sigdiff from inside a git repo.'
});
}
}
Expand All @@ -24,7 +24,7 @@ export function resolveRefs(rangeArg?: string): {
if (rangeArg) {
const parts = rangeArg.split('..');
if (parts.length !== 2 || !parts[0] || !parts[1]) {
throw new AstlogException({
throw new SigdiffException({
code: 'INVALID_REF',
message: `Invalid range format: "${rangeArg}". Expected format: <ref>..<ref>`
});
Expand All @@ -40,9 +40,9 @@ export function resolveRefs(rangeArg?: string): {
encoding: 'utf-8'
}).trim();
} catch {
throw new AstlogException({
throw new SigdiffException({
code: 'NO_TAGS',
message: 'No git tags found. Provide an explicit range: astlog <ref>..<ref>'
message: 'No git tags found. Provide an explicit range: sigdiff <ref>..<ref>'
});
}

Expand All @@ -53,13 +53,13 @@ export function extractAtRef(ref: string, entrypoint?: string): ApiSurface {
const files = discoverFiles(ref, entrypoint);

if (files.length === 0) {
throw new AstlogException({
throw new SigdiffException({
code: 'NO_TYPESCRIPT',
message: `No TypeScript files found at ref "${ref}".`
});
}

const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astlog-'));
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sigdiff-'));

try {
const pathMap = new Map<string, string>();
Expand Down Expand Up @@ -89,7 +89,7 @@ function validateRef(ref: string): void {
try {
execSync(`git rev-parse --verify ${ref}`, { stdio: 'ignore' });
} catch {
throw new AstlogException({
throw new SigdiffException({
code: 'INVALID_REF',
message: `Git ref "${ref}" does not exist.`
});
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export { extract } from './extract';
export { diff } from './diff';
export { classify } from './classify';
export { buildResult, format } from './format';
export { AstlogException } from './errors';
export { SigdiffException } from './errors';

export type {
ApiSurface,
Expand All @@ -14,4 +14,4 @@ export type {
SymbolKind
} from './types';

export type { AstlogError } from './errors';
export type { SigdiffError } from './errors';
Loading