@docsector/docsector-reader 1.3.1 β 1.4.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/README.md +77 -0
- package/bin/docsector.js +18 -1
- package/package.json +1 -1
- package/src/index.js +13 -0
- package/src/quasar.factory.js +141 -13
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ Transform Markdown content into beautiful, navigable documentation sites β wit
|
|
|
29
29
|
- πΊοΈ **Sitemap Generation** β Automatic `sitemap.xml` generation at build time with all page URLs (requires `siteUrl` in config)
|
|
30
30
|
- π€ **AI-Friendly robots.txt** β Scaffold includes a `robots.txt` explicitly allowing 23 AI crawlers (GPTBot, ClaudeBot, PerplexityBot, GrokBot, etc.)
|
|
31
31
|
- π§ **Content Signals** β Optional `Content-Signal` directive for declaring AI usage policy (`ai-train`, `search`, `ai-input`) in `robots.txt`
|
|
32
|
+
- π§© **Agent Skills Discovery Index** β Optional `/.well-known/agent-skills/index.json` with RFC v0.2.0 schema and SHA-256 digests
|
|
32
33
|
- π **Homepage Link Headers** β Auto-generated `Link` response headers for agent discovery (`api-catalog`, `service-doc`, `service-desc`, `describedby`) per RFC 8288 / RFC 9727
|
|
33
34
|
- π **MCP Server** β Auto-generated [MCP](https://modelcontextprotocol.io) server at `/mcp` for AI assistant integration (Claude Desktop, VS Code, etc.)
|
|
34
35
|
- π **llms.txt / llms-full.txt** β Auto-generated [llms.txt](https://llmstxt.org) index and full-content file for LLM discovery (requires `siteUrl` in config)
|
|
@@ -333,6 +334,68 @@ Check `checks.botAccessControl.contentSignals.status` equals `"pass"`.
|
|
|
333
334
|
|
|
334
335
|
---
|
|
335
336
|
|
|
337
|
+
## π§© Agent Skills Discovery Index
|
|
338
|
+
|
|
339
|
+
Docsector Reader can publish a discovery index at:
|
|
340
|
+
|
|
341
|
+
- `/.well-known/agent-skills/index.json`
|
|
342
|
+
|
|
343
|
+
The generated payload follows Agent Skills Discovery RFC v0.2.0 and includes:
|
|
344
|
+
|
|
345
|
+
- `$schema`
|
|
346
|
+
- `skills[]` entries with `name`, `type`, `description`, `url`, `digest`
|
|
347
|
+
|
|
348
|
+
When `digest` is omitted in config, Docsector computes it automatically from the referenced local artifact and writes it as:
|
|
349
|
+
|
|
350
|
+
- `sha256:{hex}`
|
|
351
|
+
|
|
352
|
+
### Configure
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
export default {
|
|
356
|
+
// ...other config
|
|
357
|
+
|
|
358
|
+
agentSkills: {
|
|
359
|
+
enabled: true,
|
|
360
|
+
path: '/.well-known/agent-skills/index.json',
|
|
361
|
+
schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
|
|
362
|
+
skills: [
|
|
363
|
+
{
|
|
364
|
+
name: 'my-docs-mcp',
|
|
365
|
+
type: 'skill-md',
|
|
366
|
+
description: 'Search and fetch docs pages via MCP.',
|
|
367
|
+
url: '/.well-known/agent-skills/my-docs-mcp/SKILL.md'
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Notes:
|
|
375
|
+
|
|
376
|
+
- `name` must be lowercase alphanumeric plus hyphens.
|
|
377
|
+
- `type` must be `skill-md` or `archive`.
|
|
378
|
+
- `url` should point to a locally published artifact when auto-digest is used.
|
|
379
|
+
|
|
380
|
+
### Validate
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
npx docsector build
|
|
384
|
+
cat dist/spa/.well-known/agent-skills/index.json
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
External validation:
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
curl -X POST https://isitagentready.com/api/scan \
|
|
391
|
+
-H 'Content-Type: application/json' \
|
|
392
|
+
-d '{"url":"https://YOUR-SITE.com"}'
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Check `checks.discovery.agentSkills.status` equals `"pass"`.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
336
399
|
## οΏ½π Quick Start
|
|
337
400
|
|
|
338
401
|
### π¦ Install
|
|
@@ -496,6 +559,20 @@ export default {
|
|
|
496
559
|
applyToAllBlocks: false
|
|
497
560
|
},
|
|
498
561
|
|
|
562
|
+
agentSkills: {
|
|
563
|
+
enabled: true,
|
|
564
|
+
path: '/.well-known/agent-skills/index.json',
|
|
565
|
+
schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
|
|
566
|
+
skills: [
|
|
567
|
+
{
|
|
568
|
+
name: 'my-docs-mcp',
|
|
569
|
+
type: 'skill-md',
|
|
570
|
+
description: 'Search and fetch docs pages via MCP.',
|
|
571
|
+
url: '/.well-known/agent-skills/my-docs-mcp/SKILL.md'
|
|
572
|
+
}
|
|
573
|
+
]
|
|
574
|
+
},
|
|
575
|
+
|
|
499
576
|
languages: [
|
|
500
577
|
{ image: '/images/flags/united-states-of-america.png', label: 'English (US)', value: 'en-US' },
|
|
501
578
|
{ image: '/images/flags/brazil.png', label: 'PortuguΓͺs (BR)', value: 'pt-BR' }
|
package/bin/docsector.js
CHANGED
|
@@ -23,7 +23,7 @@ const packageRoot = resolve(__dirname, '..')
|
|
|
23
23
|
const args = process.argv.slice(2)
|
|
24
24
|
const command = args[0]
|
|
25
25
|
|
|
26
|
-
const VERSION = '1.
|
|
26
|
+
const VERSION = '1.4.0'
|
|
27
27
|
|
|
28
28
|
const HELP = `
|
|
29
29
|
Docsector Reader v${VERSION}
|
|
@@ -181,6 +181,23 @@ export default {
|
|
|
181
181
|
// applyToAllBlocks: false
|
|
182
182
|
// },
|
|
183
183
|
|
|
184
|
+
// @ Agent Skills discovery index (optional)
|
|
185
|
+
// Publishes /.well-known/agent-skills/index.json (RFC v0.2.0)
|
|
186
|
+
// and computes sha256 digests from local artifacts.
|
|
187
|
+
// agentSkills: {
|
|
188
|
+
// enabled: true,
|
|
189
|
+
// path: '/.well-known/agent-skills/index.json',
|
|
190
|
+
// schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
|
|
191
|
+
// skills: [
|
|
192
|
+
// {
|
|
193
|
+
// name: 'my-docs-mcp',
|
|
194
|
+
// type: 'skill-md',
|
|
195
|
+
// description: 'Search and read docs pages through MCP.',
|
|
196
|
+
// url: '/.well-known/agent-skills/my-docs-mcp/SKILL.md'
|
|
197
|
+
// }
|
|
198
|
+
// ]
|
|
199
|
+
// },
|
|
200
|
+
|
|
184
201
|
// @ Languages
|
|
185
202
|
languages: [
|
|
186
203
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A documentation rendering engine built with Vue 3, Quasar v2 and Vite. Transform Markdown into beautiful, navigable documentation sites.",
|
|
5
5
|
"productName": "Docsector Reader",
|
|
6
6
|
"author": "Rodrigo de Araujo Vieira",
|
package/src/index.js
CHANGED
|
@@ -73,6 +73,11 @@
|
|
|
73
73
|
* @param {'yes'|'no'|boolean} [config.contentSignals.aiInput='yes'] - Permission for AI input/inference-time consumption
|
|
74
74
|
* @param {string} [config.contentSignals.userAgent='*'] - Target User-agent block for directive injection
|
|
75
75
|
* @param {boolean} [config.contentSignals.applyToAllBlocks=false] - When true, applies directive to every User-agent block
|
|
76
|
+
* @param {Object} [config.agentSkills] - Agent Skills discovery index settings
|
|
77
|
+
* @param {boolean} [config.agentSkills.enabled=false] - Enables generation of Agent Skills discovery index
|
|
78
|
+
* @param {string} [config.agentSkills.path='/.well-known/agent-skills/index.json'] - Output URI path for Agent Skills index
|
|
79
|
+
* @param {string} [config.agentSkills.schema='https://schemas.agentskills.io/discovery/0.2.0/schema.json'] - JSON Schema identifier for index payload
|
|
80
|
+
* @param {Array<{name:string,type:'skill-md'|'archive',description:string,url:string,digest?:string}>} [config.agentSkills.skills=[]] - Skills to publish in discovery index
|
|
76
81
|
* @returns {Object} Resolved Docsector configuration
|
|
77
82
|
*/
|
|
78
83
|
export function createDocsector (config = {}) {
|
|
@@ -156,6 +161,14 @@ export function createDocsector (config = {}) {
|
|
|
156
161
|
userAgent: '*',
|
|
157
162
|
applyToAllBlocks: false,
|
|
158
163
|
...config.contentSignals
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
agentSkills: {
|
|
167
|
+
enabled: false,
|
|
168
|
+
path: '/.well-known/agent-skills/index.json',
|
|
169
|
+
schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
|
|
170
|
+
skills: [],
|
|
171
|
+
...config.agentSkills
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
174
|
}
|
package/src/quasar.factory.js
CHANGED
|
@@ -755,6 +755,21 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
755
755
|
const webBotAuthSignatureLabel = webBotAuthConfig.signatureLabel || 'sig1'
|
|
756
756
|
const contentSignalsConfig = config.contentSignals || {}
|
|
757
757
|
const contentSignalsEnabled = contentSignalsConfig.enabled === true
|
|
758
|
+
const agentSkillsConfig = config.agentSkills || {}
|
|
759
|
+
const agentSkillsEnabled = agentSkillsConfig.enabled === true
|
|
760
|
+
|
|
761
|
+
const toUrl = (href) => {
|
|
762
|
+
if (!href) return null
|
|
763
|
+
if (/^https?:\/\//i.test(href)) return href
|
|
764
|
+
const normalizedHref = href.startsWith('/') ? href : `/${href}`
|
|
765
|
+
return siteUrl ? `${siteUrl}${normalizedHref}` : normalizedHref
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const normalizeLocalPath = (href) => {
|
|
769
|
+
if (!href || /^https?:\/\//i.test(href)) return null
|
|
770
|
+
const path = href.startsWith('/') ? href.slice(1) : href
|
|
771
|
+
return path || null
|
|
772
|
+
}
|
|
758
773
|
|
|
759
774
|
if (markdownNegotiationEnabled || webBotAuthEnabled) {
|
|
760
775
|
const functionsDir = resolve(projectRoot, 'functions')
|
|
@@ -1022,6 +1037,81 @@ export async function onRequest (context) {
|
|
|
1022
1037
|
}
|
|
1023
1038
|
}
|
|
1024
1039
|
|
|
1040
|
+
if (agentSkillsEnabled) {
|
|
1041
|
+
const agentSkillsPath = agentSkillsConfig.path || '/.well-known/agent-skills/index.json'
|
|
1042
|
+
const agentSkillsSchema = agentSkillsConfig.schema || 'https://schemas.agentskills.io/discovery/0.2.0/schema.json'
|
|
1043
|
+
const indexDistPath = normalizeLocalPath(agentSkillsPath)
|
|
1044
|
+
|
|
1045
|
+
if (!indexDistPath) {
|
|
1046
|
+
console.warn(`\x1b[33m[docsector]\x1b[0m Skipped Agent Skills index generation: path must be a local URI path, got "${agentSkillsPath}"`)
|
|
1047
|
+
} else {
|
|
1048
|
+
const indexHref = agentSkillsPath.startsWith('/') ? agentSkillsPath : `/${agentSkillsPath}`
|
|
1049
|
+
const configuredSkills = Array.isArray(agentSkillsConfig.skills)
|
|
1050
|
+
? agentSkillsConfig.skills
|
|
1051
|
+
: []
|
|
1052
|
+
|
|
1053
|
+
const normalizedSkills = configuredSkills.map((skill, index) => {
|
|
1054
|
+
if (!skill || typeof skill !== 'object') {
|
|
1055
|
+
throw new Error(`[docsector] agentSkills.skills[${index}] must be an object`)
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const name = String(skill.name || '').trim().toLowerCase()
|
|
1059
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
1060
|
+
throw new Error(`[docsector] agentSkills.skills[${index}].name must be lowercase alphanumeric and hyphen-separated`)
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const type = normalizeAgentSkillType(skill.type, index)
|
|
1064
|
+
const description = String(skill.description || '').trim()
|
|
1065
|
+
if (!description) {
|
|
1066
|
+
throw new Error(`[docsector] agentSkills.skills[${index}].description is required`)
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
const url = toUrl(skill.url)
|
|
1070
|
+
if (!url) {
|
|
1071
|
+
throw new Error(`[docsector] agentSkills.skills[${index}].url is required`)
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
let digest = normalizeAgentSkillDigest(skill.digest)
|
|
1075
|
+
if (!digest) {
|
|
1076
|
+
const artifactPath = resolveAgentSkillArtifactPath(url, { siteUrl, distDir })
|
|
1077
|
+
if (!artifactPath || !existsSync(artifactPath)) {
|
|
1078
|
+
throw new Error(`[docsector] Unable to compute digest for agentSkills.skills[${index}] (${name}). Artifact not found at ${url}`)
|
|
1079
|
+
}
|
|
1080
|
+
const artifactContents = readFileSync(artifactPath)
|
|
1081
|
+
digest = `sha256:${createHash('sha256').update(artifactContents).digest('hex')}`
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
return {
|
|
1085
|
+
name,
|
|
1086
|
+
type,
|
|
1087
|
+
description,
|
|
1088
|
+
url,
|
|
1089
|
+
digest
|
|
1090
|
+
}
|
|
1091
|
+
})
|
|
1092
|
+
|
|
1093
|
+
const agentSkillsIndex = {
|
|
1094
|
+
$schema: agentSkillsSchema,
|
|
1095
|
+
skills: normalizedSkills
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const indexDir = resolve(distDir, indexDistPath, '..')
|
|
1099
|
+
mkdirSync(indexDir, { recursive: true })
|
|
1100
|
+
writeFileSync(
|
|
1101
|
+
resolve(distDir, indexDistPath),
|
|
1102
|
+
JSON.stringify(agentSkillsIndex, null, 2) + '\n'
|
|
1103
|
+
)
|
|
1104
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Generated ${indexHref}`)
|
|
1105
|
+
|
|
1106
|
+
const headersWithSkills = readFileSync(headersPath, 'utf-8')
|
|
1107
|
+
if (!headersWithSkills.includes(indexHref)) {
|
|
1108
|
+
const skillsHeaders = `${indexHref}\n Content-Type: application/json; charset=utf-8\n`
|
|
1109
|
+
writeFileSync(headersPath, headersWithSkills.trimEnd() + '\n\n' + skillsHeaders)
|
|
1110
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for ${indexHref}`)
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1025
1115
|
console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for .md files`)
|
|
1026
1116
|
|
|
1027
1117
|
// Add homepage Link headers for agent discovery (RFC 8288 / RFC 9727)
|
|
@@ -1081,19 +1171,6 @@ export async function onRequest (context) {
|
|
|
1081
1171
|
const apiCatalogEnabled = apiCatalogConfig.enabled !== false
|
|
1082
1172
|
const apiCatalogPath = (apiCatalogConfig.path || apiCatalogHref || '/.well-known/api-catalog')
|
|
1083
1173
|
|
|
1084
|
-
const toUrl = (href) => {
|
|
1085
|
-
if (!href) return null
|
|
1086
|
-
if (/^https?:\/\//i.test(href)) return href
|
|
1087
|
-
const normalizedHref = href.startsWith('/') ? href : `/${href}`
|
|
1088
|
-
return siteUrl ? `${siteUrl}${normalizedHref}` : normalizedHref
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
const normalizeLocalPath = (href) => {
|
|
1092
|
-
if (!href || /^https?:\/\//i.test(href)) return null
|
|
1093
|
-
const path = href.startsWith('/') ? href.slice(1) : href
|
|
1094
|
-
return path || null
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
1174
|
if (apiCatalogEnabled && apiCatalogPath) {
|
|
1098
1175
|
const catalogDistPath = normalizeLocalPath(apiCatalogPath)
|
|
1099
1176
|
|
|
@@ -1376,6 +1453,57 @@ function applyContentSignalsToRobots (robotsContent, { contentSignalLine, userAg
|
|
|
1376
1453
|
return updated.join('\n').replace(/\n+$/g, '') + '\n'
|
|
1377
1454
|
}
|
|
1378
1455
|
|
|
1456
|
+
function normalizeAgentSkillType (type, index) {
|
|
1457
|
+
const normalizedType = String(type || '').trim()
|
|
1458
|
+
if (normalizedType !== 'skill-md' && normalizedType !== 'archive') {
|
|
1459
|
+
throw new Error(`[docsector] agentSkills.skills[${index}].type must be "skill-md" or "archive"`)
|
|
1460
|
+
}
|
|
1461
|
+
return normalizedType
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
function normalizeAgentSkillDigest (digest) {
|
|
1465
|
+
if (digest === null || digest === undefined || digest === '') return null
|
|
1466
|
+
const normalizedDigest = String(digest).trim().toLowerCase()
|
|
1467
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(normalizedDigest)) {
|
|
1468
|
+
throw new Error('[docsector] agentSkills.skills[*].digest must follow "sha256:{hex}"')
|
|
1469
|
+
}
|
|
1470
|
+
return normalizedDigest
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function resolveAgentSkillArtifactPath (artifactUrl, { siteUrl, distDir }) {
|
|
1474
|
+
if (!artifactUrl || typeof artifactUrl !== 'string') {
|
|
1475
|
+
return null
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
let pathname = null
|
|
1479
|
+
|
|
1480
|
+
if (/^https?:\/\//i.test(artifactUrl)) {
|
|
1481
|
+
if (!siteUrl) return null
|
|
1482
|
+
|
|
1483
|
+
let artifact
|
|
1484
|
+
let base
|
|
1485
|
+
try {
|
|
1486
|
+
artifact = new URL(artifactUrl)
|
|
1487
|
+
base = new URL(siteUrl)
|
|
1488
|
+
} catch {
|
|
1489
|
+
return null
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
if (artifact.origin !== base.origin) {
|
|
1493
|
+
return null
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
pathname = artifact.pathname
|
|
1497
|
+
} else {
|
|
1498
|
+
pathname = artifactUrl.startsWith('/') ? artifactUrl : `/${artifactUrl}`
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
const relativePath = pathname.replace(/^\/+/, '')
|
|
1502
|
+
if (!relativePath) return null
|
|
1503
|
+
|
|
1504
|
+
return resolve(distDir, relativePath)
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1379
1507
|
/**
|
|
1380
1508
|
* Create a complete Quasar configuration for a docsector-reader consumer project.
|
|
1381
1509
|
*
|