Skip to content

Support multiple docs source mounts#31

Merged
KayleeWilliams merged 8 commits into
mainfrom
KayleeWilliams/multiple-sources
May 12, 2026
Merged

Support multiple docs source mounts#31
KayleeWilliams merged 8 commits into
mainfrom
KayleeWilliams/multiple-sources

Conversation

@KayleeWilliams
Copy link
Copy Markdown
Collaborator

Summary

Adds first-class support for generating docs artifacts from multiple source folders and mapping selected sources to custom public URL prefixes.

What changed

  • Allows repeated --docs-dir values in leadtype generate.
  • Supports <dir>=<url-prefix> mounts such as changelog=/changelog.
  • Propagates mounted URL metadata through LLM files, navigation, Agent Readability artifacts, and search metadata.
  • Copies mounted markdown mirrors for static routes, for example public/changelog/*.md.
  • Documents multi-source and reusable include authoring patterns.
  • Adds a patch changeset for leadtype.

Validation

  • bun test packages/leadtype/src/cli.test.ts packages/leadtype/src/internal/docs-url.test.ts
  • bun run --filter leadtype check-types
  • bun x ultracite check packages/leadtype/src/cli/generate.ts packages/leadtype/src/cli.test.ts packages/leadtype/src/internal/docs-url.ts packages/leadtype/src/internal/docs-url.test.ts packages/leadtype/src/llm/llm.ts packages/leadtype/src/llm/readability.ts packages/leadtype/src/search/node.ts docs/authoring/components.mdx docs/build/connect-docs-site.mdx docs/reference/cli.mdx docs/reference/convert.mdx docs/reference/llm.mdx docs/reference/remark.mdx docs/reference/search.mdx .changeset/multiple-docs-source-mounts.md
  • Full commit hook test run: 167 tests passed

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds repeatable --docs-dir parsing with optional =url-prefix mounts, resolves multiple docs sources into a merged staging mirror or fast-path, mirrors mounted markdown for site builds, threads a mounts mapping through URL utilities and all artifact generators, prepends remarkInclude in MDX conversion, and updates docs, tests, and an example app.

Changes

Multiple Documentation Sources with Mounted URL Prefixes

Layer / File(s) Summary
URL mount contracts & helpers
packages/leadtype/src/internal/docs-url.ts, packages/leadtype/src/internal/docs-url.test.ts
Adds DocsPathMount type, normalizeUrlPrefix(), mount matching by pathPrefix specificity, mount-aware toDocsUrlPath(relativePath, mounts) and toMountedMarkdownUrlPath(relativePath, mounts). Tests validate mounted path derivation and index handling.
CLI parsing & generate core
packages/leadtype/src/cli/generate.ts, packages/leadtype/src/cli.test.ts
Replaces single docsDir with docsDirs: string[], makes --docs-dir repeatable with dir or dir=url-prefix syntax, parses/validates mounts (no site-root mounts, collision checks), resolves docs sources, updates JSON output to include docsDirs and mounts, and adds tests for multi-source merges, include expansion, and mount behavior.
Source mirror, staging, and mounted mirroring
packages/leadtype/src/cli/generate.ts
Refactors createSourceMirror to accept multiple ResolvedDocsSource[], implements staging/merge fast path and cleanup, throws when no MDX matches, and implements copyMountedMarkdownMirrors to write mounted markdown mirrors into site-mode output directories safely.
MDX conversion & remarkInclude wiring
packages/leadtype/src/cli/generate.ts, docs/reference/remark.mdx, docs/authoring/components.mdx
Prepends remarkInclude to the remark plugin stack used in conversion so include partials are expanded before emitting markdown; documentation updated with usage, examples, and section-reuse rules.
Type-table remark updates
packages/leadtype/src/remark/plugins/type-table.remark.ts, packages/leadtype/src/remark/remark-output.test.ts
Treats AutoTypeTable as an alias for ExtractedTypeTable, routes both to the extracted-type processor, and adds a test asserting expected markdown output.
LLM, Agent Readability & Navigation plumbing
packages/leadtype/src/llm/llm.ts, packages/leadtype/src/llm/readability.ts
Adds optional mounts?: DocsPathMount[] to LLM/readability/navigation configs, uses toMountedMarkdownUrlPath for generated markdown URLs, threads mounts into readSourceDocs/readMarkdownDocs, updates /llms.txt, full-context generation, agent-readability artifacts, and prefers manifest-aware markdown mirror resolution.
Search generation integration
packages/leadtype/src/search/node.ts
Extends GenerateDocsSearchFilesConfig with mounts, updates readMarkdownDocs to accept mounts and compute urlPath via mounts so search result URLs respect mounted prefixes while reading internal mirror files.
Docs & CLI reference updates
docs/*, .changeset/multiple-docs-source-mounts.md
Updates documentation across authoring, build, CLI, convert, llm, remark, and search to document repeatable --docs-dir, mounts usage, pipeline examples, and remarkInclude behavior. Adds a changeset entry for a patch release note.
Example app & tooling
apps/c15t-example/*
Adds a c15t example app (Vite + React + MDX) with scripts to setup a local .docs-src clone, generate artifacts using mounted sources (docs and changelog), styles, TS/Vite configs, ambient MDX type declarations, and a generate-type-tables helper.
Tests, ignore patterns & configs
packages/leadtype/src/cli.test.ts, .gitignore, apps/c15t-example/*
Adds Vitest assertions for multi-source generation, include expansion, and mount URL behavior; adds .docs-src/ to .gitignore, along with example app configs and package manifest entries.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • inthhq/leadtype#28: Related CLI generation and docs-config loading changes that intersect with multi-source/mount handling.
  • inthhq/leadtype#21: Prior work on docs URL/path normalization and related utilities that this PR extends with mounts.
  • inthhq/leadtype#26: Changes to LLM/full-context artifact generation related to this PR’s mount-aware LLM updates.

"🐰 I stitched the docs from many nests,
mounting changelog paths at rest.
Includes expanded, flattened bits —
search and LLMs now see it.
Hooray for mirrors, tidy and blessed!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Support multiple docs source mounts' clearly and concisely describes the main change—adding multi-source documentation support with URL mount prefixes.
Description check ✅ Passed The description comprehensively documents the feature additions, changes, and validation steps, directly relating to the changeset implementing support for multiple docs sources with custom URL prefixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch KayleeWilliams/multiple-sources

Comment @coderabbitai help to get the list of available commands and usage tips.

@KayleeWilliams KayleeWilliams marked this pull request as ready for review May 11, 2026 23:13
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3a6e4049d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

});

await generateLlmsTxt({
srcDir: sourceRoot,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fix the programmatic multi-source example

In this example, the changelog pages converted into public/docs/changelog will not be included in llms.txt (and the same issue applies to the resolveDocsNavigation call below), because generateLlmsTxt still builds its source map by scanning only ${srcDir}/docs (readSourceDocs(srcDir, ...)), not the generated outDir/docs tree. Users following this documented workflow will get search/readability artifacts for /changelog/*, but the generated docs summary/sidebar will silently omit those pages unless they first merge the sibling source under a temporary docs/ tree as the CLI does.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/build/connect-docs-site.mdx`:
- Line 107: Rewrite the sentence so artifact types are explicit: state that an
internal generated copy remains at public/docs/changelog/v1.md for
search/runtime helpers, that a markdown mirror is written to
public/changelog/v1.md, and that canonical route links (e.g., /changelog/v1) are
emitted into llms.txt, search metadata, sitemap entries, and
agent-readability.json while the .md mirror URL (/changelog/v1.md) is emitted
only where markdown artifacts are represented; update the phrasing to separate
route canonical URLs from markdown mirror URLs and mention the specific
artifacts llms.txt and agent-readability.json to avoid ambiguity.

In `@docs/reference/cli.mdx`:
- Line 88: Update the wording in docs/reference/cli.mdx to distinguish canonical
page URLs (route paths) from markdown mirror URLs: say that the internal copy is
kept at `public/docs/changelog/v1.md`, a static markdown mirror is written to
`public/changelog/v1.md`, but canonical page URLs emitted in artifacts like
`llms.txt`, sitemap entries, search metadata and `agent-readability.json` should
use the route path (e.g. `/changelog/v1`) while the markdown mirror links remain
`/changelog/v1.md`; adjust the sentence that currently lists `/changelog/v1.md`
as the canonical link so it clearly separates the canonical route
(`/changelog/v1`) from the `.md` mirror (`/changelog/v1.md`).

In `@docs/reference/llm.mdx`:
- Line 166: The documentation currently mixes markdown mirror URLs (e.g.
`/docs/quickstart.md` and `/changelog/v1.md`) with the route-style paths
expected by sitemap/navigation/search (referenced in llms.txt and llms-full.txt
and the navigation/sitemap/Agent Readability/search metadata). Update the docs
so all navigation, sitemap, Agent Readability, and search metadata use route
paths (e.g. `/docs/quickstart` and `/changelog/v1`) while leaving markdown
mirror links only where a file path is required (e.g. in content references),
and ensure llms.txt / llms-full.txt entries are regenerated or adjusted to
contain the route-style paths.

In `@packages/leadtype/src/cli/generate.ts`:
- Around line 726-732: The catch block that handles resolveDocsSources currently
writes directly to stderr (io.stderr.write) and should instead use the existing
logger pattern when args.format === "json" to keep output structured; update the
catch to call logger.error with a JSON/structured payload (matching the style
used in the block handling lines 739-755) when args.format === "json", otherwise
fall back to io.stderr.write or logger.error plain text, and still return 1;
reference resolveDocsSources, docsSources, args.format, and logger to locate and
change the error handling.

In `@packages/leadtype/src/internal/docs-url.ts`:
- Around line 61-96: Add a short comment inside the resolveDocsPathMount
function explaining its guaranteed fallback behavior: that it always returns a
mount and mountedRelativePath, and when no configured mount matches it returns
the default mount { pathPrefix: "", urlPrefix: "/docs" } with the normalized
relative path; place this clarification near the normalizedMounts calculation or
before the final return so future readers understand why the function never
returns null/undefined and why the default mount is used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4d80cd65-be96-434a-a524-bd0814e8a23c

📥 Commits

Reviewing files that changed from the base of the PR and between 3e03d9d and b3a6e40.

📒 Files selected for processing (15)
  • .changeset/multiple-docs-source-mounts.md
  • docs/authoring/components.mdx
  • docs/build/connect-docs-site.mdx
  • docs/reference/cli.mdx
  • docs/reference/convert.mdx
  • docs/reference/llm.mdx
  • docs/reference/remark.mdx
  • docs/reference/search.mdx
  • packages/leadtype/src/cli.test.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/internal/docs-url.test.ts
  • packages/leadtype/src/internal/docs-url.ts
  • packages/leadtype/src/llm/llm.ts
  • packages/leadtype/src/llm/readability.ts
  • packages/leadtype/src/search/node.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/internal/docs-url.test.ts
  • packages/leadtype/src/llm/readability.ts
  • packages/leadtype/src/search/node.ts
  • packages/leadtype/src/cli.test.ts
  • packages/leadtype/src/internal/docs-url.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/llm/llm.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/internal/docs-url.test.ts
  • packages/leadtype/src/llm/readability.ts
  • packages/leadtype/src/search/node.ts
  • packages/leadtype/src/cli.test.ts
  • packages/leadtype/src/internal/docs-url.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/llm/llm.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/internal/docs-url.test.ts
  • packages/leadtype/src/cli.test.ts
🪛 markdownlint-cli2 (0.22.1)
.changeset/multiple-docs-source-mounts.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (23)
.changeset/multiple-docs-source-mounts.md (1)

1-5: Changeset entry is correctly scoped and release note text matches the shipped behavior.

docs/reference/remark.mdx (1)

92-95: ⚡ Quick win

No changes needed. The <import> and <include-c15t> tags shown in lines 92-95 are supported by the remarkInclude plugin and are valid examples. The plugin implementation explicitly includes these tag names in its processing list.

			> Likely an incorrect or invalid review comment.
packages/leadtype/src/internal/docs-url.ts (2)

38-46: LGTM: Clean mount type definition and URL normalization.

The DocsPathMount type is well-defined, and normalizeUrlPrefix correctly handles the edge case where normalization yields an empty string by returning "/".


98-127: LGTM: Mount-aware URL path generation is correct.

Both functions correctly handle mounted paths. The distinction between .md and /index.md suffixes in toMountedMarkdownUrlPath properly accounts for index vs. regular files.

packages/leadtype/src/internal/docs-url.test.ts (1)

20-32: LGTM: Comprehensive test coverage for mounted routes.

The test validates both regular files and index files with custom mounts, covering the key scenarios introduced by this PR.

packages/leadtype/src/llm/readability.ts (2)

475-499: LGTM: Manifest-aware markdown resolution improves accuracy.

Using the manifest to resolve markdown mirror targets is more robust than the heuristic approach, especially with custom URL mounts. The function correctly validates the resolved path and builds the target.


634-636: LGTM: Sensible fallback chain for markdown target resolution.

Trying manifest-aware resolution first, then falling back to the heuristic approach, provides the best of both worlds—accuracy when manifest data is available, compatibility when it's not.

packages/leadtype/src/llm/llm.ts (3)

145-171: LGTM: Consistent addition of optional mounts configuration.

The mounts parameter is added to all relevant config types with proper typing and as an optional field, maintaining backward compatibility.


464-506: LGTM: Link rendering correctly uses mounted markdown URL paths.

Both pageToRenderedLink and resolveCuratedLink now use toMountedMarkdownUrlPath to generate links that respect the configured URL mounts. The fallback to toMarkdownUrlPath for unknown curated links is appropriate.


527-629: LGTM: Source reading functions properly propagate mounts.

Both readSourceDocs and readMarkdownDocs correctly pass mounts to toUrlPath when computing URL paths, ensuring that the generated docs metadata reflects the configured URL structure.

packages/leadtype/src/search/node.ts (1)

31-39: LGTM: Search generation correctly uses mounts for URL paths.

The addition of mounts to the config and its use in toDocsUrlPath ensures search results link to the correct mounted URLs.

Also applies to: 87-108

packages/leadtype/src/cli/generate.ts (7)

159-217: LGTM: CLI argument parsing correctly supports multiple docs directories.

The shift from singular docsDir to plural docsDirs array, with accumulation on each --docs-dir flag, properly enables the multi-source feature. The default fallback when no --docs-dir is provided maintains backward compatibility.


443-464: LGTM: Mount syntax parsing has good validation.

The parsing of <dir>=<url-prefix> syntax includes appropriate guards:

  • Rejects empty dir or urlPrefix values
  • Prevents mounting at the site root

These validations prevent common configuration errors.


466-502: LGTM: Source resolution with mount collision detection.

The mount path collision check (lines 483-489) correctly prevents multiple sources from resolving to the same internal mount path, which would cause file conflicts. The default URL prefix logic appropriately distinguishes between the first source (/docs) and subsequent sources (/docs/<mountPath>).


511-539: LGTM: File conflict detection prevents silent overwrites.

Checking for existing files before copying (lines 530-533) and throwing an error with context ensures that multi-source configurations don't silently overwrite files, which would be difficult to debug.


541-587: LGTM: Proper cleanup in filtered source copying.

The staging directory is correctly cleaned up in the finally block, and errors are re-thrown after cleanup, ensuring temporary files don't leak even when the pipeline fails.


598-641: LGTM: Mounted markdown mirror copying with safety checks.

The validation that mounted output directories stay inside outDir (lines 616-624) prevents path traversal issues. The optimization to skip copying when the URL prefix matches the expected default (lines 606-608) avoids unnecessary work.


643-694: LGTM: Source mirror creation with fast path optimization.

The fast path for the default single-source case (lines 654-668) avoids unnecessary copying when no multi-source or filtering is needed. Error cleanup (lines 682-683) prevents temporary directory leaks.

packages/leadtype/src/cli.test.ts (2)

377-466: LGTM: Comprehensive test for multi-source generation.

The test validates the complete multi-source flow:

  • Source merging from separate directories
  • JSON output structure with docsDirs array
  • Generated file locations in the merged tree
  • Downstream artifact correctness (llms.txt, agent-readability.json)

468-572: LGTM: Thorough test coverage for URL prefix mounting.

This test validates the entire mounted URL feature:

  • Mount syntax parsing (changelog=/changelog)
  • JSON output includes mount configuration
  • Markdown mirrors exist in both internal and mounted locations
  • All downstream artifacts (llms.txt, manifest, search index) use the mounted URL prefix

The assertions at lines 558-564 are particularly valuable—they verify that the generated metadata contains the expected mounted URLs rather than the default paths.

docs/reference/search.mdx (1)

39-52: LGTM: Clear documentation of mounts parameter.

The example effectively demonstrates the mounts configuration and explains the key behavior: indexing reads content from the internal docs directory structure while generating search results that point to the mounted URL paths.

docs/reference/cli.mdx (1)

64-87: Strong multi-source CLI documentation update.

This section is clear and actionable; the two examples make the default mount behavior vs explicit URL mount behavior easy to follow.

docs/build/connect-docs-site.mdx (1)

172-230: Great end-to-end custom pipeline example.

This is a solid, implementation-ready sequence (conversion → mounts-aware generators → runtime outputs), and Line 230 closes a common integration gap for static mounted markdown paths.

--base-url https://docs.example.com
```

That still keeps an internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, but it also writes `public/changelog/v1.md` and emits canonical links such as `/changelog/v1` and `/changelog/v1.md` in `llms.txt`, search metadata, sitemap entries, and `agent-readability.json`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify artifact-specific URL forms on Line 107.

The sentence currently groups canonical route URLs and markdown mirror URLs together across all artifacts. Splitting by artifact type will prevent readers from assuming sitemap/navigation canonical entries include .md.

Suggested wording tweak
-That still keeps an internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, but it also writes `public/changelog/v1.md` and emits canonical links such as `/changelog/v1` and `/changelog/v1.md` in `llms.txt`, search metadata, sitemap entries, and `agent-readability.json`.
+That still keeps an internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, but it also writes `public/changelog/v1.md`. Route metadata uses canonical paths such as `/changelog/v1` (for example in sitemap and manifest/search page URLs), while markdown-link artifacts use `/changelog/v1.md` (for example in `llms.txt`).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
That still keeps an internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, but it also writes `public/changelog/v1.md` and emits canonical links such as `/changelog/v1` and `/changelog/v1.md` in `llms.txt`, search metadata, sitemap entries, and `agent-readability.json`.
That still keeps an internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, but it also writes `public/changelog/v1.md`. Route metadata uses canonical paths such as `/changelog/v1` (for example in sitemap and manifest/search page URLs), while markdown-link artifacts use `/changelog/v1.md` (for example in `llms.txt`).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/build/connect-docs-site.mdx` at line 107, Rewrite the sentence so
artifact types are explicit: state that an internal generated copy remains at
public/docs/changelog/v1.md for search/runtime helpers, that a markdown mirror
is written to public/changelog/v1.md, and that canonical route links (e.g.,
/changelog/v1) are emitted into llms.txt, search metadata, sitemap entries, and
agent-readability.json while the .md mirror URL (/changelog/v1.md) is emitted
only where markdown artifacts are represented; update the phrasing to separate
route canonical URLs from markdown mirror URLs and mention the specific
artifacts llms.txt and agent-readability.json to avoid ambiguity.

Comment thread docs/reference/cli.mdx
--out public
```

That keeps the internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, also writes a static markdown mirror at `public/changelog/v1.md`, and emits canonical links such as `/changelog/v1.md` in `llms.txt`, search metadata, sitemap entries, and `agent-readability.json`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Separate canonical page URLs from markdown mirror URLs.

Line 88 currently implies .md URLs are canonical in artifacts like sitemap/manifest. Canonical page URLs are typically route paths (for example /changelog/v1), while markdown mirror links are .md (for example /changelog/v1.md). Tightening this wording avoids routing confusion.

Suggested wording tweak
-That keeps the internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, also writes a static markdown mirror at `public/changelog/v1.md`, and emits canonical links such as `/changelog/v1.md` in `llms.txt`, search metadata, sitemap entries, and `agent-readability.json`.
+That keeps the internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, also writes a static markdown mirror at `public/changelog/v1.md`, emits route URLs such as `/changelog/v1` in sitemap/manifest/navigation metadata, and emits markdown mirror URLs such as `/changelog/v1.md` where markdown links are expected (for example in `llms.txt`).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
That keeps the internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, also writes a static markdown mirror at `public/changelog/v1.md`, and emits canonical links such as `/changelog/v1.md` in `llms.txt`, search metadata, sitemap entries, and `agent-readability.json`.
That keeps the internal generated copy at `public/docs/changelog/v1.md` for search and runtime helpers, also writes a static markdown mirror at `public/changelog/v1.md`, emits route URLs such as `/changelog/v1` in sitemap/manifest/navigation metadata, and emits markdown mirror URLs such as `/changelog/v1.md` where markdown links are expected (for example in `llms.txt`).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/cli.mdx` at line 88, Update the wording in
docs/reference/cli.mdx to distinguish canonical page URLs (route paths) from
markdown mirror URLs: say that the internal copy is kept at
`public/docs/changelog/v1.md`, a static markdown mirror is written to
`public/changelog/v1.md`, but canonical page URLs emitted in artifacts like
`llms.txt`, sitemap entries, search metadata and `agent-readability.json` should
use the route path (e.g. `/changelog/v1`) while the markdown mirror links remain
`/changelog/v1.md`; adjust the sentence that currently lists `/changelog/v1.md`
as the canonical link so it clearly separates the canonical route
(`/changelog/v1`) from the `.md` mirror (`/changelog/v1.md`).

Comment thread docs/reference/llm.mdx
];
```

With those mounts, `public/docs/quickstart.md` is still `/docs/quickstart.md`, while `public/docs/changelog/v1.md` is described as `/changelog/v1.md` in `llms.txt`, `llms-full.txt`, navigation, sitemap data, Agent Readability metadata, and search metadata.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Line 166 mixes markdown URLs with navigation/sitemap URL paths.

Navigation and sitemap data should generally describe route paths (for example /changelog/v1), not markdown mirror paths (.md). Please split these to keep API expectations precise.

Suggested wording tweak
-With those mounts, `public/docs/quickstart.md` is still `/docs/quickstart.md`, while `public/docs/changelog/v1.md` is described as `/changelog/v1.md` in `llms.txt`, `llms-full.txt`, navigation, sitemap data, Agent Readability metadata, and search metadata.
+With those mounts, `public/docs/quickstart.md` still maps to `/docs/quickstart(.md)`, while `public/docs/changelog/v1.md` maps to `/changelog/v1(.md)`. Markdown artifacts (for example `llms.txt`) use `.md` links, while navigation/sitemap/manifest/search route metadata uses route paths (for example `/changelog/v1`).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
With those mounts, `public/docs/quickstart.md` is still `/docs/quickstart.md`, while `public/docs/changelog/v1.md` is described as `/changelog/v1.md` in `llms.txt`, `llms-full.txt`, navigation, sitemap data, Agent Readability metadata, and search metadata.
With those mounts, `public/docs/quickstart.md` still maps to `/docs/quickstart(.md)`, while `public/docs/changelog/v1.md` maps to `/changelog/v1(.md)`. Markdown artifacts (for example `llms.txt`) use `.md` links, while navigation/sitemap/manifest/search route metadata uses route paths (for example `/changelog/v1`).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/llm.mdx` at line 166, The documentation currently mixes
markdown mirror URLs (e.g. `/docs/quickstart.md` and `/changelog/v1.md`) with
the route-style paths expected by sitemap/navigation/search (referenced in
llms.txt and llms-full.txt and the navigation/sitemap/Agent Readability/search
metadata). Update the docs so all navigation, sitemap, Agent Readability, and
search metadata use route paths (e.g. `/docs/quickstart` and `/changelog/v1`)
while leaving markdown mirror links only where a file path is required (e.g. in
content references), and ensure llms.txt / llms-full.txt entries are regenerated
or adjusted to contain the route-style paths.

Comment on lines +726 to +732
let docsSources: ResolvedDocsSource[];
try {
docsSources = resolveDocsSources(srcDir, args.docsDirs);
} catch (error) {
io.stderr.write(`${String(error)}\n`);
return 1;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use logger for consistent error formatting.

When args.format === "json", errors should use logger.error to provide structured output, similar to the handling at lines 739-755. Currently, source resolution errors bypass the logger and write directly to stderr, breaking JSON formatting.

🔧 Suggested fix for consistent error handling
   let docsSources: ResolvedDocsSource[];
   try {
     docsSources = resolveDocsSources(srcDir, args.docsDirs);
   } catch (error) {
-    io.stderr.write(`${String(error)}\n`);
+    const message = error instanceof Error ? error.message : String(error);
+    if (args.format === "json") {
+      logger.error({
+        human: { message },
+        json: {
+          event: "generate.resolve_sources_failed",
+          fields: { error: message, docsDirs: args.docsDirs },
+        },
+      });
+    } else {
+      io.stderr.write(`leadtype generate: ${message}\n`);
+    }
     return 1;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/cli/generate.ts` around lines 726 - 732, The catch
block that handles resolveDocsSources currently writes directly to stderr
(io.stderr.write) and should instead use the existing logger pattern when
args.format === "json" to keep output structured; update the catch to call
logger.error with a JSON/structured payload (matching the style used in the
block handling lines 739-755) when args.format === "json", otherwise fall back
to io.stderr.write or logger.error plain text, and still return 1; reference
resolveDocsSources, docsSources, args.format, and logger to locate and change
the error handling.

Comment on lines +61 to +96
function resolveDocsPathMount(
relativePath: string,
mounts: DocsPathMount[] | undefined
): { mount: DocsPathMount; mountedRelativePath: string } {
const normalizedPath = normalizeDocsPath(relativePath);
const normalizedMounts = (
mounts && mounts.length > 0
? mounts
: [{ pathPrefix: "", urlPrefix: "/docs" }]
)
.map((mount) => ({
pathPrefix: normalizeMountPathPrefix(mount.pathPrefix),
urlPrefix: normalizeUrlPrefix(mount.urlPrefix),
}))
.sort((left, right) => right.pathPrefix.length - left.pathPrefix.length);

for (const mount of normalizedMounts) {
if (!mount.pathPrefix) {
return { mount, mountedRelativePath: normalizedPath };
}
if (
normalizedPath === mount.pathPrefix ||
normalizedPath.startsWith(`${mount.pathPrefix}/`)
) {
return {
mount,
mountedRelativePath: normalizedPath.slice(mount.pathPrefix.length + 1),
};
}
}

return {
mount: { pathPrefix: "", urlPrefix: "/docs" },
mountedRelativePath: normalizedPath,
};
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider documenting the fallback behavior.

The function always returns a mount (falling back to the default /docs mount when no match is found). A brief comment explaining this guarantee would help future maintainers understand why the function doesn't return null or undefined.

📝 Optional documentation improvement
+/**
+ * Resolve a relative docs path to its mount and mounted relative path.
+ * Always returns a mount; falls back to the default /docs mount when no match is found.
+ */
 function resolveDocsPathMount(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/internal/docs-url.ts` around lines 61 - 96, Add a short
comment inside the resolveDocsPathMount function explaining its guaranteed
fallback behavior: that it always returns a mount and mountedRelativePath, and
when no configured mount matches it returns the default mount { pathPrefix: "",
urlPrefix: "/docs" } with the normalized relative path; place this clarification
near the normalizedMounts calculation or before the final return so future
readers understand why the function never returns null/undefined and why the
default mount is used.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/c15t-example/src/app.tsx`:
- Around line 239-253: collectHeadings currently generates IDs from heading text
via slugify(element.textContent) which can create duplicates; change the ID
assignment so it produces unique IDs by deriving a base slug (using slugify)
then, if document.getElementById(candidate) already exists, append a numeric
suffix (-1, -2, ...) incrementing until the id is unique, then assign element.id
= candidate; keep the rest of the mapping (id, level, text) and the final filter
the same so TOC links point to distinct sections.
- Around line 273-289: ShellLink currently swallows native anchor props and
intercepts all internal clicks; update the ShellLink component to accept and
forward all anchor props (use rest props like ...props and/or convert to
React.forwardRef to forward ref) and only intercept plain left-clicks for
internal links: compute isInternal from href, and in the onClick handler return
early if the click has modifiers (event.metaKey, event.ctrlKey, event.shiftKey,
event.altKey), if event.button !== 0 (middle/right click), or if
event.defaultPrevented; only then call event.preventDefault() and
navigateTo(href). Ensure you pass {...props} through to the <a> so attributes
like target, rel, aria-* are preserved.

In `@apps/c15t-example/src/styles.css`:
- Line 7: Change the text-rendering value to lowercase to satisfy stylelint:
locate the text-rendering declaration (text-rendering: optimizeLegibility;) and
replace the value with its lowercase form (optimizelegibility) so the keyword
conforms to the configured value-keyword-case rule.

In `@packages/leadtype/src/cli/generate.ts`:
- Around line 466-495: resolveDocsSources currently doesn't check for duplicate
or overlapping public prefixes, so different mounts can resolve to the same or
nested resolvedUrlPrefix and later clash in copyMountedMarkdownMirrors; modify
resolveDocsSources to compute the final resolvedUrlPrefix for each normalized
input (using normalizeUrlPrefix when needed) and maintain a set of normalized,
lower-cased resolved prefixes, rejecting any new resolvedUrlPrefix that is equal
to or a path-prefix of an existing one (and vice versa) by throwing a clear
Error; reference resolveDocsSources, normalizedInputs, resolvedUrlPrefix,
urlPrefix, mountPath and ensure the same validation logic is applied where
mounts are constructed so copyMountedMarkdownMirrors cannot receive overlapping
prefixes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a6a023d1-9c3a-4903-8861-fb5542938515

📥 Commits

Reviewing files that changed from the base of the PR and between b3a6e40 and 82cd6ab.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .changeset/multiple-docs-source-mounts.md
  • .gitignore
  • apps/c15t-example/.gitignore
  • apps/c15t-example/README.md
  • apps/c15t-example/index.html
  • apps/c15t-example/package.json
  • apps/c15t-example/scripts/generate-artifacts.ts
  • apps/c15t-example/scripts/setup-source.ts
  • apps/c15t-example/src/app.tsx
  • apps/c15t-example/src/main.tsx
  • apps/c15t-example/src/styles.css
  • apps/c15t-example/src/vite-env.d.ts
  • apps/c15t-example/tsconfig.json
  • apps/c15t-example/vite.config.ts
  • docs/authoring/components.mdx
  • docs/reference/remark.mdx
  • packages/leadtype/src/cli.test.ts
  • packages/leadtype/src/cli/generate.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • apps/c15t-example/vite.config.ts
  • apps/c15t-example/src/main.tsx
  • apps/c15t-example/scripts/setup-source.ts
  • apps/c15t-example/src/vite-env.d.ts
  • apps/c15t-example/scripts/generate-artifacts.ts
  • apps/c15t-example/src/app.tsx
  • packages/leadtype/src/cli.test.ts
  • packages/leadtype/src/cli/generate.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • apps/c15t-example/vite.config.ts
  • apps/c15t-example/src/main.tsx
  • apps/c15t-example/scripts/setup-source.ts
  • apps/c15t-example/src/vite-env.d.ts
  • apps/c15t-example/scripts/generate-artifacts.ts
  • apps/c15t-example/src/app.tsx
  • packages/leadtype/src/cli.test.ts
  • packages/leadtype/src/cli/generate.ts
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/c15t-example/index.html
  • apps/c15t-example/src/main.tsx
  • apps/c15t-example/src/app.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/c15t-example/src/main.tsx
  • apps/c15t-example/src/app.tsx
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/cli.test.ts
🪛 markdownlint-cli2 (0.22.1)
.changeset/multiple-docs-source-mounts.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🪛 Stylelint (17.11.0)
apps/c15t-example/src/styles.css

[error] 7-7: Expected "optimizeLegibility" to be "optimizelegibility" (value-keyword-case)

(value-keyword-case)

🔇 Additional comments (15)
apps/c15t-example/.gitignore (1)

1-2: Ignore rules look correct for generated outputs.

dist and public being ignored here is appropriate for this example app workflow.

.changeset/multiple-docs-source-mounts.md (1)

1-6: Changeset content is clear and release-scoped.

Patch classification and description align with the implementation scope in this PR.

apps/c15t-example/tsconfig.json (1)

1-15: TS config wiring looks consistent with the app setup.

The path aliases, includes, and no-emit typecheck profile are well aligned with this example project.

apps/c15t-example/package.json (1)

6-36: Script and dependency setup is coherent for the example app.

The workflow from source setup → artifact generation → Vite dev/build is wired cleanly.

apps/c15t-example/scripts/setup-source.ts (1)

14-30: Source bootstrap flow looks solid.

Clone vs refresh behavior is clear, and git failures are surfaced immediately.

.gitignore (1)

14-18: New root ignore entries are appropriate.

Ignoring .docs-src/ and .playwright-mcp/ is consistent with local-only artifact handling.

apps/c15t-example/scripts/generate-artifacts.ts (1)

20-42: CLI invocation and mount wiring look correct.

The multi-source --docs-dir usage and mapped changelog mount are passed through as expected.

apps/c15t-example/vite.config.ts (1)

51-64: MDX/React plugin pipeline looks solid.

remarkInclude is wired before conversion, and the MDX+React integration is correctly scoped to .mdx and JS/TS entrypoints.

packages/leadtype/src/cli.test.ts (1)

511-615: Great coverage for custom mount propagation.

This test validates mount-aware URLs across summary, readability manifest, and search artifacts, which is exactly the failure surface for this feature.

apps/c15t-example/index.html (1)

1-12: Entry HTML is clean and correct.

Document scaffolding and root bootstrap target are set up properly.

apps/c15t-example/src/main.tsx (1)

6-16: Bootstrap flow looks good.

The explicit root null-check plus createRoot(...).render(...) is correct and robust.

apps/c15t-example/README.md (1)

3-16: README guidance is clear and actionable.

The mounted-source explanation and refresh note are concise and useful for local workflows.

apps/c15t-example/src/vite-env.d.ts (1)

3-11: Type declarations are appropriate for the app setup.

The MDX and font package module declarations match the introduced imports and bundling flow.

docs/authoring/components.mdx (1)

32-73: Nice addition to authoring docs.

This section clearly explains include reuse patterns and the required plugin ordering in custom conversion scripts.

docs/reference/remark.mdx (1)

78-130: Documentation update is clear and actionable.

The expanded remarkInclude section explains ordering, include forms, section anchors, and nested path resolution well, and it matches the behavior described in this PR.

Comment thread apps/c15t-example/src/app.tsx Outdated
Comment thread apps/c15t-example/src/app.tsx Outdated
Comment thread apps/c15t-example/src/styles.css Outdated
Comment on lines +466 to +495
function resolveDocsSources(
srcDir: string,
docsDir: string,
args: GenerateArgs
): Promise<SourceMirror> {
const filters = {
exclude: [...args.exclude],
include: [...args.include],
};
const hasFilters = filters.include.length > 0 || filters.exclude.length > 0;
docsDirs: string[]
): ResolvedDocsSource[] {
const parsedInputs = docsDirs.map(parseDocsSourceInput);
const normalizedInputs = parsedInputs.map((entry) => ({
input: normalizeDocsSourceInput(entry.docsDir),
urlPrefix: entry.urlPrefix
? normalizeUrlPrefix(entry.urlPrefix)
: undefined,
}));
const mountPaths = new Set<string>();
return normalizedInputs.map(({ input, urlPrefix }, index) => {
const docsDir = path.resolve(srcDir, input);
const mountPath =
index === 0 ? "" : normalizeDocsPath(path.basename(input || docsDir));
if (mountPath) {
const mountKey = mountPath.toLowerCase();
if (mountPaths.has(mountKey)) {
throw new Error(
`Multiple docs sources resolve to the same mount path "${mountPath}". Use distinct source folder names.`
);
}
mountPaths.add(mountKey);
}
const resolvedUrlPrefix =
urlPrefix ?? (mountPath ? `/docs/${mountPath}` : "/docs");
return { docsDir, input, mountPath, urlPrefix: resolvedUrlPrefix };
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject duplicate/overlapping mounted urlPrefix values during source resolution.

At Line 492, prefixes are accepted without cross-mount conflict checks. If mounts resolve to the same or nested public prefixes, copyMountedMarkdownMirrors can recursively delete another mount’s output (Line 625) and produce nondeterministic artifacts.

🔧 Suggested guard in resolveDocsSources
 function resolveDocsSources(
   srcDir: string,
   docsDirs: string[]
 ): ResolvedDocsSource[] {
   const parsedInputs = docsDirs.map(parseDocsSourceInput);
   const normalizedInputs = parsedInputs.map((entry) => ({
     input: normalizeDocsSourceInput(entry.docsDir),
     urlPrefix: entry.urlPrefix
       ? normalizeUrlPrefix(entry.urlPrefix)
       : undefined,
   }));
   const mountPaths = new Set<string>();
+  const urlPrefixes: string[] = [];
   return normalizedInputs.map(({ input, urlPrefix }, index) => {
     const docsDir = path.resolve(srcDir, input);
     const mountPath =
       index === 0 ? "" : normalizeDocsPath(path.basename(input || docsDir));
@@
-    const resolvedUrlPrefix =
-      urlPrefix ?? (mountPath ? `/docs/${mountPath}` : "/docs");
+    const resolvedUrlPrefix = normalizeUrlPrefix(
+      urlPrefix ?? (mountPath ? `/docs/${mountPath}` : "/docs")
+    );
+    const conflictingPrefix = urlPrefixes.find(
+      (existing) =>
+        existing === resolvedUrlPrefix ||
+        existing.startsWith(`${resolvedUrlPrefix}/`) ||
+        resolvedUrlPrefix.startsWith(`${existing}/`)
+    );
+    if (conflictingPrefix) {
+      throw new Error(
+        `Multiple docs sources resolve to overlapping URL prefixes "${conflictingPrefix}" and "${resolvedUrlPrefix}". Use distinct non-overlapping prefixes.`
+      );
+    }
+    urlPrefixes.push(resolvedUrlPrefix);
     return { docsDir, input, mountPath, urlPrefix: resolvedUrlPrefix };
   });
 }

Also applies to: 598-641

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/cli/generate.ts` around lines 466 - 495,
resolveDocsSources currently doesn't check for duplicate or overlapping public
prefixes, so different mounts can resolve to the same or nested
resolvedUrlPrefix and later clash in copyMountedMarkdownMirrors; modify
resolveDocsSources to compute the final resolvedUrlPrefix for each normalized
input (using normalizeUrlPrefix when needed) and maintain a set of normalized,
lower-cased resolved prefixes, rejecting any new resolvedUrlPrefix that is equal
to or a path-prefix of an existing one (and vice versa) by throwing a clear
Error; reference resolveDocsSources, normalizedInputs, resolvedUrlPrefix,
urlPrefix, mountPath and ensure the same validation logic is applied where
mounts are constructed so copyMountedMarkdownMirrors cannot receive overlapping
prefixes.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
apps/c15t-example/src/styles.css (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Lowercase the text-rendering keyword.

optimizeLegibility still fails the configured value-keyword-case rule, so this file will keep the stylelint check red.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/styles.css` at line 7, Change the value of the CSS
property text-rendering from "optimizeLegibility" to a lowercase keyword to
satisfy the value-keyword-case rule; locate the declaration using the
text-rendering property in apps/c15t-example/src/styles.css and replace
optimizeLegibility with optimizelegibility (same semantics, lowercase).
apps/c15t-example/src/app.tsx (2)

419-433: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Generate unique IDs for duplicate headings.

IDs are still derived only from heading text. Repeated h2/h3 values produce duplicate ids, duplicate React keys, and TOC links that jump to the first matching section instead of the intended one.

Suggested fix
 const collectHeadings = () =>
   Array.from(
     document.querySelectorAll<HTMLElement>(".doc-content h2, .doc-content h3")
   )
-    .map((element) => {
-      if (!element.id) {
-        element.id = slugify(element.textContent ?? "");
-      }
-      return {
-        id: element.id,
-        level: Number(element.tagName.slice(1)),
-        text: element.textContent ?? "",
-      };
-    })
+    .reduce<Heading[]>((acc, element) => {
+      const text = element.textContent ?? "";
+      const baseId = element.id || slugify(text);
+      if (!baseId || !text) {
+        return acc;
+      }
+
+      let candidate = baseId;
+      let suffix = 1;
+      while (document.getElementById(candidate)) {
+        if (document.getElementById(candidate) === element) {
+          break;
+        }
+        candidate = `${baseId}-${suffix}`;
+        suffix += 1;
+      }
+
+      element.id = candidate;
+      acc.push({
+        id: candidate,
+        level: Number(element.tagName.slice(1)),
+        text,
+      });
+      return acc;
+    }, [])
     .filter((heading) => heading.id && heading.text);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/app.tsx` around lines 419 - 433, collectHeadings
currently creates IDs solely from heading text which yields duplicates for
repeated headings; modify collectHeadings to track slug usage (e.g., a Map or
object counter) and when slugify(element.textContent) produces a slug already
seen, append a suffix like `-1`, `-2`, etc., assign that unique id to element.id
and return it in the object so React keys/TOC links are unique; ensure you
reference the existing collectHeadings function and the slugify call when adding
the counter logic.

453-469: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve native anchor behavior in ShellLink.

This still drops anchor props and intercepts every internal click. That breaks modified-click navigation, target/rel/aria-* passthrough, and hash-link behavior that browsers handle natively.

Suggested fix
-const ShellLink = ({ children, className, href = "" }: ComponentProps<"a">) => {
+const ShellLink = ({
+  children,
+  className,
+  href = "",
+  onClick,
+  ...rest
+}: ComponentProps<"a">) => {
   const isInternal = href.startsWith("/");
   return (
     <a
       className={className}
       href={href}
+      {...rest}
       onClick={(event) => {
-        if (!isInternal) {
+        onClick?.(event);
+        if (
+          event.defaultPrevented ||
+          !isInternal ||
+          event.button !== 0 ||
+          event.metaKey ||
+          event.ctrlKey ||
+          event.shiftKey ||
+          event.altKey
+        ) {
           return;
         }
         event.preventDefault();
         navigateTo(href);
       }}
     >
       {children}
     </a>
   );
 };
As per coding guidelines, `**/*.{jsx,tsx,html}`: Add `rel="noopener"` when using `target="_blank"` on links.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/app.tsx` around lines 453 - 469, ShellLink currently
swallows native anchor props and intercepts all internal clicks; update the
ShellLink component to accept and forward the full anchor props (use rest props
like ...props) so attributes like target/rel/aria-* pass through, and only
intercept navigation for plain left-clicks (event.button === 0), with no
modifier keys (ctrl/meta/alt/shift), event.defaultPrevented === false, and when
target is absent or "_self" and the href is a true internal navigation (e.g.,
startsWith("/") but not a hash-only or external URL); call any provided onClick
prop first and respect its defaultPrevented, then preventDefault and call
navigateTo(href) for that specific case; additionally, if target === "_blank"
and no rel provided, add rel="noopener" before spreading props. Reference
ShellLink and navigateTo to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/c15t-example/src/app.tsx`:
- Around line 551-587: PackageCommandTabs always displays commands[0] so
clicking the npm/pnpm/yarn/bun buttons doesn't change the shown command; fix by
adding local state (e.g., activeIndex via useState(0)) inside
PackageCommandTabs, update the onClick handler in the buttons map to
setActiveIndex(index) (use the mapped index when rendering buttons), render the
command using commands[activeIndex][1] instead of commands[0][1], and optionally
apply an "active" CSS class to the button when index === activeIndex for visual
feedback; reference the PackageCommandTabs component, the commands array, the
buttons map, and the <code> element to locate where to change.

---

Duplicate comments:
In `@apps/c15t-example/src/app.tsx`:
- Around line 419-433: collectHeadings currently creates IDs solely from heading
text which yields duplicates for repeated headings; modify collectHeadings to
track slug usage (e.g., a Map or object counter) and when
slugify(element.textContent) produces a slug already seen, append a suffix like
`-1`, `-2`, etc., assign that unique id to element.id and return it in the
object so React keys/TOC links are unique; ensure you reference the existing
collectHeadings function and the slugify call when adding the counter logic.
- Around line 453-469: ShellLink currently swallows native anchor props and
intercepts all internal clicks; update the ShellLink component to accept and
forward the full anchor props (use rest props like ...props) so attributes like
target/rel/aria-* pass through, and only intercept navigation for plain
left-clicks (event.button === 0), with no modifier keys (ctrl/meta/alt/shift),
event.defaultPrevented === false, and when target is absent or "_self" and the
href is a true internal navigation (e.g., startsWith("/") but not a hash-only or
external URL); call any provided onClick prop first and respect its
defaultPrevented, then preventDefault and call navigateTo(href) for that
specific case; additionally, if target === "_blank" and no rel provided, add
rel="noopener" before spreading props. Reference ShellLink and navigateTo to
locate the change.

In `@apps/c15t-example/src/styles.css`:
- Line 7: Change the value of the CSS property text-rendering from
"optimizeLegibility" to a lowercase keyword to satisfy the value-keyword-case
rule; locate the declaration using the text-rendering property in
apps/c15t-example/src/styles.css and replace optimizeLegibility with
optimizelegibility (same semantics, lowercase).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 596afc89-f80d-4671-ac50-cc50433da60a

📥 Commits

Reviewing files that changed from the base of the PR and between 82cd6ab and 4a8af53.

📒 Files selected for processing (2)
  • apps/c15t-example/src/app.tsx
  • apps/c15t-example/src/styles.css
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • apps/c15t-example/src/app.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • apps/c15t-example/src/app.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/c15t-example/src/app.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/c15t-example/src/app.tsx
🪛 Stylelint (17.11.0)
apps/c15t-example/src/styles.css

[error] 7-7: Expected "optimizeLegibility" to be "optimizelegibility" (value-keyword-case)

(value-keyword-case)

Comment thread apps/c15t-example/src/app.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/c15t-example/src/app.tsx`:
- Around line 670-696: The Tabs component currently renders all Tab children and
the item buttons do nothing; update Tabs to import React's Children and
isValidElement, maintain internal state (e.g., activeIndex) and set it via
onClick handlers on the mapped item buttons, then render only the child matching
the active index (filtering Children.toArray(children) and verifying with
isValidElement) while keeping Tab as the presentation component; ensure the
button keys still use items and that Tab's optional value prop remains supported
so the selected tab's label/value can be shown when active.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f0fc7d70-def0-4ce3-a09e-c58ee90bc7c6

📥 Commits

Reviewing files that changed from the base of the PR and between 4a8af53 and acc472a.

📒 Files selected for processing (1)
  • apps/c15t-example/src/app.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • apps/c15t-example/src/app.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • apps/c15t-example/src/app.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/c15t-example/src/app.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/c15t-example/src/app.tsx
🔇 Additional comments (8)
apps/c15t-example/src/app.tsx (8)

500-514: Duplicate heading IDs when text matches.

This issue was already flagged in a previous review. Headings with identical text will produce duplicate IDs, causing TOC links to target incorrect sections.


534-550: ShellLink still drops anchor props and intercepts modified clicks.

This issue was already flagged in a previous review. The component should forward rest props and respect modifier keys (Ctrl/Cmd/Shift clicks) for native browser behavior.


632-668: PackageCommandTabs still shows only the first command.

This issue was already flagged in a previous review. The tab buttons need state management to actually switch the displayed command.


1-70: Well-structured module setup.

Good use of top-level regex constants and properly typed Vite glob imports with eager loading.


277-324: LGTM!

Utility functions are pure, well-typed, and handle edge cases appropriately with proper null checks.


767-828: LGTM!

Good accessibility with aria-pressed on toggle buttons and defensive filtering of sidebar items against the route map.


902-930: LGTM!

Hooks are correctly structured with proper dependency arrays and cleanup functions. Using requestAnimationFrame for DOM-dependent heading collection after render is appropriate.


932-968: LGTM!

Clean layout structure with proper semantic HTML elements (article, header, main, aside) and good component composition with MDXProvider.

Comment thread apps/c15t-example/src/app.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (5)
apps/c15t-example/src/app.tsx (4)

523-537: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Ensure heading IDs are unique before building TOC links.

collectHeadings can assign duplicate IDs for repeated headings, which breaks anchor targeting and TOC keys.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/app.tsx` around lines 523 - 537, collectHeadings can
assign duplicate IDs for repeated headings; fix it by tracking used IDs (e.g., a
Set) while building IDs via slugify in collectHeadings and, if a generated id
already exists, append a suffix/counter (like -1, -2, ...) until unique, assign
that unique id to element.id, and use those unique ids for the returned objects
so TOC links and keys are always distinct.

675-711: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Package manager tabs are non-interactive right now.

Button clicks never change the displayed command; the code block always renders the first entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/app.tsx` around lines 675 - 711, PackageCommandTabs
currently always shows commands[0] and the buttons do nothing; add local
component state (e.g., selectedIndex or selectedManager) in PackageCommandTabs
to track which tab is active, initialize it to 0, wire each button's onClick to
update that state (use the manager or index from commands.map), and change the
rendered code block to use commands[selectedIndex]?.[1] so clicking a button
updates the displayed command; ensure button key/label remains manager and
optionally add an active CSS class based on the selected state.

577-593: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve native anchor semantics and forward anchor props in ShellLink.

Current interception catches all internal clicks and drops props like target, rel, and ARIA attributes.

Proposed fix
-const ShellLink = ({ children, className, href = "" }: ComponentProps<"a">) => {
+const ShellLink = ({
+  children,
+  className,
+  href = "",
+  onClick,
+  ...rest
+}: ComponentProps<"a">) => {
   const isInternal = href.startsWith("/");
   return (
     <a
       className={className}
       href={href}
+      {...rest}
       onClick={(event) => {
-        if (!isInternal) {
+        onClick?.(event);
+        if (
+          event.defaultPrevented ||
+          !isInternal ||
+          event.button !== 0 ||
+          event.metaKey ||
+          event.ctrlKey ||
+          event.shiftKey ||
+          event.altKey
+        ) {
           return;
         }
         event.preventDefault();
         navigateTo(href);
       }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/app.tsx` around lines 577 - 593, The ShellLink
component currently swallows native anchor props and always intercepts internal
clicks; update ShellLink to accept and forward all anchor props (use
ComponentProps<"a"> or AnchorHTMLAttributes<HTMLAnchorElement>) and forward a
ref (React.forwardRef) to the <a> element, then in the onClick handler only
preventDefault and call navigateTo(href) for same-origin internal links on
unmodified left-clicks (e.g., event.button === 0 && !event.metaKey &&
!event.ctrlKey && !event.shiftKey && !event.altKey) and when target is undefined
or "_self"; otherwise let the browser handle target/_blank/rel and preserve ARIA
attributes by spreading remaining props onto the <a> element.

713-732: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tabs renders controls but never switches visible content.

All tab children stay visible regardless of the selected tab, so the component doesn’t behave as tabs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/app.tsx` around lines 713 - 732, The Tabs component
renders all children but never tracks which tab is active; update the Tabs
component to use React state (e.g., useState) to store the selected tab index,
convert children to an array with React.Children.toArray, wire each button in
the inline-tab-list to set the active index on click, and render only the active
child (childrenArray[selectedIndex]) instead of rendering all children; ensure
the buttons have stable keys (use the items values) and handle missing items by
falling back to index labels.
apps/c15t-example/src/styles.css (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Lowercase the text-rendering keyword to satisfy stylelint.

Use the lowercase keyword form on Line 7 to satisfy value-keyword-case.

Proposed fix
-  text-rendering: optimizeLegibility;
+  text-rendering: optimizelegibility;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/c15t-example/src/styles.css` at line 7, Lowercase the CSS keyword for
the text-rendering property: locate the declaration using text-rendering (the
value currently "optimizeLegibility") and change the value to all lowercase
("optimizelegibility") so it satisfies stylelint's value-keyword-case rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/c15t-example/scripts/generate-type-tables.ts`:
- Around line 114-133: The extracted type paths are being resolved against
sourceRoot but references come from docsRoot, so update the call/site where
extractTypeFromFile is invoked (in the loop over references produced by
findAutoTypeTableReferences) to resolve/derive paths relative to docsRoot
instead of sourceRoot; ensure any path passed into extractTypeFromFile (and any
helper that computes the file path for a reference) uses docsRoot as the base so
valid tables discovered under docsRoot are not skipped.

In `@apps/c15t-example/src/app.tsx`:
- Around line 762-816: The UI conflates "loading" and "missing" because
typeTable uses null for both; change the semantics so undefined = loading and
null = missing: initialize typeTable to undefined, call setTypeTable(undefined)
before load in the useEffect, keep the success handler using
setTypeTable(payload.tables[typeTableKey(path, name)] ?? null) and keep
setTypeTableError as-is; then replace the existing checks: if (typeTable ===
undefined) show the "Loading extracted props..." state, if (typeTable === null)
show a distinct "No extracted props" (or similar) message; references:
loadTypeTables, typeTableKey, setTypeTable, typeTable, typeTableError,
setTypeTableError.
- Around line 559-569: The current loadTypeTables function caches a rejected
Promise in typeTablesPromise, preventing retries; modify loadTypeTables so it
does not permanently cache failures by either (a) creating and awaiting the
fetch, only assigning the resolved payload to typeTablesPromise (or a separate
cache) after a successful response, or (b) attaching a .catch handler to the
created Promise that clears typeTablesPromise on rejection before rethrowing the
error; reference loadTypeTables and the typeTablesPromise variable and ensure
subsequent calls can retry after a failed fetch.

---

Duplicate comments:
In `@apps/c15t-example/src/app.tsx`:
- Around line 523-537: collectHeadings can assign duplicate IDs for repeated
headings; fix it by tracking used IDs (e.g., a Set) while building IDs via
slugify in collectHeadings and, if a generated id already exists, append a
suffix/counter (like -1, -2, ...) until unique, assign that unique id to
element.id, and use those unique ids for the returned objects so TOC links and
keys are always distinct.
- Around line 675-711: PackageCommandTabs currently always shows commands[0] and
the buttons do nothing; add local component state (e.g., selectedIndex or
selectedManager) in PackageCommandTabs to track which tab is active, initialize
it to 0, wire each button's onClick to update that state (use the manager or
index from commands.map), and change the rendered code block to use
commands[selectedIndex]?.[1] so clicking a button updates the displayed command;
ensure button key/label remains manager and optionally add an active CSS class
based on the selected state.
- Around line 577-593: The ShellLink component currently swallows native anchor
props and always intercepts internal clicks; update ShellLink to accept and
forward all anchor props (use ComponentProps<"a"> or
AnchorHTMLAttributes<HTMLAnchorElement>) and forward a ref (React.forwardRef) to
the <a> element, then in the onClick handler only preventDefault and call
navigateTo(href) for same-origin internal links on unmodified left-clicks (e.g.,
event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey &&
!event.altKey) and when target is undefined or "_self"; otherwise let the
browser handle target/_blank/rel and preserve ARIA attributes by spreading
remaining props onto the <a> element.
- Around line 713-732: The Tabs component renders all children but never tracks
which tab is active; update the Tabs component to use React state (e.g.,
useState) to store the selected tab index, convert children to an array with
React.Children.toArray, wire each button in the inline-tab-list to set the
active index on click, and render only the active child
(childrenArray[selectedIndex]) instead of rendering all children; ensure the
buttons have stable keys (use the items values) and handle missing items by
falling back to index labels.

In `@apps/c15t-example/src/styles.css`:
- Line 7: Lowercase the CSS keyword for the text-rendering property: locate the
declaration using text-rendering (the value currently "optimizeLegibility") and
change the value to all lowercase ("optimizelegibility") so it satisfies
stylelint's value-keyword-case rule.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fdc44fdf-d2d8-431f-9fff-523e9a8ea06c

📥 Commits

Reviewing files that changed from the base of the PR and between acc472a and 1b818b9.

📒 Files selected for processing (6)
  • apps/c15t-example/scripts/generate-artifacts.ts
  • apps/c15t-example/scripts/generate-type-tables.ts
  • apps/c15t-example/src/app.tsx
  • apps/c15t-example/src/styles.css
  • packages/leadtype/src/remark/plugins/type-table.remark.ts
  • packages/leadtype/src/remark/remark-output.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/remark/remark-output.test.ts
  • packages/leadtype/src/remark/plugins/type-table.remark.ts
  • apps/c15t-example/scripts/generate-artifacts.ts
  • apps/c15t-example/scripts/generate-type-tables.ts
  • apps/c15t-example/src/app.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/remark/remark-output.test.ts
  • packages/leadtype/src/remark/plugins/type-table.remark.ts
  • apps/c15t-example/scripts/generate-artifacts.ts
  • apps/c15t-example/scripts/generate-type-tables.ts
  • apps/c15t-example/src/app.tsx
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/remark/remark-output.test.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/c15t-example/src/app.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/c15t-example/src/app.tsx
🪛 Stylelint (17.11.0)
apps/c15t-example/src/styles.css

[error] 7-7: Expected "optimizeLegibility" to be "optimizelegibility" (value-keyword-case)

(value-keyword-case)

🔇 Additional comments (3)
packages/leadtype/src/remark/plugins/type-table.remark.ts (1)

785-795: AutoTypeTable alias wiring looks consistent.

Validation, node classification, and processor registration all align for AutoTypeTable → extracted-table flow.

Also applies to: 912-918

packages/leadtype/src/remark/remark-output.test.ts (1)

110-140: Good coverage for the AutoTypeTable alias path.

This test exercises the exact alias behavior and validates extracted markdown output content.

apps/c15t-example/scripts/generate-artifacts.ts (1)

21-49: Generation flow and failure handling are clean.

The CLI exit code gate before type-table generation is the right control flow here.

Comment thread apps/c15t-example/scripts/generate-type-tables.ts Outdated
Comment thread apps/c15t-example/src/app.tsx
Comment thread apps/c15t-example/src/app.tsx
@KayleeWilliams KayleeWilliams merged commit 60c285c into main May 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant