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.
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.
- Node.js ≥ 20
- An Anthropic API key (
ANTHROPIC_API_KEY) or an OpenAI API key (OPENAI_API_KEY) - A GitHub repository with Markdown docs
npx @dev_ap/docsync initThis 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"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
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.
This walks through the full flow from scratch using a small example repo.
mkdir my-project && cd my-project
git init
mkdir src docsCreate 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"npx @dev_ap/docsync init --yesYou 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"npx @dev_ap/docsync symbols src/auth.tsprocessLogin [1-3] (function)
AuthError [5-10] (class)
You can also inspect .autodocs/map.json directly to see the symbol→doc mappings.
Create a new branch and modify the function:
git checkout -b add-mfaEdit 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"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-runExpected 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...
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.
Create docsync.config.json in your repo root to override defaults:
If the file is absent, docsync uses built-in defaults (Anthropic, claude-sonnet-4-6).
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.
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 |
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.tsdocsync 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.
docsync creates two files under .autodocs/ that should be committed to your repository:
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
}
]
}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] }
]
}Enable debug-level logging:
AUTODOCS_DEBUG=1 npx @dev_ap/docsync check --dry-runCommon 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 |
git clone https://github.com/Alok650/docsync
cd docsync
pnpm install
pnpm test # run all tests
pnpm dev # watch mode build| 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 |
- Install the tree-sitter grammar:
pnpm add tree-sitter-<lang> - Add the WASM path to
WASM_PATHinsrc/extractor/loader.ts - Add the file extensions to
EXTENSION_LANGUAGE_MAPinsrc/extractor/index.ts - Implement
extract<Lang>Symbols(tree, filePath)insrc/extractor/languages/<lang>.ts - Add a case to the
switchinparseSymbols - Add fixture files and tests under
tests/extractor/
- Add the provider name to
LLM_PROVIDERinsrc/config.ts - Implement
LLMClientinsrc/llm/<provider>.ts - Add a case to the
switchinsrc/llm/factory.ts
ISC
{ "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 } }