docsync
Finds the sentences in your docs that stopped being true when the code they describe changed.
go get github.com/ubgo/docsync
- Language
- Go
- Go version
- 1.26
- Core deps
- 0 direct
- Latest tag
- v0.1.6
- License
- Apache-2.0
- Go modules
- 6
How it fits together
Highlights from the README
- Cites by id, not by path and line. A block is marked once in the file that owns it; the id travels with it through moves, renames and reformatting, so a doc keeps pointing at the right code.
- Nothing is copied. Values, code snippets and permalinks are rendered from live source at build time, so a doc cannot hold a stale copy.
- Every finding says what to do. Each one carries the diff since the sentence was last approved and the exact command to approve it or the place to edit it.
- An approval is for one sentence, and it is recorded. An ack names the sentence, the block’s hash, and who approved it; rewriting the sentence needs a new one. Over MCP an agent may ack only by naming the human who delegated it.
- Built for agents.
ds mcpgives agentsmap,find,read,context,impact,check,defandack, with every payload fenced so repository text is never read as an instruction;ds init --agentswires it up. - Boring core. The library is Go’s standard library only, never writes a file or opens the network itself, holds 100% statement coverage, and is specified by a conformance suite rather than by its own behaviour.
Code from the README
sys, err := docsync.New(
docsync.WithFS(os.DirFS(repo)),
docsync.WithConfig(cfg), // parsed by you; the library never reads files it was not given
docsync.WithPrevious(ledger, refs), // the committed state to diff against; zero for a first run
docsync.WithAcks(acks),
docsync.WithOldContent(gitShow), // func(ledger.Row) (string, bool): the body a previous row had, for diffs and change classes
)
report, err := sys.Check(ctx, docsync.CheckOptions{})
for _, f := range report.Findings {
fmt.Println(f.State, f.Doc, f.Line, f.Remedy)
}
res, err := sys.Define(ctx, "internal/store/write.go#Store.Persist", docsync.DefineOptions{Owner: "@auth"})
// res.Edit is a proposed change to the source file; you decide whether to apply itFrom the docsync README.
At a glance (from the README)
| Package | Holds |
|---|---|
directive | the one directive grammar: ds:<verb> key=value…, comment and link carriers, continuation folding, parse and format round-trip |
id | ids as <label>-<8-char suffix>; the suffix is the identity, the label is a hint |
block | blocks, references, kinds, carriers, stability policies, change classes |
extract | the built-in tiers: markdown and HTML, AsciiDoc and reStructuredText (the document tier), config (yaml, toml, ini, env, properties), heuristic code, plain text; Locate for binding a symbol or line without a directive; exported hooks (ScanCode, Prelude, NewDef) for grammar tiers outside the root |
pick | pick= schemes for narrowing a block to one value or range |
sentence | binding a link to the sentence, list item, or table cell that contains it |
scan | walking an fs.FS under globs, remote file= defs, duplicate detection |
ledger | the committed TSV state: ledger, reverse index, append-only acks |
match | previous ledger against current scan: ok, moved, changed, moved-unmarked, rewritten, deleted, new; change classification |
check | the findings engine: every state in the spec’s table with severity, remedy, and exit code |
render | build-time expansion of directives to permalinks, values, code, chains, tables |
config | .ds/config.toml in the TOML subset the spec uses |
procplugin | both sides of the process-plugin protocol for ds-pick-*, ds-resolve-*, ds-records-*, ds-<verb> executables |
workspace | merging published ledgers and refs across repositories, the index layout, JUnit outcomes for assert= |
records | the frontmatter record source for ds:table: a directory of markdown files, filtered by kind, where, sort, cols, limit |
ext/structured (own module) | the structured tier: YAML (yaml.v3), TOML (go-toml), and HCL (hcl/v2) extractors that bind keys, tables, blocks, and attributes by parsed extent; structured.All() registers the three |
ext/treesitter (own module, cgo) | the syntax tier: Go, TypeScript, TSX, JavaScript, Python, and SQL through tree-sitter; a def binds to the next declaration as the grammar sees it, nested declarations become Class.method, single literals are values, and hashes are taken over the token stream so formatters never cry wolf; treesitter.New(Grammar{…}) registers any other grammar |
ext/records/sqlite (own module) | the SQLite record source for ds:table on modernc.org/sqlite (pure Go); kind= names the table, the rest of the query is the shared records language |
integrations/github | the composite GitHub Action and the three workflows from spec §24: pull request comments, publish after merge, the nightly job that alone enables --run and --resolve |
integrations/hugo, integrations/docusaurus, editors/vscode, .pre-commit-hooks.yaml | the Hugo module reading ds export hugo data, the Docusaurus remark plugin over ds render plus freshness from ds status --json, the VS Code client for ds lsp, and pre-commit hooks for impact --staged and report --literals (at the repository root, which is the only place pre-commit looks) |
| root | New with functional options; Scan, Check, Snapshot, Render, Define (returns an Edit), Facts, Why, Map, Context, Impact, Find, Read, Ack, Triage, Audit, Rename, Graph, Blame, Report, Adopt |