Skip to content

Alok650/docsync

Repository files navigation

docsync

docsync keeps your technical documentation in sync with your code. When a pull request changes a public function, class, or type, docsync identifies which documentation sections describe that symbol and posts a PR comment with proposed updates — generated by an LLM and ready to review.


How it works

PR opened / synchronized
        │
        ▼
  git diff → changed symbols (AST-level, not line ranges)
        │
        ▼
  Tier 1: structural index (.autodocs/lookup/)  ──found──▶  DocRef[]
        │ not found
        ▼
  Tier 2: BM25 lexical fallback (doc corpus)   ──found──▶  DocRef[]
        │
        ▼
  LLM agent: before + after code + current doc section → updated body
        │
        ▼
  PR comment posted (find-or-update, one comment per PR)

Supported languages: TypeScript, JavaScript (including .tsx, .jsx, .mjs, .cjs), Python.


Prerequisites

  • Node.js ≥ 20
  • An Anthropic API key (ANTHROPIC_API_KEY) or an OpenAI API key (OPENAI_API_KEY)
  • A GitHub repository with Markdown docs

Quick start

1. Run init in your repo

npx @dev_ap/docsync init

This will:

  • Ask where your docs and code directories are (defaults: docs/, src/)
  • Scan all code files and match symbols to doc sections via BM25
  • Write .autodocs/map.json (full bidirectional index) and .autodocs/lookup/ (sharded lookup)
  • Write .github/workflows/docsync.yml

Commit everything it creates:

git add .autodocs/ .github/workflows/docsync.yml
git commit -m "chore: add docsync index and workflow"

2. Add your API key to GitHub secrets

Go to Settings → Secrets and variables → Actions in your repository and add:

  • ANTHROPIC_API_KEY — if using Anthropic (default)
  • OPENAI_API_KEY — if using OpenAI

3. Open a pull request

docsync will run automatically on every PR. If any changed symbols map to documentation sections, you'll see a comment like this:

docsync detected doc sections that may need updating.

File: `docs/auth.md` — `## Login Flow`

  - The `processLogin` function accepts a username and password.
  + The `processLogin` function accepts a username, password, and an
  + optional MFA token. If omitted, MFA verification is skipped.

End-to-end local walkthrough

This walks through the full flow from scratch using a small example repo.

Step 1 — Create a sample project

mkdir my-project && cd my-project
git init
mkdir src docs

Create src/auth.ts:

export function processLogin(username: string, password: string): boolean {
  // validate credentials
  return true
}

export class AuthError extends Error {
  constructor(message: string) {
    super(message)
    this.name = 'AuthError'
  }
}

Create docs/auth.md:

# Authentication

## Login Flow

The `processLogin` function accepts a username and password.
Returns `true` on success, throws `AuthError` on failure.

## Error Handling

`AuthError` is thrown when authentication fails. It extends the
built-in `Error` class and sets `name` to `"AuthError"`.

Commit both:

git add . && git commit -m "initial"

Step 2 — Initialize docsync

npx @dev_ap/docsync init --yes

You should see:

ℹ Scanning 1 code files and docs in /my-project/docs...
✔ 2 symbols indexed, 2 mapped to doc sections.
ℹ Map written to .autodocs/map.json
ℹ Workflow written to .github/workflows/docsync.yml
ℹ Add ANTHROPIC_API_KEY or OPENAI_API_KEY to your GitHub repository secrets to activate docsync.

Commit the generated files:

git add .autodocs/ .github/workflows/docsync.yml
git commit -m "chore: docsync init"

Step 3 — Inspect what was indexed

npx @dev_ap/docsync symbols src/auth.ts
processLogin                   [1-3]  (function)
AuthError                      [5-10] (class)

You can also inspect .autodocs/map.json directly to see the symbol→doc mappings.

Step 4 — Simulate a code change

Create a new branch and modify the function:

git checkout -b add-mfa

Edit src/auth.ts:

export function processLogin(
  username: string,
  password: string,
  mfa?: string,            // ← new optional MFA token
): boolean {
  return true
}

Commit:

git add src/auth.ts && git commit -m "feat: add optional MFA parameter to processLogin"

Step 5 — Run a dry-run check locally

Set the required environment variables (simulating CI context):

export GITHUB_TOKEN=ghp_your_token_here
export GITHUB_REPOSITORY=your-org/my-project
export PR_NUMBER=1
export GITHUB_BASE_REF=main
export ANTHROPIC_API_KEY=sk-ant-...

Run the check without posting to GitHub:

npx @dev_ap/docsync check --dry-run

Expected output:

ℹ Running docsync check...
ℹ Found 1 proposed update(s).

docs/auth.md — ## Login Flow

--- before
The `processLogin` function accepts a username and password...

+++ after
The `processLogin` function accepts a username, password, and an
optional MFA token. If omitted, MFA verification is skipped...

Step 6 — Post to a real PR

Without --dry-run, docsync posts the comment directly to the PR:

npx @dev_ap/docsync check
ℹ Running docsync check...
ℹ Found 1 proposed update(s).
✔ PR comment posted.

Configuration

Create docsync.config.json in your repo root to override defaults:

{
  "docs": "docs",          // path to docs directory (default: "docs")
  "code": "src",           // path to code directory (default: "src")
  "llm": {
    "provider": "anthropic",          // "anthropic" or "openai"
    "model": "claude-sonnet-4-6"      // any model supported by the provider
  }
}

If the file is absent, docsync uses built-in defaults (Anthropic, claude-sonnet-4-6).


Commands

docsync init

Scans your codebase and docs, writes the symbol index, and generates the GitHub Actions workflow.

Options:
  --docs <dir>   Path to docs directory (default: "docs")
  --code <dir>   Path to code directory (default: "src")
  --yes          Skip interactive prompts and use defaults

Run this once when setting up a repo, and again after adding new documentation sections.

docsync check

Diffs the current branch against the base, finds doc sections that need updating, and posts a PR comment.

Options:
  --dry-run   Print proposed updates to stdout instead of posting to GitHub

Requires these environment variables (set automatically by the generated GitHub Actions workflow):

Variable Description
GITHUB_TOKEN Token with pull-requests: write permission
GITHUB_REPOSITORY owner/repo (e.g. acme/api)
PR_NUMBER Pull request number
GITHUB_BASE_REF Base branch name (e.g. main)
ANTHROPIC_API_KEY Required if provider is anthropic
OPENAI_API_KEY Required if provider is openai

docsync symbols <file>

Lists all public symbols extracted from a source file. Useful for verifying docsync can parse your code.

npx @dev_ap/docsync symbols src/auth/login.ts

GitHub Actions workflow

docsync init writes this workflow to .github/workflows/docsync.yml:

name: docsync
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  docsync:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0          # required — git diff needs full history
      - uses: Alok650/docsync@v1
        with:
          anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}

You can also use the action directly in an existing workflow:

- uses: actions/checkout@v4
  with:
    fetch-depth: 0
- uses: Alok650/docsync@v1
  with:
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}

fetch-depth: 0 is required so that git diff <base>...HEAD can reach the base branch.


Index files

docsync creates two files under .autodocs/ that should be committed to your repository:

map.json

Full bidirectional index — maps every symbol to its source file and documentation sections. Rebuilt by docsync init or updateMapForChangedFiles in CI.

{
  "version": 1,
  "mappings": [
    {
      "symbol": "processLogin",
      "file": "/abs/path/to/src/auth.ts",
      "docs": [
        { "file": "docs/auth.md", "section": "## Login Flow", "lines": [5, 9] }
      ],
      "fingerprint": "a3f1e2d4c5b6..."   // sha256 of source text, for incremental updates
    }
  ]
}

lookup/<shard>.json

Compact sharded lookup index used by docsync check (the CI hot path). Each file covers 1/256th of all symbols, keyed by the first two hex characters of sha256(symbolName). A PR touching 5 symbols reads at most 5 shard files regardless of total repo size.

// lookup/3a.json — all symbols whose sha256 starts with "3a"
{
  "processLogin": [
    { "file": "docs/auth.md", "section": "## Login Flow", "lines": [5, 9] }
  ]
}

Debugging

Enable debug-level logging:

AUTODOCS_DEBUG=1 npx @dev_ap/docsync check --dry-run

Common issues:

Symptom Cause Fix
Missing GitHub context Required env vars not set Set GITHUB_TOKEN, GITHUB_REPOSITORY, PR_NUMBER
No symbols found File extension not supported, or no exported symbols Run docsync symbols <file> to verify
No doc updates needed (unexpected) Symbol not mapped in index Re-run docsync init after adding doc sections
Workflow fails on git diff fetch-depth: 0 missing Ensure fetch-depth: 0 is set in the checkout step
Empty comment posted BM25 score too low Lower BM25.MATCH_MIN_SCORE in src/defaults.ts or add explicit entries to map.json

Contributing

Development setup

git clone https://github.com/Alok650/docsync
cd docsync
pnpm install
pnpm test          # run all tests
pnpm dev           # watch mode build

Key files for contributors

File Purpose
src/defaults.ts All tunable constants — BM25 params, token limits, shard width
src/constants.ts File paths and directory names
src/extractor/languages/ Add support for new languages here
src/agent/prompts.ts The system prompt sent to the LLM
src/llm/ Add a new LLM provider by implementing LLMClient
src/pipeline/check.ts The three-stage CI pipeline

Adding a language

  1. Install the tree-sitter grammar: pnpm add tree-sitter-<lang>
  2. Add the WASM path to WASM_PATH in src/extractor/loader.ts
  3. Add the file extensions to EXTENSION_LANGUAGE_MAP in src/extractor/index.ts
  4. Implement extract<Lang>Symbols(tree, filePath) in src/extractor/languages/<lang>.ts
  5. Add a case to the switch in parseSymbols
  6. Add fixture files and tests under tests/extractor/

Adding an LLM provider

  1. Add the provider name to LLM_PROVIDER in src/config.ts
  2. Implement LLMClient in src/llm/<provider>.ts
  3. Add a case to the switch in src/llm/factory.ts

License

ISC

About

Keeps your documentation in sync with your code — LLM-powered doc updates on every PR

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors