@sledorze/cairn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +77950 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +168 -0
- package/dist/config.js.map +1 -0
- package/dist/core/Config.d.ts +80 -0
- package/dist/core/Config.d.ts.map +1 -0
- package/dist/core/Config.js +135 -0
- package/dist/core/Config.js.map +1 -0
- package/dist/core/DocSummaries.d.ts +42 -0
- package/dist/core/DocSummaries.d.ts.map +1 -0
- package/dist/core/DocSummaries.js +74 -0
- package/dist/core/DocSummaries.js.map +1 -0
- package/dist/core/MarkdownLinks.bench.d.ts +2 -0
- package/dist/core/MarkdownLinks.bench.d.ts.map +1 -0
- package/dist/core/MarkdownLinks.bench.js +81 -0
- package/dist/core/MarkdownLinks.bench.js.map +1 -0
- package/dist/core/MarkdownLinks.d.ts +51 -0
- package/dist/core/MarkdownLinks.d.ts.map +1 -0
- package/dist/core/MarkdownLinks.js +115 -0
- package/dist/core/MarkdownLinks.js.map +1 -0
- package/dist/core/SummaryTree.bench.d.ts +2 -0
- package/dist/core/SummaryTree.bench.d.ts.map +1 -0
- package/dist/core/SummaryTree.bench.js +70 -0
- package/dist/core/SummaryTree.bench.js.map +1 -0
- package/dist/core/SummaryTree.d.ts +45 -0
- package/dist/core/SummaryTree.d.ts.map +1 -0
- package/dist/core/SummaryTree.js +196 -0
- package/dist/core/SummaryTree.js.map +1 -0
- package/dist/core/glob.bench.d.ts +2 -0
- package/dist/core/glob.bench.d.ts.map +1 -0
- package/dist/core/glob.bench.js +62 -0
- package/dist/core/glob.bench.js.map +1 -0
- package/dist/core/glob.d.ts +7 -0
- package/dist/core/glob.d.ts.map +1 -0
- package/dist/core/glob.js +52 -0
- package/dist/core/glob.js.map +1 -0
- package/dist/core/paths.d.ts +3 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +6 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/init/content.d.ts +3 -0
- package/dist/init/content.d.ts.map +1 -0
- package/dist/init/content.js +127 -0
- package/dist/init/content.js.map +1 -0
- package/dist/init/generate.d.ts +14 -0
- package/dist/init/generate.d.ts.map +1 -0
- package/dist/init/generate.js +121 -0
- package/dist/init/generate.js.map +1 -0
- package/dist/io/DocsFs.d.ts +30 -0
- package/dist/io/DocsFs.d.ts.map +1 -0
- package/dist/io/DocsFs.js +95 -0
- package/dist/io/DocsFs.js.map +1 -0
- package/dist/program/CheckLinks.d.ts +27 -0
- package/dist/program/CheckLinks.d.ts.map +1 -0
- package/dist/program/CheckLinks.js +122 -0
- package/dist/program/CheckLinks.js.map +1 -0
- package/dist/program/CheckSummaries.d.ts +38 -0
- package/dist/program/CheckSummaries.d.ts.map +1 -0
- package/dist/program/CheckSummaries.js +198 -0
- package/dist/program/CheckSummaries.js.map +1 -0
- package/dist/program/JsonReport.d.ts +14 -0
- package/dist/program/JsonReport.d.ts.map +1 -0
- package/dist/program/JsonReport.js +12 -0
- package/dist/program/JsonReport.js.map +1 -0
- package/dist/program/locale.d.ts +13 -0
- package/dist/program/locale.d.ts.map +1 -0
- package/dist/program/locale.js +8 -0
- package/dist/program/locale.js.map +1 -0
- package/package.json +78 -0
- package/schema/cairn.schema.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stéphane Le Dorze
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.svg" width="120" height="120" alt="Cairn logo">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# @sledorze/cairn
|
|
6
|
+
|
|
7
|
+
Keep your documentation summaries honest. `cairn` enforces a hierarchical,
|
|
8
|
+
content-hashed summary tree over any docs folder — and fails CI when a summary drifts
|
|
9
|
+
out of date or a link goes dead.
|
|
10
|
+
|
|
11
|
+
## The problem
|
|
12
|
+
|
|
13
|
+
Docs summaries rot. Someone edits `guide.md`, forgets to update `guide.summary.md`, and
|
|
14
|
+
the digest now lies. The usual fix — "regenerate summaries when the source is newer" —
|
|
15
|
+
compares modification times. But **git does not preserve mtimes**: after a clone or a CI
|
|
16
|
+
checkout, every file looks freshly written, so a time-based check silently passes on
|
|
17
|
+
summaries that are actually stale. The bug you meant to catch ships anyway.
|
|
18
|
+
|
|
19
|
+
`cairn` checks **content**, not clocks. Each summary embeds the SHA-256 of the
|
|
20
|
+
source it summarizes as its first line:
|
|
21
|
+
|
|
22
|
+
```markdown
|
|
23
|
+
<!-- source-sha256: 3f9a…(64 hex) -->
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The checker recomputes the source hash and compares. Mismatch means stale, missing means
|
|
27
|
+
missing — and it behaves identically on your laptop and in CI, before and after a clone.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pnpm add -D @sledorze/cairn
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npx cairn check
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The round-trip when you touch a doc:
|
|
42
|
+
|
|
43
|
+
1. **Edit** `docs/guide.md`.
|
|
44
|
+
2. `npx cairn check` **flags it stale** — the source hash no longer matches the
|
|
45
|
+
stamp in `guide.summary.md`.
|
|
46
|
+
3. **Write** the updated `guide.summary.md` (and update any parent `_SUMMARY.md`).
|
|
47
|
+
4. **Stamp**: `npx cairn check --summaries-only --stamp` rewrites the
|
|
48
|
+
`source-sha256` lines bottom-up.
|
|
49
|
+
5. **Check** again — `npx cairn check` exits 0. Green.
|
|
50
|
+
|
|
51
|
+
You author the prose; the tool verifies and stamps. It never invents content.
|
|
52
|
+
|
|
53
|
+
### Commands
|
|
54
|
+
|
|
55
|
+
| Command | What it does |
|
|
56
|
+
| ----------------------------------------- | -------------------------------------------------------- |
|
|
57
|
+
| `cairn check` | Check summaries + links; exit 1 on any problem |
|
|
58
|
+
| `cairn check --summaries-only` | Check only summary freshness |
|
|
59
|
+
| `cairn check --links-only` | Check only Markdown links |
|
|
60
|
+
| `cairn check --links-only --fix` | Auto-repair unambiguous dead links |
|
|
61
|
+
| `cairn check --summaries-only --stamp` | Rewrite `source-sha256` of existing summaries, bottom-up |
|
|
62
|
+
| `cairn init --agent claude\|copilot\|all` | Scaffold agent guidance files |
|
|
63
|
+
|
|
64
|
+
## The two summary kinds
|
|
65
|
+
|
|
66
|
+
**File summaries** — every Markdown file longer than the threshold (default 30 lines) gets
|
|
67
|
+
a sibling `X.summary.md`: a fast-to-read digest of the **current** content of `X.md`.
|
|
68
|
+
Front-load the thesis and the numbers; a reader should get the gist in ~10 seconds.
|
|
69
|
+
|
|
70
|
+
**Directory summaries** — every directory in scope gets a `_SUMMARY.md` that acts as a map.
|
|
71
|
+
It aggregates its direct docs (each doc's `.summary.md` if the doc is big, else the doc
|
|
72
|
+
itself) plus the `_SUMMARY.md` of each direct sub-directory, and it links to **every**
|
|
73
|
+
direct child file and sub-directory (the link-completeness rule).
|
|
74
|
+
|
|
75
|
+
### Why bottom-up
|
|
76
|
+
|
|
77
|
+
A directory summary's hash is computed over a manifest of its children's hashes — a Merkle
|
|
78
|
+
tree. So summaries must be (re)written and stamped **leaves-first**: file summaries, then
|
|
79
|
+
directories deepest-first, then stamp. Stamp top-down and parents capture stale child
|
|
80
|
+
hashes. The `--stamp` command walks the tree in the right order for you.
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
Drop a `.cairnrc.json` at the repo root (`cairn init` scaffolds one for you):
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"$schema": "./node_modules/@sledorze/cairn/schema/cairn.schema.json",
|
|
89
|
+
"roots": ["docs/**"],
|
|
90
|
+
"thresholdLines": 30,
|
|
91
|
+
"naming": {
|
|
92
|
+
"dirSummary": "_SUMMARY.md",
|
|
93
|
+
"fileSummarySuffix": ".summary.md"
|
|
94
|
+
},
|
|
95
|
+
"checks": { "summaries": true, "links": true },
|
|
96
|
+
"requireDirSummaries": true,
|
|
97
|
+
"ignore": ["**/node_modules/**"],
|
|
98
|
+
"stampCommand": "npx cairn check --summaries-only --stamp",
|
|
99
|
+
"locale": "en"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| Key | Meaning |
|
|
104
|
+
| -------------------------- | --------------------------------------------------------------------------------------- |
|
|
105
|
+
| `$schema` | JSON Schema URL for editor autocomplete/validation. Ignored by cairn. |
|
|
106
|
+
| `extends` | One or more config files to inherit from (see below) |
|
|
107
|
+
| `roots` | Documentation roots to scan (array; globs allowed). Default `docs/` |
|
|
108
|
+
| `thresholdLines` | Line count above which a file needs a `.summary.md`. Non-negative integer. Default `30` |
|
|
109
|
+
| `naming.dirSummary` | Directory summary filename. Default `_SUMMARY.md` |
|
|
110
|
+
| `naming.fileSummarySuffix` | Suffix for file summaries. Default `.summary.md` |
|
|
111
|
+
| `checks.summaries` | Enable summary freshness checking |
|
|
112
|
+
| `checks.links` | Enable Markdown link checking |
|
|
113
|
+
| `requireDirSummaries` | Require a `_SUMMARY.md` in every in-scope directory |
|
|
114
|
+
| `ignore` | Globs to exclude from scanning |
|
|
115
|
+
| `stampCommand` | Command agents should run to stamp hashes |
|
|
116
|
+
| `locale` | Prose locale for generated guidance: `en` or `fr` |
|
|
117
|
+
|
|
118
|
+
Config is validated strictly (via `effect/Schema`): an **unknown key or a wrong-typed
|
|
119
|
+
value fails loudly** with a file-scoped, actionable error, instead of being silently
|
|
120
|
+
ignored. A typo like `"thresholdLins"` is a bug you want caught, not a setting that
|
|
121
|
+
quietly reverts to the default.
|
|
122
|
+
|
|
123
|
+
### Sharing config with `extends`
|
|
124
|
+
|
|
125
|
+
A `.cairnrc.json` (or any file it extends) can inherit from one or more base presets:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{ "extends": "./base.cairnrc.json", "thresholdLines": 50 }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`extends` accepts a single path or an array; presets are applied first (in order), then
|
|
132
|
+
the extending file's own fields win. Use it to share a base config across packages in a
|
|
133
|
+
monorepo, or to publish an org-wide preset as its own package.
|
|
134
|
+
|
|
135
|
+
### Editor autocomplete
|
|
136
|
+
|
|
137
|
+
The `$schema` key (scaffolded by `cairn init`, generated from the same schema that
|
|
138
|
+
validates your config — see `schema/cairn.schema.json`) gives editors that understand
|
|
139
|
+
JSON Schema (VS Code, JetBrains, coc-json) inline docs, autocomplete, and a squiggle on
|
|
140
|
+
an invalid key — before you even run `cairn check`.
|
|
141
|
+
|
|
142
|
+
## Multi-agent guidance
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
npx cairn init --agent all
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`init` scaffolds the convention into whatever coding agents you use, so they follow it
|
|
149
|
+
automatically:
|
|
150
|
+
|
|
151
|
+
- **`.claude/rules/*.md`** — a path-scoped Claude rule (`paths:` frontmatter) loaded when an
|
|
152
|
+
agent touches your docs.
|
|
153
|
+
- **`CLAUDE.md`** — an `@AGENTS.md` import, upserted at the repo root. Claude Code
|
|
154
|
+
auto-loads `CLAUDE.md` at session start but never reads `AGENTS.md` on its own; without
|
|
155
|
+
this pointer the block below is invisible to it.
|
|
156
|
+
- **`.github/instructions/*.instructions.md`** — GitHub Copilot instructions with an
|
|
157
|
+
`applyTo:` glob.
|
|
158
|
+
- **`AGENTS.md`** — a block appended to the repo-wide agent guide.
|
|
159
|
+
- **`SKILL.md`** — the on-demand methodology for _writing good summaries_.
|
|
160
|
+
|
|
161
|
+
Pass `--agent claude`, `--agent copilot`, or `--agent all`.
|
|
162
|
+
|
|
163
|
+
## CI usage
|
|
164
|
+
|
|
165
|
+
Run the check as a required status. It is fast and clone-safe by design:
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
- run: pnpm add -D @sledorze/cairn
|
|
169
|
+
- run: npx cairn check
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A missing, stale, or broken summary exits non-zero and blocks the merge. Because freshness
|
|
173
|
+
is content-hashed rather than mtime-based, the result is identical to what you saw locally —
|
|
174
|
+
no false greens after checkout.
|
|
175
|
+
|
|
176
|
+
## Credits
|
|
177
|
+
|
|
178
|
+
Built on [Effect](https://effect.website).
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|