@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.
Files changed (104) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +1 -1
  3. package/dist/chrome/derive.d.ts +1 -1
  4. package/dist/chrome/derive.js +24 -2
  5. package/dist/config-assertions/index.d.ts +30 -0
  6. package/dist/config-assertions/index.js +41 -1
  7. package/dist/config.d.ts +18 -0
  8. package/dist/config.js +1 -0
  9. package/dist/doc-content-header/index.js +6 -0
  10. package/dist/doc-metainfo-area/index.js +1 -14
  11. package/dist/doc-page-renderer/index.d.ts +3 -3
  12. package/dist/doc-page-renderer/index.js +1 -1
  13. package/dist/doc-page-shell/index.d.ts +6 -0
  14. package/dist/doc-pager/index.d.ts +6 -0
  15. package/dist/doc-pager/index.js +5 -2
  16. package/dist/docs-schema/index.d.ts +11 -0
  17. package/dist/docs-schema/index.js +8 -1
  18. package/dist/format-date/index.d.ts +12 -0
  19. package/dist/format-date/index.js +64 -0
  20. package/dist/frontmatter-preview-defaults/index.js +6 -1
  21. package/dist/header/header.js +9 -1
  22. package/dist/home-page/index.js +3 -1
  23. package/dist/html-preview-wrapper/highlight-runtime.d.ts +7 -7
  24. package/dist/html-preview-wrapper/highlight-runtime.js +1 -1
  25. package/dist/i18n-defaults/index.js +3 -0
  26. package/dist/integrations/changelog/emit.js +19 -2
  27. package/dist/mdx-components/index.d.ts +4 -1
  28. package/dist/mdx-components/index.js +3 -1
  29. package/dist/nav-indexing/index.d.ts +2 -0
  30. package/dist/nav-indexing/index.js +2 -0
  31. package/dist/nav-indexing/note-tray-index.d.ts +29 -0
  32. package/dist/nav-indexing/note-tray-index.js +120 -0
  33. package/dist/note-tray-index/index.d.ts +45 -0
  34. package/dist/note-tray-index/index.js +52 -0
  35. package/dist/note-tray-model/index.d.ts +31 -0
  36. package/dist/note-tray-model/index.js +74 -0
  37. package/dist/plugins/codex-resources.d.ts +4 -0
  38. package/dist/plugins/codex-resources.js +31 -0
  39. package/dist/plugins/internal/claude-resources/generate.js +20 -291
  40. package/dist/plugins/internal/codex-resources/agents-md.d.ts +6 -0
  41. package/dist/plugins/internal/codex-resources/agents-md.js +70 -0
  42. package/dist/plugins/internal/codex-resources/agents.d.ts +7 -0
  43. package/dist/plugins/internal/codex-resources/agents.js +104 -0
  44. package/dist/plugins/internal/codex-resources/config.d.ts +6 -0
  45. package/dist/plugins/internal/codex-resources/config.js +111 -0
  46. package/dist/plugins/internal/codex-resources/generate.d.ts +20 -0
  47. package/dist/plugins/internal/codex-resources/generate.js +34 -0
  48. package/dist/plugins/internal/codex-resources/hooks.d.ts +6 -0
  49. package/dist/plugins/internal/codex-resources/hooks.js +174 -0
  50. package/dist/plugins/internal/codex-resources/index.d.ts +24 -0
  51. package/dist/plugins/internal/codex-resources/index.js +21 -0
  52. package/dist/plugins/internal/codex-resources/overview.d.ts +10 -0
  53. package/dist/plugins/internal/codex-resources/overview.js +33 -0
  54. package/dist/plugins/internal/codex-resources/rules.d.ts +6 -0
  55. package/dist/plugins/internal/codex-resources/rules.js +255 -0
  56. package/dist/plugins/internal/codex-resources/skills.d.ts +2 -0
  57. package/dist/plugins/internal/codex-resources/skills.js +84 -0
  58. package/dist/plugins/internal/codex-resources/utils.d.ts +14 -0
  59. package/dist/plugins/internal/codex-resources/utils.js +80 -0
  60. package/dist/plugins/internal/resource-docs-shared/fs.d.ts +3 -0
  61. package/dist/plugins/internal/resource-docs-shared/fs.js +19 -0
  62. package/dist/plugins/internal/resource-docs-shared/index.d.ts +7 -0
  63. package/dist/plugins/internal/resource-docs-shared/index.js +44 -0
  64. package/dist/plugins/internal/resource-docs-shared/links.d.ts +22 -0
  65. package/dist/plugins/internal/resource-docs-shared/links.js +44 -0
  66. package/dist/plugins/internal/resource-docs-shared/markdown-structure.d.ts +9 -0
  67. package/dist/plugins/internal/resource-docs-shared/markdown-structure.js +22 -0
  68. package/dist/plugins/internal/resource-docs-shared/mdx.d.ts +23 -0
  69. package/dist/plugins/internal/resource-docs-shared/mdx.js +63 -0
  70. package/dist/plugins/internal/resource-docs-shared/skills.d.ts +37 -0
  71. package/dist/plugins/internal/resource-docs-shared/skills.js +318 -0
  72. package/dist/plugins/internal/resource-docs-shared/walk.d.ts +20 -0
  73. package/dist/plugins/internal/resource-docs-shared/walk.js +41 -0
  74. package/dist/preset.d.ts +19 -0
  75. package/dist/preset.js +16 -1
  76. package/dist/safelist.css +1 -1
  77. package/dist/settings.d.ts +5 -0
  78. package/dist/sidebar/types.d.ts +6 -0
  79. package/dist/sidebar-tree/build-tree.js +8 -0
  80. package/dist/sidebar-tree/types.d.ts +12 -0
  81. package/dist/sidebar-tree-island/index.js +175 -1
  82. package/dist/sidebar-utils/index.d.ts +6 -0
  83. package/dist/site-schema/doc-route-entries.js +2 -0
  84. package/dist/site-schema/index.d.ts +1 -0
  85. package/dist/site-schema/index.js +3 -1
  86. package/dist/site-schema/nav-tree.js +17 -2
  87. package/dist/site-schema/note-tray-validate.d.ts +7 -0
  88. package/dist/site-schema/note-tray-validate.js +94 -0
  89. package/dist/site-schema/types.d.ts +11 -0
  90. package/dist/site-tree-nav-island/index.d.ts +5 -1
  91. package/dist/site-tree-nav-island/index.js +119 -12
  92. package/dist/theme-packs/drift/pack.css +6 -1
  93. package/dist/theme-packs/hearth/pack.css +5 -0
  94. package/dist/theme-packs/sakura/pack.css +6 -0
  95. package/dist/theme-packs/scandi/pack.css +7 -7
  96. package/dist/theme-packs/timberline/pack.css +5 -0
  97. package/eject/doc-pager/index.tsx +9 -0
  98. package/eject/header/header.tsx +26 -1
  99. package/eject/sidebar/types.ts +6 -0
  100. package/eject/sidebar-tree-island/index.tsx +238 -6
  101. package/eject/site-tree-nav-island/index.tsx +166 -16
  102. package/package.json +25 -8
  103. /package/dist/plugins/internal/{claude-resources → resource-docs-shared}/escape-for-mdx.d.ts +0 -0
  104. /package/dist/plugins/internal/{claude-resources → resource-docs-shared}/escape-for-mdx.js +0 -0
@@ -0,0 +1,34 @@
1
+ import { generateAgentsMdCategory } from "./agents-md.js";
2
+ import { generateAgentsCategory } from "./agents.js";
3
+ import { generateConfigCategory } from "./config.js";
4
+ import { generateHooksCategory } from "./hooks.js";
5
+ import { generateOverview } from "./overview.js";
6
+ import { generateRulesCategory } from "./rules.js";
7
+ import { generateSkillsCategoryDocs } from "./skills.js";
8
+ function generateCodexResourcesDocs(config) {
9
+ const agentsMd = generateAgentsMdCategory(config);
10
+ const configItems = generateConfigCategory(config);
11
+ const agents = generateAgentsCategory(config);
12
+ const hooks = generateHooksCategory(config);
13
+ const rules = generateRulesCategory(config);
14
+ const skills = generateSkillsCategoryDocs(config);
15
+ generateOverview(config, {
16
+ agentsMd: agentsMd.length > 0,
17
+ config: configItems.length > 0,
18
+ agents: agents.length > 0,
19
+ hooks: hooks.length > 0,
20
+ rules: rules.length > 0,
21
+ skills: skills.length > 0
22
+ });
23
+ return {
24
+ agentsMd: agentsMd.length,
25
+ config: configItems.length,
26
+ agents: agents.length,
27
+ hooks: hooks.length,
28
+ rules: rules.length,
29
+ skills: skills.length
30
+ };
31
+ }
32
+ export {
33
+ generateCodexResourcesDocs
34
+ };
@@ -0,0 +1,6 @@
1
+ import type { CodexResourcesConfig } from "./generate.js";
2
+ export interface HookItem {
3
+ filename: string;
4
+ slug: string;
5
+ }
6
+ export declare function generateHooksCategory(config: CodexResourcesConfig): HookItem[];
@@ -0,0 +1,174 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ formatFrontmatterString,
5
+ cleanDir,
6
+ ensureDir,
7
+ getScriptDescription,
8
+ renderCodeFence,
9
+ writeCategoryIndex
10
+ } from "../resource-docs-shared/index.js";
11
+ import {
12
+ assertUniqueSlug,
13
+ filenameSlug,
14
+ isRecord,
15
+ languageForFilename,
16
+ renderTableRow,
17
+ warn,
18
+ writeGeneratedPage
19
+ } from "./utils.js";
20
+ function readHookRows(filePath, source) {
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse(source);
24
+ } catch (error) {
25
+ warn(filePath, `unable to parse JSON, skipping hooks.json page: ${String(error)}`);
26
+ return null;
27
+ }
28
+ if (!isRecord(parsed) || !isRecord(parsed.hooks)) {
29
+ warn(filePath, "hooks.hooks must be a non-null object; skipping hooks.json page");
30
+ return null;
31
+ }
32
+ const rows = [];
33
+ for (const [event, groups] of Object.entries(parsed.hooks)) {
34
+ if (!Array.isArray(groups)) {
35
+ warn(filePath, `hooks.${event} must be an array; skipping event`);
36
+ continue;
37
+ }
38
+ for (const [groupIndex, group] of groups.entries()) {
39
+ if (!isRecord(group) || !Array.isArray(group.hooks)) {
40
+ warn(
41
+ filePath,
42
+ `hooks.${event}[${groupIndex}].hooks must be an array; skipping group`
43
+ );
44
+ continue;
45
+ }
46
+ for (const [handlerIndex, handler] of group.hooks.entries()) {
47
+ if (!isRecord(handler) || typeof handler.command !== "string") {
48
+ warn(
49
+ filePath,
50
+ `hooks.${event}[${groupIndex}].hooks[${handlerIndex}] must be an object with a string command; skipping row`
51
+ );
52
+ continue;
53
+ }
54
+ rows.push({
55
+ event,
56
+ matcher: group.matcher,
57
+ type: handler.type,
58
+ command: handler.command,
59
+ timeout: handler.timeout,
60
+ async: handler.async
61
+ });
62
+ }
63
+ }
64
+ }
65
+ return rows;
66
+ }
67
+ function generateHooksJson(config, outputDir, emitted) {
68
+ const filePath = path.join(config.codexDir, "hooks.json");
69
+ if (!fs.existsSync(filePath)) return null;
70
+ let source;
71
+ try {
72
+ source = fs.readFileSync(filePath, "utf8");
73
+ } catch (error) {
74
+ warn(filePath, `unable to read hooks.json, skipping page: ${String(error)}`);
75
+ return null;
76
+ }
77
+ const rows = readHookRows(filePath, source);
78
+ if (rows === null) return null;
79
+ const slug = "hooks-json";
80
+ assertUniqueSlug("hook", slug, "hooks.json", emitted);
81
+ const tableRows = rows.map((row) => renderTableRow([
82
+ row.event,
83
+ row.matcher,
84
+ row.type,
85
+ row.command,
86
+ row.timeout,
87
+ row.async
88
+ ]));
89
+ const body = [
90
+ "| Event | Matcher | Type | Command | Timeout | Async |",
91
+ "| --- | --- | --- | --- | --- | --- |",
92
+ ...tableRows.length > 0 ? tableRows : [renderTableRow(Array.from({ length: 6 }))],
93
+ "",
94
+ "## Source",
95
+ "",
96
+ renderCodeFence(source, "json")
97
+ ].join("\n");
98
+ writeGeneratedPage({
99
+ outputPath: path.join(outputDir, `${slug}.mdx`),
100
+ title: "hooks.json",
101
+ description: "Codex lifecycle hook configuration",
102
+ sidebarLabel: "hooks.json",
103
+ body
104
+ });
105
+ return { filename: "hooks.json", slug };
106
+ }
107
+ function generateHookScripts(config, outputDir, emitted) {
108
+ const hooksDir = path.join(config.codexDir, "hooks");
109
+ if (!fs.existsSync(hooksDir)) return [];
110
+ let entries;
111
+ try {
112
+ entries = fs.readdirSync(hooksDir, { withFileTypes: true });
113
+ } catch (error) {
114
+ warn(hooksDir, `unable to read hooks directory: ${String(error)}`);
115
+ return [];
116
+ }
117
+ const items = [];
118
+ for (const entry of entries.filter((item) => item.isFile()).sort(
119
+ (a, b) => a.name.localeCompare(b.name)
120
+ )) {
121
+ const filePath = path.join(hooksDir, entry.name);
122
+ const slug = filenameSlug(entry.name);
123
+ assertUniqueSlug("hook", slug, filePath, emitted);
124
+ let source;
125
+ try {
126
+ source = fs.readFileSync(filePath, "utf8");
127
+ } catch (error) {
128
+ warn(filePath, `unable to read hook script, skipping file: ${String(error)}`);
129
+ continue;
130
+ }
131
+ const scriptDescription = getScriptDescription(filePath).replace(/^ — /, "");
132
+ writeGeneratedPage({
133
+ outputPath: path.join(outputDir, `${slug}.mdx`),
134
+ title: entry.name,
135
+ description: scriptDescription,
136
+ sidebarLabel: entry.name,
137
+ body: `## Source
138
+
139
+ ${renderCodeFence(
140
+ source,
141
+ languageForFilename(entry.name)
142
+ )}`
143
+ });
144
+ items.push({ filename: entry.name, slug });
145
+ }
146
+ return items;
147
+ }
148
+ function generateHooksCategory(config) {
149
+ const outputDir = path.join(config.docsDir, "codex-hooks");
150
+ cleanDir(outputDir);
151
+ const hasJson = fs.existsSync(path.join(config.codexDir, "hooks.json"));
152
+ const hasScripts = fs.existsSync(path.join(config.codexDir, "hooks"));
153
+ if (!hasJson && !hasScripts) return [];
154
+ ensureDir(outputDir);
155
+ const emitted = /* @__PURE__ */ new Map();
156
+ const jsonItem = generateHooksJson(config, outputDir, emitted);
157
+ const items = [
158
+ ...jsonItem ? [jsonItem] : [],
159
+ ...generateHookScripts(config, outputDir, emitted)
160
+ ];
161
+ if (items.length > 0) {
162
+ writeCategoryIndex(
163
+ outputDir,
164
+ "Hooks",
165
+ 908,
166
+ "Lifecycle hooks",
167
+ formatFrontmatterString
168
+ );
169
+ }
170
+ return items;
171
+ }
172
+ export {
173
+ generateHooksCategory
174
+ };
@@ -0,0 +1,24 @@
1
+ import { generateCodexResourcesDocs, type CodexResourcesConfig } from "./generate.js";
2
+ export interface CodexResourcesPluginOptions {
3
+ /**
4
+ * Path to the project's `.codex/` directory. Resolved against
5
+ * `projectRoot` when relative.
6
+ */
7
+ codexDir: string;
8
+ /**
9
+ * Anchor for `codexDir`, `docsDir`, and relative `scanRoot` paths, and the
10
+ * default value of `scanRoot`. Defaults to `process.cwd()`.
11
+ */
12
+ projectRoot?: string;
13
+ /**
14
+ * Repo-wide discovery root, defaulting to `projectRoot`. It governs both the
15
+ * `AGENTS.md` / `AGENTS.override.md` walk (including relative titles/slugs)
16
+ * and the repo-level `.agents/skills/` root. `codexDir` and `docsDir` remain
17
+ * anchored to `projectRoot`.
18
+ */
19
+ scanRoot?: string;
20
+ /** Output directory, anchored to `projectRoot` when relative. */
21
+ docsDir?: string;
22
+ }
23
+ export declare function runCodexResourcesPreStep(options: CodexResourcesPluginOptions): ReturnType<typeof generateCodexResourcesDocs>;
24
+ export { generateCodexResourcesDocs, type CodexResourcesConfig };
@@ -0,0 +1,21 @@
1
+ import path from "node:path";
2
+ import {
3
+ generateCodexResourcesDocs
4
+ } from "./generate.js";
5
+ function runCodexResourcesPreStep(options) {
6
+ const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
7
+ const codexDir = path.isAbsolute(options.codexDir) ? options.codexDir : path.resolve(projectRoot, options.codexDir);
8
+ const scanRoot = options.scanRoot === void 0 ? projectRoot : path.isAbsolute(options.scanRoot) ? options.scanRoot : path.resolve(projectRoot, options.scanRoot);
9
+ const docsDirInput = options.docsDir ?? "src/content/docs";
10
+ const docsDir = path.isAbsolute(docsDirInput) ? docsDirInput : path.resolve(projectRoot, docsDirInput);
11
+ return generateCodexResourcesDocs({
12
+ codexDir,
13
+ projectRoot,
14
+ scanRoot,
15
+ docsDir
16
+ });
17
+ }
18
+ export {
19
+ generateCodexResourcesDocs,
20
+ runCodexResourcesPreStep
21
+ };
@@ -0,0 +1,10 @@
1
+ import type { CodexResourcesConfig } from "./generate.js";
2
+ export interface CodexCategoryPresence {
3
+ agentsMd: boolean;
4
+ config: boolean;
5
+ agents: boolean;
6
+ hooks: boolean;
7
+ rules: boolean;
8
+ skills: boolean;
9
+ }
10
+ export declare function generateOverview(config: CodexResourcesConfig, presence: CodexCategoryPresence): void;
@@ -0,0 +1,33 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { formatFrontmatterString } from "../resource-docs-shared/index.js";
4
+ import { cleanDir, ensureDir } from "../resource-docs-shared/index.js";
5
+ function generateOverview(config, presence) {
6
+ const outputDir = path.join(config.docsDir, "codex");
7
+ cleanDir(outputDir);
8
+ ensureDir(outputDir);
9
+ const slugs = [];
10
+ if (presence.agentsMd) slugs.push("codex-agents-md");
11
+ if (presence.config) slugs.push("codex-config");
12
+ if (presence.agents) slugs.push("codex-agents");
13
+ if (presence.hooks) slugs.push("codex-hooks");
14
+ if (presence.rules) slugs.push("codex-rules");
15
+ if (presence.skills) slugs.push("codex-skills");
16
+ fs.writeFileSync(
17
+ path.join(outputDir, "index.mdx"),
18
+ `---
19
+ title: ${formatFrontmatterString("Codex")}
20
+ description: ${formatFrontmatterString("OpenAI Codex configuration reference.")}
21
+ sidebar_position: 904
22
+ generated: true
23
+ ---
24
+
25
+ ## Resources
26
+
27
+ <CategoryNav categories={${JSON.stringify(slugs)}} />
28
+ `
29
+ );
30
+ }
31
+ export {
32
+ generateOverview
33
+ };
@@ -0,0 +1,6 @@
1
+ import type { CodexResourcesConfig } from "./generate.js";
2
+ export interface RuleItem {
3
+ filename: string;
4
+ slug: string;
5
+ }
6
+ export declare function generateRulesCategory(config: CodexResourcesConfig): RuleItem[];
@@ -0,0 +1,255 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ formatFrontmatterString,
5
+ cleanDir,
6
+ ensureDir,
7
+ renderCodeFence,
8
+ writeCategoryIndex
9
+ } from "../resource-docs-shared/index.js";
10
+ import {
11
+ assertUniqueSlug,
12
+ renderTableRow,
13
+ warn,
14
+ writeGeneratedPage
15
+ } from "./utils.js";
16
+ function freshState() {
17
+ return { quote: null, triple: false, escaped: false, comment: false };
18
+ }
19
+ function advanceState(text, index, state) {
20
+ const char = text[index] ?? "";
21
+ if (state.comment) {
22
+ if (char === "\n") state.comment = false;
23
+ return 1;
24
+ }
25
+ if (state.quote !== null) {
26
+ if (state.escaped) {
27
+ state.escaped = false;
28
+ return 1;
29
+ }
30
+ if (char === "\\") {
31
+ state.escaped = true;
32
+ return 1;
33
+ }
34
+ if (state.triple && text.slice(index, index + 3) === state.quote.repeat(3)) {
35
+ state.quote = null;
36
+ state.triple = false;
37
+ return 3;
38
+ }
39
+ if (!state.triple && char === state.quote) state.quote = null;
40
+ return 1;
41
+ }
42
+ if (char === "#") {
43
+ state.comment = true;
44
+ return 1;
45
+ }
46
+ if (char === "'" || char === '"') {
47
+ state.quote = char;
48
+ state.triple = text.slice(index, index + 3) === char.repeat(3);
49
+ return state.triple ? 3 : 1;
50
+ }
51
+ return 1;
52
+ }
53
+ function findPrefixRuleBodies(source) {
54
+ const bodies = [];
55
+ const state = freshState();
56
+ let index = 0;
57
+ while (index < source.length) {
58
+ if (state.quote === null && !state.comment && source.startsWith("prefix_rule", index)) {
59
+ const before = source[index - 1];
60
+ const after = source[index + "prefix_rule".length];
61
+ if ((!before || !/[\w]/.test(before)) && (!after || !/[\w]/.test(after))) {
62
+ let open = index + "prefix_rule".length;
63
+ while (/\s/.test(source[open] ?? "")) open++;
64
+ if (source[open] === "(") {
65
+ const callState = freshState();
66
+ let depth = 1;
67
+ let cursor = open + 1;
68
+ const bodyStart = cursor;
69
+ while (cursor < source.length) {
70
+ if (callState.quote === null && !callState.comment) {
71
+ if (source[cursor] === "(") depth++;
72
+ if (source[cursor] === ")") {
73
+ depth--;
74
+ if (depth === 0) break;
75
+ }
76
+ }
77
+ cursor += advanceState(source, cursor, callState);
78
+ }
79
+ bodies.push(source.slice(bodyStart, cursor));
80
+ index = cursor < source.length ? cursor + 1 : source.length;
81
+ continue;
82
+ }
83
+ }
84
+ }
85
+ index += advanceState(source, index, state);
86
+ }
87
+ return bodies;
88
+ }
89
+ function splitTopLevel(text, delimiter) {
90
+ const parts = [];
91
+ const state = freshState();
92
+ let square = 0;
93
+ let round = 0;
94
+ let curly = 0;
95
+ let start = 0;
96
+ let index = 0;
97
+ while (index < text.length) {
98
+ if (state.quote === null && !state.comment) {
99
+ const char = text[index];
100
+ if (char === "[") square++;
101
+ else if (char === "]") square--;
102
+ else if (char === "(") round++;
103
+ else if (char === ")") round--;
104
+ else if (char === "{") curly++;
105
+ else if (char === "}") curly--;
106
+ else if (char === delimiter && square === 0 && round === 0 && curly === 0) {
107
+ parts.push(text.slice(start, index));
108
+ start = index + 1;
109
+ }
110
+ }
111
+ index += advanceState(text, index, state);
112
+ }
113
+ parts.push(text.slice(start));
114
+ return parts;
115
+ }
116
+ function parseStringLiteral(value) {
117
+ const text = value.trim();
118
+ const quote = text[0];
119
+ if (quote !== '"' && quote !== "'" || text.at(-1) !== quote) return void 0;
120
+ if (text.startsWith(quote.repeat(3)) && text.endsWith(quote.repeat(3))) {
121
+ return text.slice(3, -3);
122
+ }
123
+ const inner = text.slice(1, -1);
124
+ let result = "";
125
+ for (let i = 0; i < inner.length; i++) {
126
+ const char = inner[i];
127
+ if (char !== "\\") {
128
+ result += char;
129
+ continue;
130
+ }
131
+ const next = inner[++i];
132
+ if (next === void 0) return void 0;
133
+ const escapes = {
134
+ n: "\n",
135
+ r: "\r",
136
+ t: " ",
137
+ "\\": "\\",
138
+ "'": "'",
139
+ '"': '"'
140
+ };
141
+ result += escapes[next] ?? next;
142
+ }
143
+ return result;
144
+ }
145
+ function parsePatternValue(text) {
146
+ const trimmed = text.trim();
147
+ const stringValue = parseStringLiteral(trimmed);
148
+ if (stringValue !== void 0) return stringValue;
149
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
150
+ const inner = trimmed.slice(1, -1);
151
+ if (inner.trim() === "") return [];
152
+ const values = [];
153
+ for (const part of splitTopLevel(inner, ",")) {
154
+ if (part.trim() === "") continue;
155
+ const parsed = parsePatternValue(part);
156
+ if (parsed === void 0) return void 0;
157
+ values.push(parsed);
158
+ }
159
+ return values;
160
+ }
161
+ function formatPattern(value) {
162
+ if (!Array.isArray(value)) return value;
163
+ const parts = [];
164
+ for (const element of value) {
165
+ if (Array.isArray(element)) {
166
+ if (element.some(Array.isArray)) return void 0;
167
+ parts.push(element.join("|"));
168
+ } else {
169
+ parts.push(element);
170
+ }
171
+ }
172
+ return parts.join(" ");
173
+ }
174
+ function parseRule(body) {
175
+ const args = /* @__PURE__ */ new Map();
176
+ for (const part of splitTopLevel(body, ",")) {
177
+ const assignment = splitTopLevel(part, "=");
178
+ if (assignment.length !== 2) continue;
179
+ const key = assignment[0]?.trim();
180
+ const value = assignment[1];
181
+ if (key && value !== void 0) args.set(key, value.trim());
182
+ }
183
+ const rawPattern = args.get("pattern");
184
+ const parsedPattern = rawPattern === void 0 ? void 0 : parsePatternValue(rawPattern);
185
+ const decision = args.has("decision") ? parseStringLiteral(args.get("decision") ?? "") : "allow";
186
+ return {
187
+ pattern: parsedPattern === void 0 ? void 0 : formatPattern(parsedPattern),
188
+ decision,
189
+ justification: args.has("justification") ? parseStringLiteral(args.get("justification") ?? "") : void 0
190
+ };
191
+ }
192
+ function generateRulesCategory(config) {
193
+ const rulesDir = path.join(config.codexDir, "rules");
194
+ const outputDir = path.join(config.docsDir, "codex-rules");
195
+ cleanDir(outputDir);
196
+ if (!fs.existsSync(rulesDir)) return [];
197
+ let files;
198
+ try {
199
+ files = fs.readdirSync(rulesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".rules")).map((entry) => entry.name).sort();
200
+ } catch (error) {
201
+ warn(rulesDir, `unable to read rules directory: ${String(error)}`);
202
+ return [];
203
+ }
204
+ if (files.length === 0) return [];
205
+ ensureDir(outputDir);
206
+ const items = [];
207
+ const emitted = /* @__PURE__ */ new Map();
208
+ for (const filename of files) {
209
+ const filePath = path.join(rulesDir, filename);
210
+ const slug = filename.replace(/\.rules$/, "");
211
+ assertUniqueSlug("rule", slug, filePath, emitted);
212
+ let source;
213
+ try {
214
+ source = fs.readFileSync(filePath, "utf8");
215
+ } catch (error) {
216
+ warn(filePath, `unable to read rules file, skipping it: ${String(error)}`);
217
+ continue;
218
+ }
219
+ const rows = findPrefixRuleBodies(source).map(parseRule);
220
+ const body = [
221
+ "## Rules",
222
+ "",
223
+ "| Pattern | Decision | Justification |",
224
+ "| --- | --- | --- |",
225
+ ...rows.length > 0 ? rows.map(
226
+ (row) => renderTableRow([row.pattern, row.decision, row.justification])
227
+ ) : [renderTableRow(Array.from({ length: 3 }))],
228
+ "",
229
+ "## Source",
230
+ "",
231
+ renderCodeFence(source, "python")
232
+ ].join("\n");
233
+ writeGeneratedPage({
234
+ outputPath: path.join(outputDir, `${slug}.mdx`),
235
+ title: filename,
236
+ description: `Command approval rules from ${filename}`,
237
+ sidebarLabel: filename,
238
+ body
239
+ });
240
+ items.push({ filename, slug });
241
+ }
242
+ if (items.length > 0) {
243
+ writeCategoryIndex(
244
+ outputDir,
245
+ "Rules",
246
+ 909,
247
+ "Command approval rules",
248
+ formatFrontmatterString
249
+ );
250
+ }
251
+ return items;
252
+ }
253
+ export {
254
+ generateRulesCategory
255
+ };
@@ -0,0 +1,2 @@
1
+ import type { CodexResourcesConfig } from "./generate.js";
2
+ export declare function generateSkillsCategoryDocs(config: CodexResourcesConfig): import("../resource-docs-shared/skills.js").SkillItem[];
@@ -0,0 +1,84 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import matter from "gray-matter";
4
+ import {
5
+ escapeForMdx,
6
+ formatFrontmatterString,
7
+ generateSkillsCategory
8
+ } from "../resource-docs-shared/index.js";
9
+ import { isRecord, warn } from "./utils.js";
10
+ function optionalMetadataString(record, field, filePath) {
11
+ const value = record[field];
12
+ if (value === void 0) return void 0;
13
+ if (typeof value === "string") return value;
14
+ warn(filePath, `${field} must be a string; omitting it`);
15
+ return void 0;
16
+ }
17
+ function renderOpenAiMetadata(skillAbsDir) {
18
+ const filePath = path.join(skillAbsDir, "agents", "openai.yaml");
19
+ if (!fs.existsSync(filePath)) return "";
20
+ let data;
21
+ try {
22
+ const yaml = fs.readFileSync(filePath, "utf8");
23
+ data = matter(`---
24
+ ${yaml}
25
+ ---
26
+ `).data;
27
+ } catch (error) {
28
+ warn(filePath, `unable to parse YAML, omitting metadata: ${String(error)}`);
29
+ return "";
30
+ }
31
+ if (!isRecord(data)) {
32
+ warn(filePath, "YAML root must be an object; omitting metadata");
33
+ return "";
34
+ }
35
+ let interfaceData;
36
+ if (data.interface !== void 0) {
37
+ if (isRecord(data.interface)) interfaceData = data.interface;
38
+ else warn(filePath, "interface must be an object; omitting interface metadata");
39
+ }
40
+ let policy;
41
+ if (data.policy !== void 0) {
42
+ if (isRecord(data.policy)) policy = data.policy;
43
+ else warn(filePath, "policy must be an object; omitting policy metadata");
44
+ }
45
+ const displayName = interfaceData ? optionalMetadataString(interfaceData, "display_name", filePath) : void 0;
46
+ const shortDescription = interfaceData ? optionalMetadataString(interfaceData, "short_description", filePath) : void 0;
47
+ let explicitOnly = false;
48
+ if (policy?.allow_implicit_invocation !== void 0) {
49
+ if (typeof policy.allow_implicit_invocation === "boolean") {
50
+ explicitOnly = policy.allow_implicit_invocation === false;
51
+ } else {
52
+ warn(
53
+ filePath,
54
+ "policy.allow_implicit_invocation must be a boolean; omitting invocation metadata"
55
+ );
56
+ }
57
+ }
58
+ return [
59
+ displayName ? `**Display name:** ${escapeForMdx(displayName)}` : "",
60
+ shortDescription ? `**Short description:** ${escapeForMdx(shortDescription)}` : "",
61
+ explicitOnly ? `**Invocation:** explicit only (\`$${path.basename(skillAbsDir)}\`)` : ""
62
+ ].filter(Boolean).join("\n");
63
+ }
64
+ function generateSkillsCategoryDocs(config) {
65
+ const projectRoot = config.projectRoot ?? config.codexDir;
66
+ const scanRoot = config.scanRoot ?? projectRoot;
67
+ return generateSkillsCategory({
68
+ skillsDirs: [
69
+ path.join(config.codexDir, "skills"),
70
+ path.join(projectRoot, ".agents", "skills"),
71
+ path.join(scanRoot, ".agents", "skills")
72
+ ],
73
+ outputDir: path.join(config.docsDir, "codex-skills"),
74
+ label: "Skills",
75
+ position: 910,
76
+ description: "Skill packages",
77
+ sourceLabel: ".codex/skills",
78
+ renderExtraHeader: renderOpenAiMetadata,
79
+ renderFrontmatterString: formatFrontmatterString
80
+ });
81
+ }
82
+ export {
83
+ generateSkillsCategoryDocs
84
+ };
@@ -0,0 +1,14 @@
1
+ export declare function warn(filePath: string, message: string): void;
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
3
+ export declare function filenameSlug(filename: string): string;
4
+ export declare function renderTableRow(values: unknown[]): string;
5
+ export declare function assertUniqueSlug(resource: string, slug: string, filePath: string, emitted: Map<string, string>): void;
6
+ export declare function writeGeneratedPage({ outputPath, title, description, sidebarLabel, sidebarPosition, body, }: {
7
+ outputPath: string;
8
+ title: string;
9
+ description: string;
10
+ sidebarLabel?: string;
11
+ sidebarPosition?: number;
12
+ body: string;
13
+ }): void;
14
+ export declare function languageForFilename(filename: string): string;