@marlinjai/clearify 1.20.0 → 2.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.
@@ -37,7 +37,6 @@ declare const ClearifyDataSchema: z.ZodObject<{
37
37
  projects: z.ZodDefault<z.ZodArray<z.ZodObject<{
38
38
  name: z.ZodString;
39
39
  description: z.ZodString;
40
- href: z.ZodOptional<z.ZodString>;
41
40
  repo: z.ZodOptional<z.ZodString>;
42
41
  status: z.ZodDefault<z.ZodEnum<{
43
42
  active: "active";
@@ -48,20 +47,33 @@ declare const ClearifyDataSchema: z.ZodObject<{
48
47
  icon: z.ZodOptional<z.ZodString>;
49
48
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
49
  group: z.ZodOptional<z.ZodString>;
51
- mode: z.ZodOptional<z.ZodEnum<{
52
- link: "link";
53
- embed: "embed";
54
- inject: "inject";
55
- }>>;
56
- git: z.ZodOptional<z.ZodObject<{
50
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
51
+ kind: z.ZodLiteral<"none">;
52
+ }, z.core.$strip>, z.ZodObject<{
53
+ kind: z.ZodLiteral<"git">;
57
54
  repo: z.ZodString;
58
55
  ref: z.ZodOptional<z.ZodString>;
59
56
  path: z.ZodOptional<z.ZodString>;
60
57
  sparse: z.ZodOptional<z.ZodBoolean>;
61
- }, z.core.$strip>>;
62
- embedSections: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodLiteral<"public">, z.ZodArray<z.ZodString>]>>;
63
- injectInto: z.ZodOptional<z.ZodString>;
64
- docsPath: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strip>, z.ZodObject<{
59
+ kind: z.ZodLiteral<"url">;
60
+ url: z.ZodString;
61
+ }, z.core.$strip>, z.ZodObject<{
62
+ kind: z.ZodLiteral<"inline">;
63
+ markdown: z.ZodString;
64
+ }, z.core.$strip>], "kind">;
65
+ placement: z.ZodDiscriminatedUnion<[z.ZodObject<{
66
+ kind: z.ZodLiteral<"card">;
67
+ href: z.ZodString;
68
+ }, z.core.$strip>, z.ZodObject<{
69
+ kind: z.ZodLiteral<"tab">;
70
+ sections: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodLiteral<"public">, z.ZodArray<z.ZodString>]>>;
71
+ }, z.core.$strip>, z.ZodObject<{
72
+ kind: z.ZodLiteral<"nested">;
73
+ into: z.ZodString;
74
+ docsPath: z.ZodOptional<z.ZodString>;
75
+ group: z.ZodOptional<z.ZodString>;
76
+ }, z.core.$strip>], "kind">;
65
77
  }, z.core.$strip>>>;
66
78
  scan: z.ZodOptional<z.ZodString>;
67
79
  cacheDir: z.ZodOptional<z.ZodString>;
@@ -8,7 +8,7 @@ import {
8
8
  resolveSections,
9
9
  scanHubProjects,
10
10
  writeDataConfig
11
- } from "../chunk-2WML2D77.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
+ };
@@ -79,6 +79,7 @@ async function secretExists(token, owner, repo, name) {
79
79
  const res = await ghApiFetch(url, { headers: { Authorization: `token ${token}` } });
80
80
  return res.status === 200;
81
81
  }
82
+ var __internal = { encryptSecret };
82
83
  async function promptSecretMode() {
83
84
  const { createInterface } = await import("readline");
84
85
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -212,12 +213,13 @@ async function updateHubRegistry(token, hub, project) {
212
213
  name: project.name,
213
214
  description: project.description,
214
215
  status: "active",
215
- mode: "embed",
216
- git: {
216
+ source: {
217
+ kind: "git",
217
218
  repo: project.gitRepoUrl,
218
219
  ref: project.gitRef,
219
220
  path: project.docsPath
220
- }
221
+ },
222
+ placement: { kind: "tab" }
221
223
  };
222
224
  if (existingIdx >= 0) {
223
225
  currentData.hub.projects[existingIdx] = entry;
@@ -396,5 +398,6 @@ function printSuccessFooter(currentRepo) {
396
398
  `);
397
399
  }
398
400
  export {
401
+ __internal,
399
402
  registerWithHub
400
403
  };
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  buildSite
3
- } from "../chunk-XR4WG237.js";
3
+ } from "../chunk-PZA4UAFG.js";
4
4
  import "../chunk-PRTER35L.js";
5
5
  import "../chunk-SCZZB7OE.js";
6
- import "../chunk-GWYMXKDL.js";
6
+ import "../chunk-TVDWHKQF.js";
7
+ import "../chunk-Q5FUAK6J.js";
7
8
  import "../chunk-B2Q23JW3.js";
8
- import "../chunk-2WML2D77.js";
9
+ import "../chunk-ZWF6VCSQ.js";
9
10
  import "../chunk-AJBCHKBP.js";
10
11
  import "../chunk-V7LLYIRO.js";
11
12
  import "../chunk-CTL4YL7C.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  checkFrontmatter,
3
3
  checkLinks
4
- } from "../chunk-HZRIVY25.js";
5
- import "../chunk-2WML2D77.js";
4
+ } from "../chunk-GZE6QLJT.js";
5
+ import "../chunk-ZWF6VCSQ.js";
6
6
  import "../chunk-AJBCHKBP.js";
7
7
  import "../chunk-V7LLYIRO.js";
8
8
  import "../chunk-CTL4YL7C.js";
@@ -1,25 +1,26 @@
1
1
  import {
2
2
  buildSite
3
- } from "../chunk-XR4WG237.js";
3
+ } from "../chunk-PZA4UAFG.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-GWYMXKDL.js";
11
+ } from "../chunk-Q5FUAK6J.js";
11
12
  import "../chunk-B2Q23JW3.js";
12
13
  import {
13
14
  init
14
- } from "../chunk-JZZ2BNIL.js";
15
+ } from "../chunk-GPBEDIOY.js";
15
16
  import {
16
17
  checkFrontmatter,
17
18
  checkLinks
18
- } from "../chunk-HZRIVY25.js";
19
+ } from "../chunk-GZE6QLJT.js";
19
20
  import {
20
21
  loadUserConfig,
21
22
  resolveConfig
22
- } from "../chunk-2WML2D77.js";
23
+ } from "../chunk-ZWF6VCSQ.js";
23
24
  import "../chunk-AJBCHKBP.js";
24
25
  import "../chunk-V7LLYIRO.js";
25
26
  import "../chunk-CTL4YL7C.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  init
3
- } from "../chunk-JZZ2BNIL.js";
3
+ } from "../chunk-GPBEDIOY.js";
4
4
  export {
5
5
  init
6
6
  };
@@ -58,10 +58,16 @@ interface ClearifyConfig {
58
58
  [key: string]: string | undefined;
59
59
  };
60
60
  hub?: HubConfig;
61
- hubProject?: Omit<HubProject, 'name'>;
61
+ hubProject?: HubProjectPartial;
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;
@@ -92,10 +98,54 @@ interface RouteEntry {
92
98
  componentPath?: string;
93
99
  redirectTo?: string;
94
100
  }
101
+ /**
102
+ * Where a hub project's content comes from.
103
+ *
104
+ * `none`: no content pulled, card links out only.
105
+ * `git`: sparse-clone a repo subdirectory.
106
+ * `url`: reserved for future remote-docs fetch, not implemented.
107
+ * `inline`: reserved for future inline markdown, not implemented.
108
+ */
109
+ type HubProjectSource = {
110
+ kind: 'none';
111
+ } | {
112
+ kind: 'git';
113
+ repo: string;
114
+ /** branch, tag, or SHA. Default: 'main' */
115
+ ref?: string;
116
+ /** subdirectory to sparse-checkout. Default: 'docs/public' */
117
+ path?: string;
118
+ /** defaults to true when path is set */
119
+ sparse?: boolean;
120
+ } | {
121
+ kind: 'url';
122
+ url: string;
123
+ } | {
124
+ kind: 'inline';
125
+ markdown: string;
126
+ };
127
+ /**
128
+ * How a hub project shows up in the hub UI.
129
+ *
130
+ * `card`: grid entry that links to an external href.
131
+ * `tab`: full tab in the hub nav, rendering the cloned sections.
132
+ * `nested`: folded into an existing hub section as a subfolder.
133
+ */
134
+ type HubProjectPlacement = {
135
+ kind: 'card';
136
+ href: string;
137
+ } | {
138
+ kind: 'tab';
139
+ sections?: 'all' | 'public' | string[];
140
+ } | {
141
+ kind: 'nested';
142
+ into: string;
143
+ docsPath?: string;
144
+ group?: string;
145
+ };
95
146
  interface HubProject {
96
147
  name: string;
97
148
  description: string;
98
- href?: string;
99
149
  repo?: string;
100
150
  status?: 'active' | 'beta' | 'planned' | 'deprecated';
101
151
  icon?: string;
@@ -103,11 +153,25 @@ interface HubProject {
103
153
  group?: string;
104
154
  hubUrl?: string;
105
155
  hubName?: string;
106
- mode?: 'link' | 'embed' | 'inject';
107
- git?: RemoteGitSource;
108
- embedSections?: 'all' | 'public' | string[];
109
- injectInto?: string;
110
- docsPath?: string;
156
+ source: HubProjectSource;
157
+ placement: HubProjectPlacement;
158
+ }
159
+ /**
160
+ * The subset of HubProject fields a sub-project can declare about itself in
161
+ * `clearify.config.ts`. The hub scanner composes this with the sub-project's
162
+ * `siteUrl` and assigns `source` + `placement` on its own side.
163
+ */
164
+ interface HubProjectPartial {
165
+ description: string;
166
+ repo?: string;
167
+ status?: 'active' | 'beta' | 'planned' | 'deprecated';
168
+ icon?: string;
169
+ tags?: string[];
170
+ group?: string;
171
+ /** URL of the parent hub site, used by the Sidebar back-link. */
172
+ hubUrl?: string;
173
+ /** Display name for the Sidebar back-link. Default: 'Hub'. */
174
+ hubName?: string;
111
175
  }
112
176
  interface HubConfig {
113
177
  projects: HubProject[];
@@ -135,4 +199,4 @@ interface ClearifyDataConfig {
135
199
  }
136
200
  declare function defineConfig(config: Partial<ClearifyConfig>): Partial<ClearifyConfig>;
137
201
 
138
- export { type ClearifyConfig, type ClearifyDataConfig, type DocCategory, type HubConfig, type HubProject, type NavigationItem, type PageFrontmatter, type RemoteGitSource, type ResolvedSection, type RouteEntry, type SectionConfig, type SectionNavigation, defineConfig };
202
+ 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 };
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  clearifyPlugin
3
- } from "../chunk-GWYMXKDL.js";
3
+ } from "../chunk-Q5FUAK6J.js";
4
4
  import "../chunk-B2Q23JW3.js";
5
- import "../chunk-2WML2D77.js";
5
+ import "../chunk-ZWF6VCSQ.js";
6
6
  import "../chunk-AJBCHKBP.js";
7
7
  import "../chunk-V7LLYIRO.js";
8
8
  import "../chunk-CTL4YL7C.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marlinjai/clearify",
3
- "version": "1.20.0",
3
+ "version": "2.1.0",
4
4
  "description": "An open-source documentation site generator. Turn markdown into beautiful docs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,7 +28,8 @@
28
28
  "postbuild": "node scripts/generate-schema.mjs",
29
29
  "dev:self": "node bin/clearify.js dev",
30
30
  "prepare": "husky",
31
- "release": "semantic-release"
31
+ "release": "semantic-release",
32
+ "test": "vitest run"
32
33
  },
33
34
  "dependencies": {
34
35
  "@inquirer/password": "^5.0.11",
@@ -82,7 +83,8 @@
82
83
  "puppeteer": "^24.37.2",
83
84
  "semantic-release": "^25.0.3",
84
85
  "tsup": "^8.3.6",
85
- "typescript": "^5.7.3"
86
+ "typescript": "^5.7.3",
87
+ "vitest": "^4.1.4"
86
88
  },
87
89
  "packageManager": "pnpm@9.15.0",
88
90
  "license": "MIT",
@@ -1,13 +1,13 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
2
  import { Link, useLocation, useNavigate } from 'react-router-dom';
3
3
  import clsx from 'clsx';
4
- import type { NavigationItem, SectionNavigation, HubProject } from '../types/index.js';
4
+ import type { NavigationItem, SectionNavigation, HubProjectPartial } from '../types/index.js';
5
5
 
6
6
  interface SidebarProps {
7
7
  sections: SectionNavigation[];
8
8
  open: boolean;
9
9
  onClose: () => void;
10
- hubProject?: Omit<HubProject, 'name'>;
10
+ hubProject?: HubProjectPartial;
11
11
  }
12
12
 
13
13
  function findActiveSection(pathname: string, sections: SectionNavigation[]): string {