Skip to content

Commit b13a24d

Browse files
committed
Scaffold afdocs
0 parents  commit b13a24d

60 files changed

Lines changed: 6306 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
trim_trailing_whitespace = true
7+
charset = utf-8
8+
9+
[*.{ts,js,mjs,json,yml,yaml}]
10+
indent_style = space
11+
indent_size = 2
12+
13+
[Makefile]
14+
indent_style = tab

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
check:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
node-version: [20, 22]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- uses: actions/setup-node@v4
21+
with:
22+
node-version: ${{ matrix.node-version }}
23+
cache: npm
24+
25+
- run: npm ci
26+
27+
- name: Lint
28+
run: npm run lint
29+
30+
- name: Format check
31+
run: npm run format:check
32+
33+
- name: Test
34+
run: npm test
35+
36+
- name: Build
37+
run: npm run build

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dist/
3+
coverage/
4+
*.tsbuildinfo
5+
.DS_Store

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dist/
2+
coverage/
3+
node_modules/
4+
package-lock.json

.prettierrc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"singleQuote": true,
3+
"trailingComma": "all",
4+
"printWidth": 100,
5+
"semi": true
6+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Dachary Carey
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.

README.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# afdocs
2+
3+
Test your documentation site against the [Agent-Friendly Documentation Spec](https://agentdocsspec.com).
4+
5+
Agents don't use docs like humans. They hit truncation limits, get walls of CSS instead of content, can't follow cross-host redirects, and don't know about quality-of-life improvements like `llms.txt` or `.md` docs pages that would make life swell. Maybe this is because the industry has lacked guidance - until now.
6+
7+
afdocs runs 19 checks across 7 categories to evaluate how well your docs serve agent consumers.
8+
9+
## Quick start
10+
11+
```bash
12+
npx afdocs check https://docs.example.com
13+
```
14+
15+
Example output:
16+
17+
```
18+
Agent-Friendly Docs Check: https://react.dev
19+
20+
llms-txt
21+
✓ llms-txt-exists: llms.txt found at 1 location(s)
22+
✓ llms-txt-valid: llms.txt follows the proposed structure
23+
✓ llms-txt-size: llms.txt is 14,347 characters (under 50,000 threshold)
24+
✓ llms-txt-links-resolve: All 50 tested links resolve (177 total links)
25+
✓ llms-txt-links-markdown: 50/50 links point to markdown content (100%)
26+
27+
Summary
28+
5 passed, 14 skipped (19 total)
29+
```
30+
31+
## Install
32+
33+
```bash
34+
npm install afdocs
35+
```
36+
37+
## CLI usage
38+
39+
```bash
40+
# Run all checks
41+
afdocs check https://docs.example.com
42+
43+
# Run specific checks
44+
afdocs check https://docs.example.com --checks llms-txt-exists,llms-txt-valid,llms-txt-size
45+
46+
# JSON output
47+
afdocs check https://docs.example.com --format json
48+
49+
# Adjust thresholds
50+
afdocs check https://docs.example.com --pass-threshold 30000 --fail-threshold 80000
51+
```
52+
53+
### Options
54+
55+
| Option | Default | Description |
56+
|--------|---------|-------------|
57+
| `--format <format>` | `text` | Output format: `text` or `json` |
58+
| `--checks <ids>` | all | Comma-separated list of check IDs |
59+
| `--max-concurrency <n>` | `3` | Maximum concurrent HTTP requests |
60+
| `--request-delay <ms>` | `200` | Delay between requests |
61+
| `--max-links <n>` | `50` | Maximum links to test in link checks |
62+
| `--pass-threshold <n>` | `50000` | Size pass threshold (characters) |
63+
| `--fail-threshold <n>` | `100000` | Size fail threshold (characters) |
64+
65+
### Exit codes
66+
67+
- `0` if all checks pass or warn
68+
- `1` if any check fails
69+
70+
## Programmatic API
71+
72+
```ts
73+
import { runChecks, createContext, getCheck } from 'afdocs';
74+
75+
// Run all checks
76+
const report = await runChecks('https://docs.example.com');
77+
78+
// Run a single check
79+
const ctx = createContext('https://docs.example.com');
80+
const check = getCheck('llms-txt-exists')!;
81+
const result = await check.run(ctx);
82+
```
83+
84+
## Test helpers
85+
86+
afdocs includes vitest helpers so you can add agent-friendliness checks to your docs site's test suite.
87+
88+
### Config-driven
89+
90+
Create `agent-docs.config.yml`:
91+
92+
```yaml
93+
url: https://docs.example.com
94+
checks:
95+
- llms-txt-exists
96+
- llms-txt-valid
97+
- llms-txt-size
98+
```
99+
100+
Then in your test file:
101+
102+
```ts
103+
import { describeAgentDocs } from 'afdocs/helpers';
104+
105+
describeAgentDocs();
106+
```
107+
108+
This reads the config and generates one test assertion covering all specified checks.
109+
110+
### Direct imports
111+
112+
```ts
113+
import { createContext, getCheck } from 'afdocs';
114+
import { describe, it, expect } from 'vitest';
115+
116+
describe('agent-friendliness', () => {
117+
it('has a valid llms.txt', async () => {
118+
const ctx = createContext('https://docs.example.com');
119+
const check = getCheck('llms-txt-exists')!;
120+
const result = await check.run(ctx);
121+
expect(result.status).toBe('pass');
122+
});
123+
});
124+
```
125+
126+
## Checks
127+
128+
19 checks across 7 categories. Checks marked with * are not yet implemented and will return `skip`.
129+
130+
### Category 1: llms.txt
131+
132+
| Check | Description |
133+
|-------|-------------|
134+
| `llms-txt-exists` | Whether `llms.txt` is discoverable at candidate locations |
135+
| `llms-txt-valid` | Whether `llms.txt` follows the llmstxt.org structure |
136+
| `llms-txt-size` | Whether `llms.txt` fits within agent truncation limits |
137+
| `llms-txt-links-resolve` | Whether URLs in `llms.txt` return 200 |
138+
| `llms-txt-links-markdown` | Whether URLs in `llms.txt` point to markdown content |
139+
140+
### Category 2: Markdown Availability
141+
142+
| Check | Description |
143+
|-------|-------------|
144+
| `markdown-url-support` * | Whether `.md` URL variants return markdown |
145+
| `content-negotiation` * | Whether the server honors `Accept: text/markdown` |
146+
147+
### Category 3: Page Size and Truncation Risk
148+
149+
| Check | Description |
150+
|-------|-------------|
151+
| `page-size-markdown` * | Character count when served as markdown |
152+
| `page-size-html` * | Character count of HTML and post-conversion size |
153+
| `content-start-position` * | How far into the response actual content begins |
154+
155+
### Category 4: Content Structure
156+
157+
| Check | Description |
158+
|-------|-------------|
159+
| `tabbed-content-serialization` * | Whether tabbed content creates oversized output |
160+
| `section-header-quality` * | Whether headers in tabbed sections include context |
161+
| `markdown-code-fence-validity` * | Whether markdown has unclosed code fences |
162+
163+
### Category 5: URL Stability and Redirects
164+
165+
| Check | Description |
166+
|-------|-------------|
167+
| `http-status-codes` * | Whether error pages return correct status codes |
168+
| `redirect-behavior` * | Whether redirects are same-host HTTP redirects |
169+
170+
### Category 6: Agent Discoverability Directives
171+
172+
| Check | Description |
173+
|-------|-------------|
174+
| `llms-txt-directive` * | Whether pages include a directive pointing to `llms.txt` |
175+
176+
### Category 7: Observability and Content Health
177+
178+
| Check | Description |
179+
|-------|-------------|
180+
| `llms-txt-freshness` * | Whether `llms.txt` reflects current site state |
181+
| `markdown-content-parity` * | Whether markdown and HTML versions match |
182+
| `cache-header-hygiene` * | Whether cache headers allow timely updates |
183+
184+
## Check dependencies
185+
186+
Some checks depend on others. If a dependency doesn't pass, the dependent check is skipped automatically.
187+
188+
- `llms-txt-valid`, `llms-txt-size`, `llms-txt-links-resolve`, `llms-txt-links-markdown` require `llms-txt-exists`
189+
- `page-size-markdown` requires `markdown-url-support` or `content-negotiation`
190+
- `section-header-quality` requires `tabbed-content-serialization`
191+
- `markdown-code-fence-validity` requires `markdown-url-support` or `content-negotiation`
192+
- `llms-txt-freshness` requires `llms-txt-exists`
193+
- `markdown-content-parity` requires `markdown-url-support` or `content-negotiation`
194+
195+
## Responsible use
196+
197+
afdocs makes HTTP requests to the sites it checks. It enforces delays between requests (200ms default), caps concurrent connections, and honors `Retry-After` headers. The goal is to help documentation teams improve agent accessibility, not to load-test their infrastructure.
198+
199+
## License
200+
201+
MIT

bin/afdocs.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env node
2+
import { run } from '../dist/cli/index.js';
3+
run(process.argv);

eslint.config.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import eslint from '@eslint/js';
2+
import tseslint from 'typescript-eslint';
3+
4+
export default tseslint.config(
5+
eslint.configs.recommended,
6+
...tseslint.configs.recommended,
7+
{
8+
rules: {
9+
'@typescript-eslint/no-unused-vars': [
10+
'error',
11+
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
12+
],
13+
'@typescript-eslint/no-explicit-any': 'warn',
14+
'@typescript-eslint/consistent-type-imports': 'error',
15+
},
16+
},
17+
{
18+
ignores: ['dist/', 'coverage/', 'node_modules/', 'bin/'],
19+
},
20+
);

0 commit comments

Comments
 (0)