@open-agent-toolkit/cli 0.3.26 → 0.3.27
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/README.md +6 -0
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/contributing/documentation.md +39 -6
- package/assets/docs/contributing/markdown-features.md +14 -2
- package/assets/docs/docs-tooling/add-docs-to-a-repo.md +12 -9
- package/assets/docs/docs-tooling/commands.md +1 -1
- package/assets/docs/docs-tooling/workflows.md +14 -10
- package/assets/docs/getting-started/tool-packs.md +5 -3
- package/assets/docs/skills/index.md +8 -8
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-docs-analyze/SKILL.md +2 -2
- package/assets/skills/oat-docs-analyze/references/legacy-scaffold-checks.md +4 -0
- package/assets/skills/oat-docs-bootstrap/SKILL.md +39 -59
- package/assets/templates/docs-app-fuma/AGENTS.md +50 -0
- package/assets/templates/docs-app-fuma/app/[[...slug]]/page.tsx +7 -3
- package/assets/templates/docs-app-fuma/app/llms/[...slug]/route.ts +15 -0
- package/assets/templates/docs-app-fuma/app/llms-full.txt/route.ts +7 -0
- package/assets/templates/docs-app-fuma/app/llms.txt/route.ts +7 -0
- package/assets/templates/docs-app-fuma/docs/contributing/docs-toolchain.md +48 -0
- package/assets/templates/docs-app-fuma/docs/contributing/index.md +13 -0
- package/assets/templates/docs-app-fuma/docs/contributing.md +77 -41
- package/assets/templates/docs-app-fuma/docs/getting-started.md +3 -33
- package/assets/templates/docs-app-fuma/docs/index.md +2 -2
- package/assets/templates/docs-app-fuma/lib/source.ts +5 -0
- package/assets/templates/docs-app-mkdocs/AGENTS.md +46 -0
- package/assets/templates/docs-app-mkdocs/docs/contributing/docs-toolchain.md +44 -0
- package/assets/templates/docs-app-mkdocs/docs/contributing/index.md +13 -0
- package/assets/templates/docs-app-mkdocs/docs/contributing.md +105 -21
- package/assets/templates/docs-app-mkdocs/docs/getting-started.md +7 -25
- package/assets/templates/docs-app-mkdocs/docs/index.md +7 -2
- package/assets/templates/docs-app-mkdocs/mkdocs.yml +1 -0
- package/dist/commands/docs/init/docs-commands.d.ts +1 -0
- package/dist/commands/docs/init/docs-commands.d.ts.map +1 -1
- package/dist/commands/docs/init/docs-commands.js +8 -3
- package/dist/commands/docs/init/scaffold.d.ts.map +1 -1
- package/dist/commands/docs/init/scaffold.js +24 -10
- package/dist/commands/docs/nav/fumadocs.d.ts.map +1 -1
- package/dist/commands/docs/nav/fumadocs.js +32 -10
- package/package.json +2 -2
- package/assets/skills/oat-docs-bootstrap/assets/AGENTS.md.template +0 -64
|
@@ -2,10 +2,15 @@ import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { basename, dirname, isAbsolute, join, relative, resolve, } from 'node:path';
|
|
3
3
|
import { dirExists, ensureDir, fileExists } from '../../../fs/io.js';
|
|
4
4
|
import { OAT_VERSION } from '../../../shared/oat-version.js';
|
|
5
|
-
import { buildDocsCommands } from './docs-commands.js';
|
|
5
|
+
import { buildDocsCommands, shellQuote } from './docs-commands.js';
|
|
6
6
|
import { applyMarkdownDocsPlan, planMarkdownDocs } from './markdown.js';
|
|
7
7
|
import { getTemplateDir } from './resolve-options.js';
|
|
8
8
|
const MKDOCS_TEMPLATE_FILES = [
|
|
9
|
+
{ source: 'AGENTS.md', destination: 'AGENTS.md' },
|
|
10
|
+
...['index.md', 'docs-toolchain.md'].map((name) => ({
|
|
11
|
+
source: join('docs', 'contributing', name),
|
|
12
|
+
destination: join('docs', 'contributing', name),
|
|
13
|
+
})),
|
|
9
14
|
{ source: '.gitignore', destination: '.gitignore' },
|
|
10
15
|
{ source: 'mkdocs.yml', destination: 'mkdocs.yml' },
|
|
11
16
|
{ source: 'package.json.template', destination: 'package.json' },
|
|
@@ -22,6 +27,15 @@ const MKDOCS_TEMPLATE_FILES = [
|
|
|
22
27
|
},
|
|
23
28
|
];
|
|
24
29
|
const FUMA_TEMPLATE_FILES = [
|
|
30
|
+
{ source: 'AGENTS.md', destination: 'AGENTS.md' },
|
|
31
|
+
...['index.md', 'docs-toolchain.md'].map((name) => ({
|
|
32
|
+
source: join('docs', 'contributing', name),
|
|
33
|
+
destination: join('docs', 'contributing', name),
|
|
34
|
+
})),
|
|
35
|
+
...['llms.txt', 'llms-full.txt', 'llms/[...slug]'].map((route) => ({
|
|
36
|
+
source: join('app', route, 'route.ts'),
|
|
37
|
+
destination: join('app', route, 'route.ts'),
|
|
38
|
+
})),
|
|
25
39
|
{ source: '.gitignore', destination: '.gitignore' },
|
|
26
40
|
{ source: 'next.config.js', destination: 'next.config.js' },
|
|
27
41
|
{ source: 'postcss.config.mjs', destination: 'postcss.config.mjs' },
|
|
@@ -148,11 +162,6 @@ function buildFumaDevDependencies(lint, format) {
|
|
|
148
162
|
}
|
|
149
163
|
return `,\n${entries.join(',\n')}`;
|
|
150
164
|
}
|
|
151
|
-
function shellQuote(value) {
|
|
152
|
-
return /^[a-zA-Z0-9_./-]+$/.test(value)
|
|
153
|
-
? value
|
|
154
|
-
: `'${value.replaceAll("'", "'\\''")}'`;
|
|
155
|
-
}
|
|
156
165
|
function jsonStringContent(value) {
|
|
157
166
|
return JSON.stringify(value).slice(1, -1);
|
|
158
167
|
}
|
|
@@ -224,9 +233,10 @@ async function buildPackageManagerField(options, appRoot, dependencies) {
|
|
|
224
233
|
const version = normalizePnpmVersion(await dependencies.detectPnpmVersion());
|
|
225
234
|
return `,\n "packageManager": ${JSON.stringify(`pnpm@${version}`)}`;
|
|
226
235
|
}
|
|
227
|
-
function renderTemplate(template, options, depContext, packageManagerField, nodeRequirement) {
|
|
236
|
+
function renderTemplate(template, options, depContext, packageManagerField, nodeRequirement, packageJson) {
|
|
228
237
|
const repoName = basename(options.repoRoot);
|
|
229
238
|
const commands = buildDocsCommands(options.repoShape, options.targetDir, options.appName);
|
|
239
|
+
const command = packageJson ? jsonStringContent : (value) => value;
|
|
230
240
|
const replacements = {
|
|
231
241
|
'// {{TURBOPACK_ROOT_IMPORTS}}\n': options.repoShape === 'nested-standalone'
|
|
232
242
|
? "import { dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n"
|
|
@@ -260,8 +270,12 @@ function renderTemplate(template, options, depContext, packageManagerField, node
|
|
|
260
270
|
'{{FUMA_DEV_DEPENDENCIES}}': buildFumaDevDependencies(options.lint, options.format),
|
|
261
271
|
'{{REPO_NAME}}': repoName,
|
|
262
272
|
'{{APP_DIR}}': options.targetDir,
|
|
263
|
-
'{{
|
|
264
|
-
'{{
|
|
273
|
+
'{{APP_CWD}}': shellQuote(options.targetDir),
|
|
274
|
+
'{{LINT_STATUS}}': options.lint === 'none' ? 'disabled' : 'enabled (markdownlint-cli2)',
|
|
275
|
+
'{{FORMAT_STATUS}}': options.format === 'none' ? 'disabled' : 'enabled (oxfmt)',
|
|
276
|
+
'{{NAV_SYNC_CMD}}': command(buildNavSyncCheckCmd(depContext.isOatRepo, options.targetDir).replace(/ --check$/, '')),
|
|
277
|
+
'{{GENERATE_INDEX_CMD}}': command(buildGenerateIndexCmd(depContext.isOatRepo, options.targetDir)),
|
|
278
|
+
'{{NAV_SYNC_CHECK_CMD}}': command(buildNavSyncCheckCmd(depContext.isOatRepo, options.targetDir)),
|
|
265
279
|
'{{OAT_DOCS_CONFIG_DEP}}': oatDepVersion(depContext, 'docs-config'),
|
|
266
280
|
'{{OAT_DOCS_THEME_DEP}}': oatDepVersion(depContext, 'docs-theme'),
|
|
267
281
|
'{{OAT_DOCS_TRANSFORMS_DEP}}': oatDepVersion(depContext, 'docs-transforms'),
|
|
@@ -361,7 +375,7 @@ export async function scaffoldDocsApp(options, overrides = {}) {
|
|
|
361
375
|
const source = join(templateRoot, templateFile.source);
|
|
362
376
|
const destination = join(appRoot, templateFile.destination);
|
|
363
377
|
const template = await readFile(source, 'utf8');
|
|
364
|
-
const rendered = renderTemplate(template, options, depContext, packageManagerField, nodeRequirement);
|
|
378
|
+
const rendered = renderTemplate(template, options, depContext, packageManagerField, nodeRequirement, templateFile.destination === 'package.json');
|
|
365
379
|
await ensureDir(dirname(destination));
|
|
366
380
|
await writeFile(destination, rendered, 'utf8');
|
|
367
381
|
createdFiles.push(templateFile.destination);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fumadocs.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/nav/fumadocs.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA6DD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoCvE;
|
|
1
|
+
{"version":3,"file":"fumadocs.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/nav/fumadocs.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA6DD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoCvE;AAsCD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAqND;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,GAAG,OAAO,CAAC,4BAA4B,CAAC,CA8ExC"}
|
|
@@ -104,7 +104,7 @@ export function resolveFolderTitle(markdown) {
|
|
|
104
104
|
* slugs plugin with the scaffolded `baseUrl: '/'`: group folders are dropped,
|
|
105
105
|
* segments are URI-encoded, and an `index` file maps to its folder.
|
|
106
106
|
*/
|
|
107
|
-
function pageUrl(pagePath) {
|
|
107
|
+
function pageUrl(pagePath, nonIndexUrls = new Set()) {
|
|
108
108
|
const slugs = parentDir(pagePath)
|
|
109
109
|
.split('/')
|
|
110
110
|
.filter((segment) => segment.length > 0 && segment !== '.')
|
|
@@ -114,7 +114,12 @@ function pageUrl(pagePath) {
|
|
|
114
114
|
if (name !== 'index') {
|
|
115
115
|
slugs.push(encodeURI(name));
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
const url = `/${slugs.join('/')}`;
|
|
118
|
+
// core16.10.2 defers index slugs and appends /index when a non-index
|
|
119
|
+
// page has already claimed the folder URL.
|
|
120
|
+
return name === 'index' && nonIndexUrls.has(url)
|
|
121
|
+
? `${url.replace(/\/$/, '')}/index`
|
|
122
|
+
: url;
|
|
118
123
|
}
|
|
119
124
|
/** Whether the Fumadocs page-tree builder reads `item` as a directive, not a path. */
|
|
120
125
|
function isFumadocsDirective(item) {
|
|
@@ -139,9 +144,11 @@ function hrefFragment(href) {
|
|
|
139
144
|
/**
|
|
140
145
|
* Build one folder's `pages` from its index.md Contents map.
|
|
141
146
|
*
|
|
142
|
-
* A page in this folder or an immediate subfolder is referenced by name.
|
|
143
|
-
*
|
|
144
|
-
*
|
|
147
|
+
* A page in this folder or an immediate subfolder is referenced by name.
|
|
148
|
+
* Local same-name page/folder collisions use a precise source-file item, as
|
|
149
|
+
* does `../contributing.md` from its own contributing/ map: pinned core16.10.2
|
|
150
|
+
* selects the folder first for an extensionless reference. Other non-local
|
|
151
|
+
* targets become link entries. There is never a `"..."` rest entry: pages the Contents map does
|
|
145
152
|
* not list stay out of the tree and are reported instead.
|
|
146
153
|
*/
|
|
147
154
|
/**
|
|
@@ -162,7 +169,7 @@ function hasImplicitIndex(relativeDir, existing) {
|
|
|
162
169
|
const pagesIndex = existing?.pagesIndex;
|
|
163
170
|
return pagesIndex === undefined || pagesIndex === 'index';
|
|
164
171
|
}
|
|
165
|
-
async function buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex, matcher) {
|
|
172
|
+
async function buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex, matcher, inventory, nonIndexUrls) {
|
|
166
173
|
const indexPath = join(docsRoot, relativeDir, 'index.md');
|
|
167
174
|
const entries = parseIndexContents(markdown, indexPath);
|
|
168
175
|
// A folder the loader gives no implicit index (the docs root, a `root: true`
|
|
@@ -177,7 +184,16 @@ async function buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex,
|
|
|
177
184
|
continue;
|
|
178
185
|
const targetDir = parentDir(target.path);
|
|
179
186
|
let item;
|
|
180
|
-
if (target.kind === '
|
|
187
|
+
if (target.kind === 'page' &&
|
|
188
|
+
(targetDir === relativeDir ||
|
|
189
|
+
target.path.slice(0, -extname(target.path).length) === relativeDir) &&
|
|
190
|
+
inventory.directories.includes(target.path.slice(0, -extname(target.path).length))) {
|
|
191
|
+
// An extensionless reference selects the same-name folder first.
|
|
192
|
+
// Keep source identity only in local/owning placement. A nonlocal map
|
|
193
|
+
// needs an authored URL link: core16.10.2 globally dedups source refs.
|
|
194
|
+
item = toFumadocsPageItem(posix.relative(relativeDir, target.path));
|
|
195
|
+
}
|
|
196
|
+
else if (target.kind === 'section' && targetDir === relativeDir) {
|
|
181
197
|
continue;
|
|
182
198
|
}
|
|
183
199
|
else if (target.kind === 'section' &&
|
|
@@ -188,7 +204,7 @@ async function buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex,
|
|
|
188
204
|
item = toFumadocsPageItem(basename(target.path, extname(target.path)));
|
|
189
205
|
}
|
|
190
206
|
else {
|
|
191
|
-
item = `[${entry.title}](${pageUrl(target.path)}${hrefFragment(entry.href)})`;
|
|
207
|
+
item = `[${entry.title}](${pageUrl(target.path, nonIndexUrls)}${hrefFragment(entry.href)})`;
|
|
192
208
|
}
|
|
193
209
|
if (!pages.includes(item)) {
|
|
194
210
|
if (entry.group !== currentGroup) {
|
|
@@ -228,6 +244,7 @@ function mergeMeta(existing, meta) {
|
|
|
228
244
|
};
|
|
229
245
|
}
|
|
230
246
|
function collectUnlisted(inventory, metas, implicitIndexDirs) {
|
|
247
|
+
const pagePaths = new Set(inventory.pages);
|
|
231
248
|
const reachableDirs = new Set();
|
|
232
249
|
const reachablePages = new Set();
|
|
233
250
|
const pagesByFlattenPath = new Map(inventory.pages.map((page) => [
|
|
@@ -257,7 +274,9 @@ function collectUnlisted(inventory, metas, implicitIndexDirs) {
|
|
|
257
274
|
visit(itemPath);
|
|
258
275
|
continue;
|
|
259
276
|
}
|
|
260
|
-
const page =
|
|
277
|
+
const page = pagePaths.has(itemPath)
|
|
278
|
+
? itemPath
|
|
279
|
+
: pagesByFlattenPath.get(itemPath);
|
|
261
280
|
if (page) {
|
|
262
281
|
reachablePages.add(page);
|
|
263
282
|
}
|
|
@@ -291,6 +310,9 @@ export async function syncFumadocsNavigation(options) {
|
|
|
291
310
|
const { docsRoot } = options;
|
|
292
311
|
const matcher = createExclusionMatcher(options.nonReaderPaths ?? []);
|
|
293
312
|
const inventory = await inventoryDocs(docsRoot, matcher);
|
|
313
|
+
const nonIndexUrls = new Set(inventory.pages
|
|
314
|
+
.filter((page) => basename(page, extname(page)) !== 'index')
|
|
315
|
+
.map((page) => pageUrl(page)));
|
|
294
316
|
const metas = new Map();
|
|
295
317
|
const existingMetas = new Map();
|
|
296
318
|
const implicitIndexDirs = new Set();
|
|
@@ -312,7 +334,7 @@ export async function syncFumadocsNavigation(options) {
|
|
|
312
334
|
const title = resolveFolderTitle(markdown);
|
|
313
335
|
metas.set(relativeDir, {
|
|
314
336
|
...(title === undefined ? {} : { title }),
|
|
315
|
-
pages: await buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex, matcher),
|
|
337
|
+
pages: await buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex, matcher, inventory, nonIndexUrls),
|
|
316
338
|
});
|
|
317
339
|
}
|
|
318
340
|
if (!metas.has('.')) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.27",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Open Agent Toolkit CLI",
|
|
6
6
|
"homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"ora": "^9.0.0",
|
|
36
36
|
"yaml": "2.8.2",
|
|
37
37
|
"zod": "^3.25.76",
|
|
38
|
-
"@open-agent-toolkit/control-plane": "0.3.
|
|
38
|
+
"@open-agent-toolkit/control-plane": "0.3.27"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.10.0",
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
<!-- Framework docs-app bridge only: Fumadocs/MkDocs. Never render this template into a plain Markdown content root; use managed root Documentation guidance and the contributing page there. -->
|
|
2
|
-
# AGENTS — {{SITE_NAME}}
|
|
3
|
-
|
|
4
|
-
This file tells agents how to work inside `{{APP_DIR}}`, the documentation app for `{{REPO_NAME}}`. It is not bootstrap documentation. If you're looking for how this app was scaffolded, see `docs/getting-started.md` or the root-level `AGENTS.md` section on documentation.
|
|
5
|
-
|
|
6
|
-
## Purpose and scope
|
|
7
|
-
|
|
8
|
-
`{{APP_DIR}}` is the documentation surface for `{{REPO_NAME}}`. Humans read the rendered site; agents read the Markdown source and the navigation structure. This file tells agents the conventions that keep both audiences served, and points at the tooling that enforces those conventions. Everything here is about ongoing work inside the docs app — adding, restructuring, auditing, and bulk-editing content.
|
|
9
|
-
|
|
10
|
-
## When you need to add a new page
|
|
11
|
-
|
|
12
|
-
1. Create the Markdown file under `docs/` at the directory that best matches the page's topic. Every content directory is an `index.md` map — find the right one before picking a path. **Prefer `.md`** for plain content pages; reach for `.mdx` only when the page actually needs embedded JSX (a custom component, an interactive widget, a specialized layout). Plain `.md` is the default because agents, linters, and grep rules all handle it more reliably than `.mdx`.
|
|
13
|
-
2. Add frontmatter with at minimum `title:` and `description:`. The title drives nav display and page `<title>`; the description drives search previews, social cards, and sibling summaries. Empty descriptions hurt all three.
|
|
14
|
-
3. Update the nearest `index.md`'s `## Contents` section to include a link to the new page. Use `.md`-suffixed relative links: `[Title](page.md)` for leaf pages, `[Section](subdir/index.md)` for subdirectories. The `@open-agent-toolkit/docs-transforms` remark-links plugin normalizes these for Fumadocs routing at build time (strips `.md`, collapses `dir/index.md` → `dir`), so the suffixed form renders correctly **and** lets agents follow links to the target file without path inference. The `## Contents` section is the machine-readable local map — anything not listed there is effectively invisible to the navigation tooling.
|
|
15
|
-
4. If the new page introduces a new subdirectory, create an `index.md` in that subdirectory with its own `## Contents` section. Every Markdown-bearing content directory must have an `index.md`; asset-only directories are exempt. Preserve useful audience/scope context and honor configured excludes and existing local instructions.
|
|
16
|
-
5. Regenerate the derived navigation artifacts for this framework. For Fumadocs, run `oat docs nav sync` (from the app directory, or with `--target-dir <app-dir>`) so the committed `docs/**/meta.json` sidebar files follow authored `## Contents`, and run the generated-index command (`{{GENERATE_INDEX_CMD}}`) so the app-root `index.md` manifest reflects the current Markdown file tree. Fumadocs nav sync is strict: a page no `## Contents` map lists stays out of the sidebar and is reported, so list the new page rather than editing `meta.json`. For MkDocs, run `oat docs nav sync` or the local equivalent so `mkdocs.yml` `nav:` reflects authored `## Contents`. Derived artifacts never replace the authored `## Contents`; keep authored maps current and compare generated output against them when checking freshness.
|
|
17
|
-
|
|
18
|
-
## When you need to restructure navigation
|
|
19
|
-
|
|
20
|
-
1. Make changes in the authored `## Contents` sections of each affected `index.md`. That is the authoritative local map.
|
|
21
|
-
2. Do **not** hand-edit generated navigation artifacts. The Fumadocs app-root `index.md` manifest, Fumadocs `meta.json` files, `mkdocs.yml` `nav:` (for MkDocs), and any other derived nav file is rewritten by the local generation workflow.
|
|
22
|
-
3. After editing `## Contents`, run `oat docs nav sync` and the framework's generation command to refresh derived artifacts.
|
|
23
|
-
4. If you're moving pages between directories, update both source and destination `index.md` `## Contents` entries in the same commit so the site isn't broken mid-way through history.
|
|
24
|
-
5. If you're reparenting an entire subtree, consider whether the moved directory's own `index.md` needs a revised "scope" paragraph.
|
|
25
|
-
|
|
26
|
-
## When you need to audit or bulk-edit docs
|
|
27
|
-
|
|
28
|
-
1. Use `oat-docs-analyze` to audit the docs surface against the `## Contents` contract and surface gaps, drift, and coverage opportunities. This is read-only; it produces a report artifact under `.oat/repo/analysis/`.
|
|
29
|
-
2. Review the report with the user. The analyze output is a set of ordered recommendations — apply them selectively, not wholesale.
|
|
30
|
-
3. Use `oat-docs-apply` to execute approved recommendations. Apply creates a branch, performs the approved changes, runs nav sync, and optionally opens a PR. It will not make unapproved changes or invent new conventions.
|
|
31
|
-
4. Do not bypass analyze/apply for sweeping content changes. The branch-and-review discipline catches mistakes that would otherwise land silently and accumulate over time.
|
|
32
|
-
|
|
33
|
-
## When you're unsure where content belongs
|
|
34
|
-
|
|
35
|
-
1. Start at `docs/index.md` and read its `## Contents` section. That's the top-level map. Follow the link that best matches the topic.
|
|
36
|
-
2. Repeat at each directory level — every `index.md` has its own `## Contents`. Descend until you find the right scope.
|
|
37
|
-
3. If none of the existing sections fit, the content probably belongs in a new subdirectory. Create it with an `index.md` (see "When you need to add a new page").
|
|
38
|
-
4. If in doubt, match existing siblings' style and depth rather than inventing a new pattern. Consistency across neighboring pages matters more than exact placement.
|
|
39
|
-
|
|
40
|
-
## When you need project-level documentation updates
|
|
41
|
-
|
|
42
|
-
1. Use the `oat-project-document` skill during OAT project workflows. It reads project artifacts (`discovery.md`, `spec.md`, `design.md`, `plan.md`, `implementation.md`) and proposes evidence-backed doc updates — no speculation, no content that doesn't trace back to the source artifacts.
|
|
43
|
-
2. The skill generates recommendations; you approve them before they land. Treat it like analyze/apply but scoped to a single project's documentation delta.
|
|
44
|
-
3. Do not hand-write project-derived docs that bypass this flow. Bypassing means future runs of the skill will re-surface the same content as "missing" because the provenance chain is broken.
|
|
45
|
-
|
|
46
|
-
## What not to do
|
|
47
|
-
|
|
48
|
-
- **Don't hand-edit generated files.** For Fumadocs, the root-level `index.md` generated by `{{GENERATE_INDEX_CMD}}` is rewritten on every build. For MkDocs, generated or synchronized navigation lives in `mkdocs.yml` `nav:`. Edits to derived artifacts are silently clobbered. Edit the authored source (`docs/index.md` and its `## Contents`), then regenerate.
|
|
49
|
-
- **Don't default to `.mdx` when `.md` would do.** MDX adds JSX-in-Markdown capability (embedded components), which breaks simple link rewrites, confuses agent tooling, and requires imports that plain `.md` doesn't. Reach for `.mdx` only when a page actually needs a component it can't express in plain Markdown.
|
|
50
|
-
- **Don't author `## Contents` links without `.md` extensions.** Extension-less links (`[Title](page)` instead of `[Title](page.md)`) force agents to infer the target file type, break simple grep-based navigation, and diverge from the convention used throughout `{{REPO_NAME}}`. The build pipeline (`@open-agent-toolkit/docs-transforms` remark-links) strips `.md` for Fumadocs routing — so suffixed authored links render correctly and stay agent-friendly.
|
|
51
|
-
- **Don't invent new navigation conventions outside `## Contents`.** A sidebar config in a framework file, a hand-rolled TOC, a separate nav YAML — all of these diverge from the contract and break the tooling. Extend via `## Contents`.
|
|
52
|
-
- **Don't bypass `oat-docs-apply` for bulk content changes.** The analyze/apply flow exists to catch mistakes. Bulk edits done by hand or by a one-off script skip the safety net and often regress something unexpected.
|
|
53
|
-
- **Don't create `overview.md` files.** They're deprecated in favor of `index.md` with a `## Contents` section.
|
|
54
|
-
- **Don't edit the scaffolded `{{SITE_NAME}}` value in isolation.** The site name flows to multiple files (layout branding, metadata, page headings). If you're renaming the docs site, update all locations in the same commit or you'll get inconsistent chrome.
|
|
55
|
-
|
|
56
|
-
## Reference
|
|
57
|
-
|
|
58
|
-
- `docs/getting-started.md` — first-run setup instructions for contributors joining this docs app.
|
|
59
|
-
- `docs/contributing.md` — authoring conventions (frontmatter requirements, Markdown features like GFM alerts / mermaid / code blocks, nav contract summary).
|
|
60
|
-
- Root `AGENTS.md` `## Documentation` section in `{{REPO_NAME}}` — repo-wide pointer telling any agent "docs live at `{{APP_DIR}}`."
|
|
61
|
-
- `oat-docs-analyze` — read-only audit command.
|
|
62
|
-
- `oat-docs-apply` — apply approved audit recommendations.
|
|
63
|
-
- `oat-project-document` skill — propose evidence-backed doc updates from project artifacts.
|
|
64
|
-
- `{{GENERATE_INDEX_CMD}}` — for Fumadocs, regenerates the machine-readable root `index.md` from `docs/`. Runs automatically on `predev` / `prebuild`; don't hand-edit its output.
|