@open-agent-toolkit/cli 0.1.26 → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.1.26",
3
- "docs-config": "0.1.26",
4
- "docs-theme": "0.1.26",
5
- "docs-transforms": "0.1.26"
2
+ "cli": "0.1.27",
3
+ "docs-config": "0.1.27",
4
+ "docs-theme": "0.1.27",
5
+ "docs-transforms": "0.1.27"
6
6
  }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: oat-docs-bootstrap
3
- version: 1.1.0
4
- description: Use when bootstrapping a new OAT docs app in a repo. Guides the user through preflight detection, richer input gathering than the raw CLI, `oat docs init` invocation with labeled post-patches for open CLI gaps, build verification, post-scaffold config inspection, and an educational walkthrough. Supports Fumadocs (full path) and MkDocs (lean path with defined minimum contract).
3
+ version: 1.2.0
4
+ description: Use when bootstrapping a new OAT docs app in a repo. Guides the user through preflight detection, richer input gathering than the raw CLI, `oat docs init` invocation with gated post-patches for open CLI gaps, build verification, post-scaffold config inspection, and an educational walkthrough. Supports Fumadocs (full path) and MkDocs (lean path with defined minimum contract).
5
5
  argument-hint: '<optional-target-dir>'
6
6
  disable-model-invocation: true
7
7
  user-invocable: true
@@ -10,7 +10,7 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
10
10
 
11
11
  # Docs Bootstrap
12
12
 
13
- Bootstrap a docs app in this repo and guide the user through understanding how it works. Wraps `oat docs init` with preflight detection, richer input gathering, labeled post-patches for open CLI gaps (FP-11 Turbopack root, FP-12 site-title coherence, FP-13 template content, FP-15 docs-app AGENTS.md, FP-16 `## Contents` link extensions, FP-17 `contributing.md` three-surfaces cleanup), build verification, post-scaffold config inspection, and an educational walkthrough covering the `index.md` + `## Contents` navigation contract, scaffolded agent-instruction surfaces, and the OAT docs ecosystem (`oat-project-document`, `oat-docs-analyze`, `oat-docs-apply`).
13
+ Bootstrap a docs app in this repo and guide the user through understanding how it works. Wraps `oat docs init` with preflight detection, richer input gathering, gated post-patches for open CLI gaps (FP-11 Turbopack root, FP-12 site-title coherence, FP-13 template content, FP-15 docs-app AGENTS.md, FP-16 `## Contents` link extensions, FP-17 `contributing.md` three-surfaces cleanup), build verification, post-scaffold config inspection, and an educational walkthrough covering the `index.md` + `## Contents` navigation contract, scaffolded agent-instruction surfaces, and the OAT docs ecosystem (`oat-project-document`, `oat-docs-analyze`, `oat-docs-apply`).
14
14
 
15
15
  ## Prerequisites
16
16
 
@@ -22,7 +22,7 @@ Bootstrap a docs app in this repo and guide the user through understanding how i
22
22
 
23
23
  **OAT MODE: Docs Bootstrap**
24
24
 
25
- **Purpose:** Turn `oat docs init` scaffolding into a support-rich onboarding experience — preflight, inputs, CLI invocation with labeled post-patches, build verification, config inspection, educational walkthrough, and optional content kickoff. Two framework paths: Fumadocs (full) and MkDocs (lean with defined minimum contract).
25
+ **Purpose:** Turn `oat docs init` scaffolding into a support-rich onboarding experience — preflight, inputs, CLI invocation with gated post-patches, build verification, config inspection, educational walkthrough, and optional content kickoff. Two framework paths: Fumadocs (full) and MkDocs (lean with defined minimum contract).
26
26
 
27
27
  **BLOCKED Activities:**
28
28
 
@@ -36,7 +36,7 @@ Bootstrap a docs app in this repo and guide the user through understanding how i
36
36
  - Inspecting repo state read-only before any mutation
37
37
  - Prompting the user for richer inputs than the CLI asks for (notably a site name separate from the package/app name)
38
38
  - Invoking `oat docs init` non-interactively with collected flags
39
- - Applying labeled, idempotent post-patches for open CLI gaps (FP-11/FP-12/FP-13/FP-15/FP-16/FP-17) only when capability detection shows the CLI has not addressed them
39
+ - Applying gated, idempotent post-patches for open CLI gaps (FP-11/FP-12/FP-13/FP-15/FP-16/FP-17) only when capability detection shows the CLI has not addressed them
40
40
  - Running install + build and classifying failures against known patterns
41
41
  - Reading `.oat/config.json` back to verify paths and prompting for `requireForProjectCompletion`
42
42
  - Narrating the scaffolded output as a chunked educational walkthrough
@@ -151,7 +151,7 @@ Conflicts are **recorded**, not **resolved**. Resolution happens in Step 2 (Inpu
151
151
  Derive:
152
152
 
153
153
  - `repoName`: `basename($REPO_ROOT)`
154
- - `siteName` default: humanized form of `repoName` (e.g., `cyclone-app` → `Cyclone App`, `vox_mobile` → `Vox Mobile`, `docs` → `Docs`). Do not append " Documentation" here that's a concern of the scaffold, not of the default.
154
+ - `siteName` default: humanized form of `repoName` (e.g., `cyclone-app` → `Cyclone App`, `vox_mobile` → `Vox Mobile`, `docs` → `Docs`). Do not append " Documentation" here; the CLI and skill use the display title verbatim.
155
155
  - `appName` default:
156
156
  - If shape is `monorepo`: `{repoName}-docs` (e.g., `open-agent-toolkit-docs`)
157
157
  - If shape is `single-package` or `nested-standalone`: `docs`
@@ -226,7 +226,7 @@ Before proceeding to scaffold, summarize what the user chose and confirm:
226
226
  Here's what I'll scaffold:
227
227
 
228
228
  Framework: {framework}
229
- Display title: {siteName} Documentation ← shown in header / tab / page headings
229
+ Display title: {siteName} ← shown in header / tab / page headings
230
230
  Package name: {appName} ← used by pnpm filter / directory name
231
231
  Target dir: {targetDir} ← relative to repo root
232
232
  Description: {siteDescription | "(none)"}
@@ -342,7 +342,7 @@ If any mutation fails or any invariant check fails, **stop the flow before scaff
342
342
 
343
343
  ### Step 3: Scaffold Runner
344
344
 
345
- Invoke `oat docs init` with the collected inputs and then apply labeled post-patches for any CLI gaps the Capability Detection sub-procedure reports as still-open. The Scaffold Runner has four phases: **CLI invocation** (authored here), **Capability Detection** (p03-t02), **site-identity patches** (p03-t03), and **scaffold-integrity patches** (p03-t04).
345
+ Invoke `oat docs init` with the collected inputs and then apply gated post-patches for any CLI gaps the Capability Detection sub-procedure reports as still-open. The Scaffold Runner has four phases: **CLI invocation** (authored here), **Capability Detection** (p03-t02), **site-identity patches** (p03-t03), and **scaffold-integrity patches** (p03-t04).
346
346
 
347
347
  Print `[3/7] Scaffolding…` at the start of this step.
348
348
 
@@ -356,7 +356,7 @@ Base flags (always passed):
356
356
 
357
357
  - `--yes` — non-interactive
358
358
  - `--framework {framework}` — from Input Result
359
- - `--name {appName}` — from Input Result
359
+ - `--app-name {appName}` — from Input Result
360
360
  - `--target-dir {targetDir}` — from Input Result
361
361
  - `--description {siteDescription}` — from Input Result (pass empty string if user left blank)
362
362
  - `--lint {lint}` — from Input Result
@@ -365,6 +365,7 @@ Base flags (always passed):
365
365
  Capability-gated flags (added only when the Capability Probe reports support):
366
366
 
367
367
  - `--site-name {siteName}` — only if `capabilities.siteNameFlag === true` (FP-12 upstream fix landed). If `false`, **do not pass it**, and queue the FP-12 post-patch instead (applied in p03-t03).
368
+ - `--no-root-patch` — only if `capabilities.noRootPatchFlag === true` and the Preflight / Conflict Resolution result says root package mutation should be skipped. This is recommended for no-root-package repositories or when the user explicitly chose no root mutation; current CLI builds also auto-skip when no root `package.json` exists, so the flag is optional documentation of intent, not a recovery mechanism.
368
369
 
369
370
  Do not pass flags the CLI doesn't advertise. Never assume a flag exists because the skill wants it — the Capability Probe is the source of truth for what's safe to pass.
370
371
 
@@ -377,6 +378,7 @@ Before CLI invocation, probe the installed CLI to discover which FP-11 / FP-12 /
377
378
  - `siteNameFlag` — `true` if `--site-name` (or equivalent alias like `--title`) appears in the help output. Implies FP-12 upstream fix has landed.
378
379
  - `turbopackRootFlag` — `true` if a flag for forwarding a Turbopack root (or a `--framework-config` passthrough that accepts `turbopack.root`) appears. Implies FP-11 fix has landed in some form.
379
380
  - `agentsMdScaffoldFlag` — `true` if the help output mentions `AGENTS.md` scaffolding or the CLI's scaffold template list includes `AGENTS.md`. Implies FP-15 fix has landed.
381
+ - `noRootPatchFlag` — `true` if `--no-root-patch` appears in help output. If supported, pass it when root package mutation should be skipped rather than relying only on auto-skip behavior.
380
382
 
381
383
  For FP-16, there is no CLI-level help-probe marker; the capability is detected purely by file-shape check on the scaffolded `docs/index.md` (see below). When the CLI scaffold template is fixed upstream, the file-shape check will classify as `patched-shape` and the FP-16 patch will skip automatically.
382
384
 
@@ -387,24 +389,24 @@ If the `--help` invocation fails (non-zero exit or empty stdout), treat all capa
387
389
  For each patch, the classification rule is:
388
390
 
389
391
  - **FP-12 targets** — the patch sets display-title references in four files:
390
- - `<appRoot>/app/layout.tsx` (Fumadocs only): scaffold shape has `DocsLayout` with `branding: { title: '{appName}' }` (the CLI writes the package name); patched shape has `branding: { title: '{siteName}' }` surrounded by `<!-- FP-12 patch -->` markers.
391
- - `<appRoot>/docs/index.md`: scaffold shape has frontmatter `title: '{appName}'` and `# {appName}` H1; patched shape substitutes `{siteName}` in both positions.
392
- - `<appRoot>/docs/getting-started.md`: scaffold shape has a body reference to `{appName}`; patched shape uses `{siteName}`.
393
- - `<appRoot>/docs/contributing.md`: scaffold shape has `# {appName}` H1; patched shape uses `{siteName}`.
394
- - Classification: if the marker string (`appName` where expected in scaffold shape, `siteName` where expected in patched shape) matches exactly, the file is in that shape. If neither pattern matches (e.g., the user already renamed things manually), classify as `drift`.
392
+ - `<appRoot>/app/layout.tsx` (Fumadocs only): scaffold shape has `DocsLayout` with `branding` pointing at `{appName}` and no `export const metadata`; patched/upstream shape has `branding.title` using `{siteName}` and module-scope Next metadata. Code-file patches may use `/* FP-12 */` markers where syntactically valid.
393
+ - `<appRoot>/docs/index.md`: scaffold shape has frontmatter `title: '{appName}'` and `# {appName}` H1; patched/upstream shape substitutes `{siteName}` in both positions. Do not add hidden FP comments to rendered Markdown; use content shape for idempotency.
394
+ - `<appRoot>/docs/getting-started.md`: scaffold shape has a body reference to `{appName}`; patched/upstream shape uses `{siteName}`.
395
+ - `<appRoot>/docs/contributing.md`: scaffold shape has `# {appName}` H1; patched/upstream shape uses `{siteName}`.
396
+ - Classification: if the expected literal (`appName` for scaffold shape, `siteName` for patched/upstream shape) matches exactly, the file is in that shape. If neither pattern matches (e.g., the user already renamed things manually), classify as `drift`.
395
397
  - **FP-11 target** — `<appRoot>/next.config.js` (Fumadocs, nested-standalone only):
396
398
  - Scaffold shape: single-line `export default createDocsConfig()` or near-equivalent with no `turbopack` option.
397
399
  - Patched shape (via passthrough): `createDocsConfig({ turbopack: { root: __dirname } })` plus `<!-- FP-11 patch -->` marker comment.
398
400
  - Patched shape (via wrapper replacement): explicit `createMDX()` + hand-written config with `turbopack: { root: __dirname }` and the `<!-- FP-11 patch -->` marker.
399
401
  - Classification: pattern-match on the wrapper call or the `createMDX()` import; any other shape is `drift`.
400
- - **FP-13 targets** — the five sub-findings (empty descriptions, bare commands, false lint claim, generated-file warning, Node version mismatch) each target a specific line or section (see design.md). Scaffold shape for each is "unchanged CLI scaffold output"; patched shape has the `<!-- FP-13 patch -->` marker comment adjacent to the rewrite. Sub-finding E (Node version) additionally reads `.nvmrc` / `package.json` `engines.node` at scaffold time to determine the correct version to write.
402
+ - **FP-13 targets** — the five sub-findings (empty descriptions, bare commands, false lint claim, old authored-file generated warning, Node version mismatch) each target a specific line or section. Scaffold shape for each is "unchanged historical CLI scaffold output"; patched/upstream shape is the corrected content. Markdown patches do not add hidden FP comments; they classify by content shape. Sub-finding E (Node version) additionally reads `.nvmrc` / `package.json` `engines.node` at scaffold time to determine the correct version to write.
401
403
  - **FP-16 target** — `<appRoot>/docs/index.md` `## Contents` section (Fumadocs only):
402
404
  - Scaffold shape: `## Contents` list items have extension-less link targets, e.g. `- [Getting Started](getting-started) - ...`. Detect by regex-matching bulleted list items where the link target is a slug (no file extension, no trailing slash) that corresponds to a real `.md` file in the same directory (or an `index.md` inside a matching subdirectory).
403
- - Patched shape: `## Contents` list items have `.md`-suffixed link targets (e.g. `- [Getting Started](getting-started.md) - ...`) with a `<!-- FP-16 patch -->` / `<!-- /FP-16 patch -->` marker wrapping the whole `## Contents` block.
405
+ - Patched/upstream shape: `## Contents` list items have `.md`-suffixed link targets (e.g. `- [Getting Started](getting-started.md) - ...`). No hidden FP comments are added to rendered Markdown.
404
406
  - Drift: neither scaffold nor patched shape — likely user-edited with mixed or inconsistent extensions. Record as `refused` with a suggested manual fix ("normalize all `## Contents` links to `.md`-suffixed form so the `docs-transforms` remark-links plugin handles routing").
405
407
  - **FP-17 target** — `<appRoot>/docs/contributing.md` `## Agent guidance` section (Fumadocs only):
406
408
  - Scaffold shape: the section contains a short bulleted list of generic agent advice (typically two bullets: treating `index.md` as truth, preferring links to source files). Detect by matching the `## Agent guidance` heading followed by a small bulleted list.
407
- - Patched shape: the section contains a one-paragraph pointer to the docs-app `AGENTS.md`, wrapped in `<!-- FP-17 patch: three-surfaces cleanup -->` / `<!-- /FP-17 patch -->` markers.
409
+ - Patched/upstream shape: the section contains a one-paragraph pointer to the docs-app `AGENTS.md`. No hidden FP comments are added to rendered Markdown.
408
410
  - Drift: user-edited with content beyond the scaffold bullets (paragraphs, custom agent rules, links to other files). Classify as `refused` — do not collapse user-authored content; instead suggest the user move custom guidance into `<appRoot>/AGENTS.md` and trim `contributing.md` to the pointer form manually.
409
411
  - **FP-15 target** — `<appRoot>/AGENTS.md`:
410
412
  - Scaffold shape: file **does not exist** (current CLI doesn't scaffold it).
@@ -451,7 +453,7 @@ Capture both stdout and stderr. **Do not** stream output directly to the user; t
451
453
  oat docs init exited with code 2.
452
454
 
453
455
  Flags passed:
454
- --yes --framework fumadocs --name docs --target-dir docs
456
+ --yes --framework fumadocs --app-name docs --target-dir docs
455
457
  --description "" --lint none --format oxfmt
456
458
 
457
459
  CLI stderr:
@@ -485,7 +487,7 @@ Each patch:
485
487
 
486
488
  - Reads the relevant capability flag from `Scaffold Result.capabilities` and the file-shape detection from Capability Detection (3b).
487
489
  - Runs only if the capability is **absent** (the CLI didn't already address the gap) AND the file-shape check passes.
488
- - Is **labeled** with a comment marker (e.g., `<!-- FP-12 patch -->`) so it can be found and removed deterministically when the CLI fix lands upstream.
490
+ - Uses a marker comment only in code files where the comment syntax is valid (`/* FP-NN */` in TSX/JS, `// ...` where appropriate). Rendered Markdown patches do **not** add hidden `<!-- FP-NN -->` comments; they rely on content-shape idempotency and drift checks, and they defer to the project's normal comment conventions for user-facing prose.
489
491
  - Is **idempotent** — running the patch twice is a no-op.
490
492
  - Appends an entry to `Scaffold Result.patchesApplied` with `{ id, status: 'applied' | 'skipped' | 'refused', reason? }`.
491
493
 
@@ -506,7 +508,7 @@ Per-file edits (Fumadocs only — MkDocs has no `layout.tsx`; its title handling
506
508
  ```tsx
507
509
  /* FP-12 patch: site metadata */
508
510
  export const metadata = {
509
- title: '{siteName} Documentation',
511
+ title: '{siteName}',
510
512
  description: '{siteDescription}',
511
513
  };
512
514
  /* /FP-12 patch */
@@ -519,24 +521,26 @@ Per-file edits (Fumadocs only — MkDocs has no `layout.tsx`; its title handling
519
521
  - Setting `branding.title` alone and calling it done. `branding` populates nav chrome only; browser tab, search previews, and social cards all read `metadata`.
520
522
  - Inserting `metadata` into a non-layout file (e.g., `page.tsx` under `[[...slug]]`). For site-wide metadata, `layout.tsx` is the authoritative location.
521
523
 
522
- - **`<appRoot>/app/layout.tsx` — `branding.title`.** Scaffold shape has `branding: { title: '{appName}' }`. Replace the literal string value with `{siteName}` (the user-supplied display title) wrapped in FP-12 markers:
524
+ - **`<appRoot>/app/layout.tsx` — `branding.title`.** Scaffold shape has `branding: { title: '{appName}' }`. Replace the literal string value with `{siteName}` (the user-supplied display title). If adding a marker, put it inside the object literal where TSX accepts JavaScript comments:
523
525
 
524
526
  ```tsx
525
- <!-- FP-12 patch: branding title -->
526
- branding={{ title: '{siteName}' }}
527
- <!-- /FP-12 patch -->
527
+ branding={{
528
+ /* FP-12 patch: branding title */
529
+ title: '{siteName}',
530
+ /* /FP-12 patch */
531
+ }}
528
532
  ```
529
533
 
530
- Idempotency: if the marker `<!-- FP-12 patch: branding title -->` is already present, skip and record `status: 'skipped'`. This is the nav-chrome display title — a separate concern from the metadata export above.
534
+ Idempotency: if `branding.title` already equals `{siteName}` or the marker is already present, skip and record `status: 'skipped'`. This is the nav-chrome display title — a separate concern from the metadata export above.
531
535
 
532
536
  - **`<appRoot>/docs/index.md`.** Two edits:
533
537
  1. Frontmatter `title: '{appName}'` → `title: '{siteName}'`.
534
538
  2. First H1 `# {appName}` → `# {siteName}`.
535
- Wrap each edit with HTML-comment markers (`<!-- FP-12 patch: index title -->` / `<!-- /FP-12 patch -->` around the frontmatter line; same pattern around the H1 line). Idempotency: marker absence = scaffold shape, safe to patch; marker present = already patched, skip.
539
+ Do not add hidden FP comments. Idempotency: `{appName}` in both expected places = scaffold shape, safe to patch; `{siteName}` in both expected places = patched/upstream shape, skip; mixed or unexpected text = drift.
536
540
 
537
- - **`<appRoot>/docs/getting-started.md`.** Body contains references to `{appName}` in the intro paragraph (per the scaffold template). Replace those references with `{siteName}`, wrapped in an HTML-comment marker on the first occurrence. Idempotency: marker absence = patch; marker present = skip.
541
+ - **`<appRoot>/docs/getting-started.md`.** Body contains references to `{appName}` in the intro paragraph (per the historical scaffold template). Replace those references with `{siteName}`. Do not add hidden FP comments; classify by literal content shape.
538
542
 
539
- - **`<appRoot>/docs/contributing.md`.** H1 `# {appName}` → `# {siteName}`, same marker pattern as `index.md`'s H1.
543
+ - **`<appRoot>/docs/contributing.md`.** H1 `# {appName}` → `# {siteName}`. Do not add hidden FP comments; classify by literal content shape.
540
544
 
541
545
  Failure handling: if Capability Detection (3b) classified any of these files as `drift`, the patch is already recorded as `refused` in `Scaffold Result.patchesApplied` — skip and move on. Do not re-check drift here.
542
546
 
@@ -554,11 +558,10 @@ Procedure when gate passes:
554
558
  - `{{SITE_NAME}}` → `{siteName}` (Input Result)
555
559
  - `{{APP_DIR}}` → `{targetDir}` (Input Result; the scaffolded app path relative to repo root)
556
560
  - `{{REPO_NAME}}` → `{repoName}` (Preflight Result)
557
- - `{{GENERATE_INDEX_CMD}}` → rendered per repo shape:
558
- - `monorepo`: `pnpm --filter {appName} run docs:generate-index` (if the scaffold exposes that script) or the equivalent `pnpm -w run cli -- docs generate-index --docs-dir {appDir}/docs --output {appDir}/index.md`
559
- - `nested-standalone`: `(cd {appDir} && pnpm run docs:generate-index)` or the `-w` equivalent
560
- - `single-package`: `pnpm run docs:generate-index`
561
- - Fall back to the `pnpm -w run cli -- docs generate-index ...` form if the scaffold did not write a `docs:generate-index` script (determined by grepping `<appRoot>/package.json` after scaffold).
561
+ - `{{GENERATE_INDEX_CMD}}` → derive from the scaffolded `<appRoot>/package.json`:
562
+ 1. Parse JSON and inspect `scripts.predev` first, then `scripts.prebuild`.
563
+ 2. Find the `fumadocs-mdx &&` segment and use the trimmed command that follows it.
564
+ 3. If neither script contains that segment, fall back by repo shape: monorepo uses `pnpm -w run cli -- docs generate-index --docs-dir {appDir}/docs --output {appDir}/index.md`; nested-standalone and single-package use `oat docs generate-index --docs-dir docs --output index.md` from the app root.
562
565
  3. Write the rendered content to `<appRoot>/AGENTS.md`.
563
566
  4. Wrap the entire file in an HTML comment banner at the top — not for idempotency (file existence is the idempotency check), but so the user knows this file was written by the skill and can be regenerated: `<!-- Generated by oat-docs-bootstrap (FP-15 bridge). Safe to hand-edit after generation. -->`.
564
567
  5. Record `{ id: 'FP-15', status: 'applied', target: '<appRoot>/AGENTS.md' }` in `patchesApplied`.
@@ -611,71 +614,53 @@ Two code paths, selected by the file-shape classification of `<appRoot>/next.con
611
614
 
612
615
  **FP-13: template-content patches.**
613
616
 
614
- Four sub-findings, each with its own gate, target, and idempotency check. All sub-findings apply to Fumadocs; MkDocs handling lives in the Walkthrough's MkDocs Minimum Contract (Step 6, p05-t02) because the MkDocs scaffold's content layout differs and doesn't share these specific gaps.
617
+ Five sub-findings, each with its own gate, target, and idempotency check. All sub-findings apply to Fumadocs; MkDocs handling lives in the Walkthrough's MkDocs Minimum Contract (Step 6, p05-t02) because the MkDocs scaffold's content layout differs and doesn't share these specific gaps.
615
618
 
616
619
  - **Sub-finding A: Empty `description:` frontmatter.** Targets: `<appRoot>/docs/getting-started.md:3` and `<appRoot>/docs/contributing.md:3`.
617
620
  - Gate: the target line matches `description: ''` (scaffold shape).
618
621
  - Patch: replace with static defaults:
619
622
  - `getting-started.md`: `description: 'Set up the local environment and preview the docs site.'`
620
623
  - `contributing.md`: `description: 'Authoring conventions and navigation rules for this docs site.'`
621
- - Wrap each edit with `<!-- FP-13 patch: description-A -->` / `<!-- /FP-13 patch -->` on the line above.
622
- - Idempotency: marker present = skip.
624
+ - Do not add hidden FP comments. Idempotency: non-empty description matching the static default = patched/upstream shape, skip.
623
625
  - Rationale: these are per-page descriptions, not site-level — do not template on `{siteDescription}`; use static text that describes the page's own purpose.
624
626
 
625
627
  - **Sub-finding B: Bare install/build commands lack working-directory context.** Target: `<appRoot>/docs/getting-started.md` (the install/build code block, per scaffold lines 15-33 or nearest equivalent after FP-12 patches).
626
628
  - Gate: the block contains a bare `pnpm install` / `pnpm dev` / `pnpm build` sequence with no `--filter` or `cd` prefix (scaffold shape).
627
- - Patch: rewrite the block per `repoShape`:
628
- - `monorepo`: prefix each command with `pnpm --filter {appName}` (e.g., `pnpm --filter {appName} install`, `... dev`, `... build`).
629
+ - Patch: rewrite the block using the same command shapes as the CLI command renderer:
630
+ - `monorepo`: install `pnpm install` at repo root; dev `pnpm --filter {appName} dev`; build `pnpm --filter {appName} build`.
629
631
  - `nested-standalone`: prefix each command with `cd {appDir} && ` (e.g., `cd apps/docs && pnpm install`).
630
- - `single-package`: leave bare form it works correctly from repo root.
631
- - Wrap the entire rewritten block with `<!-- FP-13 patch: commands-B -->` / `<!-- /FP-13 patch -->`.
632
- - Idempotency: marker present = skip. If the block has drifted (not bare, not already patched), classify as `drift` and refuse.
632
+ - `single-package`: leave bare form when the app is at repo root; if the app lives in a subdirectory, use `cd {appDir} && pnpm <command>`.
633
+ - Do not add hidden FP comments. Idempotency: command block already matches the renderer output = patched/upstream shape, skip. If the block has drifted (not bare, not renderer-shaped), classify as `drift` and refuse.
633
634
 
634
635
  - **Sub-finding C: False `docs:lint` claim.** Target: `<appRoot>/docs/contributing.md` line describing the `docs:lint` script (near line 31 per scaffold, may have shifted).
635
636
  - Gate: `lint === 'none'` (Input Result) AND the line contains the exact scaffold string "Run Markdown formatting and linting as configured for this docs app."
636
637
  - Patch: replace with "Run Markdown formatting as configured for this docs app." (dropping "and linting").
637
638
  - If `lint === 'markdownlint-cli2'`, the original claim is accurate — skip with `status: 'skipped', reason: 'linting is configured'`.
638
- - Wrap with `<!-- FP-13 patch: lint-claim-C -->` / `<!-- /FP-13 patch -->`.
639
- - Idempotency: marker present = skip.
639
+ - Do not add hidden FP comments. Idempotency: corrected sentence already present = patched/upstream shape, skip.
640
640
 
641
- - **Sub-finding D: Generated `index.md` lacks "do not hand-edit" warning.** Two sub-targets:
641
+ - **Sub-finding D: Generated-file guidance and old authored-header cleanup.** Two sub-targets:
642
642
  - **D.1 — `contributing.md` explanatory section.** Append a "Generated files" section to `<appRoot>/docs/contributing.md` (before any closing References section if present). Two sentences:
643
643
 
644
644
  ```markdown
645
- <!-- FP-13 patch: generated-files-D -->
646
-
647
645
  ## Generated files
648
646
 
649
647
  For Fumadocs apps, the root-level generated manifest at `<appRoot>/index.md` is regenerated from the Markdown file tree under `docs/` on every `predev` / `prebuild`. Do not hand-edit it; edit the authored source under `docs/` instead, and compare the generated manifest against authored `## Contents` when checking freshness. For MkDocs apps, the derived navigation artifact is the `nav:` section of `mkdocs.yml`, not a root manifest.
650
-
651
- <!-- /FP-13 patch -->
652
- ```
653
-
654
- Idempotency: marker present = skip.
655
-
656
- - **D.2 — Generated `index.md` header comment.** The header to prepend:
657
-
658
- ```markdown
659
- <!-- AUTOGENERATED by `oat docs generate-index`. Do not hand-edit; changes are clobbered on every `predev` / `prebuild`. -->
660
648
  ```
661
649
 
662
- **Two-pass strategy. Both passes MUST run do not treat the second as optional.**
650
+ Idempotency: an existing `## Generated files` section with equivalent generated/root-manifest guidance = patched/upstream shape, skip. Do not add hidden FP comments.
663
651
 
664
- **Pass 1 (best-effort, at scaffold time).** If `<appRoot>/index.md` exists after the CLI's `init` completes, prepend the header directly. Record `{ id: 'FP-13/D.2', status: 'applied', pass: 'scaffold-time' }`. If the file does not yet exist, also add a one-time hook: modify `<appRoot>/package.json` to wrap `prebuild`/`predev` so that the generated `index.md` is prefixed with the header on first run. Hook marker: `<!-- FP-13 patch: generated-header-D2 -->`.
665
-
666
- **Pass 2 (correctness backstop, after Build Verifier).** After the Build Verifier (Step 4) completes install + build, the `prebuild`/`predev` script should have run and `<appRoot>/index.md` should now exist with the header. Verify:
667
- - If `<appRoot>/index.md` exists AND starts with the header no action; record `{ id: 'FP-13/D.2', status: 'already-present', pass: 'post-build-verify' }`.
668
- - If `<appRoot>/index.md` exists AND does NOT start with the header **prepend it directly** (the hook did not fire, or was skipped). Record `{ id: 'FP-13/D.2', status: 'applied-directly', pass: 'post-build-verify' }`.
669
- - If `<appRoot>/index.md` still does not exist → the build did not run generate-index. This is a deeper scaffold issue — surface as a refused patch with `reason: 'generated index.md not present after build; check prebuild script'` and let the user investigate.
670
-
671
- The direct-write pass is the correctness backstop. The hook alone is not reliable enough (some users run `pnpm dev` before touching anything else; the hook might be skipped if another prebuild step short-circuits; the hook modification may conflict with user-customized package.json scripts).
652
+ - **D.2 Authored `docs/index.md` false generated-header backstop.** Older scaffolds could place a generated-file warning on the authored source file at `<appRoot>/docs/index.md`. That warning is wrong: the authored file is edited by humans and agents; the generated Fumadocs manifest is `<appRoot>/index.md`.
653
+ - Gate: `<appRoot>/docs/index.md` contains the exact historical `AUTOGENERATED by \`oat docs generate-index\`` warning before the first H1.
654
+ - Patch: remove only that warning line, preserving frontmatter, the H1, and `## Contents`.
655
+ - Idempotency: no authored-file warning = patched/upstream shape, skip. If a different top comment is present, treat it as project prose and do not remove it.
656
+ - The generated root `<appRoot>/index.md` warning is CLI-owned. If it is missing after build, surface that as an Inspector warning; do not inject hooks into `package.json`.
672
657
 
673
658
  - **E — Node version mismatch.** Target: `<appRoot>/docs/getting-started.md` "Prerequisites" section. The scaffold's `- Node.js 20+` claim (or any hard-coded major version) should reflect the **consuming repo's** actual Node requirement, not a scaffold-time default.
674
659
  - Gate: always run when `<appRoot>/docs/getting-started.md` exists and has a `- Node.js` bullet in a "Prerequisites" section.
675
660
  - Read (in priority order) `$REPO_ROOT/.nvmrc`, then `$REPO_ROOT/package.json` `engines.node`. If a version is found and it's different from the hard-coded version in the scaffold, rewrite the bullet to reference the detected version.
676
- - Wrap the rewritten line with `<!-- FP-13 patch: node-version-E -->` / `<!-- /FP-13 patch -->`.
661
+ - Do not add hidden FP comments.
677
662
  - If neither source is present or parseable, leave the scaffold default and record `{ id: 'FP-13/E', status: 'skipped', reason: 'no .nvmrc or engines.node to detect' }`.
678
- - Idempotency: marker present = skip.
663
+ - Idempotency: the bullet already reflects the detected version = patched/upstream shape, skip.
679
664
 
680
665
  **FP-16: `## Contents` link-extension patch.**
681
666
 
@@ -687,20 +672,16 @@ Target: `<appRoot>/docs/index.md` `## Contents` section. For each list item whos
687
672
  - If the target corresponds to a sibling subdirectory with an `index.md` (e.g., `reference` → `reference/`, with `reference/index.md`), rewrite to `reference/index.md`.
688
673
  - Leave absolute URLs, anchors (`#section`), and already-suffixed links untouched.
689
674
 
690
- Wrap the rewritten `## Contents` block with markers:
675
+ Rewrite the `## Contents` block in place without hidden FP comments:
691
676
 
692
677
  ```markdown
693
- <!-- FP-16 patch: .md-suffixed ## Contents -->
694
-
695
678
  ## Contents
696
679
 
697
680
  - [Getting Started](getting-started.md) - Set up the local docs toolchain and preview the site.
698
681
  - [Contributing](contributing.md) - Authoring conventions and navigation rules.
699
-
700
- <!-- /FP-16 patch -->
701
682
  ```
702
683
 
703
- Idempotency: if the marker `<!-- FP-16 patch: .md-suffixed ## Contents -->` is already present, skip with `status: 'skipped'`. If the `## Contents` section has drifted (mixed extensions, non-bulleted structure, extra prose between items), classify as `drift` per Capability Detection and record `refused` — do not attempt a partial rewrite.
684
+ Idempotency: if all local file links in `## Contents` are already `.md`-suffixed or `dir/index.md`-suffixed, skip with `status: 'skipped'`. If the `## Contents` section has drifted (mixed extensions, non-bulleted structure, extra prose between items), classify as `drift` per Capability Detection and record `refused` — do not attempt a partial rewrite.
704
685
 
705
686
  **Rationale.** The `@open-agent-toolkit/docs-transforms` remark-links plugin strips `.md` / `dir/index.md` at build time for Fumadocs routing, so `.md`-suffixed authored links render correctly. `.md`-suffixed form is what `apps/oat-docs` uses in practice and is what agents can follow via direct file open without path inference. Extension-less links are the scaffold's historical form but are agent-hostile; FP-16 normalizes to the correct convention. When the CLI scaffold template is fixed upstream, the file-shape check will skip this patch automatically.
706
687
 
@@ -714,19 +695,15 @@ Target: the `## Agent guidance` section in `<appRoot>/docs/contributing.md`. Thi
714
695
  - `<appRoot>/AGENTS.md` → agent runtime reference (how to add pages, restructure nav, audit/apply).
715
696
  - Root `AGENTS.md` `## Documentation` section → repo-wide pointer.
716
697
 
717
- Patch: replace the section body with a one-line pointer and marker:
698
+ Patch: replace the section body with a one-line pointer:
718
699
 
719
700
  ```markdown
720
- <!-- FP-17 patch: three-surfaces cleanup -->
721
-
722
701
  ## Agent guidance
723
702
 
724
703
  See `AGENTS.md` in this directory for how agents should work inside this docs app. This `contributing.md` covers human authoring conventions; `AGENTS.md` covers agent runtime discipline (adding pages, restructuring nav, audit/apply, three agent-instruction surfaces). Keeping those concerns separate keeps each file useful to its audience.
725
-
726
- <!-- /FP-17 patch -->
727
704
  ```
728
705
 
729
- Idempotency: if the marker `<!-- FP-17 patch: three-surfaces cleanup -->` is already present, skip with `status: 'skipped'`. If the `## Agent guidance` section has been edited beyond scaffold shape (more than the two or three default bullets; contains content that isn't "docs conventions already in AGENTS.md"), classify as `drift` and refuse — do not collapse user-authored content.
706
+ Idempotency: if the section already contains the one-paragraph pointer to the docs-app `AGENTS.md`, skip with `status: 'skipped'`. If the `## Agent guidance` section has been edited beyond scaffold shape (more than the two or three default bullets; contains content that isn't "docs conventions already in AGENTS.md"), classify as `drift` and refuse — do not collapse user-authored content.
730
707
 
731
708
  **Rationale.** Without this patch, agents reading `docs/contributing.md` find partial agent guidance that duplicates (and risks diverging from) the docs-app `AGENTS.md`. The three-surfaces model only works if each surface is the single-source-of-truth for its audience.
732
709
 
@@ -741,7 +718,7 @@ Inside 3d, patches run in this order:
741
718
 
742
719
  **Design discipline:**
743
720
 
744
- - **CLI is the source of truth for templates.** The Scaffold Runner never rewrites template content, re-renders frontmatter, or fabricates files the CLI was supposed to create. Post-patches only adjust specific known-gap locations, and every patch is labeled.
721
+ - **CLI is the source of truth for templates.** The Scaffold Runner never rewrites template content, re-renders frontmatter, or fabricates files the CLI was supposed to create. Post-patches only adjust specific known-gap locations and use either syntactically valid code markers or content-shape idempotency, depending on the target file type.
745
722
  - **Capability Detection gates every patch.** No patch runs blindly; the file-shape check must pass, and the capability must be absent. This means the skill self-ratchets as CLI fixes land — when `--site-name` is supported upstream, FP-12 patches are skipped automatically.
746
723
  - **Failure is loud, not silent.** CLI non-zero exit stops the flow with the verbatim error. Ambiguous file shapes record a `refused` patch status rather than guessing.
747
724
 
@@ -953,7 +930,7 @@ Narrate:
953
930
  - **The authored source** at `<appRoot>/docs/index.md`. This is the file the user edits. It has frontmatter (`title`, `description`) and a `## Contents` section listing direct children of the docs root. It is the top of a fractal: every directory has its own `index.md`, each with its own `## Contents`.
954
931
  - **The Fumadocs generated map** at `<appRoot>/index.md`. Regenerated on every `predev` / `prebuild` by the `oat docs generate-index` command. Machine-shaped: inventories Markdown files with titles and descriptions so tools have a single searchable map. **Hand-edits are silently clobbered.**
955
932
  - **The MkDocs derived nav** in `mkdocs.yml` `nav:`. MkDocs does not use a Fumadocs-style generated app-root manifest; its generated/derived navigation boundary is the YAML nav block.
956
- - **How to tell them apart when opening a file.** If the file sits inside `docs/`, it's authored. If it sits at `<appRoot>/index.md` (outside `docs/`), it's the Fumadocs generated manifest — and when the CLI or Scaffold-integrity FP-13/D.2 wrote the warning correctly, the first line is `<!-- AUTOGENERATED by \`oat docs generate-index\`... -->`.
933
+ - **How to tell them apart when opening a file.** If the file sits inside `docs/`, it's authored. If it sits at `<appRoot>/index.md` (outside `docs/`), it's the Fumadocs generated manifest — and when the CLI wrote the warning correctly, the first line is `<!-- AUTOGENERATED by \`oat docs generate-index\`... -->`.
957
934
 
958
935
  End with: "Always edit `docs/index.md` and the `## Contents` sections. In Fumadocs, never edit the root-level `index.md` — your edits will disappear next build. In MkDocs, treat `mkdocs.yml` `nav:` as derived from authored Contents unless the local workflow says otherwise."
959
936
 
@@ -1156,7 +1133,7 @@ A successful `oat-docs-bootstrap` run satisfies every invariant below. Any failu
1156
1133
  - Conflict Resolution Contract's "Allowed mutations" completed for the chosen resolution; pre-scaffold invariant satisfied before `oat docs init` ran.
1157
1134
  - Scaffold Runner invoked `oat docs init` non-interactively with capability-gated flags; CLI exited zero; `Scaffold Result.appRoot` resolved from `.oat/config.json` (not from input alone).
1158
1135
  - Capability Detection produced a deterministic `capabilities` record before scaffold (for flag gating) and classified each patch target after scaffold (scaffold-shape / patched-shape / drift).
1159
- - Every applied post-patch is labeled with a marker comment (`<!-- FP-NN patch -->` or `/* FP-NN patch */`) and is idempotent; no patch ran without passing both its capability gate and its file-shape check.
1136
+ - Every applied post-patch is idempotent and file-type aware: code-file patches use syntactically valid code comments when markers are useful, while rendered Markdown patches rely on content-shape checks instead of hidden FP comments. No patch ran without passing both its capability gate and its file-shape check.
1160
1137
  - Build Verifier ran install + build with commands matching `repoShape`; failures classified against known patterns; unknown errors halted the flow before Inspector/Walkthrough.
1161
1138
  - Post-Scaffold Inspector verified every `documentation.*` path against disk; drift findings recorded with `suggestedFix`; nested-standalone dual-config surfaced without reconciliation.
1162
1139
  - `requireForProjectCompletion` decision collected from user; written to `.oat/config.json` only on opt-in; never silently default-true.
@@ -1,14 +1,28 @@
1
1
  import { DocsPage, Mermaid, Tab, Tabs } from '@open-agent-toolkit/docs-theme';
2
2
  import defaultComponents from 'fumadocs-ui/mdx';
3
+ import type { Metadata } from 'next';
3
4
  import { notFound } from 'next/navigation';
4
5
 
5
6
  import { source } from '@/lib/source';
6
7
 
7
8
  const mdxComponents = { ...defaultComponents, Mermaid, Tab, Tabs };
8
9
 
9
- export default async function Page(props: {
10
+ type PageProps = {
10
11
  params: Promise<{ slug?: string[] }>;
11
- }) {
12
+ };
13
+
14
+ export async function generateMetadata(props: PageProps): Promise<Metadata> {
15
+ const params = await props.params;
16
+ const page = source.getPage(params.slug);
17
+ if (!page) notFound();
18
+
19
+ return {
20
+ title: page.data.title,
21
+ description: page.data.description,
22
+ };
23
+ }
24
+
25
+ export default async function Page(props: PageProps) {
12
26
  const params = await props.params;
13
27
  const page = source.getPage(params.slug);
14
28
  if (!page) notFound();
@@ -3,9 +3,13 @@ import { RootProvider } from 'fumadocs-ui/provider/next';
3
3
  import type { ReactNode } from 'react';
4
4
 
5
5
  import './globals.css';
6
+ import StaticSearchDialog from '@/components/search';
6
7
  import { source } from '@/lib/source';
7
8
 
8
- const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? '';
9
+ export const metadata = {
10
+ title: '{{SITE_NAME}}',
11
+ description: '{{SITE_DESCRIPTION}}',
12
+ };
9
13
 
10
14
  export default function Layout({ children }: { children: ReactNode }) {
11
15
  return (
@@ -13,10 +17,7 @@ export default function Layout({ children }: { children: ReactNode }) {
13
17
  <body>
14
18
  <RootProvider
15
19
  search={{
16
- options: {
17
- type: 'static' as const,
18
- api: `${basePath}/api/search`,
19
- },
20
+ SearchDialog: StaticSearchDialog,
20
21
  }}
21
22
  >
22
23
  <DocsLayout
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { useDocsSearch } from 'fumadocs-core/search/client';
4
+ import {
5
+ SearchDialog,
6
+ SearchDialogClose,
7
+ SearchDialogContent,
8
+ SearchDialogHeader,
9
+ SearchDialogIcon,
10
+ SearchDialogInput,
11
+ SearchDialogList,
12
+ SearchDialogOverlay,
13
+ type SharedProps,
14
+ } from 'fumadocs-ui/components/dialog/search';
15
+
16
+ // Static export has no search server, so the dialog runs a client-side static
17
+ // index search. fumadocs-ui's DefaultSearchDialog is hardwired to the fetch
18
+ // client, so we provide a custom dialog backed by the static client. `from`
19
+ // mirrors the basePath-aware static index emitted by app/api/search.
20
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? '';
21
+
22
+ export default function StaticSearchDialog(props: SharedProps) {
23
+ const { search, setSearch, query } = useDocsSearch({
24
+ type: 'static',
25
+ from: `${basePath}/api/search`,
26
+ });
27
+
28
+ return (
29
+ <SearchDialog
30
+ search={search}
31
+ onSearchChange={setSearch}
32
+ isLoading={query.isLoading}
33
+ {...props}
34
+ >
35
+ <SearchDialogOverlay />
36
+ <SearchDialogContent>
37
+ <SearchDialogHeader>
38
+ <SearchDialogIcon />
39
+ <SearchDialogInput />
40
+ <SearchDialogClose />
41
+ </SearchDialogHeader>
42
+ <SearchDialogList items={query.data !== 'empty' ? query.data : null} />
43
+ </SearchDialogContent>
44
+ </SearchDialog>
45
+ );
46
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Contributing
3
- description: ''
3
+ description: 'Authoring conventions and navigation rules.'
4
4
  ---
5
5
 
6
6
  # Contributing to {{SITE_NAME}}
@@ -19,16 +19,16 @@ Documentation should ship with the code it explains. This docs app is scaffolded
19
19
  1. Install dependencies:
20
20
 
21
21
  ```bash
22
- pnpm install
22
+ {{INSTALL_CMD}}
23
23
  ```
24
24
 
25
25
  2. Run the live preview:
26
26
 
27
27
  ```bash
28
- pnpm dev
28
+ {{DEV_CMD}}
29
29
  ```
30
30
 
31
- 3. Run Markdown formatting and linting as configured for this docs app.
31
+ 3. Run Markdown {{LINT_PHRASE}} as configured for this docs app.
32
32
 
33
33
  ## Supported Markdown features
34
34
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Getting Started
3
- description: ''
3
+ description: 'Set up the local docs toolchain and preview the site.'
4
4
  ---
5
5
 
6
6
  # Getting Started
@@ -15,19 +15,19 @@ Use this guide to set up your local environment and preview {{SITE_NAME}}.
15
15
  ## Install dependencies
16
16
 
17
17
  ```bash
18
- pnpm install
18
+ {{INSTALL_CMD}}
19
19
  ```
20
20
 
21
21
  ## Run the docs locally
22
22
 
23
23
  ```bash
24
- pnpm dev
24
+ {{DEV_CMD}}
25
25
  ```
26
26
 
27
27
  ## Build for production
28
28
 
29
29
  ```bash
30
- pnpm build
30
+ {{BUILD_CMD}}
31
31
  ```
32
32
 
33
33
  The static output is generated in the `out/` directory.
@@ -3,8 +3,6 @@ title: '{{SITE_NAME}}'
3
3
  description: '{{SITE_DESCRIPTION}}'
4
4
  ---
5
5
 
6
- <!-- AUTOGENERATED by `oat docs generate-index`. Do not hand-edit; changes are clobbered on every `predev` / `prebuild`. -->
7
-
8
6
  # {{SITE_NAME}}
9
7
 
10
8
  Use this file as the map to the repository's central documentation site. The site is organized to support both human readers and AI agents, with every directory exposing an `index.md` entrypoint.
@@ -3,7 +3,5 @@ import { createDocsConfig } from '@open-agent-toolkit/docs-config';
3
3
  const basePath = process.env.NEXT_PUBLIC_BASE_PATH || undefined;
4
4
 
5
5
  export default createDocsConfig({
6
- title: '{{SITE_NAME}}',
7
- description: '{{SITE_DESCRIPTION}}',
8
6
  ...(basePath ? { basePath } : {}),
9
7
  });
@@ -2,7 +2,7 @@
2
2
  "name": "{{PACKAGE_NAME}}",
3
3
  "type": "module",
4
4
  "private": true,
5
- "version": "0.1.0",
5
+ "version": "0.1.0"{{PACKAGE_MANAGER_FIELD}},
6
6
  "description": "{{SITE_DESCRIPTION}}",
7
7
  "scripts": {
8
8
  "predev": "fumadocs-mdx && {{GENERATE_INDEX_CMD}}",
@@ -2,7 +2,7 @@
2
2
  "name": "{{PACKAGE_NAME}}",
3
3
  "type": "module",
4
4
  "private": true,
5
- "version": "0.1.0",
5
+ "version": "0.1.0"{{PACKAGE_MANAGER_FIELD}},
6
6
  "description": "MkDocs Material documentation for {{SITE_NAME}}.",
7
7
  "scripts": {
8
8
  "docs:dev": "mkdocs serve",
@@ -0,0 +1,8 @@
1
+ import type { DocsRepoShape } from './resolve-options.js';
2
+ export interface DocsCommands {
3
+ install: string;
4
+ dev: string;
5
+ build: string;
6
+ }
7
+ export declare function buildDocsCommands(repoShape: DocsRepoShape, targetDir: string, appName: string): DocsCommands;
8
+ //# sourceMappingURL=docs-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/docs-commands.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,YAAY,CAsBd"}
@@ -0,0 +1,25 @@
1
+ import { normalize } from 'node:path';
2
+ function isRootTarget(targetDir) {
3
+ return targetDir.trim() === '' || normalize(targetDir) === '.';
4
+ }
5
+ export function buildDocsCommands(repoShape, targetDir, appName) {
6
+ if (repoShape === 'monorepo') {
7
+ return {
8
+ install: 'pnpm install',
9
+ dev: `pnpm --filter ${appName} dev`,
10
+ build: `pnpm --filter ${appName} build`,
11
+ };
12
+ }
13
+ if (isRootTarget(targetDir)) {
14
+ return {
15
+ install: 'pnpm install',
16
+ dev: 'pnpm dev',
17
+ build: 'pnpm build',
18
+ };
19
+ }
20
+ return {
21
+ install: `cd ${targetDir} && pnpm install`,
22
+ dev: `cd ${targetDir} && pnpm dev`,
23
+ build: `cd ${targetDir} && pnpm build`,
24
+ };
25
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,YAAY,EAElB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,KAAK,SAAS,EAGf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAE5C,OAAO,EAIL,KAAK,uBAAuB,EAK7B,MAAM,mBAAmB,CAAC;AAkB3B,UAAU,oBAAoB;IAC5B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,gBAAgB,CAAC,CAAC;IAC9E,gBAAgB,EAAE,CAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,eAAe,EAAE,CAAC,CAAC,SAAS,MAAM,EAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,EAC1B,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvB,WAAW,EAAE,CACX,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,uBAAuB,EAChC,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,qBAAqB,EAAE,CACrB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAClC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1E;AAyGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAc7E;AA+GD,wBAAgB,qBAAqB,CACnC,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CAuCT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,YAAY,EAElB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,KAAK,SAAS,EAGf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAG5C,OAAO,EAIL,KAAK,uBAAuB,EAK7B,MAAM,mBAAmB,CAAC;AAmB3B,UAAU,oBAAoB;IAC5B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,gBAAgB,CAAC,CAAC;IAC9E,gBAAgB,EAAE,CAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,eAAe,EAAE,CAAC,CAAC,SAAS,MAAM,EAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,EAC1B,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvB,WAAW,EAAE,CACX,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,uBAAuB,EAChC,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,qBAAqB,EAAE,CACrB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAClC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1E;AAyGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAc7E;AAoHD,wBAAgB,qBAAqB,CACnC,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CA6CT"}
@@ -5,6 +5,7 @@ import { readGlobalOptions } from '../../shared/shared.utils.js';
5
5
  import { readOatConfig, writeOatConfig, } from '../../../config/oat-config.js';
6
6
  import { resolveAssetsRoot } from '../../../fs/assets.js';
7
7
  import { Command, Option } from 'commander';
8
+ import { buildDocsCommands } from './docs-commands.js';
8
9
  import { DEFAULT_DOCS_REPO_SHAPE_DEPENDENCIES, detectDocsRepoShape, getDefaultDocsAppName, resolveDocsInitOptions, } from './resolve-options.js';
9
10
  import { patchRootPackageJson, } from './root-package.js';
10
11
  import { scaffoldDocsApp } from './scaffold.js';
@@ -115,6 +116,7 @@ async function runDocsInitCommand(context, options, dependencies) {
115
116
  acceptDefaults: options.yes ?? false,
116
117
  providedFramework: options.framework,
117
118
  providedAppName: options.appName,
119
+ providedSiteName: options.siteName,
118
120
  providedTargetDir: options.targetDir,
119
121
  providedSiteDescription: options.description,
120
122
  providedLint: options.lint,
@@ -166,13 +168,11 @@ async function runDocsInitCommand(context, options, dependencies) {
166
168
  if (!context.json) {
167
169
  context.logger.info('');
168
170
  context.logger.info('Next steps:');
169
- if (resolved.repoShape === 'single-package') {
170
- context.logger.info(` cd ${resolved.targetDir} && pnpm install`);
171
- context.logger.info(' pnpm build');
172
- }
173
- else {
174
- context.logger.info(' pnpm install');
175
- context.logger.info(` pnpm --filter ${resolved.appName} build`);
171
+ const commands = buildDocsCommands(resolved.repoShape, resolved.targetDir, resolved.appName);
172
+ context.logger.info(` ${commands.install}`);
173
+ context.logger.info(` ${commands.dev}`);
174
+ context.logger.info(` ${commands.build}`);
175
+ if (resolved.repoShape === 'monorepo') {
176
176
  const defaultName = getDefaultDocsAppName(context.cwd, resolved.repoShape);
177
177
  if (resolved.appName !== defaultName) {
178
178
  context.logger.info('');
@@ -205,6 +205,7 @@ export function createDocsInitCommand(overrides = {}) {
205
205
  'mkdocs',
206
206
  ]))
207
207
  .addOption(new Option('--app-name <name>', 'Docs app name'))
208
+ .addOption(new Option('--site-name <name>', 'Display title (distinct from --app-name)'))
208
209
  .addOption(new Option('--target-dir <path>', 'Target directory for the docs app'))
209
210
  .addOption(new Option('--description <text>', 'Site description'))
210
211
  .addOption(new Option('--lint <mode>', 'Markdown lint mode').choices([
@@ -1,5 +1,6 @@
1
1
  import type { PromptContext, SelectChoice } from '../../shared/shared.prompts.js';
2
- export type DocsRepoShape = 'monorepo' | 'single-package';
2
+ export type DocsRepoShape = 'monorepo' | 'single-package' | 'nested-standalone';
3
+ export type DocsDetectedRepoShape = Exclude<DocsRepoShape, 'nested-standalone'>;
3
4
  export type DocsFramework = 'fumadocs' | 'mkdocs';
4
5
  export type DocsLintMode = 'none' | 'markdownlint-cli2';
5
6
  export type DocsFormatMode = 'oxfmt' | 'none';
@@ -8,6 +9,7 @@ export interface DocsInitResolvedOptions {
8
9
  repoShape: DocsRepoShape;
9
10
  framework: DocsFramework;
10
11
  appName: string;
12
+ siteName: string;
11
13
  targetDir: string;
12
14
  siteDescription: string;
13
15
  lint: DocsLintMode;
@@ -21,6 +23,7 @@ export interface ResolveDocsInitOptionsInput {
21
23
  acceptDefaults: boolean;
22
24
  providedFramework?: DocsFramework;
23
25
  providedAppName?: string;
26
+ providedSiteName?: string;
24
27
  providedTargetDir?: string;
25
28
  providedSiteDescription?: string;
26
29
  providedLint?: DocsLintMode;
@@ -36,8 +39,10 @@ export interface DocsRepoShapeDependencies {
36
39
  }
37
40
  export declare function getDefaultDocsAppName(repoRoot: string, repoShape: DocsRepoShape): string;
38
41
  export declare function getDefaultDocsTargetDir(repoShape: DocsRepoShape, appName: string): string;
39
- export declare function detectDocsRepoShape(repoRoot: string, dependencies: DocsRepoShapeDependencies): Promise<DocsRepoShape>;
42
+ export declare function humanizeAppName(appName: string): string;
43
+ export declare function detectDocsRepoShape(repoRoot: string, dependencies: DocsRepoShapeDependencies): Promise<DocsDetectedRepoShape>;
40
44
  export declare function getTemplateDir(framework: DocsFramework): string;
45
+ export declare function resolveEffectiveDocsShape(repoShape: DocsRepoShape, framework: DocsFramework, repoRoot: string, targetDir: string): DocsRepoShape;
41
46
  export declare function resolveDocsInitOptions(input: ResolveDocsInitOptionsInput): Promise<DocsInitResolvedOptions | null>;
42
47
  export declare const DEFAULT_DOCS_REPO_SHAPE_DEPENDENCIES: DocsRepoShapeDependencies;
43
48
  //# sourceMappingURL=resolve-options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-options.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/resolve-options.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACb,MAAM,iCAAiC,CAAC;AAGzC,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC1D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,mBAAmB,CAAC;AACxD,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,CAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,eAAe,EAAE,CAAC,CAAC,SAAS,MAAM,EAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,EAC1B,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACvE;AAiBD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,aAAa,GACvB,MAAM,CAMR;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,MAAM,GACd,MAAM,CAMR;AAoBD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,yBAAyB,GACtC,OAAO,CAAC,aAAa,CAAC,CAqBxB;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAE/D;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAgFzC;AAED,eAAO,MAAM,oCAAoC,EAAE,yBAOlD,CAAC"}
1
+ {"version":3,"file":"resolve-options.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/resolve-options.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACb,MAAM,iCAAiC,CAAC;AAGzC,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAChF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;AAChF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,mBAAmB,CAAC;AACxD,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,CAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,eAAe,EAAE,CAAC,CAAC,SAAS,MAAM,EAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,EAC1B,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACvE;AAiBD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,aAAa,GACvB,MAAM,CAMR;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,MAAM,GACd,MAAM,CAMR;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvD;AAoBD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,yBAAyB,GACtC,OAAO,CAAC,qBAAqB,CAAC,CAqBhC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAE/D;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,aAAa,CAkBf;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAmGzC;AAED,eAAO,MAAM,oCAAoC,EAAE,yBAOlD,CAAC"}
@@ -1,4 +1,4 @@
1
- import { basename, join } from 'node:path';
1
+ import { basename, isAbsolute, join, relative, resolve } from 'node:path';
2
2
  import { dirExists, fileExists } from '../../../fs/io.js';
3
3
  const FRAMEWORK_CHOICES = [
4
4
  { label: 'Fumadocs (Next.js + MDX, static export)', value: 'fumadocs' },
@@ -24,6 +24,13 @@ export function getDefaultDocsTargetDir(repoShape, appName) {
24
24
  }
25
25
  return appName;
26
26
  }
27
+ export function humanizeAppName(appName) {
28
+ return appName
29
+ .split(/[-_]/g)
30
+ .filter(Boolean)
31
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
32
+ .join(' ');
33
+ }
27
34
  function hasWorkspaceConfig(rawPackageJson) {
28
35
  try {
29
36
  const parsed = JSON.parse(rawPackageJson);
@@ -57,6 +64,21 @@ export async function detectDocsRepoShape(repoRoot, dependencies) {
57
64
  export function getTemplateDir(framework) {
58
65
  return framework === 'fumadocs' ? 'docs-app-fuma' : 'docs-app-mkdocs';
59
66
  }
67
+ export function resolveEffectiveDocsShape(repoShape, framework, repoRoot, targetDir) {
68
+ if (repoShape !== 'single-package' || framework !== 'fumadocs') {
69
+ return repoShape;
70
+ }
71
+ if (!targetDir.trim()) {
72
+ return repoShape;
73
+ }
74
+ const targetPath = resolve(repoRoot, targetDir);
75
+ const relativeTarget = relative(resolve(repoRoot), targetPath);
76
+ const isRepoSubdirectory = relativeTarget !== '' &&
77
+ relativeTarget !== '.' &&
78
+ !relativeTarget.startsWith('..') &&
79
+ !isAbsolute(relativeTarget);
80
+ return isRepoSubdirectory ? 'nested-standalone' : repoShape;
81
+ }
60
82
  export async function resolveDocsInitOptions(input) {
61
83
  const ctx = { interactive: input.interactive };
62
84
  const framework = input.providedFramework ||
@@ -74,6 +96,14 @@ export async function resolveDocsInitOptions(input) {
74
96
  if (!appName) {
75
97
  return null;
76
98
  }
99
+ const defaultSiteName = humanizeAppName(appName);
100
+ const siteName = input.providedSiteName ??
101
+ (input.interactive && !input.acceptDefaults
102
+ ? await input.inputWithDefault('Site name', defaultSiteName, ctx)
103
+ : defaultSiteName);
104
+ if (siteName === null) {
105
+ return null;
106
+ }
77
107
  const defaultTargetDir = getDefaultDocsTargetDir(input.repoShape, appName);
78
108
  const targetDir = input.providedTargetDir?.trim() ||
79
109
  (input.interactive && !input.acceptDefaults
@@ -100,11 +130,13 @@ export async function resolveDocsInitOptions(input) {
100
130
  if (!format) {
101
131
  return null;
102
132
  }
133
+ const repoShape = resolveEffectiveDocsShape(input.repoShape, framework, input.repoRoot, targetDir);
103
134
  return {
104
135
  repoRoot: input.repoRoot,
105
- repoShape: input.repoShape,
136
+ repoShape,
106
137
  framework,
107
138
  appName,
139
+ siteName,
108
140
  targetDir,
109
141
  siteDescription,
110
142
  lint,
@@ -13,7 +13,14 @@ export interface OatDepContext {
13
13
  localPackages: Set<string>;
14
14
  oatPackageVersions: Record<string, string>;
15
15
  }
16
+ type MaybePromise<T> = T | Promise<T>;
17
+ export interface ScaffoldDocsAppDependencies {
18
+ detectPnpmVersion: () => MaybePromise<string | null | undefined>;
19
+ hasInheritedPackageManager: (repoRoot: string, appRoot: string) => MaybePromise<boolean>;
20
+ }
21
+ export declare const PNPM_FALLBACK = "10.13.1";
16
22
  export declare function detectIsOatRepo(repoRoot: string): Promise<boolean>;
17
23
  export declare function resolveOatDepContext(repoRoot: string, assetsRoot: string): Promise<OatDepContext>;
18
- export declare function scaffoldDocsApp(options: ScaffoldDocsAppOptions): Promise<ScaffoldDocsAppResult>;
24
+ export declare function scaffoldDocsApp(options: ScaffoldDocsAppOptions, overrides?: Partial<ScaffoldDocsAppDependencies>): Promise<ScaffoldDocsAppResult>;
25
+ export {};
19
26
  //# sourceMappingURL=scaffold.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/scaffold.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAIjE,OAAO,KAAK,EAGV,uBAAuB,EAExB,MAAM,mBAAmB,CAAC;AA+E3B,MAAM,WAAW,sBAAuB,SAAQ,uBAAuB;IACrE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,sBAAsB,CAAC;CAC7C;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAWD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOxE;AAgDD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAaxB;AA4ID,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAoChC"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/init/scaffold.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAKjE,OAAO,KAAK,EAGV,uBAAuB,EAExB,MAAM,mBAAmB,CAAC;AAmF3B,MAAM,WAAW,sBAAuB,SAAQ,uBAAuB;IACrE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,sBAAsB,CAAC;CAC7C;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEtC,MAAM,WAAW,2BAA2B;IAC1C,iBAAiB,EAAE,MAAM,YAAY,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACjE,0BAA0B,EAAE,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,KACZ,YAAY,CAAC,OAAO,CAAC,CAAC;CAC5B;AAUD,eAAO,MAAM,aAAa,YAAY,CAAC;AAEvC,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOxE;AAgDD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAaxB;AAgOD,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,EAC/B,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAAC,qBAAqB,CAAC,CAkDhC"}
@@ -1,7 +1,8 @@
1
1
  import { readdir, readFile, writeFile } from 'node:fs/promises';
2
- import { basename, dirname, join } from 'node:path';
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
6
  import { getTemplateDir } from './resolve-options.js';
6
7
  const MKDOCS_TEMPLATE_FILES = [
7
8
  { source: '.gitignore', destination: '.gitignore' },
@@ -38,6 +39,10 @@ const FUMA_TEMPLATE_FILES = [
38
39
  source: join('app', 'layout.tsx'),
39
40
  destination: join('app', 'layout.tsx'),
40
41
  },
42
+ {
43
+ source: join('components', 'search.tsx'),
44
+ destination: join('components', 'search.tsx'),
45
+ },
41
46
  {
42
47
  source: join('app', '[[...slug]]', 'page.tsx'),
43
48
  destination: join('app', '[[...slug]]', 'page.tsx'),
@@ -74,6 +79,7 @@ const OAT_DEP_PACKAGES = [
74
79
  ];
75
80
  const DEFAULT_OAT_PUBLISHED_VERSION = OAT_VERSION;
76
81
  const PUBLIC_PACKAGE_VERSIONS_FILE = 'public-package-versions.json';
82
+ export const PNPM_FALLBACK = '10.13.1';
77
83
  export async function detectIsOatRepo(repoRoot) {
78
84
  for (const pkg of OAT_DEP_PACKAGES) {
79
85
  if (!(await fileExists(join(repoRoot, 'packages', pkg, 'package.json')))) {
@@ -128,13 +134,6 @@ export async function resolveOatDepContext(repoRoot, assetsRoot) {
128
134
  };
129
135
  return { isOatRepo, localPackages, oatPackageVersions };
130
136
  }
131
- function humanizeAppName(appName) {
132
- return appName
133
- .split(/[-_]/g)
134
- .filter(Boolean)
135
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
136
- .join(' ');
137
- }
138
137
  function buildDevDependencies(lint, format) {
139
138
  const entries = [];
140
139
  if (lint === 'markdownlint-cli2') {
@@ -170,17 +169,75 @@ function oatDepVersion(depContext, packageName) {
170
169
  }
171
170
  return `^${depContext.oatPackageVersions[packageName] ?? DEFAULT_OAT_PUBLISHED_VERSION}`;
172
171
  }
173
- function renderTemplate(template, options, depContext) {
172
+ function detectPnpmVersionFromUserAgent() {
173
+ const userAgent = process.env.npm_config_user_agent ?? '';
174
+ return userAgent.match(/\bpnpm\/([^\s]+)/)?.[1] ?? null;
175
+ }
176
+ function isDescendantOrSame(root, candidate) {
177
+ const relativePath = relative(root, candidate);
178
+ return (relativePath === '' ||
179
+ (!relativePath.startsWith('..') && !isAbsolute(relativePath)));
180
+ }
181
+ async function packageJsonHasPackageManager(packageJsonPath) {
182
+ if (!(await fileExists(packageJsonPath))) {
183
+ return false;
184
+ }
185
+ try {
186
+ const parsed = JSON.parse(await readFile(packageJsonPath, 'utf8'));
187
+ return (typeof parsed.packageManager === 'string' &&
188
+ parsed.packageManager.trim().length > 0);
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ async function hasInheritedPackageManager(repoRoot, appRoot) {
195
+ const root = resolve(repoRoot);
196
+ let current = resolve(dirname(appRoot));
197
+ while (isDescendantOrSame(root, current)) {
198
+ if (await packageJsonHasPackageManager(join(current, 'package.json'))) {
199
+ return true;
200
+ }
201
+ if (current === root) {
202
+ break;
203
+ }
204
+ current = dirname(current);
205
+ }
206
+ return false;
207
+ }
208
+ const DEFAULT_SCAFFOLD_DEPENDENCIES = {
209
+ detectPnpmVersion: detectPnpmVersionFromUserAgent,
210
+ hasInheritedPackageManager,
211
+ };
212
+ function normalizePnpmVersion(version) {
213
+ const trimmed = version?.trim();
214
+ return trimmed && trimmed.length > 0 ? trimmed : PNPM_FALLBACK;
215
+ }
216
+ async function buildPackageManagerField(options, appRoot, dependencies) {
217
+ if (await dependencies.hasInheritedPackageManager(options.repoRoot, appRoot)) {
218
+ return '';
219
+ }
220
+ const version = normalizePnpmVersion(await dependencies.detectPnpmVersion());
221
+ return `,\n "packageManager": ${JSON.stringify(`pnpm@${version}`)}`;
222
+ }
223
+ function renderTemplate(template, options, depContext, packageManagerField) {
174
224
  const repoName = basename(options.repoRoot);
175
- const siteName = `${humanizeAppName(options.appName)} Documentation`;
225
+ const commands = buildDocsCommands(options.repoShape, options.targetDir, options.appName);
176
226
  const replacements = {
177
227
  '{{APP_NAME}}': options.appName,
178
228
  '{{PACKAGE_NAME}}': options.appName,
179
- '{{SITE_NAME}}': siteName,
229
+ '{{SITE_NAME}}': options.siteName,
180
230
  '{{SITE_DESCRIPTION}}': options.siteDescription,
231
+ '{{INSTALL_CMD}}': commands.install,
232
+ '{{DEV_CMD}}': commands.dev,
233
+ '{{BUILD_CMD}}': commands.build,
234
+ '{{PACKAGE_MANAGER_FIELD}}': packageManagerField,
181
235
  '{{DOCS_LINT_SCRIPT}}': options.lint === 'markdownlint-cli2'
182
236
  ? "markdownlint-cli2 'docs/**/*.md'"
183
237
  : "echo 'docs lint disabled'",
238
+ '{{LINT_PHRASE}}': options.lint === 'markdownlint-cli2'
239
+ ? 'formatting and linting'
240
+ : 'formatting',
184
241
  '{{DOCS_FORMAT_SCRIPT}}': options.format === 'oxfmt'
185
242
  ? "oxfmt 'docs/**/*.md'"
186
243
  : "echo 'docs formatting disabled'",
@@ -223,23 +280,28 @@ async function ensureTargetWritable(appRoot) {
223
280
  throw new Error(`Docs app target directory is not empty: ${appRoot}`);
224
281
  }
225
282
  }
226
- export async function scaffoldDocsApp(options) {
283
+ export async function scaffoldDocsApp(options, overrides = {}) {
227
284
  const appRoot = join(options.repoRoot, options.targetDir);
228
285
  const templateDir = getTemplateDir(options.framework);
229
286
  const templateRoot = join(options.assetsRoot, 'templates', templateDir);
230
287
  const frameworkConfig = FRAMEWORK_CONFIGS[options.framework];
231
288
  const createdFiles = [];
232
289
  const depContext = await resolveOatDepContext(options.repoRoot, options.assetsRoot);
290
+ const dependencies = {
291
+ ...DEFAULT_SCAFFOLD_DEPENDENCIES,
292
+ ...overrides,
293
+ };
233
294
  if (!(await fileExists(join(templateRoot, frameworkConfig.sentinelFile)))) {
234
295
  throw new Error(`Docs app templates not found under ${templateRoot}`);
235
296
  }
236
297
  await ensureTargetWritable(appRoot);
237
298
  await ensureDir(appRoot);
299
+ const packageManagerField = await buildPackageManagerField(options, appRoot, dependencies);
238
300
  for (const templateFile of frameworkConfig.templateFiles) {
239
301
  const source = join(templateRoot, templateFile.source);
240
302
  const destination = join(appRoot, templateFile.destination);
241
303
  const template = await readFile(source, 'utf8');
242
- const rendered = renderTemplate(template, options, depContext);
304
+ const rendered = renderTemplate(template, options, depContext, packageManagerField);
243
305
  await ensureDir(dirname(destination));
244
306
  await writeFile(destination, rendered, 'utf8');
245
307
  createdFiles.push(templateFile.destination);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.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",
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.0.0",
35
35
  "yaml": "2.8.2",
36
36
  "zod": "^3.25.76",
37
- "@open-agent-toolkit/control-plane": "0.1.26"
37
+ "@open-agent-toolkit/control-plane": "0.1.27"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.10.0",