@marlinjai/clearify 2.0.0 → 2.2.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.
@@ -10,6 +10,16 @@ import { globbySync } from "globby";
10
10
  function toTitleCase(str) {
11
11
  return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
12
12
  }
13
+ function inferDocType(filePath) {
14
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
15
+ const file = basename(normalized, extname(normalized));
16
+ if (file === "readme") return "readme";
17
+ if (file === "changelog" || file === "changes") return "changelog";
18
+ if (file === "roadmap") return "roadmap";
19
+ if (file.includes("handover") || /\/handovers(\/|$)/.test(normalized)) return "handover";
20
+ if (/\/(plans|specs|superpowers|research|decisions)(\/|$)/.test(normalized)) return "plan";
21
+ return "documentation";
22
+ }
13
23
  function fileToRoutePath(filePath, docsDir, basePath = "/") {
14
24
  const rel = relative(docsDir, filePath);
15
25
  const ext = extname(rel);
@@ -42,7 +52,8 @@ function scanDocs(docsDir, exclude = [], basePath = "/") {
42
52
  icon: data.icon,
43
53
  order: data.order,
44
54
  summary: data.summary,
45
- category: data.category,
55
+ // `type` per the shared contract; inferred from path when omitted.
56
+ type: data.type ?? inferDocType(filePath),
46
57
  tags: data.tags,
47
58
  projects: data.projects,
48
59
  status: data.status,
@@ -197,6 +208,7 @@ function buildSectionData(section, rootFiles, customNavigation) {
197
208
  }
198
209
 
199
210
  export {
211
+ inferDocType,
200
212
  scanDocs,
201
213
  buildNavigation,
202
214
  buildRoutes,
@@ -10,10 +10,10 @@ import {
10
10
  resolveSections,
11
11
  scanHubProjects,
12
12
  writeDataConfig
13
- } from "./chunk-PGTNVDNF.js";
13
+ } from "./chunk-ZWF6VCSQ.js";
14
14
  import {
15
15
  buildSectionData
16
- } from "./chunk-CTL4YL7C.js";
16
+ } from "./chunk-77IOLCAM.js";
17
17
 
18
18
  // src/vite-plugin/index.ts
19
19
  import { resolve, dirname } from "path";
@@ -0,0 +1,134 @@
1
+ // src/core/llms-txt.ts
2
+ import { readFileSync } from "fs";
3
+ import matter from "gray-matter";
4
+ function normalizeBase(siteUrl) {
5
+ if (!siteUrl) return "";
6
+ return siteUrl.replace(/\/$/, "");
7
+ }
8
+ function toAbsoluteUrl(base, routePath) {
9
+ if (!base) return routePath;
10
+ if (!routePath.startsWith("/")) return `${base}/${routePath}`;
11
+ return `${base}${routePath}`;
12
+ }
13
+ function stripFrontmatterAndH1(markdown) {
14
+ const { content } = matter(markdown);
15
+ return content.replace(/^\s*#\s+[^\n]*\n+/, "").trimStart();
16
+ }
17
+ function firstParagraphSummary(markdown) {
18
+ const parsed = matter(markdown);
19
+ const fromFrontmatter = typeof parsed.data.summary === "string" && parsed.data.summary.trim() || typeof parsed.data.description === "string" && parsed.data.description.trim() || "";
20
+ const body = parsed.content;
21
+ const lines = body.split(/\r?\n/);
22
+ let inFence = false;
23
+ const paragraph = [];
24
+ for (const rawLine of lines) {
25
+ const line = rawLine.trimEnd();
26
+ if (/^```/.test(line)) {
27
+ inFence = !inFence;
28
+ if (paragraph.length > 0) break;
29
+ continue;
30
+ }
31
+ if (inFence) continue;
32
+ if (!line.trim()) {
33
+ if (paragraph.length > 0) break;
34
+ continue;
35
+ }
36
+ if (/^(#{1,6}\s|>\s|[-*+]\s|\d+\.\s|\|.*\|\s*$|<)/.test(line)) {
37
+ if (paragraph.length > 0) break;
38
+ continue;
39
+ }
40
+ paragraph.push(line.trim());
41
+ }
42
+ const text = paragraph.join(" ").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/([*_]{1,3})([^*_]+)\1/g, "$2").replace(/\s+/g, " ").trim();
43
+ return text || fromFrontmatter;
44
+ }
45
+ function uniqueOrdered(items) {
46
+ const seen = /* @__PURE__ */ new Set();
47
+ const out = [];
48
+ for (const item of items) {
49
+ if (seen.has(item)) continue;
50
+ seen.add(item);
51
+ out.push(item);
52
+ }
53
+ return out;
54
+ }
55
+ function buildHeader(config) {
56
+ const name = config.name.trim();
57
+ const tagline = config.hubProject?.description && config.hubProject.description.trim() || `Documentation for ${name}.`;
58
+ return `# ${name}
59
+
60
+ > ${tagline}
61
+
62
+ `;
63
+ }
64
+ function generateLlmsTxt(config, docs) {
65
+ const base = normalizeBase(config.siteUrl);
66
+ let out = buildHeader(config);
67
+ const mainDocs = docs.filter((d) => !d.optional);
68
+ const optionalDocs = docs.filter((d) => d.optional);
69
+ const sectionLabels = uniqueOrdered(mainDocs.map((d) => d.sectionLabel));
70
+ for (const label of sectionLabels) {
71
+ const sectionDocs = mainDocs.filter((d) => d.sectionLabel === label);
72
+ if (sectionDocs.length === 0) continue;
73
+ out += `## ${label}
74
+
75
+ `;
76
+ for (const doc of sectionDocs) {
77
+ const url = toAbsoluteUrl(base, doc.routePath);
78
+ const summary = firstParagraphSummary(doc.markdown);
79
+ if (summary) {
80
+ out += `- [${doc.title}](${url}): ${summary}
81
+ `;
82
+ } else {
83
+ out += `- [${doc.title}](${url})
84
+ `;
85
+ }
86
+ }
87
+ out += "\n";
88
+ }
89
+ if (optionalDocs.length > 0) {
90
+ out += `## Optional
91
+
92
+ `;
93
+ for (const doc of optionalDocs) {
94
+ const url = toAbsoluteUrl(base, doc.routePath);
95
+ const summary = firstParagraphSummary(doc.markdown);
96
+ if (summary) {
97
+ out += `- [${doc.title}](${url}): ${summary}
98
+ `;
99
+ } else {
100
+ out += `- [${doc.title}](${url})
101
+ `;
102
+ }
103
+ }
104
+ out += "\n";
105
+ }
106
+ return out.trimEnd() + "\n";
107
+ }
108
+ function generateLlmsFullTxt(config, docs) {
109
+ const base = normalizeBase(config.siteUrl);
110
+ let out = buildHeader(config);
111
+ for (const doc of docs) {
112
+ const url = toAbsoluteUrl(base, doc.routePath);
113
+ const body = stripFrontmatterAndH1(doc.markdown);
114
+ out += `## ${doc.title}
115
+
116
+ `;
117
+ out += `Source: ${url}
118
+
119
+ `;
120
+ if (body) {
121
+ out += body.trimEnd() + "\n\n";
122
+ }
123
+ }
124
+ return out.trimEnd() + "\n";
125
+ }
126
+ function readMarkdownFile(filePath) {
127
+ return readFileSync(filePath, "utf-8");
128
+ }
129
+
130
+ export {
131
+ generateLlmsTxt,
132
+ generateLlmsFullTxt,
133
+ readMarkdownFile
134
+ };
@@ -2,25 +2,31 @@ import {
2
2
  loadUserConfig,
3
3
  resolveConfig,
4
4
  resolveSections
5
- } from "./chunk-PGTNVDNF.js";
5
+ } from "./chunk-ZWF6VCSQ.js";
6
6
  import {
7
7
  scanDocs
8
- } from "./chunk-CTL4YL7C.js";
8
+ } from "./chunk-77IOLCAM.js";
9
9
 
10
10
  // src/node/check.ts
11
11
  import { resolve, relative } from "path";
12
12
  import { readFileSync, existsSync } from "fs";
13
13
  import matter from "gray-matter";
14
- var VALID_CATEGORIES = [
14
+ var VALID_TYPES = [
15
+ "readme",
15
16
  "documentation",
16
- "internal",
17
17
  "plan",
18
- "research",
19
- "decision",
20
18
  "roadmap",
21
- "changelog"
19
+ "changelog",
20
+ "handover"
21
+ ];
22
+ var VALID_STATUSES = [
23
+ "draft",
24
+ "decided",
25
+ "in-progress",
26
+ "completed",
27
+ "archived",
28
+ "rejected"
22
29
  ];
23
- var VALID_STATUSES = ["active", "superseded", "archived"];
24
30
  function isStrictPath(filePath) {
25
31
  const normalized = filePath.replace(/\\/g, "/");
26
32
  return /\/docs\/plans(\/|$)/.test(normalized) || /\/docs\/internal(\/|$)/.test(normalized);
@@ -43,8 +49,8 @@ function validateFrontmatter(doc, userRoot) {
43
49
  if (!rawFm.summary) {
44
50
  issues.push({ file: rel, field: "summary", message: "missing required field", severity: "error" });
45
51
  }
46
- if (!rawFm.category) {
47
- issues.push({ file: rel, field: "category", message: "missing required field", severity: "error" });
52
+ if (!rawFm.type) {
53
+ issues.push({ file: rel, field: "type", message: "recommended field missing (inferred from path)", severity: "warning" });
48
54
  }
49
55
  } else {
50
56
  if (!rawFm.title) {
@@ -53,16 +59,16 @@ function validateFrontmatter(doc, userRoot) {
53
59
  if (!rawFm.summary) {
54
60
  issues.push({ file: rel, field: "summary", message: "recommended field missing", severity: "warning" });
55
61
  }
56
- if (!rawFm.category) {
57
- issues.push({ file: rel, field: "category", message: "recommended field missing", severity: "warning" });
62
+ if (!rawFm.type) {
63
+ issues.push({ file: rel, field: "type", message: "recommended field missing (inferred from path)", severity: "warning" });
58
64
  }
59
65
  }
60
- if (rawFm.category !== void 0) {
61
- if (!VALID_CATEGORIES.includes(rawFm.category)) {
66
+ if (rawFm.type !== void 0) {
67
+ if (!VALID_TYPES.includes(rawFm.type)) {
62
68
  issues.push({
63
69
  file: rel,
64
- field: "category",
65
- message: `invalid value "${rawFm.category}" (allowed: ${VALID_CATEGORIES.join(", ")})`,
70
+ field: "type",
71
+ message: `invalid value "${rawFm.type}" (allowed: ${VALID_TYPES.join(", ")})`,
66
72
  severity: "error"
67
73
  });
68
74
  }
@@ -259,6 +265,8 @@ function levenshtein(a, b) {
259
265
  }
260
266
 
261
267
  export {
268
+ VALID_TYPES,
269
+ VALID_STATUSES,
262
270
  checkFrontmatter,
263
271
  checkLinks
264
272
  };
@@ -6,17 +6,21 @@ import {
6
6
  import {
7
7
  mermaidContentHash
8
8
  } from "./chunk-SCZZB7OE.js";
9
+ import {
10
+ generateLlmsFullTxt,
11
+ generateLlmsTxt
12
+ } from "./chunk-TVDWHKQF.js";
9
13
  import {
10
14
  clearifyPlugin
11
- } from "./chunk-RW4IKHY4.js";
15
+ } from "./chunk-LVPVSOXK.js";
12
16
  import {
13
17
  loadUserConfig,
14
18
  resolveConfig,
15
19
  resolveSections
16
- } from "./chunk-PGTNVDNF.js";
20
+ } from "./chunk-ZWF6VCSQ.js";
17
21
  import {
18
22
  buildSectionData
19
- } from "./chunk-CTL4YL7C.js";
23
+ } from "./chunk-77IOLCAM.js";
20
24
 
21
25
  // src/node/build.ts
22
26
  import { build as viteBuild } from "vite";
@@ -72,6 +76,29 @@ function generateSitemap(routes, siteUrl) {
72
76
  ${urls.join("\n")}
73
77
  </urlset>`;
74
78
  }
79
+ function formatBytes(bytes) {
80
+ if (bytes < 1024) return `${bytes} B`;
81
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
82
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
83
+ }
84
+ function collectLlmsDocs(sections) {
85
+ const out = [];
86
+ for (const sd of sections) {
87
+ for (const doc of sd.docs) {
88
+ const title = doc.frontmatter.title ?? "Untitled";
89
+ const optional = (doc.frontmatter.order ?? 0) >= 9e3;
90
+ const markdown = existsSync(doc.filePath) ? readFileSync(doc.filePath, "utf-8") : "";
91
+ out.push({
92
+ title,
93
+ routePath: doc.routePath,
94
+ sectionLabel: sd.section.label,
95
+ markdown,
96
+ optional
97
+ });
98
+ }
99
+ }
100
+ return out;
101
+ }
75
102
  function generateRobotsTxt(siteUrl) {
76
103
  const lines = ["User-agent: *", "Allow: /"];
77
104
  if (siteUrl) {
@@ -312,6 +339,19 @@ async function buildSite() {
312
339
  const sitemap = generateSitemap(sitemapRoutes, config.siteUrl);
313
340
  writeFileSync(resolve(outDir, "sitemap.xml"), sitemap);
314
341
  console.log(" Generated sitemap.xml");
342
+ if (config.generateLlmsTxt !== false) {
343
+ const llmsDocs = collectLlmsDocs(sectionDataList);
344
+ const llmsTxt = generateLlmsTxt(config, llmsDocs);
345
+ const llmsFullTxt = generateLlmsFullTxt(config, llmsDocs);
346
+ const llmsTxtPath = resolve(outDir, "llms.txt");
347
+ const llmsFullTxtPath = resolve(outDir, "llms-full.txt");
348
+ writeFileSync(llmsTxtPath, llmsTxt);
349
+ writeFileSync(llmsFullTxtPath, llmsFullTxt);
350
+ const llmsTxtSize = Buffer.byteLength(llmsTxt, "utf-8");
351
+ const llmsFullSize = Buffer.byteLength(llmsFullTxt, "utf-8");
352
+ console.log(` Generated ${llmsTxtPath} (${formatBytes(llmsTxtSize)})`);
353
+ console.log(` Generated ${llmsFullTxtPath} (${formatBytes(llmsFullSize)})`);
354
+ }
315
355
  rmSync(ssrOutDir, { recursive: true, force: true });
316
356
  console.log(" Cleaned up SSR build artifacts.");
317
357
  if (mermaidStrategy === "build") {
@@ -117,7 +117,8 @@ var ClearifyConfigSchema = z.object({
117
117
  hubProject: HubProjectPartialSchema,
118
118
  customCss: z.string().optional(),
119
119
  headTags: z.array(z.string()).optional(),
120
- includeReadme: z.boolean().default(false)
120
+ includeReadme: z.boolean().default(false),
121
+ generateLlmsTxt: z.boolean().default(true)
121
122
  });
122
123
  var ClearifyDataSchema = z.object({
123
124
  name: z.string().optional(),
@@ -265,6 +266,11 @@ function resolveConfig(userConfig = {}, root) {
265
266
  if (!userConfig.name && root) {
266
267
  parsed.name = detectProjectName(root);
267
268
  }
269
+ if (parsed.generateLlmsTxt && !parsed.siteUrl) {
270
+ throw new Error(
271
+ "clearify config: `generateLlmsTxt` is enabled (default) but `siteUrl` is not set. Set `siteUrl` to the production URL of your docs site, or set `generateLlmsTxt: false` to disable."
272
+ );
273
+ }
268
274
  return parsed;
269
275
  }
270
276
  function slugify(str) {
@@ -8,7 +8,7 @@ import {
8
8
  resolveSections,
9
9
  scanHubProjects,
10
10
  writeDataConfig
11
- } from "../chunk-PGTNVDNF.js";
11
+ } from "../chunk-ZWF6VCSQ.js";
12
12
  import "../chunk-AJBCHKBP.js";
13
13
  import {
14
14
  defineConfig
@@ -0,0 +1,54 @@
1
+ import { ClearifyConfig } from '../types/index.js';
2
+
3
+ /**
4
+ * A doc entry as consumed by the llms.txt generators. Produced from the
5
+ * scanned section data by callers (e.g. the build pipeline) so these
6
+ * functions stay pure string builders.
7
+ */
8
+ interface LlmsDocEntry {
9
+ /** Page title (from frontmatter or derived). */
10
+ title: string;
11
+ /** Route path on the site, e.g. `/getting-started`. */
12
+ routePath: string;
13
+ /** Section label the page belongs to, e.g. `Documentation`. */
14
+ sectionLabel: string;
15
+ /** Raw markdown source (frontmatter optional, handled by the generator). */
16
+ markdown: string;
17
+ /**
18
+ * Optional flag. When true the page goes under the `## Optional` bucket at
19
+ * the end of llms.txt rather than the main section list. Use for auxiliary
20
+ * content like a changelog or roadmap.
21
+ */
22
+ optional?: boolean;
23
+ }
24
+ /**
25
+ * Generate the `llms.txt` index file content.
26
+ *
27
+ * Format:
28
+ * # {name}
29
+ *
30
+ * > {tagline}
31
+ *
32
+ * ## {Section label}
33
+ *
34
+ * - [Title](https://site/path): first-paragraph summary
35
+ * ...
36
+ *
37
+ * ## Optional
38
+ * - [Title](https://site/path): summary
39
+ */
40
+ declare function generateLlmsTxt(config: ClearifyConfig, docs: LlmsDocEntry[]): string;
41
+ /**
42
+ * Generate the `llms-full.txt` bundle file content. Concatenates the entire
43
+ * markdown body of every doc under an H2 with a `Source:` line, so an LLM
44
+ * with no browsing can ingest the whole site from one file.
45
+ */
46
+ declare function generateLlmsFullTxt(config: ClearifyConfig, docs: LlmsDocEntry[]): string;
47
+ /**
48
+ * Read a markdown file from disk. Returned as-is, including frontmatter.
49
+ * Exists here so callers in the build pipeline stay thin and the unit tests
50
+ * can stub in-memory markdown without touching the filesystem.
51
+ */
52
+ declare function readMarkdownFile(filePath: string): string;
53
+
54
+ export { type LlmsDocEntry, generateLlmsFullTxt, generateLlmsTxt, readMarkdownFile };
@@ -0,0 +1,10 @@
1
+ import {
2
+ generateLlmsFullTxt,
3
+ generateLlmsTxt,
4
+ readMarkdownFile
5
+ } from "../chunk-TVDWHKQF.js";
6
+ export {
7
+ generateLlmsFullTxt,
8
+ generateLlmsTxt,
9
+ readMarkdownFile
10
+ };
@@ -1,2 +1,2 @@
1
1
  import '../types/index.js';
2
- export { D as DocFile, R as RootFileOptions, S as SectionData, b as buildNavigation, a as buildRoutes, c as buildSectionData, s as scanDocs } from './search.js';
2
+ export { D as DocFile, R as RootFileOptions, S as SectionData, b as buildNavigation, a as buildRoutes, c as buildSectionData, i as inferDocType, s as scanDocs } from './search.js';
@@ -2,12 +2,14 @@ import {
2
2
  buildNavigation,
3
3
  buildRoutes,
4
4
  buildSectionData,
5
+ inferDocType,
5
6
  scanDocs
6
- } from "../chunk-CTL4YL7C.js";
7
+ } from "../chunk-77IOLCAM.js";
7
8
  import "../chunk-5TD7NQIW.js";
8
9
  export {
9
10
  buildNavigation,
10
11
  buildRoutes,
11
12
  buildSectionData,
13
+ inferDocType,
12
14
  scanDocs
13
15
  };
@@ -1,4 +1,4 @@
1
- import { PageFrontmatter, ResolvedSection, SectionNavigation, RouteEntry, NavigationItem } from '../types/index.js';
1
+ import { PageFrontmatter, ResolvedSection, SectionNavigation, RouteEntry, NavigationItem, DocType } from '../types/index.js';
2
2
 
3
3
  interface SearchEntry {
4
4
  id: number;
@@ -11,6 +11,13 @@ interface SearchEntry {
11
11
  }
12
12
  declare function buildSearchIndex(docs: DocFile[], sectionId?: string, sectionLabel?: string): SearchEntry[];
13
13
 
14
+ /**
15
+ * Infer the document `type` from its file path when frontmatter omits it.
16
+ * Mirrors the path-inference rules in the shared contract:
17
+ * knowledge-base/standards/document-lifecycle.md (the single source of truth).
18
+ * Returns undefined when nothing matches, leaving `type` unset (never filtered).
19
+ */
20
+ declare function inferDocType(filePath: string): DocType | undefined;
14
21
  interface DocFile {
15
22
  filePath: string;
16
23
  routePath: string;
@@ -33,4 +40,4 @@ interface RootFileOptions {
33
40
  }
34
41
  declare function buildSectionData(section: ResolvedSection, rootFiles?: RootFileOptions, customNavigation?: NavigationItem[] | null): SectionData;
35
42
 
36
- export { type DocFile as D, type RootFileOptions as R, type SectionData as S, type SearchEntry, buildRoutes as a, buildNavigation as b, buildSearchIndex, buildSectionData as c, scanDocs as s };
43
+ export { type DocFile as D, type RootFileOptions as R, type SectionData as S, type SearchEntry, buildRoutes as a, buildNavigation as b, buildSearchIndex, buildSectionData as c, inferDocType as i, scanDocs as s };
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  buildSite
3
- } from "../chunk-BCEGDIR6.js";
3
+ } from "../chunk-XAX53RY5.js";
4
4
  import "../chunk-PRTER35L.js";
5
5
  import "../chunk-SCZZB7OE.js";
6
- import "../chunk-RW4IKHY4.js";
6
+ import "../chunk-TVDWHKQF.js";
7
+ import "../chunk-LVPVSOXK.js";
7
8
  import "../chunk-B2Q23JW3.js";
8
- import "../chunk-PGTNVDNF.js";
9
+ import "../chunk-ZWF6VCSQ.js";
9
10
  import "../chunk-AJBCHKBP.js";
10
11
  import "../chunk-V7LLYIRO.js";
11
- import "../chunk-CTL4YL7C.js";
12
+ import "../chunk-77IOLCAM.js";
12
13
  import "../chunk-5TD7NQIW.js";
13
14
  export {
14
15
  buildSite
@@ -1,10 +1,14 @@
1
+ import { DocStatus, DocType } from '../types/index.js';
2
+
1
3
  interface CheckOptions {
2
4
  frontmatterOnly?: boolean;
3
5
  }
6
+ declare const VALID_TYPES: DocType[];
7
+ declare const VALID_STATUSES: DocStatus[];
4
8
  declare function checkFrontmatter(): Promise<{
5
9
  errors: number;
6
10
  warnings: number;
7
11
  }>;
8
12
  declare function checkLinks(options?: CheckOptions): Promise<void>;
9
13
 
10
- export { type CheckOptions, checkFrontmatter, checkLinks };
14
+ export { type CheckOptions, VALID_STATUSES, VALID_TYPES, checkFrontmatter, checkLinks };
@@ -1,13 +1,17 @@
1
1
  import {
2
+ VALID_STATUSES,
3
+ VALID_TYPES,
2
4
  checkFrontmatter,
3
5
  checkLinks
4
- } from "../chunk-CK2SLBAG.js";
5
- import "../chunk-PGTNVDNF.js";
6
+ } from "../chunk-V5J36LF5.js";
7
+ import "../chunk-ZWF6VCSQ.js";
6
8
  import "../chunk-AJBCHKBP.js";
7
9
  import "../chunk-V7LLYIRO.js";
8
- import "../chunk-CTL4YL7C.js";
10
+ import "../chunk-77IOLCAM.js";
9
11
  import "../chunk-5TD7NQIW.js";
10
12
  export {
13
+ VALID_STATUSES,
14
+ VALID_TYPES,
11
15
  checkFrontmatter,
12
16
  checkLinks
13
17
  };
@@ -2,6 +2,7 @@ import * as vite from 'vite';
2
2
  export { buildSite as build } from './build.js';
3
3
  export { init } from './init.js';
4
4
  export { checkFrontmatter, checkLinks } from './check.js';
5
+ import '../types/index.js';
5
6
 
6
7
  declare function createServer(options?: {
7
8
  port?: number;
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  buildSite
3
- } from "../chunk-BCEGDIR6.js";
3
+ } from "../chunk-XAX53RY5.js";
4
4
  import {
5
5
  remarkMermaidToComponent
6
6
  } from "../chunk-PRTER35L.js";
7
7
  import "../chunk-SCZZB7OE.js";
8
+ import "../chunk-TVDWHKQF.js";
8
9
  import {
9
10
  clearifyPlugin
10
- } from "../chunk-RW4IKHY4.js";
11
+ } from "../chunk-LVPVSOXK.js";
11
12
  import "../chunk-B2Q23JW3.js";
12
13
  import {
13
14
  init
@@ -15,14 +16,14 @@ import {
15
16
  import {
16
17
  checkFrontmatter,
17
18
  checkLinks
18
- } from "../chunk-CK2SLBAG.js";
19
+ } from "../chunk-V5J36LF5.js";
19
20
  import {
20
21
  loadUserConfig,
21
22
  resolveConfig
22
- } from "../chunk-PGTNVDNF.js";
23
+ } from "../chunk-ZWF6VCSQ.js";
23
24
  import "../chunk-AJBCHKBP.js";
24
25
  import "../chunk-V7LLYIRO.js";
25
- import "../chunk-CTL4YL7C.js";
26
+ import "../chunk-77IOLCAM.js";
26
27
  import "../chunk-5TD7NQIW.js";
27
28
 
28
29
  // src/node/index.ts
@@ -62,6 +62,12 @@ interface ClearifyConfig {
62
62
  customCss?: string;
63
63
  headTags?: string[];
64
64
  includeReadme?: boolean;
65
+ /**
66
+ * When true (default), `clearify build` writes `llms.txt` and
67
+ * `llms-full.txt` into the output directory for AI agents and crawlers.
68
+ * Requires `siteUrl` to be set. Set to `false` to skip generation.
69
+ */
70
+ generateLlmsTxt?: boolean;
65
71
  }
66
72
  interface NavigationItem {
67
73
  label: string;
@@ -71,17 +77,35 @@ interface NavigationItem {
71
77
  badgeColor?: string;
72
78
  children?: NavigationItem[];
73
79
  }
74
- type DocCategory = 'documentation' | 'internal' | 'plan' | 'research' | 'decision' | 'roadmap' | 'changelog';
80
+ /**
81
+ * Document type vocabulary. Source of truth:
82
+ * knowledge-base/standards/document-lifecycle.md (the single contract both
83
+ * the Session Dashboard and Clearify read). Keep this union in sync with the
84
+ * "Type" table in that standard. `category` (the legacy field) and its values
85
+ * (`internal`, `research`, `decision`) were reconciled away in favor of `type`.
86
+ */
87
+ type DocType = 'readme' | 'documentation' | 'plan' | 'roadmap' | 'changelog' | 'handover';
88
+ /**
89
+ * Document status vocabulary. Source of truth:
90
+ * knowledge-base/standards/document-lifecycle.md.
91
+ *
92
+ * For `type: plan` this tracks the workflow lifecycle (all six values).
93
+ * For `type: documentation` only `draft` is meaningful (omitted = published).
94
+ * Status is optional on every type and a missing status is never filtered.
95
+ */
96
+ type DocStatus = 'draft' | 'decided' | 'in-progress' | 'completed' | 'archived' | 'rejected';
75
97
  interface PageFrontmatter {
76
98
  title?: string;
77
99
  description?: string;
78
100
  icon?: string;
79
101
  order?: number;
80
102
  summary?: string;
81
- category?: DocCategory;
103
+ /** See {@link DocType}. Inferred from path when omitted. */
104
+ type?: DocType;
82
105
  tags?: string[];
83
106
  projects?: string[];
84
- status?: 'active' | 'superseded' | 'archived';
107
+ /** See {@link DocStatus}. Missing status is treated as unset (not filtered). */
108
+ status?: DocStatus;
85
109
  date?: string;
86
110
  }
87
111
  interface RouteEntry {
@@ -193,4 +217,4 @@ interface ClearifyDataConfig {
193
217
  }
194
218
  declare function defineConfig(config: Partial<ClearifyConfig>): Partial<ClearifyConfig>;
195
219
 
196
- export { type ClearifyConfig, type ClearifyDataConfig, type DocCategory, type HubConfig, type HubProject, type HubProjectPartial, type HubProjectPlacement, type HubProjectSource, type NavigationItem, type PageFrontmatter, type RemoteGitSource, type ResolvedSection, type RouteEntry, type SectionConfig, type SectionNavigation, defineConfig };
220
+ export { type ClearifyConfig, type ClearifyDataConfig, type DocStatus, type DocType, type HubConfig, type HubProject, type HubProjectPartial, type HubProjectPlacement, type HubProjectSource, type NavigationItem, type PageFrontmatter, type RemoteGitSource, type ResolvedSection, type RouteEntry, type SectionConfig, type SectionNavigation, defineConfig };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  clearifyPlugin
3
- } from "../chunk-RW4IKHY4.js";
3
+ } from "../chunk-LVPVSOXK.js";
4
4
  import "../chunk-B2Q23JW3.js";
5
- import "../chunk-PGTNVDNF.js";
5
+ import "../chunk-ZWF6VCSQ.js";
6
6
  import "../chunk-AJBCHKBP.js";
7
7
  import "../chunk-V7LLYIRO.js";
8
- import "../chunk-CTL4YL7C.js";
8
+ import "../chunk-77IOLCAM.js";
9
9
  import "../chunk-5TD7NQIW.js";
10
10
  export {
11
11
  clearifyPlugin
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@marlinjai/clearify",
3
- "version": "2.0.0",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "2.2.0",
4
7
  "description": "An open-source documentation site generator. Turn markdown into beautiful docs.",
5
8
  "type": "module",
6
9
  "bin": {