@takazudo/zudo-doc 5.7.0 → 5.9.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 +17 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +1 -0
- package/dist/eject/index.js +9 -0
- package/dist/i18n-defaults/index.js +3 -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 +8 -0
- package/dist/preset.js +11 -0
- package/dist/safelist.css +1 -1
- package/dist/settings.d.ts +5 -0
- package/dist/transitions/nested-island-props-refresh.js +9 -3
- package/eject/header/gen-nav-overflow-script.mjs +771 -0
- package/eject/header/header.tsx +2 -0
- package/eject/header/nav-overflow-script.ts +11 -8
- package/package.json +9 -4
- /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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ All notable changes to `@takazudo/zudo-doc` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
|
|
6
6
|
|
|
7
|
+
## [5.9.0] - 2026-08-20
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Added Codex Resources generation for `AGENTS.md`, Codex configuration, custom agents, hooks, rules, and skills, with dedicated parsing and rendering for each resource type. (`666c12059`)
|
|
12
|
+
- Added the public Codex Resources plugin and wired it through package settings, presets, `create-zudo-doc`, and generated project configuration. (`752a3fe5a`, `a7fa47906`, `5831cd73e`)
|
|
13
|
+
- Added Codex Resources showcase data, navigation, localization, development watching, and complete English and Japanese documentation. (`a4dca1e40`, `1a2e6d5bc`, `f1396e7a1`)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Added type validation and deterministic fallbacks for malformed skill frontmatter values. (`710a1d225`)
|
|
18
|
+
- Made generated Codex Resources frontmatter formatter-stable while preserving YAML string semantics and existing Claude Resources output.
|
|
19
|
+
|
|
20
|
+
### Other Changes
|
|
21
|
+
|
|
22
|
+
- Extracted shared resource-documentation helpers while preserving existing Claude Resources output byte-for-byte. (`c9165d5cb`)
|
|
23
|
+
- Expanded generator, scaffold, build, and responsive navigation regression coverage for Codex Resources. (`142c11aa4`)
|
|
24
|
+
|
|
25
|
+
## [5.8.0] - 2026-08-20
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
- Added strict anchor validation to the showcase and generated-project link checkers, including source-first validation for scaffolded sites. (`b50bd9aac`, `59127e7fe`)
|
|
30
|
+
- Added a published-version guard that prevents release scaffolds from pinning first-party packages that are not yet available from npm. (`4bdc37132`, `8c234c08e`)
|
|
31
|
+
- Shipped a complete nav-overflow regeneration path with ejected headers, preserving stable CSP bytes while allowing local nav behavior and class-token customization. (`1b3d501c3`, `721ff37d3`)
|
|
32
|
+
|
|
33
|
+
### Bug Fixes
|
|
34
|
+
|
|
35
|
+
- Made nested island props preservation explicit with `data-zd-props-preserve`, preventing opted-out islands from receiving props updates or remount flags during client navigation. (`e75cff084`)
|
|
36
|
+
|
|
37
|
+
### Other Changes
|
|
38
|
+
|
|
39
|
+
- Strengthened `docTags` regression coverage so an unselected feature cannot emit either a `true` or `false` config field. (`37dec787c`)
|
|
40
|
+
- Updated `@takazudo/zdtp` to 0.4.12 across the package, showcase, and generated scaffold.
|
|
41
|
+
|
|
7
42
|
## [5.7.0] - 2026-08-20
|
|
8
43
|
|
|
9
44
|
### Features
|
package/README.md
CHANGED
|
@@ -67,6 +67,23 @@ pnpm add @takazudo/zfb-md-wasm
|
|
|
67
67
|
|
|
68
68
|
Projects scaffolded by `create-zudo-doc` already include it. If you never render `<HtmlPreview>` / `<HighlightedCode>`, you can omit it.
|
|
69
69
|
|
|
70
|
+
## Ejected header customization
|
|
71
|
+
|
|
72
|
+
`zudo-doc eject header` copies a complete frozen-script regeneration path into
|
|
73
|
+
`src/components/zudo-doc/header`. After changing the ejected `nav-active.ts` or
|
|
74
|
+
`nav-class-tokens.ts`, regenerate the local client controller and commit the
|
|
75
|
+
result:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
node ./src/components/zudo-doc/header/gen-nav-overflow-script.mjs
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The command uses the two local customization inputs plus the installed
|
|
82
|
+
package's current-path and page-event inputs. Its `esbuild` transformer is
|
|
83
|
+
provided by `@takazudo/zudo-doc`; no additional project dependency is needed.
|
|
84
|
+
The resulting `nav-overflow-generated-script.ts` stays frozen for stable CSP
|
|
85
|
+
hashes across consumer bundlers.
|
|
86
|
+
|
|
70
87
|
## ⚠️ HTML preview iframe sandbox — trust assumption
|
|
71
88
|
|
|
72
89
|
`<HtmlPreview>` / `<HtmlPreviewWrapper>` render their preview inside an `<iframe srcdoc>` whose `sandbox` attribute **defaults** to:
|
package/dist/config.d.ts
CHANGED
|
@@ -72,6 +72,12 @@ type ClaudeResourcesConfig = {
|
|
|
72
72
|
projectRoot?: string;
|
|
73
73
|
scanRoot?: string;
|
|
74
74
|
} | false;
|
|
75
|
+
/** The `settings.codexResources` block (or `false` when disabled). */
|
|
76
|
+
type CodexResourcesConfig = {
|
|
77
|
+
codexDir: string;
|
|
78
|
+
projectRoot?: string;
|
|
79
|
+
scanRoot?: string;
|
|
80
|
+
} | false;
|
|
75
81
|
export declare const DEFAULT_SETTINGS: Settings;
|
|
76
82
|
export interface ZudoDocConfig {
|
|
77
83
|
/**
|
|
@@ -372,6 +378,18 @@ export interface ZudoDocConfig {
|
|
|
372
378
|
* @default false
|
|
373
379
|
*/
|
|
374
380
|
claudeResources?: ClaudeResourcesConfig;
|
|
381
|
+
/**
|
|
382
|
+
* Codex-resources ingestion config, or `false` to disable.
|
|
383
|
+
* `codexDir` is the `.codex/` directory, resolved against `projectRoot`.
|
|
384
|
+
* `projectRoot` anchors relative paths and the output base. `scanRoot` is
|
|
385
|
+
* the repo-wide discovery root for the `AGENTS.md` / `AGENTS.override.md`
|
|
386
|
+
* walk (including its relPath base for titles/slugs) and the repo-level
|
|
387
|
+
* `.agents/skills/` root; it defaults to `projectRoot`. This lets a doc site
|
|
388
|
+
* in a repo subdirectory scan repo-wide instructions and team skills while
|
|
389
|
+
* writing into its own collection (#2558).
|
|
390
|
+
* @default false
|
|
391
|
+
*/
|
|
392
|
+
codexResources?: CodexResourcesConfig;
|
|
375
393
|
/**
|
|
376
394
|
* Route prefixes served only in the default locale (never locale-prefixed).
|
|
377
395
|
* @default []
|
package/dist/config.js
CHANGED
package/dist/eject/index.js
CHANGED
|
@@ -340,6 +340,15 @@ ${status} from @takazudo/zudo-doc@${installedVersion}
|
|
|
340
340
|
`
|
|
341
341
|
)
|
|
342
342
|
);
|
|
343
|
+
if (component === "header") {
|
|
344
|
+
console.log(
|
|
345
|
+
pc.bold("Header frozen-script regeneration:") + `
|
|
346
|
+
After editing ${localDir}/nav-active.ts or nav-class-tokens.ts, run:
|
|
347
|
+
node ./${localDir}/gen-nav-overflow-script.mjs
|
|
348
|
+
Commit ${localDir}/nav-overflow-generated-script.ts with your edits.
|
|
349
|
+
`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
343
352
|
}
|
|
344
353
|
export {
|
|
345
354
|
EJECTABLE,
|
|
@@ -6,6 +6,7 @@ const defaultTranslations = {
|
|
|
6
6
|
"nav.components": "Components",
|
|
7
7
|
"nav.reference": "Reference",
|
|
8
8
|
"nav.claude": "Claude",
|
|
9
|
+
"nav.codex": "Codex",
|
|
9
10
|
"nav.changelog": "Changelog",
|
|
10
11
|
"nav.develop": "Develop",
|
|
11
12
|
"nav.previous": "Previous",
|
|
@@ -65,6 +66,7 @@ const defaultTranslations = {
|
|
|
65
66
|
"nav.components": "\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8",
|
|
66
67
|
"nav.reference": "\u30EA\u30D5\u30A1\u30EC\u30F3\u30B9",
|
|
67
68
|
"nav.claude": "Claude",
|
|
69
|
+
"nav.codex": "Codex",
|
|
68
70
|
"nav.changelog": "\u5909\u66F4\u5C65\u6B74",
|
|
69
71
|
"nav.develop": "\u958B\u767A",
|
|
70
72
|
"nav.previous": "\u524D\u3078",
|
|
@@ -124,6 +126,7 @@ const defaultTranslations = {
|
|
|
124
126
|
"nav.components": "Komponenten",
|
|
125
127
|
"nav.reference": "Referenz",
|
|
126
128
|
"nav.claude": "Claude",
|
|
129
|
+
"nav.codex": "Codex",
|
|
127
130
|
"nav.changelog": "Changelog",
|
|
128
131
|
"nav.develop": "Entwicklung",
|
|
129
132
|
"nav.previous": "Zur\xFCck",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { runCodexResourcesPreStep } from "./internal/codex-resources/index.js";
|
|
2
|
+
import { runCodexResourcesPreStep as runCodexResourcesPreStep2 } from "./internal/codex-resources/index.js";
|
|
3
|
+
const PLUGIN_NAME = "@takazudo/zudo-doc-codex-resources";
|
|
4
|
+
const plugin = {
|
|
5
|
+
name: PLUGIN_NAME,
|
|
6
|
+
async preBuild(ctx) {
|
|
7
|
+
const codexDir = ctx.options["codexDir"];
|
|
8
|
+
if (typeof codexDir !== "string" || codexDir.length === 0) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`[${PLUGIN_NAME}] preBuild: options.codexDir must be a non-empty string (got ${JSON.stringify(codexDir)})`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
const projectRootOpt = ctx.options["projectRoot"];
|
|
14
|
+
const scanRootOpt = ctx.options["scanRoot"];
|
|
15
|
+
const docsDirOpt = ctx.options["docsDir"];
|
|
16
|
+
const result = runCodexResourcesPreStep({
|
|
17
|
+
codexDir,
|
|
18
|
+
projectRoot: typeof projectRootOpt === "string" ? projectRootOpt : ctx.projectRoot,
|
|
19
|
+
scanRoot: typeof scanRootOpt === "string" ? scanRootOpt : void 0,
|
|
20
|
+
docsDir: typeof docsDirOpt === "string" ? docsDirOpt : "src/content/docs"
|
|
21
|
+
});
|
|
22
|
+
ctx.logger.info(
|
|
23
|
+
`codex-resources: ${result.agentsMd} AGENTS.md, ${result.config} config, ${result.agents} agents, ${result.hooks} hooks, ${result.rules} rules, ${result.skills} skills`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var codex_resources_default = plugin;
|
|
28
|
+
export {
|
|
29
|
+
codex_resources_default as default,
|
|
30
|
+
runCodexResourcesPreStep2 as runCodexResourcesPreStep
|
|
31
|
+
};
|
|
@@ -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
|
+
};
|