@takazudo/zudo-doc 5.8.0 → 5.10.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/CHANGELOG.md +35 -0
- package/README.md +1 -1
- package/dist/chrome/derive.d.ts +1 -1
- package/dist/chrome/derive.js +24 -2
- package/dist/config-assertions/index.d.ts +30 -0
- package/dist/config-assertions/index.js +41 -1
- package/dist/config.d.ts +18 -0
- package/dist/config.js +1 -0
- package/dist/doc-content-header/index.js +6 -0
- package/dist/doc-metainfo-area/index.js +1 -14
- package/dist/doc-page-renderer/index.d.ts +3 -3
- package/dist/doc-page-renderer/index.js +1 -1
- package/dist/doc-page-shell/index.d.ts +6 -0
- package/dist/doc-pager/index.d.ts +6 -0
- package/dist/doc-pager/index.js +5 -2
- package/dist/docs-schema/index.d.ts +11 -0
- package/dist/docs-schema/index.js +8 -1
- package/dist/format-date/index.d.ts +12 -0
- package/dist/format-date/index.js +64 -0
- package/dist/frontmatter-preview-defaults/index.js +6 -1
- package/dist/header/header.js +9 -1
- package/dist/home-page/index.js +3 -1
- package/dist/html-preview-wrapper/highlight-runtime.d.ts +7 -7
- package/dist/html-preview-wrapper/highlight-runtime.js +1 -1
- package/dist/i18n-defaults/index.js +3 -0
- package/dist/integrations/changelog/emit.js +19 -2
- package/dist/mdx-components/index.d.ts +4 -1
- package/dist/mdx-components/index.js +3 -1
- package/dist/nav-indexing/index.d.ts +2 -0
- package/dist/nav-indexing/index.js +2 -0
- package/dist/nav-indexing/note-tray-index.d.ts +29 -0
- package/dist/nav-indexing/note-tray-index.js +120 -0
- package/dist/note-tray-index/index.d.ts +45 -0
- package/dist/note-tray-index/index.js +52 -0
- package/dist/note-tray-model/index.d.ts +31 -0
- package/dist/note-tray-model/index.js +74 -0
- package/dist/plugins/codex-resources.d.ts +4 -0
- package/dist/plugins/codex-resources.js +31 -0
- package/dist/plugins/internal/claude-resources/generate.js +20 -291
- package/dist/plugins/internal/codex-resources/agents-md.d.ts +6 -0
- package/dist/plugins/internal/codex-resources/agents-md.js +70 -0
- package/dist/plugins/internal/codex-resources/agents.d.ts +7 -0
- package/dist/plugins/internal/codex-resources/agents.js +104 -0
- package/dist/plugins/internal/codex-resources/config.d.ts +6 -0
- package/dist/plugins/internal/codex-resources/config.js +111 -0
- package/dist/plugins/internal/codex-resources/generate.d.ts +20 -0
- package/dist/plugins/internal/codex-resources/generate.js +34 -0
- package/dist/plugins/internal/codex-resources/hooks.d.ts +6 -0
- package/dist/plugins/internal/codex-resources/hooks.js +174 -0
- package/dist/plugins/internal/codex-resources/index.d.ts +24 -0
- package/dist/plugins/internal/codex-resources/index.js +21 -0
- package/dist/plugins/internal/codex-resources/overview.d.ts +10 -0
- package/dist/plugins/internal/codex-resources/overview.js +33 -0
- package/dist/plugins/internal/codex-resources/rules.d.ts +6 -0
- package/dist/plugins/internal/codex-resources/rules.js +255 -0
- package/dist/plugins/internal/codex-resources/skills.d.ts +2 -0
- package/dist/plugins/internal/codex-resources/skills.js +84 -0
- package/dist/plugins/internal/codex-resources/utils.d.ts +14 -0
- package/dist/plugins/internal/codex-resources/utils.js +80 -0
- package/dist/plugins/internal/resource-docs-shared/fs.d.ts +3 -0
- package/dist/plugins/internal/resource-docs-shared/fs.js +19 -0
- package/dist/plugins/internal/resource-docs-shared/index.d.ts +7 -0
- package/dist/plugins/internal/resource-docs-shared/index.js +44 -0
- package/dist/plugins/internal/resource-docs-shared/links.d.ts +22 -0
- package/dist/plugins/internal/resource-docs-shared/links.js +44 -0
- package/dist/plugins/internal/resource-docs-shared/markdown-structure.d.ts +9 -0
- package/dist/plugins/internal/resource-docs-shared/markdown-structure.js +22 -0
- package/dist/plugins/internal/resource-docs-shared/mdx.d.ts +23 -0
- package/dist/plugins/internal/resource-docs-shared/mdx.js +63 -0
- package/dist/plugins/internal/resource-docs-shared/skills.d.ts +37 -0
- package/dist/plugins/internal/resource-docs-shared/skills.js +318 -0
- package/dist/plugins/internal/resource-docs-shared/walk.d.ts +20 -0
- package/dist/plugins/internal/resource-docs-shared/walk.js +41 -0
- package/dist/preset.d.ts +19 -0
- package/dist/preset.js +16 -1
- package/dist/safelist.css +1 -1
- package/dist/settings.d.ts +5 -0
- package/dist/sidebar/types.d.ts +6 -0
- package/dist/sidebar-tree/build-tree.js +8 -0
- package/dist/sidebar-tree/types.d.ts +12 -0
- package/dist/sidebar-tree-island/index.js +175 -1
- package/dist/sidebar-utils/index.d.ts +6 -0
- package/dist/site-schema/doc-route-entries.js +2 -0
- package/dist/site-schema/index.d.ts +1 -0
- package/dist/site-schema/index.js +3 -1
- package/dist/site-schema/nav-tree.js +17 -2
- package/dist/site-schema/note-tray-validate.d.ts +7 -0
- package/dist/site-schema/note-tray-validate.js +94 -0
- package/dist/site-schema/types.d.ts +11 -0
- package/dist/site-tree-nav-island/index.d.ts +5 -1
- package/dist/site-tree-nav-island/index.js +119 -12
- package/dist/theme-packs/drift/pack.css +6 -1
- package/dist/theme-packs/hearth/pack.css +5 -0
- package/dist/theme-packs/sakura/pack.css +6 -0
- package/dist/theme-packs/scandi/pack.css +7 -7
- package/dist/theme-packs/timberline/pack.css +5 -0
- package/eject/doc-pager/index.tsx +9 -0
- package/eject/header/header.tsx +26 -1
- package/eject/sidebar/types.ts +6 -0
- package/eject/sidebar-tree-island/index.tsx +238 -6
- package/eject/site-tree-nav-island/index.tsx +166 -16
- package/package.json +25 -8
- /package/dist/plugins/internal/{claude-resources → resource-docs-shared}/escape-for-mdx.d.ts +0 -0
- /package/dist/plugins/internal/{claude-resources → resource-docs-shared}/escape-for-mdx.js +0 -0
|
@@ -1,133 +1,17 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
return matter(content);
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function escapeTitle(s) {
|
|
22
|
-
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
23
|
-
}
|
|
24
|
-
function listFiles(dir) {
|
|
25
|
-
if (!fs.existsSync(dir)) return [];
|
|
26
|
-
return fs.readdirSync(dir, { withFileTypes: true }).filter((d) => d.isFile()).map((d) => d.name).sort();
|
|
27
|
-
}
|
|
28
|
-
function writeCategoryIndex(outputDir, label, position, description) {
|
|
29
|
-
const mdx = `---
|
|
30
|
-
title: "${escapeTitle(label)}"
|
|
31
|
-
description: "${escapeTitle(description)}"
|
|
32
|
-
sidebar_position: ${position}
|
|
33
|
-
category_no_page: true
|
|
34
|
-
generated: true
|
|
35
|
-
---
|
|
36
|
-
`;
|
|
37
|
-
fs.writeFileSync(path.join(outputDir, "index.mdx"), mdx);
|
|
38
|
-
}
|
|
39
|
-
function writeUnlistedSubPage(outputPath, title, body) {
|
|
40
|
-
fs.writeFileSync(
|
|
41
|
-
outputPath,
|
|
42
|
-
`---
|
|
43
|
-
title: "${escapeTitle(title)}"
|
|
44
|
-
unlisted: true
|
|
45
|
-
generated: true
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
${body}
|
|
49
|
-
`
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
function assertNotIndexReserved(nameOrSlug, errorMessage) {
|
|
53
|
-
if (nameOrSlug === "index") {
|
|
54
|
-
throw new Error(errorMessage);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function isRepoRelativeLink(url) {
|
|
58
|
-
const trimmed = url.trim();
|
|
59
|
-
if (trimmed === "") return false;
|
|
60
|
-
if (trimmed.startsWith("#")) return false;
|
|
61
|
-
if (trimmed.startsWith("/")) return false;
|
|
62
|
-
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) return false;
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
function downgradeRepoRelativeLinks(content) {
|
|
66
|
-
const blockPlaceholder = "\0CRLINK_BLOCK_";
|
|
67
|
-
const inlinePlaceholder = "\0CRLINK_INLINE_";
|
|
68
|
-
const codeBlocks = [];
|
|
69
|
-
const withBlocks = content.replace(/(`{3,}|~{3,})[^\n]*\n[\s\S]*?\1/g, (match) => {
|
|
70
|
-
codeBlocks.push(match);
|
|
71
|
-
return `${blockPlaceholder}${codeBlocks.length - 1}\0`;
|
|
72
|
-
});
|
|
73
|
-
const transformed = withBlocks.split(new RegExp(`(${blockPlaceholder}\\d+\0)`, "g")).map((part) => {
|
|
74
|
-
if (new RegExp(`^${blockPlaceholder}\\d+\0$`).test(part)) return part;
|
|
75
|
-
const inlineCodes = [];
|
|
76
|
-
const withInline = part.replace(
|
|
77
|
-
/(`{1,3})(?!`)([\s\S]*?[^`])\1(?!`)/g,
|
|
78
|
-
(match) => {
|
|
79
|
-
inlineCodes.push(match);
|
|
80
|
-
return `${inlinePlaceholder}${inlineCodes.length - 1}\0`;
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
const rewritten = withInline.replace(
|
|
84
|
-
/!?\[([^\]]*)\]\(([^)]+)\)/g,
|
|
85
|
-
(match, text, url) => isRepoRelativeLink(url) ? `\`${text}\`` : match
|
|
86
|
-
);
|
|
87
|
-
return rewritten.replace(
|
|
88
|
-
new RegExp(`${inlinePlaceholder}(\\d+)\0`, "g"),
|
|
89
|
-
(_, idx) => inlineCodes[Number(idx)] ?? ""
|
|
90
|
-
);
|
|
91
|
-
}).join("");
|
|
92
|
-
return transformed.replace(
|
|
93
|
-
new RegExp(`${blockPlaceholder}(\\d+)\0`, "g"),
|
|
94
|
-
(_, idx) => codeBlocks[Number(idx)] ?? ""
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
const EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
98
|
-
"node_modules",
|
|
99
|
-
"worktrees",
|
|
100
|
-
"dist",
|
|
101
|
-
"out",
|
|
102
|
-
"public",
|
|
103
|
-
"__inbox",
|
|
104
|
-
"test-results"
|
|
105
|
-
]);
|
|
106
|
-
function findClaudeMdFiles(dir, excludeDirs) {
|
|
107
|
-
const results = [];
|
|
108
|
-
if (!fs.existsSync(dir)) return results;
|
|
109
|
-
const excludes = excludeDirs.map(
|
|
110
|
-
(d) => d.endsWith(path.sep) ? d.slice(0, -path.sep.length) : d
|
|
111
|
-
);
|
|
112
|
-
for (const item of fs.readdirSync(dir)) {
|
|
113
|
-
if (EXCLUDED_DIR_NAMES.has(item)) continue;
|
|
114
|
-
if (item.startsWith(".")) continue;
|
|
115
|
-
const itemPath = path.join(dir, item);
|
|
116
|
-
if (excludes.some((d) => itemPath === d || itemPath.startsWith(d + path.sep))) continue;
|
|
117
|
-
let stat;
|
|
118
|
-
try {
|
|
119
|
-
stat = fs.lstatSync(itemPath);
|
|
120
|
-
} catch {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (stat.isDirectory()) {
|
|
124
|
-
results.push(...findClaudeMdFiles(itemPath, excludes));
|
|
125
|
-
} else if (stat.isFile() && item === "CLAUDE.md") {
|
|
126
|
-
results.push(itemPath);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return results;
|
|
130
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
assertNotIndexReserved,
|
|
5
|
+
cleanDir,
|
|
6
|
+
downgradeRepoRelativeLinks,
|
|
7
|
+
ensureDir,
|
|
8
|
+
escapeForMdx,
|
|
9
|
+
escapeTitle,
|
|
10
|
+
findNamedFiles,
|
|
11
|
+
generateSkillsCategory,
|
|
12
|
+
parseFrontmatter,
|
|
13
|
+
writeCategoryIndex
|
|
14
|
+
} from "../resource-docs-shared/index.js";
|
|
131
15
|
function generateClaudemdDocs(config) {
|
|
132
16
|
const projectRoot = config.projectRoot ?? config.claudeDir;
|
|
133
17
|
const scanRoot = config.scanRoot ?? projectRoot;
|
|
@@ -138,7 +22,7 @@ function generateClaudemdDocs(config) {
|
|
|
138
22
|
path.join(projectRoot, "e2e", "fixtures"),
|
|
139
23
|
path.join(config.docsDir)
|
|
140
24
|
];
|
|
141
|
-
const files =
|
|
25
|
+
const files = findNamedFiles(scanRoot, excludeDirs, ["CLAUDE.md"]);
|
|
142
26
|
if (files.length === 0) return [];
|
|
143
27
|
ensureDir(outputDir);
|
|
144
28
|
const items = [];
|
|
@@ -220,170 +104,15 @@ ${escapeForMdx(parsed.content.trim())}
|
|
|
220
104
|
writeCategoryIndex(outputDir, "Commands", 901, "Custom slash commands");
|
|
221
105
|
return items;
|
|
222
106
|
}
|
|
223
|
-
function getSkillFileTree(skillDir, subDirs) {
|
|
224
|
-
const lines = [`${skillDir}/`];
|
|
225
|
-
const entries = [{ isDir: false, name: "SKILL.md" }];
|
|
226
|
-
for (const sub of subDirs) {
|
|
227
|
-
entries.push({ isDir: true, name: sub.name, children: sub.files });
|
|
228
|
-
}
|
|
229
|
-
for (let i = 0; i < entries.length; i++) {
|
|
230
|
-
const entry = entries[i];
|
|
231
|
-
if (!entry) continue;
|
|
232
|
-
const isLast = i === entries.length - 1;
|
|
233
|
-
const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
234
|
-
if (!entry.isDir) {
|
|
235
|
-
lines.push(`${prefix}${entry.name}`);
|
|
236
|
-
} else {
|
|
237
|
-
lines.push(`${prefix}${entry.name}/`);
|
|
238
|
-
for (let j = 0; j < entry.children.length; j++) {
|
|
239
|
-
const child = entry.children[j];
|
|
240
|
-
if (!child) continue;
|
|
241
|
-
const childIsLast = j === entry.children.length - 1;
|
|
242
|
-
const continuation = isLast ? " " : "\u2502 ";
|
|
243
|
-
const childPrefix = childIsLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
244
|
-
lines.push(`${continuation}${childPrefix}${child}`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return lines.join("\n");
|
|
249
|
-
}
|
|
250
|
-
function getScriptDescription(filePath) {
|
|
251
|
-
try {
|
|
252
|
-
const topLines = fs.readFileSync(filePath, "utf8").split("\n", 2);
|
|
253
|
-
const firstLine = topLines[0] ?? "";
|
|
254
|
-
const commentLine = firstLine.startsWith("#!") ? topLines[1] ?? "" : firstLine;
|
|
255
|
-
const match = commentLine.match(/^(?:#|\/\/)\s*(.+)/);
|
|
256
|
-
return match ? ` \u2014 ${match[1]}` : "";
|
|
257
|
-
} catch {
|
|
258
|
-
return "";
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
function getSkillReferences(skillsDir, skillDir) {
|
|
262
|
-
const refsDir = path.join(skillsDir, skillDir, "references");
|
|
263
|
-
if (!fs.existsSync(refsDir)) return [];
|
|
264
|
-
return fs.readdirSync(refsDir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
265
|
-
const content = fs.readFileSync(path.join(refsDir, f), "utf8");
|
|
266
|
-
const name = f.replace(/\.md$/, "");
|
|
267
|
-
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
268
|
-
const title = h1Match?.[1] ?? name;
|
|
269
|
-
return { name, title, content };
|
|
270
|
-
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
271
|
-
}
|
|
272
107
|
function generateSkillsDocs(config) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
108
|
+
return generateSkillsCategory({
|
|
109
|
+
skillsDirs: [path.join(config.claudeDir, "skills")],
|
|
110
|
+
outputDir: path.join(config.docsDir, "claude-skills"),
|
|
111
|
+
label: "Skills",
|
|
112
|
+
position: 902,
|
|
113
|
+
description: "Skill packages",
|
|
114
|
+
sourceLabel: ".claude/skills"
|
|
280
115
|
});
|
|
281
|
-
if (dirs.length === 0) return [];
|
|
282
|
-
ensureDir(outputDir);
|
|
283
|
-
const items = [];
|
|
284
|
-
for (const dir of dirs) {
|
|
285
|
-
assertNotIndexReserved(
|
|
286
|
-
dir,
|
|
287
|
-
`claude-resources: skill directory ".claude/skills/index/" uses the reserved name "index", which is used for the category metadata file. Rename the skill directory to resolve the conflict.`
|
|
288
|
-
);
|
|
289
|
-
const content = fs.readFileSync(
|
|
290
|
-
path.join(skillsDir, dir, "SKILL.md"),
|
|
291
|
-
"utf8"
|
|
292
|
-
);
|
|
293
|
-
const parsed = parseFrontmatter(content);
|
|
294
|
-
if (!parsed) continue;
|
|
295
|
-
const name = parsed.data.name || dir;
|
|
296
|
-
const description = parsed.data.description || "";
|
|
297
|
-
const references = getSkillReferences(skillsDir, dir);
|
|
298
|
-
items.push({ name, dir, description, references });
|
|
299
|
-
const scriptFiles = listFiles(path.join(skillsDir, dir, "scripts"));
|
|
300
|
-
const assetFiles = listFiles(path.join(skillsDir, dir, "assets"));
|
|
301
|
-
const refFiles = references.map((r) => `${r.name}.md`);
|
|
302
|
-
const subDirs = [];
|
|
303
|
-
if (scriptFiles.length > 0) subDirs.push({ name: "scripts", files: scriptFiles });
|
|
304
|
-
if (refFiles.length > 0) subDirs.push({ name: "references", files: refFiles });
|
|
305
|
-
if (assetFiles.length > 0) subDirs.push({ name: "assets", files: assetFiles });
|
|
306
|
-
let fileStructureSection = "";
|
|
307
|
-
if (subDirs.length > 0) {
|
|
308
|
-
const tree = `\`\`\`
|
|
309
|
-
${getSkillFileTree(dir, subDirs)}
|
|
310
|
-
\`\`\``;
|
|
311
|
-
const links = [];
|
|
312
|
-
for (const ref of references) {
|
|
313
|
-
links.push(`- [references/${ref.name}.md](./ref-${ref.name})`);
|
|
314
|
-
}
|
|
315
|
-
for (const f of scriptFiles.filter((s) => s.endsWith(".md"))) {
|
|
316
|
-
const slug = f.replace(/\.md$/, "");
|
|
317
|
-
links.push(`- [scripts/${f}](./script-${slug})`);
|
|
318
|
-
}
|
|
319
|
-
for (const f of assetFiles.filter((a) => a.endsWith(".md"))) {
|
|
320
|
-
const slug = f.replace(/\.md$/, "");
|
|
321
|
-
links.push(`- [assets/${f}](./asset-${slug})`);
|
|
322
|
-
}
|
|
323
|
-
const linkList = links.length > 0 ? `
|
|
324
|
-
|
|
325
|
-
${links.join("\n")}` : "";
|
|
326
|
-
fileStructureSection = `## File Structure
|
|
327
|
-
|
|
328
|
-
${tree}${linkList}`;
|
|
329
|
-
}
|
|
330
|
-
const shortDesc = description.length > 200 ? description.substring(0, 200) + "..." : description;
|
|
331
|
-
let skillBody = parsed.content.trim();
|
|
332
|
-
skillBody = skillBody.replace(/\]\(references\/([^)]+)\.md\)/g, "](./ref-$1)").replace(/\]\(scripts\/([^)]+)\.md\)/g, "](./script-$1)").replace(/\]\(assets\/([^)]+)\.md\)/g, "](./asset-$1)");
|
|
333
|
-
const body = [
|
|
334
|
-
fileStructureSection,
|
|
335
|
-
escapeForMdx(skillBody)
|
|
336
|
-
].filter(Boolean).join("\n\n");
|
|
337
|
-
const mdx = `---
|
|
338
|
-
title: "${escapeTitle(name)}"
|
|
339
|
-
description: "${escapeTitle(shortDesc)}"
|
|
340
|
-
sidebar_label: "${escapeTitle(name)}"
|
|
341
|
-
generated: true
|
|
342
|
-
---
|
|
343
|
-
|
|
344
|
-
${body}`;
|
|
345
|
-
const skillDirOut = path.join(outputDir, dir);
|
|
346
|
-
ensureDir(skillDirOut);
|
|
347
|
-
fs.writeFileSync(path.join(skillDirOut, "index.mdx"), mdx);
|
|
348
|
-
for (const ref of references) {
|
|
349
|
-
writeUnlistedSubPage(
|
|
350
|
-
path.join(skillDirOut, `ref-${ref.name}.mdx`),
|
|
351
|
-
ref.title,
|
|
352
|
-
escapeForMdx(ref.content.trim())
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
for (const f of scriptFiles.filter((s) => s.endsWith(".md"))) {
|
|
356
|
-
const slug = f.replace(/\.md$/, "");
|
|
357
|
-
const raw = fs.readFileSync(
|
|
358
|
-
path.join(skillsDir, dir, "scripts", f),
|
|
359
|
-
"utf8"
|
|
360
|
-
);
|
|
361
|
-
const h1Match = raw.match(/^#\s+(.+)$/m);
|
|
362
|
-
const title = h1Match?.[1] ?? slug;
|
|
363
|
-
writeUnlistedSubPage(
|
|
364
|
-
path.join(skillDirOut, `script-${slug}.mdx`),
|
|
365
|
-
title,
|
|
366
|
-
escapeForMdx(raw.trim())
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
for (const f of assetFiles.filter((a) => a.endsWith(".md"))) {
|
|
370
|
-
const slug = f.replace(/\.md$/, "");
|
|
371
|
-
const raw = fs.readFileSync(
|
|
372
|
-
path.join(skillsDir, dir, "assets", f),
|
|
373
|
-
"utf8"
|
|
374
|
-
);
|
|
375
|
-
const h1Match = raw.match(/^#\s+(.+)$/m);
|
|
376
|
-
const title = h1Match?.[1] ?? slug;
|
|
377
|
-
writeUnlistedSubPage(
|
|
378
|
-
path.join(skillDirOut, `asset-${slug}.mdx`),
|
|
379
|
-
title,
|
|
380
|
-
escapeForMdx(raw.trim())
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
items.sort((a, b) => a.name.localeCompare(b.name));
|
|
385
|
-
writeCategoryIndex(outputDir, "Skills", 902, "Skill packages");
|
|
386
|
-
return items;
|
|
387
116
|
}
|
|
388
117
|
function generateAgentsDocs(config) {
|
|
389
118
|
const agentsDir = path.join(config.claudeDir, "agents");
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
formatFrontmatterString,
|
|
5
|
+
cleanDir,
|
|
6
|
+
downgradeRepoRelativeLinks,
|
|
7
|
+
ensureDir,
|
|
8
|
+
escapeForMdx,
|
|
9
|
+
escapeMarkdownTableCell,
|
|
10
|
+
findNamedFiles,
|
|
11
|
+
writeCategoryIndex
|
|
12
|
+
} from "../resource-docs-shared/index.js";
|
|
13
|
+
import { assertUniqueSlug, writeGeneratedPage } from "./utils.js";
|
|
14
|
+
function generateAgentsMdCategory(config) {
|
|
15
|
+
const projectRoot = config.projectRoot ?? config.codexDir;
|
|
16
|
+
const scanRoot = config.scanRoot ?? projectRoot;
|
|
17
|
+
const outputDir = path.join(config.docsDir, "codex-agents-md");
|
|
18
|
+
cleanDir(outputDir);
|
|
19
|
+
const excludeDirs = [
|
|
20
|
+
path.join(scanRoot, "e2e", "fixtures"),
|
|
21
|
+
path.join(projectRoot, "e2e", "fixtures"),
|
|
22
|
+
config.docsDir
|
|
23
|
+
];
|
|
24
|
+
const files = findNamedFiles(scanRoot, excludeDirs, [
|
|
25
|
+
"AGENTS.md",
|
|
26
|
+
"AGENTS.override.md"
|
|
27
|
+
]);
|
|
28
|
+
if (files.length === 0) return [];
|
|
29
|
+
const items = files.map((absPath) => {
|
|
30
|
+
const relPath = path.relative(scanRoot, absPath);
|
|
31
|
+
const dirPart = path.dirname(relPath);
|
|
32
|
+
const baseSlug = dirPart === "." ? "root" : dirPart.split(path.sep).join("--");
|
|
33
|
+
const slug = path.basename(absPath) === "AGENTS.override.md" ? `${baseSlug}--override` : baseSlug;
|
|
34
|
+
return { absPath, relPath, slug };
|
|
35
|
+
});
|
|
36
|
+
items.sort((a, b) => {
|
|
37
|
+
if (a.slug === "root") return -1;
|
|
38
|
+
if (b.slug === "root") return 1;
|
|
39
|
+
return a.relPath.localeCompare(b.relPath);
|
|
40
|
+
});
|
|
41
|
+
ensureDir(outputDir);
|
|
42
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
43
|
+
items.forEach((item, index) => {
|
|
44
|
+
assertUniqueSlug("AGENTS.md", item.slug, item.relPath, emitted);
|
|
45
|
+
const content = fs.readFileSync(item.absPath, "utf8");
|
|
46
|
+
writeGeneratedPage({
|
|
47
|
+
outputPath: path.join(outputDir, `${item.slug}.mdx`),
|
|
48
|
+
title: `/${item.relPath}`,
|
|
49
|
+
description: `Codex instructions at /${item.relPath}`,
|
|
50
|
+
sidebarPosition: index + 1,
|
|
51
|
+
sidebarLabel: item.relPath,
|
|
52
|
+
body: `**Path:** ${escapeMarkdownTableCell(item.relPath)}
|
|
53
|
+
|
|
54
|
+
${escapeForMdx(
|
|
55
|
+
downgradeRepoRelativeLinks(content.trim())
|
|
56
|
+
)}`
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
writeCategoryIndex(
|
|
60
|
+
outputDir,
|
|
61
|
+
"AGENTS.md",
|
|
62
|
+
905,
|
|
63
|
+
"Project instructions for Codex",
|
|
64
|
+
formatFrontmatterString
|
|
65
|
+
);
|
|
66
|
+
return items.map(({ relPath, slug }) => ({ relPath, slug }));
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
generateAgentsMdCategory
|
|
70
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse } from "smol-toml";
|
|
4
|
+
import {
|
|
5
|
+
formatFrontmatterString,
|
|
6
|
+
cleanDir,
|
|
7
|
+
ensureDir,
|
|
8
|
+
escapeForMdx,
|
|
9
|
+
escapeMarkdownTableCell,
|
|
10
|
+
renderCodeFence,
|
|
11
|
+
writeCategoryIndex
|
|
12
|
+
} from "../resource-docs-shared/index.js";
|
|
13
|
+
import {
|
|
14
|
+
assertUniqueSlug,
|
|
15
|
+
filenameSlug,
|
|
16
|
+
isRecord,
|
|
17
|
+
warn,
|
|
18
|
+
writeGeneratedPage
|
|
19
|
+
} from "./utils.js";
|
|
20
|
+
function optionalString(record, field, filePath) {
|
|
21
|
+
const value = record[field];
|
|
22
|
+
if (value === void 0) return void 0;
|
|
23
|
+
if (typeof value === "string") return value;
|
|
24
|
+
warn(filePath, `${field} must be a string; omitting it`);
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
function generateAgentsCategory(config) {
|
|
28
|
+
const agentsDir = path.join(config.codexDir, "agents");
|
|
29
|
+
const outputDir = path.join(config.docsDir, "codex-agents");
|
|
30
|
+
cleanDir(outputDir);
|
|
31
|
+
if (!fs.existsSync(agentsDir)) return [];
|
|
32
|
+
let files;
|
|
33
|
+
try {
|
|
34
|
+
files = fs.readdirSync(agentsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".toml")).map((entry) => entry.name).sort();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
warn(agentsDir, `unable to read agents directory: ${String(error)}`);
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
if (files.length === 0) return [];
|
|
40
|
+
ensureDir(outputDir);
|
|
41
|
+
const items = [];
|
|
42
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
43
|
+
for (const filename of files) {
|
|
44
|
+
const filePath = path.join(agentsDir, filename);
|
|
45
|
+
let source;
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
source = fs.readFileSync(filePath, "utf8");
|
|
49
|
+
parsed = parse(source);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
warn(filePath, `unable to parse TOML, skipping file: ${String(error)}`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!isRecord(parsed)) {
|
|
55
|
+
warn(filePath, "parsed TOML root was not an object, skipping file");
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const slug = filenameSlug(filename.replace(/\.toml$/, ""));
|
|
59
|
+
assertUniqueSlug("agent", slug, filename, emitted);
|
|
60
|
+
const name = optionalString(parsed, "name", filePath) ?? slug;
|
|
61
|
+
const description = optionalString(parsed, "description", filePath) ?? "";
|
|
62
|
+
const model = optionalString(parsed, "model", filePath);
|
|
63
|
+
const reasoning = optionalString(parsed, "model_reasoning_effort", filePath);
|
|
64
|
+
const sandbox = optionalString(parsed, "sandbox_mode", filePath);
|
|
65
|
+
const instructions = optionalString(parsed, "developer_instructions", filePath);
|
|
66
|
+
const badges = [
|
|
67
|
+
model ? `**Model:** ${escapeMarkdownTableCell(model)}` : "",
|
|
68
|
+
reasoning ? `**Reasoning effort:** ${escapeMarkdownTableCell(reasoning)}` : "",
|
|
69
|
+
sandbox ? `**Sandbox:** ${escapeMarkdownTableCell(sandbox)}` : ""
|
|
70
|
+
].filter(Boolean);
|
|
71
|
+
const sections = [badges.join("\n")];
|
|
72
|
+
if (instructions !== void 0) {
|
|
73
|
+
sections.push(
|
|
74
|
+
`## Developer instructions
|
|
75
|
+
|
|
76
|
+
${escapeForMdx(instructions.trim())}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
sections.push(`## Source
|
|
80
|
+
|
|
81
|
+
${renderCodeFence(source, "toml")}`);
|
|
82
|
+
writeGeneratedPage({
|
|
83
|
+
outputPath: path.join(outputDir, `${slug}.mdx`),
|
|
84
|
+
title: name,
|
|
85
|
+
description,
|
|
86
|
+
sidebarLabel: name,
|
|
87
|
+
body: sections.filter(Boolean).join("\n\n")
|
|
88
|
+
});
|
|
89
|
+
items.push({ filename, slug, name });
|
|
90
|
+
}
|
|
91
|
+
if (items.length > 0) {
|
|
92
|
+
writeCategoryIndex(
|
|
93
|
+
outputDir,
|
|
94
|
+
"Agents",
|
|
95
|
+
907,
|
|
96
|
+
"Custom subagents",
|
|
97
|
+
formatFrontmatterString
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return items;
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
generateAgentsCategory
|
|
104
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse, TomlDate } from "smol-toml";
|
|
4
|
+
import {
|
|
5
|
+
formatFrontmatterString,
|
|
6
|
+
cleanDir,
|
|
7
|
+
ensureDir,
|
|
8
|
+
escapeMarkdownTableCell,
|
|
9
|
+
renderCodeFence,
|
|
10
|
+
writeCategoryIndex
|
|
11
|
+
} from "../resource-docs-shared/index.js";
|
|
12
|
+
import {
|
|
13
|
+
assertUniqueSlug,
|
|
14
|
+
filenameSlug,
|
|
15
|
+
isRecord,
|
|
16
|
+
renderTableRow,
|
|
17
|
+
warn,
|
|
18
|
+
writeGeneratedPage
|
|
19
|
+
} from "./utils.js";
|
|
20
|
+
function configFiles(codexDir) {
|
|
21
|
+
try {
|
|
22
|
+
return fs.readdirSync(codexDir, { withFileTypes: true }).filter(
|
|
23
|
+
(entry) => entry.isFile() && (entry.name === "config.toml" || entry.name === "config.toml.example" || entry.name.endsWith(".config.toml"))
|
|
24
|
+
).map((entry) => entry.name).sort((a, b) => {
|
|
25
|
+
if (a === "config.toml") return -1;
|
|
26
|
+
if (b === "config.toml") return 1;
|
|
27
|
+
return a.localeCompare(b);
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
warn(codexDir, `unable to read config files: ${String(error)}`);
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function renderValue(value) {
|
|
35
|
+
if (typeof value === "string") return value;
|
|
36
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
37
|
+
return String(value);
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
40
|
+
if (value instanceof TomlDate) return value.toISOString();
|
|
41
|
+
return String(value);
|
|
42
|
+
}
|
|
43
|
+
function generateConfigCategory(config) {
|
|
44
|
+
const outputDir = path.join(config.docsDir, "codex-config");
|
|
45
|
+
cleanDir(outputDir);
|
|
46
|
+
if (!fs.existsSync(config.codexDir)) return [];
|
|
47
|
+
const files = configFiles(config.codexDir);
|
|
48
|
+
if (files.length === 0) return [];
|
|
49
|
+
ensureDir(outputDir);
|
|
50
|
+
const items = [];
|
|
51
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const filename of files) {
|
|
53
|
+
const filePath = path.join(config.codexDir, filename);
|
|
54
|
+
let source;
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
source = fs.readFileSync(filePath, "utf8");
|
|
58
|
+
parsed = parse(source);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
warn(filePath, `unable to parse TOML, skipping file: ${String(error)}`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (!isRecord(parsed)) {
|
|
64
|
+
warn(filePath, "parsed TOML root was not an object, skipping file");
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const slug = filenameSlug(filename);
|
|
68
|
+
assertUniqueSlug("config", slug, filename, emitted);
|
|
69
|
+
const settings = Object.entries(parsed).filter(([, value]) => !isRecord(value) || value instanceof TomlDate).map(([key, value]) => renderTableRow([key, renderValue(value)]));
|
|
70
|
+
const sections = source.match(/^\s*\[\[?[^\]]+\]\]?/gm) ?? [];
|
|
71
|
+
const sectionList = sections.length === 0 ? "\u2014" : sections.map(
|
|
72
|
+
(section) => `- ${escapeMarkdownTableCell(section.trim())}`
|
|
73
|
+
).join("\n");
|
|
74
|
+
const body = [
|
|
75
|
+
"## Settings",
|
|
76
|
+
"",
|
|
77
|
+
"| Key | Value |",
|
|
78
|
+
"| --- | --- |",
|
|
79
|
+
...settings.length > 0 ? settings : [renderTableRow([void 0, void 0])],
|
|
80
|
+
"",
|
|
81
|
+
"## Sections",
|
|
82
|
+
"",
|
|
83
|
+
sectionList,
|
|
84
|
+
"",
|
|
85
|
+
"## Source",
|
|
86
|
+
"",
|
|
87
|
+
renderCodeFence(source, "toml")
|
|
88
|
+
].join("\n");
|
|
89
|
+
writeGeneratedPage({
|
|
90
|
+
outputPath: path.join(outputDir, `${slug}.mdx`),
|
|
91
|
+
title: filename,
|
|
92
|
+
description: `Codex configuration from ${filename}`,
|
|
93
|
+
sidebarLabel: filename,
|
|
94
|
+
body
|
|
95
|
+
});
|
|
96
|
+
items.push({ filename, slug });
|
|
97
|
+
}
|
|
98
|
+
if (items.length > 0) {
|
|
99
|
+
writeCategoryIndex(
|
|
100
|
+
outputDir,
|
|
101
|
+
"Config",
|
|
102
|
+
906,
|
|
103
|
+
"config.toml and profiles",
|
|
104
|
+
formatFrontmatterString
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return items;
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
generateConfigCategory
|
|
111
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CodexResourcesConfig {
|
|
2
|
+
codexDir: string;
|
|
3
|
+
/** The doc site's own root and the default value of `scanRoot`. */
|
|
4
|
+
projectRoot?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Repo-wide discovery root for both the AGENTS.md walk and the repo-level
|
|
7
|
+
* `.agents/skills/` root. Also serves as the relative-path base for the
|
|
8
|
+
* generated AGENTS.md titles and slugs. Defaults to `projectRoot`.
|
|
9
|
+
*/
|
|
10
|
+
scanRoot?: string;
|
|
11
|
+
docsDir: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function generateCodexResourcesDocs(config: CodexResourcesConfig): {
|
|
14
|
+
agentsMd: number;
|
|
15
|
+
config: number;
|
|
16
|
+
agents: number;
|
|
17
|
+
hooks: number;
|
|
18
|
+
rules: number;
|
|
19
|
+
skills: number;
|
|
20
|
+
};
|