Skip to content

Repository files navigation

🌅 Sunset

Codebase Indexer — Parse your source code into structured Markdown documentation using tree-sitter.

Sunset scans your project, extracts functions, types, imports, and docstrings, then generates a set of Markdown files with YAML frontmatter — ready for AI consumption, documentation, or codebase exploration.

Features

  • 🌳 Tree-sitter powered — Accurate parsing via concrete syntax trees (CST)
  • 📝 Markdown output — YAML frontmatter + structured body per file
  • 🔍 Summary & Full CST — Choose between function-level overview or full tree dump
  • 📦 Multi-language — Go, JavaScript, TypeScript, Python out of the box
  • Incremental — SHA256 caching, only re-parse changed files (29x speedup)
  • 🔗 Dependency graph — Import resolution with circular dependency detection
  • 🚀 Parallel — Worker pool with configurable concurrency

Install

The current public release is v1.0.0. Download available stable archives and checksums.txt from the GitHub Releases page; it is the authoritative record of public availability.

v1.0.1 is a draft-validation release and is not a public installation source.

Draft v1.0.1 validation targets

OS Arch Archive Verified on
Linux amd64 sunset_1.0.1_linux_amd64.tar.gz ubuntu-24.04
Linux arm64 sunset_1.0.1_linux_arm64.tar.gz ubuntu-24.04-arm
macOS amd64 sunset_1.0.1_darwin_amd64.tar.gz macos-15-intel
macOS arm64 sunset_1.0.1_darwin_arm64.tar.gz macos-15
Windows amd64 sunset_1.0.1_windows_amd64.zip windows-2025

Targets are built and verified on the named GitHub Actions runner environments. No minimum OS or libc compatibility is claimed.

Draft verification snippets

These maintainer snippets verify a staged v1.0.1 archive. They require SUNSET_BASE_URL to point at a staging or loopback server and are not public installation instructions.

SHA-256 detects corruption and byte mismatches but does NOT authenticate the publisher. Signing and attestations remain future work.

Linux

#!/usr/bin/env bash
# install-linux.sh — Verify a staged sunset v1.0.1 archive on Linux (amd64 or arm64).
#
# Downloads ONLY the target archive plus checksums.txt, verifies the matching
# SHA-256 entry, extracts the archive, and runs `sunset version`.
#
# v1.0.1 is a draft-validation artifact, not a public installation source.
# Set SUNSET_BASE_URL to the staging or loopback server:
#
#   SUNSET_BASE_URL=http://127.0.0.1:8080 ./install-linux.sh [amd64|arm64]
#
# SHA-256 detects corruption and byte mismatches.  It does NOT authenticate
# the publisher; signing and attestations remain future work.
set -euo pipefail

VERSION="1.0.1"
BASE_URL="${SUNSET_BASE_URL:?SUNSET_BASE_URL is required for draft verification}"
ARCH="${1:-$(uname -m)}"

case "$ARCH" in
    x86_64)         ARCH="amd64" ;;
    aarch64|arm64)  ARCH="arm64" ;;
    amd64|arm64)    ;;
    *)
        echo "install-linux: unsupported arch '$ARCH' (expected amd64 or arm64)" >&2
        exit 2
        ;;
esac

ARCHIVE="sunset_${VERSION}_linux_${ARCH}.tar.gz"
INSTALL_DIR="${SUNSET_INSTALL_DIR:-/usr/local/bin}"

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

echo "==> Downloading $ARCHIVE and checksums.txt"
curl -fsSL -o "$WORK/$ARCHIVE"      "${BASE_URL}/${ARCHIVE}"
curl -fsSL -o "$WORK/checksums.txt" "${BASE_URL}/checksums.txt"

echo "==> Verifying SHA-256 (selecting the $ARCHIVE entry)"
( cd "$WORK" && grep -F "$ARCHIVE" checksums.txt | sha256sum --check - )

echo "==> Extracting and installing to $INSTALL_DIR"
tar -xzf "$WORK/$ARCHIVE" -C "$WORK"
if [ -w "$INSTALL_DIR" ]; then
    mv "$WORK/sunset" "$INSTALL_DIR/sunset"
else
    sudo mv "$WORK/sunset" "$INSTALL_DIR/sunset"
fi

echo "==> Verifying install"
"$INSTALL_DIR/sunset" version

macOS

#!/usr/bin/env bash
# install-macos.sh — Verify a staged sunset v1.0.1 archive on macOS (amd64 or arm64).
#
# Downloads ONLY the target archive plus checksums.txt, verifies the matching
# SHA-256 entry, extracts the archive, and runs `sunset version`.
#
# v1.0.1 is a draft-validation artifact, not a public installation source.
# Set SUNSET_BASE_URL to the staging or loopback server:
#
#   SUNSET_BASE_URL=http://127.0.0.1:8080 ./install-macos.sh [amd64|arm64]
#
# SHA-256 detects corruption and byte mismatches.  It does NOT authenticate
# the publisher; signing and attestations remain future work.
set -euo pipefail

VERSION="1.0.1"
BASE_URL="${SUNSET_BASE_URL:?SUNSET_BASE_URL is required for draft verification}"
ARCH="${1:-$(uname -m)}"

case "$ARCH" in
    x86_64)      ARCH="amd64" ;;
    arm64)       ARCH="arm64" ;;
    amd64|arm64) ;;
    *)
        echo "install-macos: unsupported arch '$ARCH' (expected amd64 or arm64)" >&2
        exit 2
        ;;
esac

ARCHIVE="sunset_${VERSION}_darwin_${ARCH}.tar.gz"
INSTALL_DIR="${SUNSET_INSTALL_DIR:-/usr/local/bin}"

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

echo "==> Downloading $ARCHIVE and checksums.txt"
curl -fsSL -o "$WORK/$ARCHIVE"      "${BASE_URL}/${ARCHIVE}"
curl -fsSL -o "$WORK/checksums.txt" "${BASE_URL}/checksums.txt"

echo "==> Verifying SHA-256 (selecting the $ARCHIVE entry)"
( cd "$WORK" && grep -F "$ARCHIVE" checksums.txt | shasum -a 256 -c - )

echo "==> Extracting and installing to $INSTALL_DIR"
tar -xzf "$WORK/$ARCHIVE" -C "$WORK"
if [ -w "$INSTALL_DIR" ]; then
    mv "$WORK/sunset" "$INSTALL_DIR/sunset"
else
    sudo mv "$WORK/sunset" "$INSTALL_DIR/sunset"
fi

echo "==> Verifying install"
"$INSTALL_DIR/sunset" version

Windows

# install-windows.ps1 — Verify a staged sunset v1.0.1 archive on Windows (amd64).
#
# Downloads ONLY the target archive plus checksums.txt, verifies the matching
# SHA-256 digest, extracts the archive, and runs `sunset version`.
#
# v1.0.1 is a draft-validation artifact, not a public installation source.
# Set SUNSET_BASE_URL to the staging or loopback server:
#
#   $env:SUNSET_BASE_URL = "http://127.0.0.1:8080"; .\install-windows.ps1
#
# SHA-256 detects corruption and byte mismatches.  It does NOT authenticate
# the publisher; signing and attestations remain future work.
$ErrorActionPreference = "Stop"

$Version    = "1.0.1"
$BaseUrl    = $env:SUNSET_BASE_URL
if (-not $BaseUrl) { throw "SUNSET_BASE_URL is required for draft verification" }
$Arch       = "amd64"

$Archive    = "sunset_${Version}_windows_${Arch}.zip"
$InstallDir = if ($env:SUNSET_INSTALL_DIR) { $env:SUNSET_INSTALL_DIR } else { "$env:LOCALAPPDATA\Programs\sunset" }

$Work = Join-Path $env:TEMP "sunset-install-$([guid]::NewGuid())"
New-Item -ItemType Directory -Path $Work -Force | Out-Null

try {
    Write-Host "==> Downloading $Archive and checksums.txt"
    Invoke-WebRequest -Uri "$BaseUrl/$Archive"      -OutFile "$Work\$Archive"      -UseBasicParsing
    Invoke-WebRequest -Uri "$BaseUrl/checksums.txt" -OutFile "$Work\checksums.txt" -UseBasicParsing

    Write-Host "==> Verifying SHA-256 (selecting the $Archive entry)"
    $line = Get-Content "$Work\checksums.txt" | Where-Object { $_ -like "*$Archive" }
    if (-not $line) { throw "no checksum entry for $Archive in checksums.txt" }
    $expected = ($line -split '\s+')[0].Trim()
    $actual   = (Get-FileHash -Algorithm SHA256 "$Work\$Archive").Hash
    if ($actual.ToLower() -ne $expected.ToLower()) {
        throw "checksum mismatch for $Archive (expected $expected, got $actual)"
    }

    Write-Host "==> Extracting and installing to $InstallDir"
    Expand-Archive -Path "$Work\$Archive" -DestinationPath $Work -Force
    New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
    Move-Item -Path "$Work\sunset.exe" -Destination "$InstallDir\sunset.exe" -Force

    Write-Host "==> Verifying install"
    & "$InstallDir\sunset.exe" version
}
finally {
    Remove-Item -Recurse -Force $Work -ErrorAction SilentlyContinue
}

Development installation from source

Clone the public repository and install from the checkout. This follows the main branch and is intended for development; use a published release for a stable installation.

git clone https://github.com/enolalabs/sunset.git
cd sunset
go install ./cmd/sunset
sunset version

Import the library:

import "github.com/enolalabs/sunset/pkg/sunset"

Build locally

git clone https://github.com/enolalabs/sunset.git
cd sunset
make build
# Binary: bin/sunset

Quick Start

# Parse current directory
sunset parse .

# Parse a specific project
sunset parse /path/to/project

# Full CST output
sunset parse . --detail full

# Exclude test files
sunset parse . --exclude "*_test.go,*.test.ts"

# Incremental update (only changed files)
sunset update

# List supported languages
sunset languages

# Clean generated files
sunset clean

Output Structure

your-project/
└── .sunset/
    ├── output/
    │   ├── index.md              # Project overview
    │   └── files/
    │       ├── main.go.md         # Per-file documentation
    │       ├── handler_user.go.md
    │       └── utils_helper.py.md
    └── cache/
        └── cache.json             # File hashes for incremental updates

Example: Per-file Markdown

---
file: main.go
language: go
package: main
lines: 28
function_count: 2
type_count: 0
import_count: 3
tags:
  - has-functions
  - has-imports
---
## Functions

### main
- **Signature**: `func main()`
- **Line**: 15-19
- **Doc**: main starts the HTTP server and registers routes.

### setupRouter
- **Signature**: `func setupRouter() *http.ServeMux`
- **Line**: 23-27
- **Doc**: setupRouter creates and configures the HTTP router.

## Imports

| Import | Line |
|---|---|
| fmt | 5 |
| net/http | 6 |

CLI Reference

Command Description
sunset parse <path> Parse files and generate Markdown
sunset update [path] Incremental update (re-parse changed only)
sunset languages List supported languages
sunset version Show version
sunset clean [path] Remove cache and output

Parse Flags

Flag Default Description
--output <path>/.sunset/output Output directory
--detail summary summary or full (full CST)
--exclude Comma-separated glob patterns
--concurrency NumCPU Max parallel parsers
--max-depth 0 (unlimited) Tree depth limit for full mode
--no-cache false Force full re-parse
--quiet false Suppress non-error output

Supported Languages

Language Extensions Docstring Format
Go .go // Comment above declarations
JavaScript .js, .jsx /** JSDoc */
TypeScript .ts, .tsx /** JSDoc */
Python .py """docstring""" inside body

Adding a new language requires only one file — see CONTRIBUTING.md.

As a Go Library

package main

import (
    "fmt"
    "github.com/enolalabs/sunset/pkg/sunset"
)

func main() {
    result, err := sunset.ParseFile("main.go")
    if err != nil {
        panic(err)
    }
    defer result.Close()

    root := result.Tree.RootNode()
    fmt.Printf("Language: %s\n", result.Language)
    fmt.Printf("Root: %s (%d children)\n", root.Type(), root.ChildCount())

    // Walk the tree
    result.Tree.Walk(func(node *sunset.Node, depth int) bool {
        if node.Type() == "function_declaration" {
            position := node.Position()
            fmt.Printf("Function at %d:%d\n", position.Row, position.Column)
        }
        return true
    })
}

Performance

Real-world Benchmarks

Tested on popular open-source repositories (single run, --no-cache, default concurrency)

Repository Language Files Functions Types Duration Files/sec Output
kubernetes/kubernetes Go 12,615 104,925 18,165 70.4s 179 69 MB
golang/go Go 10,302 89,294 17,999 57.3s 180 55 MB
microsoft/vscode TypeScript 10,002 31,640 16,404 59.1s 169 44 MB
facebook/react JS/TS 4,345 12,885 535 9.3s 467 18 MB
tensorflow/tensorflow Python/Go 3,194 64,695 6,864 125.9s 25 25 MB
django/django Python/JS 2,945 31,928 10,951 6.2s 475 15 MB

Total: 43,403 files parsed · 335,367 functions extracted · 70,918 types · 226 MB of structured documentation

Micro-benchmarks (50 files)

Metric Value
Full parse ~20ms
Incremental (1 changed) ~0.7ms (29x faster)
Memory per file ~66KB
Walker: 5,800 nodes ~1.1ms

Development

make test           # Run all tests with race detector
make lint           # Run golangci-lint
make bench          # Run benchmarks
make test-coverage  # Generate coverage report
make clean          # Remove build artifacts

License

MIT

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages