@open-agent-toolkit/cli 0.3.9 → 0.3.11
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 +2 -1
- package/assets/bundle-metadata.json +1 -1
- package/assets/config/dispatch-matrix-recommendation.json +6 -6
- package/assets/docs/cli-utilities/configuration.md +12 -8
- package/assets/docs/cli-utilities/meta.json +13 -0
- package/assets/docs/cli-utilities/tool-packs.md +8 -2
- package/assets/docs/cli-utilities/workflow-gates.md +14 -1
- package/assets/docs/contributing/code.md +8 -2
- package/assets/docs/contributing/documentation.md +7 -1
- package/assets/docs/contributing/meta.json +15 -0
- package/assets/docs/contributing/updating-model-guidance.md +34 -6
- package/assets/docs/contributing/verifying-cursor-pins.md +38 -9
- package/assets/docs/docs-tooling/add-docs-to-a-repo.md +14 -5
- package/assets/docs/docs-tooling/commands.md +26 -8
- package/assets/docs/docs-tooling/meta.json +4 -0
- package/assets/docs/docs-tooling/workflows.md +2 -2
- package/assets/docs/guide/meta.json +4 -0
- package/assets/docs/meta.json +14 -0
- package/assets/docs/provider-sync/instruction-sync.md +8 -0
- package/assets/docs/provider-sync/meta.json +11 -0
- package/assets/docs/provider-sync/providers.md +4 -4
- package/assets/docs/provider-sync/scope-and-surface.md +3 -4
- package/assets/docs/reference/cli-reference.md +5 -2
- package/assets/docs/reference/docs-index-contract.md +22 -6
- package/assets/docs/reference/file-locations.md +7 -2
- package/assets/docs/reference/index.md +1 -1
- package/assets/docs/reference/meta.json +10 -0
- package/assets/docs/reference/troubleshooting.md +41 -6
- package/assets/docs/workflows/ideas/meta.json +4 -0
- package/assets/docs/workflows/meta.json +4 -0
- package/assets/docs/workflows/projects/cursor-cloud.md +8 -3
- package/assets/docs/workflows/projects/dispatch-ceiling.md +21 -14
- package/assets/docs/workflows/projects/evidence-layers.md +6 -8
- package/assets/docs/workflows/projects/implementation-execution.md +34 -5
- package/assets/docs/workflows/projects/lifecycle.md +7 -1
- package/assets/docs/workflows/projects/meta.json +25 -0
- package/assets/docs/workflows/projects/orchestration-model.md +5 -6
- package/assets/docs/workflows/skills/meta.json +10 -0
- package/assets/docs/workflows/skills/recon.md +28 -3
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-cursor-cloud-projects/SKILL.md +30 -16
- package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -12
- package/assets/skills/oat-dispatch-subagents/references/managed-claude-example.json +1 -1
- package/assets/skills/oat-dispatch-subagents/references/provider-codex.md +51 -0
- package/assets/skills/oat-dispatch-subagents/references/provider-cursor.md +5 -2
- package/assets/skills/oat-dispatch-subagents/references/record-schema.md +8 -7
- package/assets/skills/oat-docs-analyze/SKILL.md +28 -3
- package/assets/skills/oat-docs-apply/SKILL.md +3 -3
- package/assets/skills/oat-docs-authoring/SKILL.md +1 -1
- package/assets/skills/oat-docs-authoring/references/oat-fumadocs-contract.md +5 -2
- package/assets/skills/oat-docs-bootstrap/SKILL.md +4 -3
- package/assets/skills/oat-docs-bootstrap/assets/AGENTS.md.template +3 -3
- package/assets/skills/oat-pjm-decision/SKILL.md +3 -2
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +4 -4
- package/assets/skills/oat-project-complete/SKILL.md +47 -2
- package/assets/skills/oat-project-design/SKILL.md +10 -6
- package/assets/skills/oat-project-discover/SKILL.md +3 -3
- package/assets/skills/oat-project-dispatch-subagents/SKILL.md +3 -10
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +135 -3
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +8 -10
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-implement/references/plan-and-resume.md +1 -1
- package/assets/skills/oat-project-import-plan/SKILL.md +3 -3
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-new/SKILL.md +3 -3
- package/assets/skills/oat-project-next/SKILL.md +50 -7
- package/assets/skills/oat-project-plan/SKILL.md +4 -4
- package/assets/skills/oat-project-plan-writing/SKILL.md +4 -4
- package/assets/skills/oat-project-pr-final/SKILL.md +11 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +2 -2
- package/assets/skills/oat-project-quick-start/SKILL.md +12 -8
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-retro/SKILL.md +6 -3
- package/assets/skills/oat-project-review-provide/SKILL.md +2 -2
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +2 -2
- package/assets/skills/oat-project-spec/SKILL.md +3 -3
- package/assets/skills/oat-project-summary/SKILL.md +22 -3
- package/assets/skills/oat-repo-improve/SKILL.md +2 -2
- package/assets/skills/recon/SKILL.md +24 -3
- package/assets/skills/recon/references/packet-contract.md +36 -6
- package/assets/skills/recon/references/profiles.md +3 -0
- package/assets/skills/recon/references/worker-contract.md +16 -3
- package/assets/skills/recon/scripts/create-review-brief.mjs +17 -86
- package/assets/skills/recon/scripts/lib/contracts.mjs +158 -41
- package/assets/skills/recon/scripts/lib/review-binding.mjs +123 -0
- package/assets/skills/recon/scripts/lib/routing.mjs +5 -2
- package/assets/skills/recon/scripts/reconcile-ledger.mjs +18 -7
- package/assets/skills/recon/scripts/render-packet.mjs +93 -1
- package/assets/skills/recon/scripts/validate-packet.mjs +119 -88
- package/assets/skills/subagent-orchestration/SKILL.md +1 -1
- package/assets/skills/subagent-orchestration/references/evidence-and-refresh.md +20 -8
- package/assets/skills/subagent-orchestration/references/provider-claude.md +25 -12
- package/assets/skills/subagent-orchestration/references/provider-codex.md +27 -18
- package/assets/skills/subagent-orchestration/references/provider-cursor.md +40 -10
- package/assets/templates/state.md +1 -0
- package/dist/commands/backlog/new.d.ts +2 -2
- package/dist/commands/backlog/new.d.ts.map +1 -1
- package/dist/commands/backlog/new.js +2 -2
- package/dist/commands/decision/new.d.ts +2 -2
- package/dist/commands/decision/new.d.ts.map +1 -1
- package/dist/commands/decision/new.js +2 -2
- package/dist/commands/docs/nav/contents.d.ts +9 -0
- package/dist/commands/docs/nav/contents.d.ts.map +1 -1
- package/dist/commands/docs/nav/contents.js +7 -2
- package/dist/commands/docs/nav/fumadocs.d.ts +51 -0
- package/dist/commands/docs/nav/fumadocs.d.ts.map +1 -0
- package/dist/commands/docs/nav/fumadocs.js +301 -0
- package/dist/commands/docs/nav/sync.d.ts +31 -1
- package/dist/commands/docs/nav/sync.d.ts.map +1 -1
- package/dist/commands/docs/nav/sync.js +163 -6
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +2 -0
- package/dist/commands/instructions/instructions.utils.d.ts +19 -0
- package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.utils.js +89 -17
- package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
- package/dist/commands/instructions/sync/sync.js +49 -2
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +2 -2
- package/dist/commands/project/closeout-check/closeout-invariant.d.ts +97 -0
- package/dist/commands/project/closeout-check/closeout-invariant.d.ts.map +1 -0
- package/dist/commands/project/closeout-check/closeout-invariant.js +301 -0
- package/dist/commands/project/closeout-check/index.d.ts +38 -0
- package/dist/commands/project/closeout-check/index.d.ts.map +1 -0
- package/dist/commands/project/closeout-check/index.js +117 -0
- package/dist/commands/project/complete-state/index.d.ts +4 -0
- package/dist/commands/project/complete-state/index.d.ts.map +1 -1
- package/dist/commands/project/complete-state/index.js +11 -13
- package/dist/commands/project/dispatch/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch/index.js +6 -30
- package/dist/commands/project/dispatch/record.d.ts +9 -18
- package/dist/commands/project/dispatch/record.d.ts.map +1 -1
- package/dist/commands/project/dispatch/record.js +15 -212
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +2 -0
- package/dist/commands/project/new/scaffold.d.ts +0 -1
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +19 -20
- package/dist/commands/project/promote/promote.d.ts +2 -2
- package/dist/commands/project/promote/promote.d.ts.map +1 -1
- package/dist/commands/project/promote/promote.js +13 -11
- package/dist/commands/shared/frontmatter.d.ts +21 -0
- package/dist/commands/shared/frontmatter.d.ts.map +1 -1
- package/dist/commands/shared/frontmatter.js +70 -4
- package/dist/commands/shared/template-source.d.ts +29 -0
- package/dist/commands/shared/template-source.d.ts.map +1 -0
- package/dist/commands/{pjm → shared}/template-source.js +24 -7
- package/dist/commands/state/generate.js +2 -2
- package/dist/commands/template/index.d.ts +10 -0
- package/dist/commands/template/index.d.ts.map +1 -0
- package/dist/commands/template/index.js +87 -0
- package/dist/commands/template/resolve.d.ts +31 -0
- package/dist/commands/template/resolve.d.ts.map +1 -0
- package/dist/commands/template/resolve.js +86 -0
- package/dist/fs/io.d.ts +0 -41
- package/dist/fs/io.d.ts.map +1 -1
- package/dist/fs/io.js +2 -414
- package/dist/providers/claude/dispatch-envelope.d.ts.map +1 -1
- package/dist/providers/claude/dispatch-envelope.js +1 -0
- package/dist/providers/claude/targets.d.ts +1 -0
- package/dist/providers/claude/targets.d.ts.map +1 -1
- package/dist/providers/claude/targets.js +3 -1
- package/dist/providers/codex/codec/shared.d.ts +3 -0
- package/dist/providers/codex/codec/shared.d.ts.map +1 -1
- package/dist/providers/codex/codec/shared.js +4 -0
- package/dist/providers/cursor/codec/catalog.d.ts +2 -2
- package/dist/providers/cursor/codec/catalog.d.ts.map +1 -1
- package/dist/providers/cursor/codec/catalog.js +45 -0
- package/dist/providers/cursor/codec/mapping-validation.d.ts +4 -0
- package/dist/providers/cursor/codec/mapping-validation.d.ts.map +1 -0
- package/dist/providers/cursor/codec/mapping-validation.js +25 -0
- package/dist/providers/cursor/codec/materialize.d.ts.map +1 -1
- package/dist/providers/cursor/codec/materialize.js +2 -11
- package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/cursor/codec/sync-extension.js +2 -11
- package/dist/providers/identity/absolute-paths.d.ts +4 -13
- package/dist/providers/identity/absolute-paths.d.ts.map +1 -1
- package/dist/providers/identity/absolute-paths.js +4 -17
- package/dist/providers/identity/generic-dispatch-record.d.ts +1 -1
- package/dist/providers/identity/generic-dispatch-record.js +2 -2
- package/dist/providers/identity/oat-dispatch-record.d.ts +4 -357
- package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -1
- package/dist/providers/identity/oat-dispatch-record.js +4 -236
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +12 -4
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +32 -6
- package/package.json +2 -2
- package/dist/commands/pjm/template-source.d.ts +0 -14
- package/dist/commands/pjm/template-source.d.ts.map +0 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, extname, join, posix } from 'node:path';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
+
import { getFrontmatterBlock, parseFrontmatterScalarFields, } from '../../shared/frontmatter.js';
|
|
5
|
+
import { fileExists } from '../../../fs/io.js';
|
|
6
|
+
import { parseIndexContents, resolveEntryTarget } from './contents.js';
|
|
7
|
+
const PAGE_EXTENSIONS = ['.md', '.mdx'];
|
|
8
|
+
const GROUP_FOLDER = /^\(.+\)$/;
|
|
9
|
+
const H1 = /^#\s+(.+?)\s*#*\s*$/;
|
|
10
|
+
const FENCE = /^\s*(```|~~~)/;
|
|
11
|
+
function parentDir(path) {
|
|
12
|
+
return posix.dirname(path);
|
|
13
|
+
}
|
|
14
|
+
async function inventoryDocs(docsRoot) {
|
|
15
|
+
const directories = [];
|
|
16
|
+
const pages = [];
|
|
17
|
+
async function walk(relativeDir) {
|
|
18
|
+
directories.push(relativeDir);
|
|
19
|
+
const entries = await readdir(join(docsRoot, relativeDir), {
|
|
20
|
+
withFileTypes: true,
|
|
21
|
+
});
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const entryPath = posix.join(relativeDir, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
await walk(entryPath);
|
|
29
|
+
}
|
|
30
|
+
else if (entry.isFile() &&
|
|
31
|
+
PAGE_EXTENSIONS.includes(extname(entry.name))) {
|
|
32
|
+
pages.push(entryPath);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
await walk('.');
|
|
37
|
+
directories.sort();
|
|
38
|
+
pages.sort();
|
|
39
|
+
return { directories, pages };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reduce a Markdown heading to the plain text a sidebar shows: images and
|
|
43
|
+
* links keep their text, code spans keep their content, and emphasis and
|
|
44
|
+
* strikethrough markers are dropped.
|
|
45
|
+
*/
|
|
46
|
+
export function stripInlineMarkdown(text) {
|
|
47
|
+
return text
|
|
48
|
+
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
49
|
+
.replace(/(`+)(.+?)\1/g, '$2')
|
|
50
|
+
.replace(/(\*\*|__|~~)(.+?)\1/g, '$2')
|
|
51
|
+
.replace(/(^|[^\w*])[*_]([^*_]+?)[*_](?=[^\w*]|$)/g, '$1$2')
|
|
52
|
+
.replace(/\s+/g, ' ')
|
|
53
|
+
.trim();
|
|
54
|
+
}
|
|
55
|
+
/** Folder title: frontmatter `title`, else the first H1 outside code fences. */
|
|
56
|
+
export function resolveFolderTitle(markdown) {
|
|
57
|
+
const frontmatter = getFrontmatterBlock(markdown);
|
|
58
|
+
if (frontmatter !== null) {
|
|
59
|
+
const title = parseFrontmatterScalarFields(frontmatter, ['title']).values
|
|
60
|
+
.title;
|
|
61
|
+
if (title) {
|
|
62
|
+
return title;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const body = frontmatter === null
|
|
66
|
+
? markdown
|
|
67
|
+
: markdown.slice(markdown.indexOf('\n---', 4) + 4);
|
|
68
|
+
let fence = null;
|
|
69
|
+
for (const line of body.split(/\r?\n/)) {
|
|
70
|
+
const fenceMatch = line.match(FENCE);
|
|
71
|
+
if (fenceMatch) {
|
|
72
|
+
const marker = fenceMatch[1];
|
|
73
|
+
if (fence === null) {
|
|
74
|
+
fence = marker;
|
|
75
|
+
}
|
|
76
|
+
else if (fence === marker) {
|
|
77
|
+
fence = null;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (fence !== null) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const heading = line.match(H1);
|
|
85
|
+
if (heading?.[1]) {
|
|
86
|
+
return stripInlineMarkdown(heading[1]) || undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The URL Fumadocs assigns a page, following `getSlugs` in fumadocs-core's
|
|
93
|
+
* slugs plugin with the scaffolded `baseUrl: '/'`: group folders are dropped,
|
|
94
|
+
* segments are URI-encoded, and an `index` file maps to its folder.
|
|
95
|
+
*/
|
|
96
|
+
function pageUrl(pagePath) {
|
|
97
|
+
const slugs = parentDir(pagePath)
|
|
98
|
+
.split('/')
|
|
99
|
+
.filter((segment) => segment.length > 0 && segment !== '.')
|
|
100
|
+
.filter((segment) => !GROUP_FOLDER.test(segment))
|
|
101
|
+
.map((segment) => encodeURI(segment));
|
|
102
|
+
const name = basename(pagePath, extname(pagePath));
|
|
103
|
+
if (name !== 'index') {
|
|
104
|
+
slugs.push(encodeURI(name));
|
|
105
|
+
}
|
|
106
|
+
return `/${slugs.join('/')}`;
|
|
107
|
+
}
|
|
108
|
+
function hrefFragment(href) {
|
|
109
|
+
const hashIndex = href.indexOf('#');
|
|
110
|
+
return hashIndex >= 0 ? href.slice(hashIndex) : '';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build one folder's `pages` from its index.md Contents map.
|
|
114
|
+
*
|
|
115
|
+
* A page in this folder or an immediate subfolder is referenced by name. Any
|
|
116
|
+
* other target becomes a Fumadocs link entry, so no page is claimed by two
|
|
117
|
+
* folders. There is never a `"..."` rest entry: pages the Contents map does
|
|
118
|
+
* not list stay out of the tree and are reported instead.
|
|
119
|
+
*/
|
|
120
|
+
/**
|
|
121
|
+
* Whether the fumadocs-core 16.10.2 page-tree builder supplies this folder's
|
|
122
|
+
* `index` page on its own, given the folder's effective metadata.
|
|
123
|
+
*
|
|
124
|
+
* `buildFolder` attaches `<folder>/index` as the folder index only when the
|
|
125
|
+
* folder is not a root folder (the docs root, or a preserved `root: true`)
|
|
126
|
+
* and no `pagesIndex` names a different page. Otherwise the page is shown
|
|
127
|
+
* only if `pages` lists it.
|
|
128
|
+
*/
|
|
129
|
+
function hasImplicitIndex(relativeDir, existing) {
|
|
130
|
+
if (relativeDir === '.' || existing?.root === true) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const pagesIndex = existing?.pagesIndex;
|
|
134
|
+
return pagesIndex === undefined || pagesIndex === 'index';
|
|
135
|
+
}
|
|
136
|
+
async function buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex) {
|
|
137
|
+
const indexPath = join(docsRoot, relativeDir, 'index.md');
|
|
138
|
+
const entries = parseIndexContents(markdown, indexPath);
|
|
139
|
+
// A folder the loader gives no implicit index (the docs root, a `root: true`
|
|
140
|
+
// folder) must list `index` to show its landing page. Elsewhere, listing
|
|
141
|
+
// `index` would demote the folder index to an ordinary child, so it is left
|
|
142
|
+
// implicit.
|
|
143
|
+
const pages = implicitIndex ? [] : ['index'];
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
const target = await resolveEntryTarget(docsRoot, join(docsRoot, relativeDir), relativeDir, entry, PAGE_EXTENSIONS);
|
|
146
|
+
const targetDir = parentDir(target.path);
|
|
147
|
+
let item;
|
|
148
|
+
if (target.kind === 'section' && targetDir === relativeDir) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
else if (target.kind === 'section' &&
|
|
152
|
+
parentDir(targetDir) === relativeDir) {
|
|
153
|
+
item = basename(targetDir);
|
|
154
|
+
}
|
|
155
|
+
else if (target.kind === 'page' && targetDir === relativeDir) {
|
|
156
|
+
item = basename(target.path, extname(target.path));
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
item = `[${entry.title}](${pageUrl(target.path)}${hrefFragment(entry.href)})`;
|
|
160
|
+
}
|
|
161
|
+
if (!pages.includes(item)) {
|
|
162
|
+
pages.push(item);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return pages;
|
|
166
|
+
}
|
|
167
|
+
function isPlainObject(value) {
|
|
168
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
169
|
+
}
|
|
170
|
+
async function readExistingMeta(metaPath) {
|
|
171
|
+
if (!(await fileExists(metaPath))) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const parsed = JSON.parse(await readFile(metaPath, 'utf8'));
|
|
176
|
+
return isPlainObject(parsed) ? parsed : null;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// A generated file that no longer parses (for example after a merge
|
|
180
|
+
// conflict) is regenerated rather than preserved.
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function mergeMeta(existing, meta) {
|
|
185
|
+
const { title: _title, pages: _pages, ...unowned } = existing ?? {};
|
|
186
|
+
return {
|
|
187
|
+
...unowned,
|
|
188
|
+
...(meta.title === undefined ? {} : { title: meta.title }),
|
|
189
|
+
pages: meta.pages,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function collectUnlisted(inventory, metas, implicitIndexDirs) {
|
|
193
|
+
const reachableDirs = new Set();
|
|
194
|
+
const reachablePages = new Set();
|
|
195
|
+
const pagesByFlattenPath = new Map(inventory.pages.map((page) => [
|
|
196
|
+
page.slice(0, page.length - extname(page).length),
|
|
197
|
+
page,
|
|
198
|
+
]));
|
|
199
|
+
function visit(relativeDir) {
|
|
200
|
+
reachableDirs.add(relativeDir);
|
|
201
|
+
const meta = metas.get(relativeDir);
|
|
202
|
+
if (!meta) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// A landing page is reachable only when the loader attaches it as the
|
|
206
|
+
// folder index or `pages` lists it, never just because the file exists.
|
|
207
|
+
const index = pagesByFlattenPath.get(posix.join(relativeDir, 'index'));
|
|
208
|
+
if (index && implicitIndexDirs.has(relativeDir)) {
|
|
209
|
+
reachablePages.add(index);
|
|
210
|
+
}
|
|
211
|
+
for (const item of meta.pages) {
|
|
212
|
+
if (item.startsWith('[')) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const itemPath = posix.join(relativeDir, item);
|
|
216
|
+
if (metas.has(itemPath) && !reachableDirs.has(itemPath)) {
|
|
217
|
+
visit(itemPath);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const page = pagesByFlattenPath.get(itemPath);
|
|
221
|
+
if (page) {
|
|
222
|
+
reachablePages.add(page);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
visit('.');
|
|
227
|
+
const unlisted = new Set();
|
|
228
|
+
for (const page of inventory.pages) {
|
|
229
|
+
if (reachablePages.has(page)) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
let topmostHiddenDir = null;
|
|
233
|
+
for (let dir = parentDir(page); dir !== '.' && dir.length > 0; dir = parentDir(dir)) {
|
|
234
|
+
if (!reachableDirs.has(dir)) {
|
|
235
|
+
topmostHiddenDir = dir;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
unlisted.add(topmostHiddenDir === null ? page : `${topmostHiddenDir}/`);
|
|
239
|
+
}
|
|
240
|
+
return [...unlisted].sort();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Write one strict `meta.json` per docs directory that has an index.md,
|
|
244
|
+
* derived from that index's Contents map, and report every page the
|
|
245
|
+
* resulting page tree leaves out.
|
|
246
|
+
*
|
|
247
|
+
* Existing files are compared by meaning (parsed and deep-equal), so a run
|
|
248
|
+
* with no doc changes writes nothing even after a formatter rewrites them.
|
|
249
|
+
*/
|
|
250
|
+
export async function syncFumadocsNavigation(options) {
|
|
251
|
+
const { docsRoot } = options;
|
|
252
|
+
const inventory = await inventoryDocs(docsRoot);
|
|
253
|
+
const metas = new Map();
|
|
254
|
+
const existingMetas = new Map();
|
|
255
|
+
const implicitIndexDirs = new Set();
|
|
256
|
+
for (const relativeDir of inventory.directories) {
|
|
257
|
+
const indexPath = join(docsRoot, relativeDir, 'index.md');
|
|
258
|
+
if (!(await fileExists(indexPath))) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
// Index handling depends on the effective metadata, which keeps the
|
|
262
|
+
// author-owned `root` and `pagesIndex` keys of an existing meta.json.
|
|
263
|
+
const existing = await readExistingMeta(join(docsRoot, relativeDir, 'meta.json'));
|
|
264
|
+
existingMetas.set(relativeDir, existing);
|
|
265
|
+
const implicitIndex = hasImplicitIndex(relativeDir, existing);
|
|
266
|
+
if (implicitIndex) {
|
|
267
|
+
implicitIndexDirs.add(relativeDir);
|
|
268
|
+
}
|
|
269
|
+
const markdown = await readFile(indexPath, 'utf8');
|
|
270
|
+
const title = resolveFolderTitle(markdown);
|
|
271
|
+
metas.set(relativeDir, {
|
|
272
|
+
...(title === undefined ? {} : { title }),
|
|
273
|
+
pages: await buildFolderPages(docsRoot, relativeDir, markdown, implicitIndex),
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (!metas.has('.')) {
|
|
277
|
+
throw new Error(`Missing required index.md at ${join(docsRoot, 'index.md')}`);
|
|
278
|
+
}
|
|
279
|
+
const metaFiles = [];
|
|
280
|
+
for (const [relativeDir, meta] of metas) {
|
|
281
|
+
const relativePath = posix.join(relativeDir, 'meta.json');
|
|
282
|
+
const metaPath = join(docsRoot, relativePath);
|
|
283
|
+
const existing = existingMetas.get(relativeDir) ?? null;
|
|
284
|
+
const next = mergeMeta(existing, meta);
|
|
285
|
+
const changed = existing === null || !isDeepStrictEqual(existing, next);
|
|
286
|
+
if (changed && !options.check) {
|
|
287
|
+
await writeFile(metaPath, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
|
|
288
|
+
}
|
|
289
|
+
metaFiles.push({ path: relativePath, meta, changed });
|
|
290
|
+
}
|
|
291
|
+
metaFiles.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
292
|
+
const stale = metaFiles
|
|
293
|
+
.filter((file) => file.changed)
|
|
294
|
+
.map((file) => file.path);
|
|
295
|
+
return {
|
|
296
|
+
metaFiles,
|
|
297
|
+
written: options.check ? [] : stale,
|
|
298
|
+
stale,
|
|
299
|
+
unlisted: collectUnlisted(inventory, metas, implicitIndexDirs),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { type DocsNavTree } from './contents.js';
|
|
4
|
+
import { type FumadocsMetaFile } from './fumadocs.js';
|
|
5
|
+
export type DocsNavFramework = 'fumadocs' | 'mkdocs';
|
|
4
6
|
interface SyncDocsNavigationOptions {
|
|
5
7
|
appRoot: string;
|
|
8
|
+
/**
|
|
9
|
+
* `documentation.tooling` from `.oat/config.json` when the app is the
|
|
10
|
+
* configured docs root. Used only when no framework marker file is found.
|
|
11
|
+
*/
|
|
12
|
+
configuredTooling?: string | null;
|
|
13
|
+
/** Compute the navigation and compare it with the files; write nothing. */
|
|
14
|
+
check?: boolean;
|
|
6
15
|
}
|
|
7
|
-
interface
|
|
16
|
+
interface MkDocsNavigationResult {
|
|
17
|
+
framework: 'mkdocs';
|
|
8
18
|
appRoot: string;
|
|
9
19
|
docsRoot: string;
|
|
10
20
|
mkdocsPath: string;
|
|
11
21
|
nav: DocsNavTree;
|
|
22
|
+
/** `['mkdocs.yml']` when its `nav:` block differed before this run. */
|
|
23
|
+
stale: string[];
|
|
12
24
|
}
|
|
25
|
+
interface FumadocsNavigationResult {
|
|
26
|
+
framework: 'fumadocs';
|
|
27
|
+
appRoot: string;
|
|
28
|
+
docsRoot: string;
|
|
29
|
+
metaFiles: FumadocsMetaFile[];
|
|
30
|
+
written: string[];
|
|
31
|
+
stale: string[];
|
|
32
|
+
unlisted: string[];
|
|
33
|
+
}
|
|
34
|
+
type SyncDocsNavigationResult = MkDocsNavigationResult | FumadocsNavigationResult;
|
|
13
35
|
interface DocsNavSyncDependencies {
|
|
14
36
|
buildCommandContext: (options: GlobalOptions) => CommandContext;
|
|
37
|
+
resolveConfiguredTooling: (cwd: string, appRoot: string) => Promise<string | null>;
|
|
15
38
|
syncDocsNavigation: (options: SyncDocsNavigationOptions) => Promise<SyncDocsNavigationResult>;
|
|
16
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Detect the docs framework from the app's own files: `mkdocs.yml` keeps the
|
|
42
|
+
* MkDocs path (checked first, so existing MkDocs apps behave as before) and a
|
|
43
|
+
* Fumadocs `source.config.*` selects `meta.json` output. Without either
|
|
44
|
+
* marker, a configured `documentation.tooling` decides.
|
|
45
|
+
*/
|
|
46
|
+
export declare function detectDocsNavFramework(appRoot: string, configuredTooling?: string | null): Promise<DocsNavFramework>;
|
|
17
47
|
export declare function syncDocsNavigation(options: SyncDocsNavigationOptions): Promise<SyncDocsNavigationResult>;
|
|
18
48
|
export declare function createDocsNavSyncCommand(overrides?: Partial<DocsNavSyncDependencies>): Command;
|
|
19
49
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/nav/sync.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/nav/sync.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAOrD,UAAU,yBAAyB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,sBAAsB;IAC9B,SAAS,EAAE,QAAQ,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,WAAW,CAAC;IACjB,uEAAuE;IACvE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,UAAU,wBAAwB;IAChC,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,KAAK,wBAAwB,GACzB,sBAAsB,GACtB,wBAAwB,CAAC;AAE7B,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,wBAAwB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,kBAAkB,EAAE,CAClB,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACxC;AAsBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAe3B;AAqDD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CA0CnC;AAmJD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CA4BT"}
|
|
@@ -2,11 +2,65 @@ import { readFile, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
4
4
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
5
|
+
import { readOatConfig } from '../../../config/oat-config.js';
|
|
6
|
+
import { fileExists } from '../../../fs/io.js';
|
|
7
|
+
import { resolveProjectRoot } from '../../../fs/paths.js';
|
|
5
8
|
import { Command, Option } from 'commander';
|
|
6
9
|
import YAML from 'yaml';
|
|
7
10
|
import { buildDocsNavTree } from './contents.js';
|
|
11
|
+
import { syncFumadocsNavigation } from './fumadocs.js';
|
|
12
|
+
const MKDOCS_MARKERS = ['mkdocs.yml'];
|
|
13
|
+
const FUMADOCS_MARKERS = [
|
|
14
|
+
'source.config.ts',
|
|
15
|
+
'source.config.mts',
|
|
16
|
+
'source.config.js',
|
|
17
|
+
'source.config.mjs',
|
|
18
|
+
];
|
|
19
|
+
async function hasAnyMarker(appRoot, markers) {
|
|
20
|
+
for (const marker of markers) {
|
|
21
|
+
if (await fileExists(join(appRoot, marker))) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Detect the docs framework from the app's own files: `mkdocs.yml` keeps the
|
|
29
|
+
* MkDocs path (checked first, so existing MkDocs apps behave as before) and a
|
|
30
|
+
* Fumadocs `source.config.*` selects `meta.json` output. Without either
|
|
31
|
+
* marker, a configured `documentation.tooling` decides.
|
|
32
|
+
*/
|
|
33
|
+
export async function detectDocsNavFramework(appRoot, configuredTooling) {
|
|
34
|
+
if (await hasAnyMarker(appRoot, MKDOCS_MARKERS)) {
|
|
35
|
+
return 'mkdocs';
|
|
36
|
+
}
|
|
37
|
+
if (await hasAnyMarker(appRoot, FUMADOCS_MARKERS)) {
|
|
38
|
+
return 'fumadocs';
|
|
39
|
+
}
|
|
40
|
+
const tooling = configuredTooling?.trim().toLowerCase();
|
|
41
|
+
if (tooling === 'mkdocs' || tooling === 'fumadocs') {
|
|
42
|
+
return tooling;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Could not detect the docs framework in ${appRoot}: expected mkdocs.yml (MkDocs) ` +
|
|
45
|
+
'or source.config.ts (Fumadocs).');
|
|
46
|
+
}
|
|
47
|
+
async function resolveConfiguredTooling(cwd, appRoot) {
|
|
48
|
+
try {
|
|
49
|
+
const repoRoot = await resolveProjectRoot(cwd);
|
|
50
|
+
const config = await readOatConfig(repoRoot);
|
|
51
|
+
const root = config.documentation?.root?.trim();
|
|
52
|
+
if (!root || resolve(repoRoot, root) !== appRoot) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return config.documentation?.tooling?.trim() || null;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
8
61
|
const DEFAULT_DEPENDENCIES = {
|
|
9
62
|
buildCommandContext,
|
|
63
|
+
resolveConfiguredTooling,
|
|
10
64
|
syncDocsNavigation,
|
|
11
65
|
};
|
|
12
66
|
function replaceTopLevelYamlSection(source, key, replacement) {
|
|
@@ -30,27 +84,129 @@ function replaceTopLevelYamlSection(source, key, replacement) {
|
|
|
30
84
|
].join('\n');
|
|
31
85
|
}
|
|
32
86
|
export async function syncDocsNavigation(options) {
|
|
33
|
-
const mkdocsPath = join(options.appRoot, 'mkdocs.yml');
|
|
34
87
|
const docsRoot = join(options.appRoot, 'docs');
|
|
88
|
+
const framework = await detectDocsNavFramework(options.appRoot, options.configuredTooling);
|
|
89
|
+
if (framework === 'fumadocs') {
|
|
90
|
+
const result = await syncFumadocsNavigation({
|
|
91
|
+
docsRoot,
|
|
92
|
+
check: options.check,
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
framework,
|
|
96
|
+
appRoot: options.appRoot,
|
|
97
|
+
docsRoot,
|
|
98
|
+
...result,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const mkdocsPath = join(options.appRoot, 'mkdocs.yml');
|
|
35
102
|
const nav = await buildDocsNavTree({ docsRoot });
|
|
36
103
|
const mkdocsSource = await readFile(mkdocsPath, 'utf8');
|
|
37
104
|
const navSection = YAML.stringify({ nav }).trimEnd();
|
|
38
105
|
const updatedMkdocsSource = replaceTopLevelYamlSection(mkdocsSource, 'nav', navSection);
|
|
39
|
-
|
|
106
|
+
const nextMkdocsSource = `${updatedMkdocsSource.trimEnd()}\n`;
|
|
107
|
+
if (!options.check) {
|
|
108
|
+
await writeFile(mkdocsPath, nextMkdocsSource, 'utf8');
|
|
109
|
+
}
|
|
40
110
|
return {
|
|
111
|
+
framework,
|
|
41
112
|
appRoot: options.appRoot,
|
|
42
113
|
docsRoot,
|
|
43
114
|
mkdocsPath,
|
|
44
115
|
nav,
|
|
116
|
+
stale: nextMkdocsSource === mkdocsSource ? [] : ['mkdocs.yml'],
|
|
45
117
|
};
|
|
46
118
|
}
|
|
119
|
+
function reportFumadocsResult(context, targetDir, result) {
|
|
120
|
+
if (context.json) {
|
|
121
|
+
context.logger.json({
|
|
122
|
+
status: 'ok',
|
|
123
|
+
framework: result.framework,
|
|
124
|
+
appRoot: result.appRoot,
|
|
125
|
+
docsRoot: result.docsRoot,
|
|
126
|
+
metaFiles: result.metaFiles,
|
|
127
|
+
written: result.written,
|
|
128
|
+
unlisted: result.unlisted,
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
context.logger.info(`Synced docs navigation in ${targetDir}`);
|
|
133
|
+
context.logger.info(' Framework: Fumadocs (meta.json)');
|
|
134
|
+
context.logger.info(` Docs root: ${result.docsRoot}`);
|
|
135
|
+
if (result.written.length === 0) {
|
|
136
|
+
context.logger.info(' No meta.json changes');
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
context.logger.info(` Wrote ${result.written.length} meta.json file(s):`);
|
|
140
|
+
for (const path of result.written) {
|
|
141
|
+
context.logger.info(` ${path}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (result.unlisted.length > 0) {
|
|
145
|
+
context.logger.warn(` ${result.unlisted.length} path(s) are not listed in any index.md Contents map ` +
|
|
146
|
+
'and stay out of the navigation:');
|
|
147
|
+
for (const path of result.unlisted) {
|
|
148
|
+
context.logger.warn(` ${path}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* `--check`: report drift without writing. Fails (exit 1) when any generated
|
|
154
|
+
* navigation file would change or, for Fumadocs, any page or folder is
|
|
155
|
+
* unlisted, since strict navigation hides it from the sidebar.
|
|
156
|
+
*/
|
|
157
|
+
function reportCheckResult(context, targetDir, result) {
|
|
158
|
+
const unlisted = result.framework === 'fumadocs' ? result.unlisted : [];
|
|
159
|
+
const current = result.stale.length === 0 && unlisted.length === 0;
|
|
160
|
+
if (context.json) {
|
|
161
|
+
context.logger.json({
|
|
162
|
+
status: current ? 'ok' : 'drift',
|
|
163
|
+
check: true,
|
|
164
|
+
framework: result.framework,
|
|
165
|
+
appRoot: result.appRoot,
|
|
166
|
+
docsRoot: result.docsRoot,
|
|
167
|
+
stale: result.stale,
|
|
168
|
+
unlisted,
|
|
169
|
+
});
|
|
170
|
+
return current;
|
|
171
|
+
}
|
|
172
|
+
if (current) {
|
|
173
|
+
context.logger.info(`Docs navigation is up to date in ${targetDir}`);
|
|
174
|
+
return current;
|
|
175
|
+
}
|
|
176
|
+
context.logger.error(`Docs navigation is out of date in ${targetDir}`);
|
|
177
|
+
if (result.stale.length > 0) {
|
|
178
|
+
context.logger.error(` ${result.stale.length} generated file(s) differ from the index.md Contents maps:`);
|
|
179
|
+
for (const path of result.stale) {
|
|
180
|
+
context.logger.error(` ${path}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (unlisted.length > 0) {
|
|
184
|
+
context.logger.error(` ${unlisted.length} path(s) are not listed in any index.md Contents map ` +
|
|
185
|
+
'and would be hidden from the sidebar:');
|
|
186
|
+
for (const path of unlisted) {
|
|
187
|
+
context.logger.error(` ${path}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
context.logger.error(` List every page in its directory's ## Contents, then run \`oat docs nav sync --target-dir ${targetDir}\`.`);
|
|
191
|
+
return current;
|
|
192
|
+
}
|
|
47
193
|
async function runDocsNavSyncCommand(context, options, dependencies) {
|
|
48
194
|
try {
|
|
49
195
|
const targetDir = options.targetDir ?? '.';
|
|
196
|
+
const appRoot = resolve(context.cwd, targetDir);
|
|
50
197
|
const result = await dependencies.syncDocsNavigation({
|
|
51
|
-
appRoot
|
|
198
|
+
appRoot,
|
|
199
|
+
configuredTooling: await dependencies.resolveConfiguredTooling(context.cwd, appRoot),
|
|
200
|
+
check: options.check === true,
|
|
52
201
|
});
|
|
53
|
-
if (
|
|
202
|
+
if (options.check) {
|
|
203
|
+
process.exitCode = reportCheckResult(context, targetDir, result) ? 0 : 1;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (result.framework === 'fumadocs') {
|
|
207
|
+
reportFumadocsResult(context, targetDir, result);
|
|
208
|
+
}
|
|
209
|
+
else if (context.json) {
|
|
54
210
|
context.logger.json({
|
|
55
211
|
status: 'ok',
|
|
56
212
|
appRoot: result.appRoot,
|
|
@@ -83,8 +239,9 @@ export function createDocsNavSyncCommand(overrides = {}) {
|
|
|
83
239
|
...overrides,
|
|
84
240
|
};
|
|
85
241
|
return new Command('sync')
|
|
86
|
-
.description('Regenerate docs navigation from index.md contents')
|
|
87
|
-
.addOption(new Option('--target-dir <path>', 'Docs app directory containing mkdocs.yml'))
|
|
242
|
+
.description('Regenerate docs navigation from index.md contents: mkdocs.yml nav (MkDocs) or strict meta.json files (Fumadocs)')
|
|
243
|
+
.addOption(new Option('--target-dir <path>', 'Docs app directory containing mkdocs.yml or source.config.ts'))
|
|
244
|
+
.addOption(new Option('--check', 'Write nothing; exit 1 if any navigation file is stale or any page is unlisted'))
|
|
88
245
|
.action(async (options, command) => {
|
|
89
246
|
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
90
247
|
await runDocsNavSyncCommand(context, options, dependencies);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2BvD"}
|
package/dist/commands/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { createReviewCommand } from './review/index.js';
|
|
|
20
20
|
import { createStateCommand } from './state/index.js';
|
|
21
21
|
import { createStatusCommand } from './status/index.js';
|
|
22
22
|
import { createSyncCommand } from './sync/index.js';
|
|
23
|
+
import { createTemplateCommand } from './template/index.js';
|
|
23
24
|
import { createToolsCommand } from './tools/index.js';
|
|
24
25
|
export function registerCommands(program) {
|
|
25
26
|
program.addCommand(createBacklogCommand());
|
|
@@ -42,6 +43,7 @@ export function registerCommands(program) {
|
|
|
42
43
|
program.addCommand(createPjmCommand());
|
|
43
44
|
program.addCommand(createProjectCommand());
|
|
44
45
|
program.addCommand(createStateCommand());
|
|
46
|
+
program.addCommand(createTemplateCommand());
|
|
45
47
|
program.addCommand(createToolsCommand());
|
|
46
48
|
program.addCommand(createInternalCommand());
|
|
47
49
|
// Commander's .addCommand() does not inherit help config from the parent.
|
|
@@ -43,6 +43,23 @@ export type ManagedShimInspection = {
|
|
|
43
43
|
* and any regular file whose bytes are not exactly a pointer or an exact copy.
|
|
44
44
|
*/
|
|
45
45
|
export declare function inspectManagedShim(claudePath: string, agentsPath: string, dependencies: ManagedShimInspectionDependencies): Promise<ManagedShimInspection>;
|
|
46
|
+
/**
|
|
47
|
+
* The AGENTS.md files among `agentsPaths` that resolve to `claudePath`, the
|
|
48
|
+
* check that keeps `instructions sync --force` from overwriting the only copy
|
|
49
|
+
* of the instructions under a shim strategy. A regular CLAUDE.md is judged as
|
|
50
|
+
* the `none` classification judges it (hard link, or a symlink chain that
|
|
51
|
+
* ends at it); a CLAUDE.md that is itself a symlink counts only when an
|
|
52
|
+
* AGENTS.md chain passes through it, so the ordinary `CLAUDE.md -> AGENTS.md`
|
|
53
|
+
* shim is never mistaken for a link the other way.
|
|
54
|
+
*
|
|
55
|
+
* A missing CLAUDE.md or AGENTS.md links nothing. Any other read failure is
|
|
56
|
+
* returned as the reason the answer is unknown, and the caller keeps the file.
|
|
57
|
+
*/
|
|
58
|
+
export declare function findAgentsResolvingTo(claudePath: string, agentsPaths: readonly string[], dependencies: LinkResolutionDependencies): Promise<{
|
|
59
|
+
linkers: string[];
|
|
60
|
+
} | {
|
|
61
|
+
unreadable: string;
|
|
62
|
+
}>;
|
|
46
63
|
/** The filesystem reads link-chain resolution needs. */
|
|
47
64
|
export interface LinkResolutionDependencies {
|
|
48
65
|
lstat: InstructionsScanDependencies['lstat'];
|
|
@@ -57,6 +74,8 @@ export interface LinkResolutionDependencies {
|
|
|
57
74
|
* they are never reported.
|
|
58
75
|
*/
|
|
59
76
|
export declare function findLinksThrough(targetPath: string, candidates: readonly string[], dependencies: LinkResolutionDependencies): Promise<string[]>;
|
|
77
|
+
/** `a` / `a and b` / `a, b, and c`, repository-relative. */
|
|
78
|
+
export declare function describeLinkers(repoRoot: string, linkers: readonly string[]): string;
|
|
60
79
|
/**
|
|
61
80
|
* Re-verify, immediately before deletion, that a CLAUDE.md planned for removal
|
|
62
81
|
* is still the exact managed shim the scan recorded: same `lstat` identity
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructions.utils.d.ts","sourceRoot":"","sources":["../../../src/commands/instructions/instructions.utils.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,iCAAiC,EAGlC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,gCAAgC,EAChC,4BAA4B,EAC5B,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,4BAA4B,EAC5B,uBAAuB,EACvB,4BAA4B,EAE5B,mBAAmB,EACnB,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AACtD,OAAO,EAAE,iCAAiC,EAAE,CAAC;AAc7C,UAAU,4BAA4B;IACpC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AA+CD,8DAA8D;AAC9D,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAC7C,QAAQ,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACnD,aAAa,EAAE,4BAA4B,CAAC,eAAe,CAAC,CAAC;IAC7D,QAAQ,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,iCAAiC,GAC9C,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"instructions.utils.d.ts","sourceRoot":"","sources":["../../../src/commands/instructions/instructions.utils.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,iCAAiC,EAGlC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,gCAAgC,EAChC,4BAA4B,EAC5B,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,4BAA4B,EAC5B,uBAAuB,EACvB,4BAA4B,EAE5B,mBAAmB,EACnB,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AACtD,OAAO,EAAE,iCAAiC,EAAE,CAAC;AAc7C,UAAU,4BAA4B;IACpC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AA+CD,8DAA8D;AAC9D,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAC7C,QAAQ,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACnD,aAAa,EAAE,4BAA4B,CAAC,eAAe,CAAC,CAAC;IAC7D,QAAQ,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,iCAAiC,GAC9C,OAAO,CAAC,qBAAqB,CAAC,CAyIhC;AA4CD;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,YAAY,EAAE,0BAA0B,GACvC,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAkDzD;AAED,wDAAwD;AACxD,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAC7C,QAAQ,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACnD,QAAQ,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;CACpD;AAoBD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,YAAY,EAAE,0BAA0B,GACvC,OAAO,CAAC,MAAM,EAAE,CAAC,CAwCnB;AAED,4DAA4D;AAC5D,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,CAMR;AAED;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,iBAAiB,EAC1B,YAAY,EAAE,iCAAiC,GAC9C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyBxB;AA6FD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,YAAY,CAAC,EAAE,uBAAuB,EACtC,kBAAkB,CAAC,EAAE,uBAAuB,GAC3C,uBAAuB,CAIzB;AAED;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CAG9C;AA+ED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,GAAE,SAAS,MAAM,EAAO,GACpC,MAAM,EAAE,CAyBV;AA0FD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,CAAC,4BAA4B,CAAM,GACpD,OAAO,CAAC,4BAA4B,CAAC,CAiGvC;AAwJD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,EACrC,SAAS,GAAE,OAAO,CAAC,4BAA4B,CAAM,GACpD,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAqP7B;AAwDD,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,CAChB,IAAI,CACF,4BAA4B,EAC5B,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,eAAe,CAChE,CACG,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAuE/B;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,2IAC+F,CAAC;AAE3I;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,SAAS,EAAE,SAAS,kBAAkB,EAAE,GACvC,kBAAkB,EAAE,CAWtB;AAED,2EAA2E;AAC3E,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC;AAE/E;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAC9D,SAAS,EAAE,SAAS,CAAC,EAAE,EACvB,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,CAAC,EAAE,CAGL;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,gBAAgB,EAAE,GACnC,MAAM,EAAE,CAOV;AAUD,0EAA0E;AAC1E,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GACtC,MAAM,CAQR;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,IAAI,EAAE,gBAAgB,GACrB,gCAAgC,CA8ClC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,gBAAgB,EAAE,GACnC,gBAAgB,EAAE,CASpB;AAcD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,UAAU,CAAC,EAAE,GAClE,4BAA4B,EAAE,CAyBhC;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,OAAO,EAAE,uBAAuB,EAAE,GACjC,mBAAmB,CA2BrB;AAoBD,wBAAgB,wBAAwB,CAAC,EACvC,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,OAAO,EACP,aAAa,EACb,sBAAsB,EACtB,iBAAiB,GAClB,EAAE,4BAA4B,GAAG,uBAAuB,CAqCxD;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAoCR"}
|