@adkinn/astro-ai-readiness 0.0.7

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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +285 -0
  3. package/dist/chunk-36CGBNB5.js +104 -0
  4. package/dist/chunk-BDGAXJOB.js +42 -0
  5. package/dist/chunk-CADNETPK.js +48 -0
  6. package/dist/chunk-CO2DB4IE.js +141 -0
  7. package/dist/chunk-THSVWYUX.js +42 -0
  8. package/dist/chunk-V22PCH7G.js +26 -0
  9. package/dist/chunk-WP6D4L7O.js +48 -0
  10. package/dist/components/BreadcrumbSchema.astro +22 -0
  11. package/dist/components/CollectionSchema.astro +24 -0
  12. package/dist/components/FAQPageSchema.astro +24 -0
  13. package/dist/components/OrganizationSchema.astro +33 -0
  14. package/dist/components/TechArticleSchema.astro +88 -0
  15. package/dist/components/WebSiteSchema.astro +16 -0
  16. package/dist/components/index.d.ts +7 -0
  17. package/dist/components/index.js +15 -0
  18. package/dist/components/types.d.ts +55 -0
  19. package/dist/components/types.js +0 -0
  20. package/dist/config.d.ts +1044 -0
  21. package/dist/config.js +6 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +103 -0
  24. package/dist/outputs/agents-md.d.ts +19 -0
  25. package/dist/outputs/agents-md.js +9 -0
  26. package/dist/outputs/llms-full.d.ts +18 -0
  27. package/dist/outputs/llms-full.js +9 -0
  28. package/dist/outputs/llms-txt.d.ts +19 -0
  29. package/dist/outputs/llms-txt.js +9 -0
  30. package/dist/outputs/mcp-json.d.ts +21 -0
  31. package/dist/outputs/mcp-json.js +9 -0
  32. package/dist/outputs/robots-txt.d.ts +20 -0
  33. package/dist/outputs/robots-txt.js +9 -0
  34. package/dist/utils/json-ld.d.ts +26 -0
  35. package/dist/utils/json-ld.js +7 -0
  36. package/package.json +76 -0
  37. package/schemas/mcp/v1.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adam Kinney, LLC (DBA Obaron)
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,285 @@
1
+ # @adkinn/astro-ai-readiness
2
+
3
+ > AI Readiness toolkit for Astro — six JSON-LD helper components plus `dist/llms.txt`, `dist/llms-full.txt`, `dist/agents.md`, `dist/robots.txt`, and `dist/.well-known/mcp.json`.
4
+
5
+ **Status:** v0.0.7 — seventh slice in prep. **Component sub-line complete (6 of 6); five of five v0.1 file outputs live.** Multi-output orchestration on the `astro:build:done` hook now covers the full v0.1 artifact set.
6
+
7
+ ## What ships in v0.0.7
8
+
9
+ **Six JSON-LD components** (component sub-line complete):
10
+ - `<OrganizationSchema />` — Organization block. Config-driven; place in your `BaseLayout` so it ships site-wide and the `#organization` `@id` reference resolves on every page.
11
+ - `<WebSiteSchema />` — WebSite block. Config-driven; alongside `<OrganizationSchema />` in `BaseLayout`.
12
+ - `<CollectionSchema name url description? />` — CollectionPage block. Props-driven; place on collection-index pages (`/articles/`, `/tags/[tag]/`, etc.).
13
+ - `<BreadcrumbSchema items={[{ name, url }, ...]} />` — BreadcrumbList. Items-array prop; place on multi-level pages where the navigation hierarchy isn't already declared inline. Empty `items` skips emission.
14
+ - `<FAQPageSchema items={[{ question, answer }, ...]} />` — FAQPage. Items-array prop; place on pages with FAQ data. Long-form answers escape `</script>` and U+2028 / U+2029 automatically. Empty `items` skips emission.
15
+ - `<TechArticleSchema headline description datePublished {...optional} />` — TechArticle block. Heavy-props; place on article-detail pages. `author` defaults to a Person synthesized from `config.organization.founder`; pass an explicit `author` prop to override.
16
+
17
+ **Five file outputs**:
18
+ - `dist/llms.txt` — [llmstxt.org](https://llmstxt.org/) format from the `llmsTxt` config block. H1 / blockquote summary / optional body / H2 sections with bulleted links / optional canonical-reference footer. Opt-in.
19
+ - `dist/llms-full.txt` — manual full-context markdown from the `llmsFull` config block. This is config-driven in v0.0.7; content-collection introspection is a later layer.
20
+ - `dist/agents.md` — Markdown discovery file for AI-agent crawlers from the `agentsMd` config block. H1 / blockquote description / optional `## Audience`, `## Contact`, `## Links` sections. Opt-in.
21
+ - `dist/.well-known/mcp.json` — [Model Context Protocol](https://modelcontextprotocol.io/) discovery file from the `mcp` config block. Pretty-printed JSON with `$schema` reference to the toolkit-published v1 shape (per D-22). Supports `status: 'active'` (requires `url` + `tools[]`) and `status: 'planned'` (requires `planned_tools[]`; `url` forbidden by schema). Opt-in.
22
+ - `dist/robots.txt` — robots policy composition from the `robotsTxt` config block. Presets: `search-visible`, `training-opt-out` (default), and `private`; supports explicit bot rules, `Sitemap`, and optional `Content-Signal` directives.
23
+
24
+ **`@astrojs/sitemap` detection per D-8.** When you call `aiReadiness({...})` and `@astrojs/sitemap` isn't in your integrations list, the toolkit logs a build-time warning. Sitemap is an AI-Readiness baseline; when `robotsTxt` is enabled, the generated `robots.txt` includes a `Sitemap` line by default.
25
+
26
+ All six components emit canonical Schema.org JSON-LD with cross-component `@id` references (`#organization`, `#website`) so search and AI consumers can resolve the entity graph without redeclaring shared fields.
27
+
28
+ URL config fields (`site`, `organization.url`, `organization.logo`, `founder.sameAs`, `llmsTxt.*.url`) require `https://` (or `http://localhost` for dev).
29
+
30
+ Config validation errors are formatted with one issue per line — `path: message` per Zod issue — instead of the default `ZodError` JSON blob.
31
+
32
+ ## On the roadmap (toward v0.1.0)
33
+
34
+ - v0.0.8 — fixture Astro app validation against AATT / adamkinney.com install paths; refine robots presets from real traffic policy.
35
+ - v0.0.9 — content-collection assisted `llms-full.txt` generation.
36
+ - v0.1.0 — polish, docs, release hardening; full install on adamkinney.com
37
+
38
+ Track progress: <https://github.com/adkinn/astro-ai-readiness/milestones>
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ npm install @adkinn/astro-ai-readiness
44
+ ```
45
+
46
+ ## Quick start
47
+
48
+ Configure (v0.0.7 accepts `site`, `organization`, optional `webSite`, optional `llmsTxt`, optional `llmsFull`, optional `agentsMd`, optional `mcp`, and optional `robotsTxt` blocks — the Zod schema rejects unknown keys, and URL fields must use `https://` or `http://localhost`):
49
+
50
+ ```ts
51
+ // astro.config.mjs
52
+ import { defineConfig } from 'astro/config'
53
+ import sitemap from '@astrojs/sitemap'
54
+ import aiReadiness from '@adkinn/astro-ai-readiness'
55
+
56
+ export default defineConfig({
57
+ site: 'https://your-site.com',
58
+ integrations: [
59
+ sitemap(), // recommended — AI Readiness baseline (D-8)
60
+ aiReadiness({
61
+ site: 'https://your-site.com',
62
+ organization: {
63
+ name: 'Your Brand',
64
+ url: 'https://your-site.com',
65
+ logo: 'https://your-site.com/logo.png',
66
+ founder: {
67
+ name: 'Your Name',
68
+ jobTitle: 'Founder',
69
+ sameAs: ['https://your-site.com', 'https://x.com/handle'],
70
+ },
71
+ },
72
+ webSite: {
73
+ // Optional. `name` defaults to organization.name when absent.
74
+ description: 'What your site does, in one sentence.',
75
+ },
76
+ llmsTxt: {
77
+ // Optional. When set, the toolkit ships dist/llms.txt at build time.
78
+ // Note: `summary` must be single-line — multi-paragraph summaries break
79
+ // the llms.txt blockquote shape. Use `body` (free-form markdown) for
80
+ // additional prose. Multi-line summary throws at config-validation time.
81
+ summary: 'What your site does, in one sentence — for AI agents discovering your content.',
82
+ body: 'A free-form markdown paragraph or two between summary and sections. Plain prose; no top-level H2 here (those are reserved for link sections below).',
83
+ sections: [
84
+ {
85
+ title: 'Articles',
86
+ links: [
87
+ { title: 'All Articles', url: 'https://your-site.com/articles/' },
88
+ { title: 'RSS Feed', url: 'https://your-site.com/rss.xml' },
89
+ ],
90
+ },
91
+ ],
92
+ deferTo: {
93
+ // Single canonical-reference link, rendered as a footer.
94
+ title: 'Articles Index',
95
+ url: 'https://your-site.com/articles/',
96
+ },
97
+ },
98
+ llmsFull: {
99
+ // Optional. When set, the toolkit ships dist/llms-full.txt at build time.
100
+ // v0.0.7 is manual/config-driven; later slices can derive sections from
101
+ // content collections.
102
+ sections: [
103
+ {
104
+ title: 'Site Context',
105
+ content: 'Long-form context agents should read before summarizing this site.',
106
+ },
107
+ {
108
+ title: 'Canonical Resources',
109
+ content: '- https://your-site.com/articles/\n- https://your-site.com/rss.xml',
110
+ },
111
+ ],
112
+ },
113
+ agentsMd: {
114
+ // Optional. When set, the toolkit ships dist/agents.md at build time.
115
+ description: 'What your site does and who it serves — for AI-agent crawlers.',
116
+ audience: 'Developers and AI agents acting on their behalf.',
117
+ contact: 'hello@your-site.com',
118
+ links: [
119
+ { title: 'Articles', url: 'https://your-site.com/articles/', description: 'All articles' },
120
+ { title: 'RSS Feed', url: 'https://your-site.com/rss.xml', description: 'Machine-readable article stream' },
121
+ ],
122
+ },
123
+ mcp: {
124
+ // Optional. When set, the toolkit ships dist/.well-known/mcp.json at build time.
125
+ // Each server must have status: 'active' or status: 'planned'.
126
+ // Active: requires url + tools[]. Planned: requires planned_tools[]; url is forbidden.
127
+ servers: [
128
+ {
129
+ // A live MCP server — must have url + tools.
130
+ status: 'active',
131
+ name: 'your-knowledge',
132
+ url: 'https://mcp.your-site.com',
133
+ description: 'MCP server exposing your site knowledge for AI agents.',
134
+ tools: [
135
+ { name: 'search_articles', description: 'Search articles by keyword or tag.' },
136
+ { name: 'fetch_article', description: 'Fetch a specific article by slug.' },
137
+ ],
138
+ },
139
+ {
140
+ // A planned server — declares intent without a live URL.
141
+ status: 'planned',
142
+ name: 'your-future-server',
143
+ description: 'Planned MCP server for future capability.',
144
+ planned_tools: [
145
+ { name: 'lookup', description: 'Lookup by ID once the server ships.' },
146
+ ],
147
+ },
148
+ ],
149
+ },
150
+ robotsTxt: {
151
+ // Optional. When set, the toolkit ships dist/robots.txt at build time.
152
+ // Default policy is "training-opt-out": ordinary search stays open while
153
+ // common model-training bot tokens are disallowed. Some vendors bundle
154
+ // training and grounding under one token, so override rules when needed.
155
+ policy: 'training-opt-out',
156
+ // Defaults to https://your-site.com/sitemap-index.xml. Set false to omit.
157
+ sitemap: 'https://your-site.com/sitemap-index.xml',
158
+ contentSignals: {
159
+ search: 'yes',
160
+ aiTrain: 'no',
161
+ aiInput: 'yes',
162
+ },
163
+ // Add or replace rules when your site has a specific bot policy.
164
+ additionalLines: [
165
+ '# Custom lines are appended before Sitemap.',
166
+ ],
167
+ },
168
+ }),
169
+ ],
170
+ })
171
+ ```
172
+
173
+ > **Note on raw markdown.** `llmsTxt.*`, `llmsFull.*`, and `agentsMd.*` string fields are emitted as raw markdown. If you're templating user-generated content into those fields, escape `]`, `)`, and leading `>` to avoid breaking the rendered Markdown shape. Author-controlled strings (typical case) need no escaping. `mcp.json` is JSON-serialized and has no markdown-injection surface.
174
+
175
+ Use the components:
176
+
177
+ ```astro
178
+ ---
179
+ // src/layouts/BaseLayout.astro — site-wide
180
+ import { OrganizationSchema, WebSiteSchema } from '@adkinn/astro-ai-readiness/components'
181
+ ---
182
+ <head>
183
+ <OrganizationSchema />
184
+ <WebSiteSchema />
185
+ </head>
186
+ ```
187
+
188
+ `<OrganizationSchema />` declares `@id: '<site>#organization'`; `<WebSiteSchema />` references it. Order matters in the head — Organization first.
189
+
190
+ ```astro
191
+ ---
192
+ // src/pages/articles/index.astro — collection index pages
193
+ import { CollectionSchema } from '@adkinn/astro-ai-readiness/components'
194
+ ---
195
+ <CollectionSchema
196
+ name="All Articles"
197
+ url={new URL('articles/', Astro.site).toString()}
198
+ description="Production-tested articles, version-pinned environments."
199
+ />
200
+ ```
201
+
202
+ ```astro
203
+ ---
204
+ // any multi-level page — site-context navigation
205
+ import { BreadcrumbSchema } from '@adkinn/astro-ai-readiness/components'
206
+ import type { BreadcrumbItem } from '@adkinn/astro-ai-readiness/components'
207
+
208
+ const crumbs: BreadcrumbItem[] = [
209
+ { name: 'Home', url: new URL('/', Astro.site).toString() },
210
+ { name: 'Articles', url: new URL('/articles/', Astro.site).toString() },
211
+ { name: 'How agents handle errors', url: Astro.url.toString() },
212
+ ]
213
+ ---
214
+ <BreadcrumbSchema items={crumbs} />
215
+ ```
216
+
217
+ ```astro
218
+ ---
219
+ // pages with FAQ data — items-array of question/answer pairs
220
+ import { FAQPageSchema } from '@adkinn/astro-ai-readiness/components'
221
+ import type { FAQItem } from '@adkinn/astro-ai-readiness/components'
222
+
223
+ const faqs: FAQItem[] = [
224
+ { question: 'What is X?', answer: 'X is...' },
225
+ { question: 'Why does Y matter?', answer: 'Because...' },
226
+ ]
227
+ ---
228
+ <FAQPageSchema items={faqs} />
229
+ ```
230
+
231
+ ```astro
232
+ ---
233
+ // article-detail pages — required props only
234
+ import { TechArticleSchema } from '@adkinn/astro-ai-readiness/components'
235
+ ---
236
+ <TechArticleSchema
237
+ headline="How agents handle errors"
238
+ description="A pattern for agent error handling that doesn't lose context."
239
+ datePublished="2026-04-01T12:00:00Z"
240
+ />
241
+ ```
242
+
243
+ `<TechArticleSchema>` defaults `author` to a Person synthesized from `config.organization.founder` (name from `founder.name`, url from `founder.sameAs[0]`). Pass an explicit `author={{ name, url? }}` to override on multi-author sites. The component throws at build time if `author` is omitted and `founder` is unset — actionable error pointing at the `aiReadiness({...})` config.
244
+
245
+ Advanced — full prop surface:
246
+
247
+ ```astro
248
+ <TechArticleSchema
249
+ headline={article.title}
250
+ description={article.description}
251
+ datePublished={article.pubDate.toISOString()}
252
+ dateModified={(article.updatedDate ?? article.pubDate).toISOString()}
253
+ author={{ name: 'Guest Author', url: 'https://example.com' }}
254
+ image={{ url: 'https://your-site.com/og.png', width: 1200, height: 630 }}
255
+ articleSection="Tutorials"
256
+ keywords={['ai', 'agents', 'error-handling']}
257
+ proficiencyLevel="intermediate"
258
+ dependencies={['Node 22', 'Astro 5.5']}
259
+ />
260
+ ```
261
+
262
+ Build your site (`npm run build`); inspect any `dist/*.html` — you'll see inline `<script type="application/ld+json">` blocks with cross-referenced `@id`s tying the entity graph together.
263
+
264
+ ## Design principles
265
+
266
+ - **Zero client JS.** Every component emits inline `<script type="application/ld+json">` at build time. Hydrating JSON-LD would erode the very Schema.org category it's meant to lift.
267
+ - **Compose, don't clobber.** `robotsTxt` exposes explicit rules and appended lines so user intent can override presets. If you already maintain a hand-written `public/robots.txt`, compare the generated file before adopting it.
268
+ - **Composes with `@astrojs/sitemap`.** Doesn't replace it. The integration warns when sitemap is missing and `robotsTxt` defaults its `Sitemap` line to `/sitemap-index.xml`.
269
+
270
+ ## What's beyond v0.1
271
+
272
+ The v0.1 line is content → artifacts: components and files. Build-time AI-readiness self-scoring, spec validation against [llmstxt.org](https://llmstxt.org), and content lints are on the v0.2 horizon — but not committed scope yet. v0.1 ships first.
273
+
274
+ ## Shipped on
275
+
276
+ - [aiallthethings.com](https://aiallthethings.com) — AATT, the toolkit's first reference implementation. Runs all six components on production plus three toolkit-emitted files: `dist/llms.txt` at <https://aiallthethings.com/llms.txt>, `dist/agents.md` at <https://aiallthethings.com/agents.md>, and `dist/.well-known/mcp.json` at <https://aiallthethings.com/.well-known/mcp.json>. Components: Organization + WebSite (site-wide via BaseLayout), CollectionPage (articles + framework + tag indexes), BreadcrumbList (`/about`), FAQPage (articles with FAQ frontmatter), TechArticle (every article-detail page).
277
+ - [adamkinney.com](https://adamkinney.com) — Adam Kinney's site. Next SSR reference-implementation target once v0.1.0 publishes.
278
+
279
+ ## Contributing
280
+
281
+ PRs welcome. See [CONTRIBUTING.md](./.github/CONTRIBUTING.md) and our [Code of Conduct](./CODE_OF_CONDUCT.md).
282
+
283
+ ## License
284
+
285
+ MIT — Copyright (c) 2026 Adam Kinney, LLC (DBA Obaron).
@@ -0,0 +1,104 @@
1
+ import {
2
+ writeOutput
3
+ } from "./chunk-V22PCH7G.js";
4
+
5
+ // src/outputs/robots-txt.ts
6
+ var TRAINING_BOT_RULES = [
7
+ { userAgent: "GPTBot", disallow: ["/"], comment: "OpenAI training crawler" },
8
+ { userAgent: "Google-Extended", disallow: ["/"], comment: "Google Gemini training/grounding control token" },
9
+ { userAgent: "CCBot", disallow: ["/"], comment: "Common Crawl" },
10
+ { userAgent: "ClaudeBot", disallow: ["/"], comment: "Anthropic crawler" },
11
+ { userAgent: "anthropic-ai", disallow: ["/"], comment: "Anthropic crawler" },
12
+ { userAgent: "Applebot-Extended", disallow: ["/"], comment: "Apple AI training control token" }
13
+ ];
14
+ var SEARCH_BOT_RULES = [
15
+ { userAgent: "*", allow: ["/"], comment: "Default web and search discovery" },
16
+ { userAgent: "OAI-SearchBot", allow: ["/"], comment: "OpenAI search crawler" }
17
+ ];
18
+ function defaultSitemap(site) {
19
+ return new URL("sitemap-index.xml", site.endsWith("/") ? site : `${site}/`).toString();
20
+ }
21
+ function policyRules(policy) {
22
+ if (policy === "private") {
23
+ return [{ userAgent: "*", disallow: ["/"], comment: "Private site" }];
24
+ }
25
+ if (policy === "training-opt-out") {
26
+ return [...SEARCH_BOT_RULES, ...TRAINING_BOT_RULES];
27
+ }
28
+ return SEARCH_BOT_RULES;
29
+ }
30
+ function defaultContentSignals(policy) {
31
+ if (policy === "private") {
32
+ return { search: "no", aiTrain: "no", aiInput: "no" };
33
+ }
34
+ if (policy === "training-opt-out") {
35
+ return { search: "yes", aiTrain: "no", aiInput: "yes" };
36
+ }
37
+ return { search: "yes", aiTrain: "yes", aiInput: "yes" };
38
+ }
39
+ function composeRule(rule) {
40
+ const lines = [];
41
+ const userAgents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent];
42
+ if (rule.comment) lines.push(`# ${rule.comment}`);
43
+ for (const userAgent of userAgents) {
44
+ lines.push(`User-agent: ${userAgent}`);
45
+ }
46
+ for (const value of rule.allow ?? []) {
47
+ lines.push(`Allow: ${value}`);
48
+ }
49
+ for (const value of rule.disallow ?? []) {
50
+ lines.push(`Disallow: ${value}`);
51
+ }
52
+ if (rule.crawlDelay) {
53
+ lines.push(`Crawl-delay: ${rule.crawlDelay}`);
54
+ }
55
+ return lines;
56
+ }
57
+ function composeContentSignal(signals) {
58
+ const parts = [];
59
+ if (signals.search) parts.push(`search=${signals.search}`);
60
+ if (signals.aiTrain) parts.push(`ai-train=${signals.aiTrain}`);
61
+ if (signals.aiInput) parts.push(`ai-input=${signals.aiInput}`);
62
+ return `Content-Signal: ${parts.join(", ")}`;
63
+ }
64
+ function composeRobotsTxt(config) {
65
+ const robotsTxt = config.robotsTxt;
66
+ const policy = robotsTxt.policy ?? "training-opt-out";
67
+ const rules = robotsTxt.rules ?? policyRules(policy);
68
+ const signals = {
69
+ ...defaultContentSignals(policy),
70
+ ...robotsTxt.contentSignals
71
+ };
72
+ const sitemap = robotsTxt.sitemap === false ? void 0 : robotsTxt.sitemap ?? defaultSitemap(config.site);
73
+ const lines = [
74
+ "# Generated by @adkinn/astro-ai-readiness",
75
+ composeContentSignal(signals),
76
+ ""
77
+ ];
78
+ for (const rule of rules) {
79
+ lines.push(...composeRule(rule));
80
+ lines.push("");
81
+ }
82
+ if (robotsTxt.additionalLines && robotsTxt.additionalLines.length > 0) {
83
+ lines.push(...robotsTxt.additionalLines);
84
+ lines.push("");
85
+ }
86
+ if (sitemap) {
87
+ lines.push(`Sitemap: ${sitemap}`);
88
+ lines.push("");
89
+ }
90
+ return lines.join("\n").replace(/\n+$/, "\n");
91
+ }
92
+ async function writeRobotsTxt(config, dir, logger) {
93
+ if (!config.robotsTxt) return;
94
+ const content = composeRobotsTxt(
95
+ config
96
+ );
97
+ const target = new URL("robots.txt", dir);
98
+ await writeOutput(target, content, "robots.txt", logger);
99
+ }
100
+
101
+ export {
102
+ composeRobotsTxt,
103
+ writeRobotsTxt
104
+ };
@@ -0,0 +1,42 @@
1
+ import {
2
+ writeOutput
3
+ } from "./chunk-V22PCH7G.js";
4
+
5
+ // src/outputs/mcp-json.ts
6
+ var MCP_SCHEMA_URL = "https://raw.githubusercontent.com/adkinn/astro-ai-readiness/v0.0.7/schemas/mcp/v1.json";
7
+ function composeMcpJson(config) {
8
+ const { mcp } = config;
9
+ const obj = {
10
+ $schema: MCP_SCHEMA_URL,
11
+ version: mcp.version ?? "1.0",
12
+ servers: mcp.servers.map((s) => {
13
+ const server = {
14
+ name: s.name,
15
+ description: s.description,
16
+ status: s.status
17
+ };
18
+ if (s.status === "active") {
19
+ server.url = s.url;
20
+ server.tools = s.tools;
21
+ } else {
22
+ server.planned_tools = s.planned_tools;
23
+ }
24
+ return server;
25
+ })
26
+ };
27
+ return JSON.stringify(obj, null, 2) + "\n";
28
+ }
29
+ async function writeMcpJson(config, dir, logger) {
30
+ if (!config.mcp) return;
31
+ const content = composeMcpJson(
32
+ config
33
+ );
34
+ const subdir = new URL(".well-known/", dir);
35
+ const target = new URL(".well-known/mcp.json", dir);
36
+ await writeOutput(target, content, ".well-known/mcp.json", logger, subdir);
37
+ }
38
+
39
+ export {
40
+ composeMcpJson,
41
+ writeMcpJson
42
+ };
@@ -0,0 +1,48 @@
1
+ import {
2
+ writeOutput
3
+ } from "./chunk-V22PCH7G.js";
4
+
5
+ // src/outputs/agents-md.ts
6
+ function composeAgentsMd(config) {
7
+ const { agentsMd, organization } = config;
8
+ const lines = [];
9
+ lines.push(`# ${organization.name}`);
10
+ lines.push("");
11
+ lines.push(`> ${agentsMd.description}`);
12
+ lines.push("");
13
+ if (agentsMd.audience) {
14
+ lines.push("## Audience");
15
+ lines.push("");
16
+ lines.push(agentsMd.audience.trimEnd());
17
+ lines.push("");
18
+ }
19
+ if (agentsMd.contact) {
20
+ lines.push("## Contact");
21
+ lines.push("");
22
+ lines.push(agentsMd.contact.trimEnd());
23
+ lines.push("");
24
+ }
25
+ if (agentsMd.links && agentsMd.links.length > 0) {
26
+ lines.push("## Links");
27
+ lines.push("");
28
+ for (const link of agentsMd.links) {
29
+ const desc = link.description ? `: ${link.description}` : "";
30
+ lines.push(`- [${link.title}](${link.url})${desc}`);
31
+ }
32
+ lines.push("");
33
+ }
34
+ return lines.join("\n").replace(/\n+$/, "\n");
35
+ }
36
+ async function writeAgentsMd(config, dir, logger) {
37
+ if (!config.agentsMd) return;
38
+ const content = composeAgentsMd(
39
+ config
40
+ );
41
+ const target = new URL("agents.md", dir);
42
+ await writeOutput(target, content, "agents.md", logger);
43
+ }
44
+
45
+ export {
46
+ composeAgentsMd,
47
+ writeAgentsMd
48
+ };
@@ -0,0 +1,141 @@
1
+ // src/config.ts
2
+ import { z } from "zod";
3
+ var httpsUrl = z.string().url().refine(
4
+ (u) => u.startsWith("https://") || u.startsWith("http://localhost"),
5
+ { message: "URL must use https:// (or http://localhost for dev)" }
6
+ );
7
+ var founderSchema = z.object({
8
+ name: z.string().min(1),
9
+ jobTitle: z.string().optional(),
10
+ description: z.string().optional(),
11
+ sameAs: z.array(httpsUrl).optional()
12
+ });
13
+ var organizationSchema = z.object({
14
+ name: z.string().min(1),
15
+ url: httpsUrl.optional(),
16
+ logo: httpsUrl.optional(),
17
+ founder: founderSchema.optional(),
18
+ foundingDate: z.string().optional(),
19
+ knowsAbout: z.array(z.string()).optional(),
20
+ areaServed: z.string().optional()
21
+ }).strict();
22
+ var webSiteSchema = z.object({
23
+ name: z.string().optional(),
24
+ description: z.string().optional()
25
+ }).strict();
26
+ var llmsTxtLinkSchema = z.object({
27
+ title: z.string().min(1),
28
+ url: httpsUrl,
29
+ description: z.string().optional()
30
+ }).strict();
31
+ var llmsTxtSectionSchema = z.object({
32
+ title: z.string().min(1),
33
+ links: z.array(llmsTxtLinkSchema).min(1)
34
+ }).strict();
35
+ var llmsTxtSchema = z.object({
36
+ summary: z.string().min(1).refine(
37
+ (s) => !s.includes("\n"),
38
+ { message: "summary must be single-line \u2014 multi-paragraph summaries break llms.txt blockquote shape. Use the optional `body` field for additional prose." }
39
+ ),
40
+ body: z.string().optional(),
41
+ sections: z.array(llmsTxtSectionSchema).optional(),
42
+ deferTo: z.object({
43
+ title: z.string().min(1),
44
+ url: httpsUrl
45
+ }).strict().optional()
46
+ }).strict();
47
+ var llmsFullSectionSchema = z.object({
48
+ title: z.string().min(1),
49
+ content: z.string().min(1)
50
+ }).strict();
51
+ var llmsFullSchema = z.object({
52
+ title: z.string().min(1).optional(),
53
+ summary: z.string().min(1).optional(),
54
+ content: z.string().min(1).optional(),
55
+ sections: z.array(llmsFullSectionSchema).min(1).optional()
56
+ }).strict().refine(
57
+ (value) => Boolean(value.content || value.sections),
58
+ { message: "llmsFull requires either `content` or `sections`" }
59
+ );
60
+ var agentsMdLinkSchema = z.object({
61
+ title: z.string().min(1),
62
+ url: httpsUrl,
63
+ description: z.string().optional()
64
+ }).strict();
65
+ var agentsMdSchema = z.object({
66
+ description: z.string().min(1).refine(
67
+ (s) => !s.includes("\n"),
68
+ { message: "description must be single-line \u2014 use audience/contact fields for additional prose" }
69
+ ),
70
+ audience: z.string().optional(),
71
+ contact: z.string().optional(),
72
+ links: z.array(agentsMdLinkSchema).min(1).optional()
73
+ }).strict();
74
+ var mcpToolSchema = z.object({
75
+ name: z.string().min(1),
76
+ description: z.string().min(1)
77
+ }).strict();
78
+ var mcpActiveServerSchema = z.object({
79
+ status: z.literal("active"),
80
+ name: z.string().min(1),
81
+ url: z.string().url(),
82
+ description: z.string().min(1),
83
+ tools: z.array(mcpToolSchema).min(1)
84
+ }).strict();
85
+ var mcpPlannedServerSchema = z.object({
86
+ status: z.literal("planned"),
87
+ name: z.string().min(1),
88
+ description: z.string().min(1),
89
+ planned_tools: z.array(mcpToolSchema).min(1)
90
+ }).strict();
91
+ var mcpServerSchema = z.discriminatedUnion("status", [
92
+ mcpActiveServerSchema,
93
+ mcpPlannedServerSchema
94
+ ]);
95
+ var mcpSchema = z.object({
96
+ version: z.string().optional(),
97
+ servers: z.array(mcpServerSchema).min(1)
98
+ }).strict();
99
+ var robotsPath = z.string().min(1).refine(
100
+ (path) => path === "*" || path.startsWith("/"),
101
+ { message: "robots.txt paths must start with /, or be *" }
102
+ );
103
+ var robotsTxtRuleSchema = z.object({
104
+ userAgent: z.union([
105
+ z.string().min(1),
106
+ z.array(z.string().min(1)).min(1)
107
+ ]),
108
+ allow: z.array(robotsPath).min(1).optional(),
109
+ disallow: z.array(robotsPath).min(1).optional(),
110
+ crawlDelay: z.number().positive().optional(),
111
+ comment: z.string().min(1).optional()
112
+ }).strict().refine(
113
+ (rule) => Boolean(rule.allow || rule.disallow),
114
+ { message: "robots.txt rules require `allow` or `disallow`" }
115
+ );
116
+ var contentSignalValueSchema = z.enum(["yes", "no"]);
117
+ var robotsTxtSchema = z.object({
118
+ policy: z.enum(["search-visible", "training-opt-out", "private"]).optional(),
119
+ sitemap: z.union([httpsUrl, z.literal(false)]).optional(),
120
+ contentSignals: z.object({
121
+ search: contentSignalValueSchema.optional(),
122
+ aiTrain: contentSignalValueSchema.optional(),
123
+ aiInput: contentSignalValueSchema.optional()
124
+ }).strict().optional(),
125
+ rules: z.array(robotsTxtRuleSchema).min(1).optional(),
126
+ additionalLines: z.array(z.string()).optional()
127
+ }).strict();
128
+ var aiReadinessConfigSchema = z.object({
129
+ site: httpsUrl,
130
+ organization: organizationSchema,
131
+ webSite: webSiteSchema.optional(),
132
+ llmsTxt: llmsTxtSchema.optional(),
133
+ llmsFull: llmsFullSchema.optional(),
134
+ agentsMd: agentsMdSchema.optional(),
135
+ mcp: mcpSchema.optional(),
136
+ robotsTxt: robotsTxtSchema.optional()
137
+ }).strict();
138
+
139
+ export {
140
+ aiReadinessConfigSchema
141
+ };