Skip to content

Add headless docs search and AI answers#4

Closed
KayleeWilliams wants to merge 2 commits into
mainfrom
KayleeWilliams/docs-search-ai
Closed

Add headless docs search and AI answers#4
KayleeWilliams wants to merge 2 commits into
mainfrom
KayleeWilliams/docs-search-ai

Conversation

@KayleeWilliams
Copy link
Copy Markdown
Collaborator

Adds a headless @inth/docs search surface with static index generation, BM25-style runtime search, abuse guards, rate limiting, and an AI SDK streaming helper. Wires the docs-smoke app with debounced realtime search, explicit AI answers using moonshotai/kimi-k2.6 by default, API routes, heading deep links, and a generated demo index. Updates package docs, agent-docs output, route generation, and e2e coverage. Verified with package tests, app typechecks, Playwright e2e, and Ultracite checks.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a docs search experience with debounced queries, results listing, and a dedicated /search and /docs/search pages.
    • Optional AI-powered, source‑grounded answers with streaming support and gated user action.
  • Bug Fixes / Reliability

    • Per-client rate limiting on search and answer endpoints to improve stability.
  • Documentation

    • Comprehensive guides and examples for search, index generation, and AI answer usage.
  • Chores

    • Build now generates a static search index and ignores build/test output in the repo.

Walkthrough

Adds build-time static docs index generation, an in-memory search engine with query guards and rate limiting, AI answer streaming, server search/ask endpoints, client search pages (with MDX heading ID helpers), route registration, tests, docs, and packaging/build updates including a search-generation script.

Changes

Cohort / File(s) Summary
Build & Packaging
apps/docs-smoke/.gitignore, apps/docs-smoke/package.json, packages/docs/package.json, packages/docs/tsup.config.ts
Ignore new output dirs; add pipeline:search script and ai dependency/peer; export new ./search subpaths and add tsup entrypoints; mark ai external.
Index Generation Script
apps/docs-smoke/scripts/search-generate.ts, apps/docs-smoke/scripts/mdx-convert.ts
Add search-generate.ts to call package index generator and copy result into app src/generated; adjust mdx-convert path resolution to use app-level content/public.
Search Node Generator
packages/docs/src/search/node.ts, packages/docs/src/search/node-index.ts
New Node-only generateSearchIndex with config/result types; re-export node entry.
Core Search Engine
packages/docs/src/search/search.ts, packages/docs/src/search/index.ts
New in-memory search implementation (tokenization, heading-aware chunking, BM25-like scoring), API utilities (validate, readJsonWithLimit, slugify), rate limiter factory, answer-context builder, defaults, and public barrel exports.
AI Answering
packages/docs/src/search/ai.ts, packages/docs/src/search/ai-index.ts
New streamDocsAnswer implementation that builds an answer context and invokes a streaming text provider; added barrel export for AI entry.
Runtime Search Integration
apps/docs-smoke/src/lib/search.ts, apps/docs-smoke/src/lib/docs.ts
Import generated index, export typed docsSearchIndex, memory rate limiters, demo API result types, isAiAnswerEnabled, jsonResponse; add demo routes for /search and /docs/search.
Client UI & MDX components
apps/docs-smoke/src/routes/search.tsx, apps/docs-smoke/src/routes/docs/search.tsx, apps/docs-smoke/src/mdx-components.tsx
Add /search page with debounced search, streaming ask flow, and results UI; add /docs/search route rendering MDX; add heading components to auto-generate heading IDs.
Server API Routes
apps/docs-smoke/src/routes/api/docs/search.ts, apps/docs-smoke/src/routes/api/docs/ask.ts
Add /api/docs/search (GET) and /api/docs/ask (GET/POST streaming) with query validation, per-client rate limiting, and error handling.
Route Registration
apps/docs-smoke/src/routeTree.gen.ts
Register new /search, /docs/search, /api/docs/search, /api/docs/ask routes and update generated route types/children.
Tests
packages/docs/src/search/search.test.ts, packages/docs/src/search/ai.test.ts, apps/docs-smoke/tests/e2e/smoke.e2e.ts
Add comprehensive unit tests for search, AI streaming tests, and Playwright e2e tests covering search UI and API responses.
Docs & Content
packages/docs/README.md, packages/docs/agent-docs/**, apps/docs-smoke/content/docs/*, packages/docs/scripts/generate-agent-docs.ts
Add extensive documentation and example pages describing index generation, runtime search usage, AI streaming, and abuse-guard patterns; add search page to app docs/meta.

Sequence Diagram(s)

sequenceDiagram
    participant Build as rgba(90,150,255,0.5)
    participant MDX as rgba(120,200,150,0.5)
    participant Generator as rgba(255,200,100,0.5)
    participant FS as rgba(200,200,200,0.5)
    participant JSON as rgba(150,150,250,0.5)

    Build->>MDX: Read markdown docs
    Build->>Generator: Invoke generateSearchIndex(docs)
    Generator->>FS: Traverse docs/ and read files
    FS-->>Generator: Return file contents
    Generator->>Generator: Parse, tokenize, chunk, build index
    Generator->>JSON: Serialize index to search-index.json
    JSON-->>FS: Write file to disk
    Generator-->>Build: Return counts (docs/chunks/terms)
Loading
sequenceDiagram
    participant Client as rgba(120,180,255,0.5)
    participant SearchAPI as rgba(180,120,255,0.5)
    participant RateLimit as rgba(255,150,120,0.5)
    participant Engine as rgba(120,255,180,0.5)
    participant Index as rgba(200,200,100,0.5)

    Client->>Client: Debounce query
    Client->>SearchAPI: GET /api/docs/search?q=...
    SearchAPI->>RateLimit: check("search:clientId")
    alt denied
        RateLimit-->>SearchAPI: deny
        SearchAPI-->>Client: 429 + Retry-After
    else allowed
        RateLimit-->>SearchAPI: allow
        SearchAPI->>Engine: searchDocs(Index, query)
        Engine->>Index: score & rank chunks
        Engine-->>SearchAPI: results
        SearchAPI-->>Client: JSON { results }
    end
Loading
sequenceDiagram
    participant Client as rgba(120,180,255,0.5)
    participant AskAPI as rgba(180,120,255,0.5)
    participant RateLimit as rgba(255,150,120,0.5)
    participant Context as rgba(200,255,150,0.5)
    participant AI as rgba(255,200,100,0.5)
    participant Model as rgba(150,150,250,0.5)

    Client->>AskAPI: POST /api/docs/ask { query }
    AskAPI->>RateLimit: check("ask:clientId")
    alt denied
        RateLimit-->>AskAPI: deny
        AskAPI-->>Client: 429 + Retry-After
    else allowed
        RateLimit-->>AskAPI: allow
        AskAPI->>Context: createAnswerContext(index, query)
        Context-->>AskAPI: system + prompt + sources
        AskAPI->>AI: streamText(model, prompts...)
        AI->>Model: stream request
        Model-->>AI: token stream
        AI-->>AskAPI: streaming Response
        AskAPI-->>Client: proxy streamed text + sources
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 I hopped through docs, indexed each page,

Chunks and headings lined up on the stage,
BM25 hums while tokens take flight,
Rate limits keep everything polite,
Answers stream in, sources in sight. 🥕🔍

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'Add headless docs search and AI answers' clearly and concisely summarizes the main additions: a search feature and AI-powered answer capability for the docs package.
Description check ✅ Passed The description is directly related to the changeset, providing detailed context about the search surface, BM25 implementation, rate limiting, app integration, and verification approach.
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 docstrings
  • Create stacked PR
  • Commit on current branch

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

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: f960bb74e1

ℹ️ 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".

setAnswer("");
setError("");
setAnswerStatus("loading");
const nextResults = await runSearch(trimmedQuery);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Skip pre-search gating when submitting long Ask queries

handleAsk always calls runSearch before posting to /api/docs/ask, so questions longer than 400 chars are rejected by /api/docs/search (maxQueryChars) even though the UI allows 600 chars and /api/docs/ask explicitly accepts askMaxQueryChars (600). In that case the Ask flow never reaches the answer endpoint and incorrectly reports “No matching docs,” which blocks valid long-form questions.

Useful? React with 👍 / 👎.

if (chunk.done) {
break;
}
setAnswer((current) => current + decoder.decode(chunk.value));
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 Decode streamed answer chunks in incremental mode

Each streamed chunk is decoded with decoder.decode(chunk.value) without { stream: true }, so multi-byte UTF-8 characters split across chunk boundaries can be corrupted in the rendered answer text. This appears in normal SSE/text streaming whenever chunk boundaries do not align with character boundaries.

Useful? React with 👍 / 👎.

Comment on lines +555 to +557
const chunk = index.chunks.find(
(candidate) => candidate.id === posting.chunkId
);
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 Index chunks by id before scoring BM25 postings

The scorer does a linear index.chunks.find(...) for every posting and again for every scored chunk, which turns lookup cost into O(postings × chunks) and can dominate latency on larger docs sets. Since this path runs on every query, search response time degrades sharply as the index grows; building a Map of chunk IDs once would avoid the repeated scans.

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: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/docs-smoke/scripts/search-generate.ts`:
- Line 9: Replace the relative source import with the package export to exercise
the published build: change the import that currently pulls generateSearchIndex
from "../../../packages/docs/src/search/node-index.ts" to import {
generateSearchIndex } from "@inth/docs/search/node"; update any tooling or
TypeScript config if needed so the package resolution works at runtime; ensure
the symbol generateSearchIndex is imported from the package export and
tests/scripts still run.

In `@apps/docs-smoke/src/mdx-components.tsx`:
- Around line 8-16: The textFromChildren function currently only handles
strings, numbers, and arrays and returns "" for React elements, which leads to
empty heading IDs; update textFromChildren (used for HeadingProps["children"])
to detect React elements/objects and, when an object with a props property
exists, recurse into element.props.children (also handle null/undefined/boolean
children) so text is extracted from nested elements like <code> or <span>; keep
existing behavior for strings/numbers/arrays and ensure the function always
returns a trimmed string suitable for generating anchor IDs.

In `@apps/docs-smoke/src/routes/api/docs/ask.ts`:
- Around line 65-70: The handler currently returns only the response from
streamDocsAnswer and discards sources; update the endpoint to include the
returned sources so clients can display citations — call streamDocsAnswer(...)
and capture both response and sources, then return an object containing both
(e.g., { response, sources }) or, if streaming/compatibility requires, serialize
sources into a response header (e.g., "x-doc-sources") before returning; look
for the call to streamDocsAnswer and the variable docsSearchIndex to locate the
code to change.

In `@apps/docs-smoke/src/routes/search.tsx`:
- Around line 139-145: The POST to "/api/docs/ask" (the fetch call creating
`const response = await fetch("/api/docs/ask", { ... body: JSON.stringify({
query: trimmedQuery }) })`) lacks an AbortController, so previous answer streams
keep running; create an AbortController per query, pass controller.signal into
fetch, store the controller (e.g., in a local variable or React ref) and call
controller.abort() before starting a new request or when unmounting/navigating
to cancel any in-flight answer stream and cleanup resources.
- Around line 159-168: The streaming loop that calls reader.read() can throw and
currently leaves answerStatus stuck at "streaming"; wrap the entire read loop in
a try-catch (surround the while(true) { const chunk = await reader.read(); ... }
block) to catch any errors, call setAnswerStatus("error") (or set to "idle" in a
finally) and optionally append an error message via setAnswer, and ensure
resources are cleaned (e.g., close reader) — update references in this file
around reader.read(), setAnswer(...), setAnswerStatus(...) and
decoder.decode(...) to implement the try-catch/finally handling.

In `@packages/docs/src/search/ai.ts`:
- Line 68: Replace the silent onError handler that returns undefined with one
that logs the error for observability: locate the onError callback (onError: ()
=> undefined) in packages/docs/src/search/ai.ts and change it to capture the
error parameter and write a non-throwing log (e.g., console.error or the
existing logger) including context like "AI stream error" and the error object,
so errors are recorded but still don't crash the process.

In `@packages/docs/src/search/search.ts`:
- Around line 734-773: The in-memory Map entries in createMemoryRateLimiter
never get removed causing unbounded growth; update createMemoryRateLimiter to
purge expired keys from entries: when check(identifier) runs, if an existing
entry is expired remove it before creating the new entry (use
entries.delete(identifier)), and additionally add an optional periodic cleanup
(e.g., a timer started inside createMemoryRateLimiter that iterates entries and
deletes those with resetAt <= now()) or a simple max-size eviction policy to
trim the Map; reference functions/vars: createMemoryRateLimiter, entries, check,
resetAt, MemoryRateLimiterOptions.
- Around line 554-560: The loop in searchDocs calls index.chunks.find(...) for
each posting (and again later), causing O(postings × chunks) work; create a Map
keyed by chunk.id (e.g., const chunkMap = new Map(index.chunks.map(c => [c.id,
c]))) once at the start of searchDocs and replace index.chunks.find(...) calls
with chunkMap.get(posting.chunkId) (and similarly where chunk lookups repeat) so
lookups become O(1) and avoid the quadratic behavior.
🪄 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: 2214cfa4-a82f-4e88-bc7e-be0f40f01551

📥 Commits

Reviewing files that changed from the base of the PR and between b922d77 and f960bb7.

⛔ Files ignored due to path filters (2)
  • apps/docs-smoke/src/generated/docs-search-index.json is excluded by !**/generated/**
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (31)
  • apps/docs-smoke/.gitignore
  • apps/docs-smoke/package.json
  • apps/docs-smoke/scripts/mdx-convert.ts
  • apps/docs-smoke/scripts/search-generate.ts
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/lib/search.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
  • packages/docs/README.md
  • packages/docs/agent-docs-src/docs/search.mdx
  • packages/docs/agent-docs/docs/llms-full.txt
  • packages/docs/agent-docs/docs/llms-full/generation.txt
  • packages/docs/agent-docs/docs/llms-full/generation/search.txt
  • packages/docs/agent-docs/docs/llms.txt
  • packages/docs/agent-docs/docs/search.md
  • packages/docs/agent-docs/llms.txt
  • packages/docs/package.json
  • packages/docs/scripts/generate-agent-docs.ts
  • packages/docs/src/search/ai-index.ts
  • packages/docs/src/search/ai.test.ts
  • packages/docs/src/search/ai.ts
  • packages/docs/src/search/index.ts
  • packages/docs/src/search/node-index.ts
  • packages/docs/src/search/node.ts
  • packages/docs/src/search/search.test.ts
  • packages/docs/src/search/search.ts
  • packages/docs/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{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/docs-smoke/src/lib/docs.ts
  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • apps/docs-smoke/scripts/mdx-convert.ts
  • packages/docs/scripts/generate-agent-docs.ts
  • apps/docs-smoke/scripts/search-generate.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • packages/docs/src/search/ai.test.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • packages/docs/src/search/index.ts
  • apps/docs-smoke/src/routes/search.tsx
  • packages/docs/src/search/ai.ts
  • packages/docs/src/search/ai-index.ts
  • packages/docs/src/search/search.test.ts
  • apps/docs-smoke/src/routeTree.gen.ts
  • packages/docs/src/search/node.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
  • apps/docs-smoke/src/lib/search.ts
  • packages/docs/src/search/search.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/docs-smoke/src/lib/docs.ts
  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • apps/docs-smoke/scripts/mdx-convert.ts
  • packages/docs/scripts/generate-agent-docs.ts
  • apps/docs-smoke/scripts/search-generate.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • packages/docs/src/search/ai.test.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • packages/docs/src/search/index.ts
  • apps/docs-smoke/src/routes/search.tsx
  • packages/docs/src/search/ai.ts
  • packages/docs/src/search/ai-index.ts
  • packages/docs/src/search/search.test.ts
  • apps/docs-smoke/src/routeTree.gen.ts
  • packages/docs/src/search/node.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
  • apps/docs-smoke/src/lib/search.ts
  • packages/docs/src/search/search.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/docs-smoke/src/mdx-components.tsx
  • apps/docs-smoke/src/routes/search.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/docs-smoke/src/mdx-components.tsx
  • apps/docs-smoke/src/routes/search.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/docs/src/search/ai.test.ts
  • packages/docs/src/search/search.test.ts
**/index.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Avoid barrel files (index files that re-export everything)

Files:

  • packages/docs/src/search/index.ts
🧠 Learnings (69)
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/**/*.mdx : Include code examples where appropriate in MDX documentation pages of the docs app

Applied to files:

  • packages/docs/agent-docs/llms.txt
  • apps/docs-smoke/scripts/mdx-convert.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • packages/docs/agent-docs-src/docs/search.mdx
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/**/*.mdx : Use MDX for documentation pages

Applied to files:

  • packages/docs/agent-docs/llms.txt
  • apps/docs-smoke/scripts/mdx-convert.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • packages/docs/agent-docs-src/docs/search.mdx
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/app/**/*.{ts,tsx} : Use Next.js App Router conventions in the docs app

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/app/**/page.tsx : Place page components in appropriate route segments within the docs app

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/app/**/page.tsx : Place page components in appropriate route segments

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Include integration tests for component combinations

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2026-03-19T22:57:45.114Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:45.114Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Include unit tests for components, integration tests for component combinations, accessibility tests, and error boundary tests

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
  • packages/docs/src/search/search.test.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:57:45.114Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:45.114Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Document components with purpose and use case, required and optional props, context requirements, example usage, and common pitfalls

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{tsx,jsx} : For each component, documentation should include: purpose and use case, required and optional props, context requirements, example usage, and common pitfalls

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/index.{ts,tsx} : Export type definitions for public API in Elements package

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/package.json
  • packages/docs/src/search/ai-index.ts
  • packages/docs/src/search/node.ts
  • apps/docs-smoke/src/lib/search.ts
  • packages/docs/src/search/search.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.{ts,tsx} : Export type definitions for public API

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/package.json
  • packages/docs/src/search/ai-index.ts
  • packages/docs/src/search/node.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{ts,tsx} : Export type definitions for public API in TypeScript libraries

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/package.json
  • packages/docs/src/search/ai-index.ts
  • packages/docs/src/search/node.ts
  • apps/docs-smoke/src/lib/search.ts
📚 Learning: 2026-02-20T14:21:19.208Z
Learnt from: CR
Repo: consentdotio/dsar PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2026-02-20T14:21:19.208Z
Learning: Applies to **/index.{ts,tsx,js,jsx} : Avoid barrel files (index files that re-export everything)

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/src/search/ai-index.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `export type` for types

Applied to files:

  • packages/docs/src/search/node-index.ts
📚 Learning: 2026-04-18T15:01:52.690Z
Learnt from: CR
Repo: inthhq/docs PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-18T15:01:52.690Z
Learning: Applies to **/index.{js,ts,jsx,tsx} : Avoid barrel files (index files that re-export everything)

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/package.json
  • packages/docs/src/search/ai-index.ts
📚 Learning: 2026-03-19T22:57:45.114Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:45.114Z
Learning: Applies to **/*.{ts,tsx} : Export type definitions for public API and avoid TypeScript enums; use const objects or 'as const' assertions instead due to runtime overhead and tree-shaking limitations

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
📚 Learning: 2026-04-16T03:27:04.353Z
Learnt from: CR
Repo: inthhq/dsar PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-16T03:27:04.353Z
Learning: Applies to **/index.{js,jsx,ts,tsx} : Avoid barrel files (index files that re-export everything)

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/package.json
  • packages/docs/src/search/ai-index.ts
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-02-19T01:12:44.177Z
Learnt from: CR
Repo: consentdotio/agent-skills PR: 0
File: tsdoc-jsdoc-authoring/AGENTS.md:0-0
Timestamp: 2026-02-19T01:12:44.177Z
Learning: Applies to tsdoc-jsdoc-authoring/**/index.{ts,tsx} : Use `packageDocumentation` for entrypoint docs in TSDoc comments

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
  • apps/docs-smoke/scripts/search-generate.ts
  • packages/docs/README.md
  • packages/docs/src/search/index.ts
  • packages/docs/src/search/node.ts
  • packages/docs/src/search/search.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't export empty modules that don't change anything

Applied to files:

  • packages/docs/src/search/node-index.ts
  • packages/docs/tsup.config.ts
📚 Learning: 2026-02-20T14:21:40.180Z
Learnt from: CR
Repo: consentdotio/dsar PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T14:21:40.180Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Avoid barrel files (index files that re-export everything)

Applied to files:

  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/package.json
  • packages/docs/src/search/ai-index.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{ts,tsx,md} : Include TypeScript examples in Elements package documentation

Applied to files:

  • packages/docs/tsup.config.ts
  • packages/docs/src/search/index.ts
  • packages/docs/src/search/node.ts
  • packages/docs/src/search/search.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{ts,tsx} : Include TSDoc comments for components in Elements package

Applied to files:

  • packages/docs/tsup.config.ts
  • packages/docs/src/search/node.ts
  • packages/docs/src/search/search.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't use specified modules when loaded by import or require

Applied to files:

  • packages/docs/tsup.config.ts
  • packages/docs/package.json
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use the `node:` protocol for Node.js builtin modules

Applied to files:

  • packages/docs/tsup.config.ts
  • packages/docs/package.json
  • packages/docs/src/search/node.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Make sure to use the "use strict" directive in script files

Applied to files:

  • packages/docs/tsup.config.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.{ts,tsx} : Include TypeScript examples

Applied to files:

  • packages/docs/tsup.config.ts
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/**/*.mdx : Use MDX for documentation pages in the docs app

Applied to files:

  • apps/docs-smoke/scripts/mdx-convert.ts
  • apps/docs-smoke/src/mdx-components.tsx
  • packages/docs/agent-docs-src/docs/search.mdx
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't use __dirname and __filename in the global scope

Applied to files:

  • apps/docs-smoke/scripts/mdx-convert.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When managing URL search params, load the router-core search-params skill file from node_modules/tanstack/router-core/skills/router-core/search-params/SKILL.md

Applied to files:

  • packages/docs/scripts/generate-agent-docs.ts
  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.{tsx,jsx} : Follow the Radix UI primitives pattern

Applied to files:

  • apps/docs-smoke/package.json
📚 Learning: 2026-02-01T08:57:09.557Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/typography.mdc:0-0
Timestamp: 2026-02-01T08:57:09.557Z
Learning: Applies to **/*.{ts,tsx} : Use `text-title-h1` through `text-title-h6` tokens for heading elements, with appropriate line-height, letter-spacing, and font-weight already included

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2026-02-20T14:21:19.208Z
Learnt from: CR
Repo: consentdotio/dsar PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2026-02-20T14:21:19.208Z
Learning: Applies to **/*.{tsx,jsx,html} : Use proper heading hierarchy in HTML/React components

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2026-03-19T22:57:54.730Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/typography.mdc:0-0
Timestamp: 2026-03-19T22:57:54.730Z
Learning: Applies to **/*.{ts,tsx,css} : Use `text-title-h1` through `text-title-h6` tokens for heading elements with appropriate line-height, letter-spacing, and font-weight based on semantic heading level

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Give heading elements content that's accessible to screen readers (not hidden with `aria-hidden`)

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{tsx,jsx} : Support custom styling and rendering in components

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2026-03-19T22:57:54.730Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/typography.mdc:0-0
Timestamp: 2026-03-19T22:57:54.730Z
Learning: Applies to **/*.{ts,tsx,css} : Use `text-subheading-*` tokens for uppercase subheadings with wide tracking

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2026-02-20T14:21:19.208Z
Learnt from: CR
Repo: consentdotio/dsar PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2026-02-20T14:21:19.208Z
Learning: Applies to **/*.{tsx,jsx} : Use `next/head` or App Router metadata API for head elements

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/**/*.mdx : Keep documentation organized by categories

Applied to files:

  • apps/docs-smoke/src/mdx-components.tsx
  • packages/docs/README.md
📚 Learning: 2026-02-19T01:12:44.177Z
Learnt from: CR
Repo: consentdotio/agent-skills PR: 0
File: tsdoc-jsdoc-authoring/AGENTS.md:0-0
Timestamp: 2026-02-19T01:12:44.177Z
Learning: Applies to tsdoc-jsdoc-authoring/**/*.{ts,tsx,js,jsx} : Write clear summary sentences in TSDoc and JSDoc comments

Applied to files:

  • packages/docs/README.md
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When adding new routes and pages, load the router-core skill file from node_modules/tanstack/router-core/skills/router-core/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/app/**/* : Use Next.js App Router conventions

Applied to files:

  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When handling errors and 404 pages, load the router-core not-found-and-errors skill file from node_modules/tanstack/router-core/skills/router-core/not-found-and-errors/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When building API endpoints and server routes, load the start-core server-routes skill file from node_modules/tanstack/start-client-core/skills/start-core/server-routes/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/api/docs/search.ts
  • apps/docs-smoke/src/routes/api/docs/ask.ts
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Write unit tests for components in Elements package using Vitest

Applied to files:

  • packages/docs/src/search/ai.test.ts
  • packages/docs/package.json
  • packages/docs/src/search/search.test.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.test.{ts,tsx} : Use Vitest for testing framework

Applied to files:

  • packages/docs/src/search/ai.test.ts
  • packages/docs/package.json
  • packages/docs/src/search/search.test.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `import type` for types

Applied to files:

  • packages/docs/package.json
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `with { type: "json" }` for JSON module imports

Applied to files:

  • packages/docs/package.json
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Make sure all dependencies are correctly specified in React hooks

Applied to files:

  • packages/docs/package.json
📚 Learning: 2026-02-20T14:21:19.208Z
Learnt from: CR
Repo: consentdotio/dsar PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2026-02-20T14:21:19.208Z
Learning: Applies to **/*.{tsx,jsx} : Specify all dependencies in hook dependency arrays correctly

Applied to files:

  • packages/docs/package.json
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Use Vitest as the testing framework for Elements package

Applied to files:

  • packages/docs/package.json
📚 Learning: 2026-04-16T03:27:04.353Z
Learnt from: CR
Repo: inthhq/dsar PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-16T03:27:04.353Z
Learning: Applies to **/*.{jsx,tsx} : Specify all dependencies in hook dependency arrays correctly

Applied to files:

  • packages/docs/package.json
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.test.{ts,tsx} : Include integration tests

Applied to files:

  • packages/docs/package.json
  • packages/docs/src/search/search.test.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Include integration tests for Elements package components

Applied to files:

  • packages/docs/package.json
  • packages/docs/src/search/search.test.ts
📚 Learning: 2025-12-01T15:20:25.102Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/open-graph.mdc:0-0
Timestamp: 2025-12-01T15:20:25.102Z
Learning: Applies to src/**/route.{ts,tsx} : Read dynamic content from `request.nextUrl.searchParams` and pass into pure component props rather than fetching during render

Applied to files:

  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When implementing code splitting, load the router-core code-splitting skill file from node_modules/tanstack/router-core/skills/router-core/code-splitting/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/search.tsx
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When setting up navigation and links between pages, load the router-core navigation skill file from node_modules/tanstack/router-core/skills/router-core/navigation/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Test accessibility features in Elements package component tests

Applied to files:

  • packages/docs/src/search/search.test.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.test.{ts,tsx} : Test accessibility features

Applied to files:

  • packages/docs/src/search/search.test.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:58:00.031Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-19T22:58:00.031Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Write comprehensive unit tests with clear test case names describing what is being tested

Applied to files:

  • packages/docs/src/search/search.test.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.test.{ts,tsx} : Write unit tests for components

Applied to files:

  • packages/docs/src/search/search.test.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Test with screen readers in Elements package component tests

Applied to files:

  • packages/docs/src/search/search.test.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't import next/document outside of pages/_document.jsx in Next.js projects

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When adding auth and route guards, load the router-core auth-and-guards skill file from node_modules/tanstack/router-core/skills/router-core/auth-and-guards/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When fetching data in route loaders, load the router-core data-loading skill file from node_modules/tanstack/router-core/skills/router-core/data-loading/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When implementing type safety patterns, load the router-core type-safety skill file from node_modules/tanstack/router-core/skills/router-core/type-safety/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-02-19T01:12:44.177Z
Learnt from: CR
Repo: consentdotio/agent-skills PR: 0
File: tsdoc-jsdoc-authoring/AGENTS.md:0-0
Timestamp: 2026-02-19T01:12:44.177Z
Learning: Applies to tsdoc-jsdoc-authoring/**/*.{js,jsx} : Use `module` for module-level docs in JSDoc comments

Applied to files:

  • packages/docs/src/search/node.ts
  • packages/docs/src/search/search.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{ts,tsx} : Document all props and types in Elements package TypeScript code

Applied to files:

  • packages/docs/src/search/node.ts
📚 Learning: 2026-02-19T01:12:44.177Z
Learnt from: CR
Repo: consentdotio/agent-skills PR: 0
File: tsdoc-jsdoc-authoring/AGENTS.md:0-0
Timestamp: 2026-02-19T01:12:44.177Z
Learning: Applies to tsdoc-jsdoc-authoring/**/*.{js,jsx} : Document generator output with `yields` in JSDoc comments

Applied to files:

  • packages/docs/src/search/node.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Include accessibility tests for components

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
🪛 LanguageTool
packages/docs/agent-docs/docs/search.md

[style] ~106-~106: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...s or hosted search when your docs reach very large chunk counts, when cold-start memory be...

(EN_WEAK_ADJECTIVE)

packages/docs/agent-docs-src/docs/search.mdx

[style] ~105-~105: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...s or hosted search when your docs reach very large chunk counts, when cold-start memory be...

(EN_WEAK_ADJECTIVE)

packages/docs/agent-docs/docs/llms-full/generation/search.txt

[style] ~114-~114: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...s or hosted search when your docs reach very large chunk counts, when cold-start memory be...

(EN_WEAK_ADJECTIVE)

🔍 Remote MCP

I've encountered the Context7 monthly quota limit. Let me search for relevant technical details using web search to verify the key APIs and patterns being used in this PR.## Additional Context Gathered for PR Review

Based on my search of available documentation and resources, here is the relevant technical context for reviewing this PR:

Vercel AI SDK Integration

The ai package (Vercel AI SDK) reduces boilerplate for streaming responses from AI providers and allows changing providers with minimal code changes. The PR's streamDocsAnswer function in packages/docs/src/search/ai.ts follows this pattern:

  • The streamText function immediately starts streaming to enable sending data without waiting for the model, and suppresses errors to prevent server crashes.
  • The result object includes helper functions like toUIMessageStreamResponse() which creates a UI Message stream HTTP response compatible with Next.js App Router API routes. The PR creates a custom Response with Cache-Control: no-store and Content-Type: text/plain; charset=utf-8 headers instead.

Key observation: The implementation passes a streamTextImpl override parameter, which allows for custom streaming implementations and testability (as seen in ai.test.ts).

TanStack React Router File-Based Routing

Server routes in TanStack Start follow file-based routing conventions where each file with a server property in createFileRoute is treated as an API route. The PR's API routes (/api/docs/search and /api/docs/ask) follow this pattern correctly:

  • Each HTTP method handler receives an object containing the request property (the incoming request object).
  • Handlers can return a Response object or Promise<Response>.

Key observation: The implementation properly uses createFileRoute with server: { handlers: { GET/POST } } syntax for defining API handlers, which aligns with TanStack Router's conventions.

Rate Limiting and Request Guards

The PR implements several defensive patterns:

  • Per-client rate limiting: Uses docsSearchLimiters.search.check() and docsSearchLimiters.ask.check() keyed by client identifier
  • Query validation: validateDocsQuery enforces max length and rejects control characters
  • JSON size limiting: readJsonWithLimit with byte limits to prevent abuse
  • Retry-After headers: Properly formatted rate limit responses (HTTP 429)

These patterns are consistent with serverless/edge deployment best practices where state needs to be managed carefully.

Stream Response Handling

The /api/docs/ask route streams text responses while the /api/docs/search route returns JSON. The client-side search component uses AbortController for request cancellation on query changes, and streams the answer response via TextDecoder to build the answer string progressively.

MDX Component Integration

The PR adds heading ID generation in mdx-components.tsx by extracting plain text from children. This enables heading anchors needed for deep linking in search results (as evidenced by the tests checking URL hash matching like #quickstart).

[::web_search::]

🔇 Additional comments (43)
apps/docs-smoke/.gitignore (1)

1-6: LGTM!

The additions appropriately ignore build artifacts (.output/) and Playwright test results (test-results/) that align with the new search pipeline and e2e testing introduced in this PR.

apps/docs-smoke/scripts/mdx-convert.ts (1)

22-24: LGTM!

The path resolution is now consistent with search-generate.ts, using appRoot as an intermediate variable. This improves maintainability and aligns with the sibling script's directory structure pattern.

apps/docs-smoke/src/mdx-components.tsx (2)

18-31: LGTM!

The createHeading factory correctly:

  • Prioritizes explicit id props when provided
  • Falls back to slugifyDocsHeading for auto-generated IDs (matching search index anchor generation)
  • Converts empty strings to undefined to avoid invalid empty id attributes

33-46: LGTM!

The merge order is correct: custom headings → library defaults → caller overrides. This allows consuming apps to override any component while ensuring heading IDs are generated by default.

packages/docs/agent-docs/llms.txt (1)

16-16: LGTM!

The new Search entry follows the existing format and accurately describes the static search index with AI answer streaming capability.

packages/docs/agent-docs/docs/llms-full/generation.txt (1)

8-9: LGTM!

The new Search topic entry follows the established format and the description correctly matches the referenced search.txt file content.

packages/docs/agent-docs/docs/llms-full.txt (1)

14-14: LGTM!

The Search sub-topic is properly nested under Generation with consistent formatting and accurate description.

packages/docs/src/search/ai-index.ts (1)

1-5: LGTM!

This is a focused subpath entrypoint (./search/ai), not a barrel file. It correctly exports only the public API surface needed for AI answer streaming, aligning with the package.json exports configuration.

packages/docs/agent-docs/docs/llms.txt (1)

24-28: LGTM!

The Generation section summary and new Search entry are consistent with other documentation references in this PR.

apps/docs-smoke/src/lib/docs.ts (1)

9-10: LGTM!

The new /search route is properly added to both the type union and the route array. The structure is consistent with existing entries, and the description accurately captures the feature scope.

Also applies to: 39-43

packages/docs/src/search/node-index.ts (1)

1-5: LGTM!

This is a properly scoped entrypoint for the @inth/docs/search/node subpath export, not a generic barrel file. It correctly uses export type syntax for type-only exports and exposes only the intended public API surface.

packages/docs/tsup.config.ts (1)

9-11: LGTM!

The new search entrypoints follow the existing naming convention and align with the package.json subpath exports. Marking ai as external is correct since it's declared as an optional peer dependency, allowing consumers to provide their own version.

Also applies to: 49-49

apps/docs-smoke/package.json (1)

19-20: LGTM!

The pipeline script additions follow the existing pattern and maintain a logical execution order (convert → llm → search). The ai dependency version ^6.0.168 satisfies the >=6.0.0 peer dependency requirement from @inth/docs.

Also applies to: 37-37

packages/docs/scripts/generate-agent-docs.ts (1)

53-53: LGTM!

The search documentation is consistently added across all three configuration areas: bestStartingPoints, docsSections, and topics. The includePrefixes: ["search"] will correctly match search-related source files for the LLM full-context generation.

Also applies to: 70-76, 133-138

apps/docs-smoke/scripts/search-generate.ts (1)

17-27: LGTM!

The script correctly:

  • Passes outDir pointing to the public directory where converted markdown lives
  • Uses result.outputPath from the generator for the copy source
  • Outputs a clear summary with document/chunk/term counts
apps/docs-smoke/tests/e2e/smoke.e2e.ts (3)

4-6: LGTM!

Good practice defining regex patterns as top-level constants. The QUICKSTART_HEADING_HREF pattern correctly anchors the expected hash fragment.


118-144: Well-structured conditional test for AI enablement state.

The test properly:

  • Fetches the AI configuration before navigation to avoid race conditions
  • Uses an aria-live="polite" region selector for accessibility-aware result targeting
  • Validates the deep link href includes the heading anchor

146-157: LGTM!

The API test validates both the response structure and content. Using response.ok() before parsing is correct for handling non-2xx responses.

packages/docs/README.md (3)

11-13: LGTM!

The package surface listing is complete and accurately describes each subpath export's purpose.


107-117: LGTM!

The operational guidance about the in-memory limiter being suitable only for demos, and the recommendation to use shared stores for production, is practical and helps consumers make informed architectural decisions.


70-106: ⚠️ Potential issue | 🟡 Minor

Fix the model identifier format in the streamDocsAnswer example.

The model parameter uses openai/gpt-5.4-mini, which is incorrect for the Vercel AI SDK. The OpenAI provider expects a plain model ID string like 'gpt-4o-mini' or 'gpt-4-turbo', not a prefixed format. Update the example to: model: process.env.DOCS_SEARCH_MODEL ?? "gpt-4o-mini"

⛔ Skipped due to learnings
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/**/*.mdx : Use MDX for documentation pages
apps/docs-smoke/src/routeTree.gen.ts (1)

1-249: Auto-generated file — no review needed.

This file is automatically generated by TanStack Router and correctly reflects the new /search, /api/docs/search, and /api/docs/ask routes added in this PR.

packages/docs/src/search/ai.test.ts (1)

1-59: LGTM!

Well-structured test that properly validates the streamDocsAnswer function by:

  • Using a stub streamTextImpl to capture call arguments for verification
  • Checking that sources are correctly populated from the search index
  • Verifying the response stream works correctly
  • Asserting that model configuration, tokens, timeout, and prompt content are passed through correctly

The test follows Vitest best practices with async/await and meaningful assertions.

packages/docs/package.json (2)

30-41: LGTM!

The new export subpaths are correctly configured and align with the tsup entry points defined in tsup.config.ts. Each export includes both types and import fields for proper TypeScript and ESM support.


91-106: The ai package version and dependency setup are correct.

Version 6.0.168 exists on npm and has no known direct security advisories. The optional peer dependency configuration is appropriate for allowing consumers to use the library without requiring the AI SDK.

apps/docs-smoke/src/routes/api/docs/search.ts (1)

1-57: LGTM!

Clean implementation of the search API route with proper:

  • Query validation using validateDocsQuery with configured max chars
  • Per-client rate limiting with appropriate Retry-After header calculation
  • Structured error handling that distinguishes DocsSearchRequestError from unexpected errors
  • Early returns for rate-limited requests

The route follows TanStack Router server handler conventions correctly.

packages/docs/agent-docs/docs/search.md (1)

1-108: LGTM!

Comprehensive documentation that accurately covers the search API surface:

  • Correct import paths matching the package exports
  • Clear examples for build-time indexing and runtime usage
  • Appropriate warnings about in-memory rate limiting limitations in serverless environments
  • Helpful guidance on when to consider embeddings over local indexing

The static analysis style hint about "very large" is a minor stylistic preference and doesn't warrant a change.

packages/docs/agent-docs/docs/llms-full/generation/search.txt (1)

1-116: LGTM!

Full context documentation for LLM consumption is consistent with the main search.md documentation. Import paths and code examples accurately reflect the package API surface.

apps/docs-smoke/src/routes/api/docs/ask.ts (1)

1-86: LGTM overall!

Well-structured API route with:

  • Proper feature flag check returning 503 when AI is disabled
  • Per-client rate limiting with Retry-After header
  • Request body size limiting via readJsonWithLimit
  • Query validation with appropriate char limits for ask queries
  • Comprehensive error handling

The GET endpoint for checking availability status is a nice touch for client-side feature detection.

packages/docs/src/search/index.ts (1)

1-28: LGTM!

While the coding guidelines recommend avoiding barrel files, this is an appropriate use case as a package entry point defining the public API surface for @inth/docs/search. The exports are curated (not "re-export everything") and the file serves as the contract between the package internals and consumers.

This pattern is consistent with the ai-index.ts and node-index.ts entry points, and aligns with the package.json exports configuration.

packages/docs/agent-docs-src/docs/search.mdx (1)

1-107: LGTM!

The documentation is well-structured with clear sections covering the full workflow: build-time indexing, runtime search, answer context, AI SDK streaming, and abuse guards. Code examples are relevant and align with the implemented APIs in @inth/docs/search. The production guidance for replacing in-memory rate limiting with shared storage is a helpful callout.

packages/docs/src/search/search.test.ts (1)

1-279: LGTM!

Comprehensive test coverage for the search functionality including normalization, heading/anchor preservation, ranking behavior, excerpts, answer context limits, and all request-guard utilities. Tests are well-organized with descriptive names and proper use of async/await.

packages/docs/src/search/ai.ts (2)

51-79: Well-structured streaming helper with good defaults.

The function cleanly separates concerns: context building via createAnswerContext, streaming via injected streamTextImpl (enabling testability), and response formatting with appropriate cache headers. The dependency injection pattern for streamTextImpl is a nice touch for testing.


62-62: No action needed — the type cast is correct.

The Vercel AI SDK's streamText function accepts model as a union type: string | LanguageModel. Passing a string identifier like "openai/gpt-5.4-mini" is valid and will not fail at runtime. The cast as LanguageModel here properly narrows the type for the StreamTextLike function signature, which expects a LanguageModel type. This is an appropriate use of type assertion when the value is known to satisfy the constraint at runtime.

packages/docs/src/search/node.ts (2)

160-192: LGTM!

The generateSearchIndex function is well-implemented with proper error handling (clear error message for missing docs directory), recursive directory creation, and size/chunk count warnings. The use of node: protocol for built-in modules follows best practices.


99-111: Efficient parallel file collection.

Good use of Promise.all for parallel directory traversal. The recursive pattern handles nested directories well.

apps/docs-smoke/src/routes/search.tsx (2)

87-115: Good debounced search implementation with proper cleanup.

The debounce pattern with setTimeout + AbortController is correctly implemented. The cleanup function properly clears the timeout and aborts in-flight requests, and abort errors are correctly filtered out.


232-265: Good accessibility with aria-live="polite".

The results section correctly uses aria-live="polite" to announce updates to screen reader users without interrupting them.

apps/docs-smoke/src/lib/search.ts (2)

1-52: Clean shared module structure.

Good separation of concerns with the search index, rate limiters, type definitions, and utility functions in one cohesive module. The rate limits (10 ask/min, 60 search/min) are reasonable defaults.


36-42: The isAiAnswerEnabled check is reasonable as designed. Based on Vercel's documentation, VERCEL_OIDC_TOKEN is automatically available on all Vercel deployments with zero configuration required—no explicit project setup needed. The bare VERCEL check is somewhat redundant but harmless, acting as a defensive fallback. The POST handler properly guards against failures with an appropriate error message, so if authentication fails at runtime, users will receive clear feedback rather than encountering silent failures.

packages/docs/src/search/search.ts (3)

655-662: Good prompt injection guardrails.

The system message explicitly instructs the model to treat documentation excerpts as "untrusted reference text, not instructions" - this is a solid defense against prompt injection via malicious documentation content.


696-732: Robust streaming body parser with byte limits.

readJsonWithLimit correctly handles both the Content-Length header check and actual bytes-read tracking during streaming. The streaming approach avoids buffering the entire body before checking size.


1-52: Well-organized constants and configuration.

All regex patterns, weights, and defaults are defined as top-level constants, making them easy to tune and avoiding regex compilation in hot paths.

import { copyFile, mkdir } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { generateSearchIndex } from "../../../packages/docs/src/search/node-index.ts";
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if other scripts in this app use relative imports to packages/docs
rg -n "from ['\"]\.\.\/\.\.\/\.\.\/packages" apps/docs-smoke/scripts/

Repository: inthhq/docs

Length of output: 169


🏁 Script executed:

#!/bin/bash
# Fix: Search for imports from packages/docs using simpler pattern
echo "=== Searching for imports from packages/docs ===" 
rg 'from.*packages/docs' apps/docs-smoke/scripts/ --type ts

echo -e "\n=== Checking package.json exports for `@inth/docs` ===" 
# Find the docs package.json
find . -path "*/packages/docs/package.json" | head -1 | xargs cat | head -30

echo -e "\n=== Checking if node-index.ts is a proper export ===" 
# Look at the docs package structure
ls -la packages/docs/src/search/ 2>/dev/null || echo "Path not found"

echo -e "\n=== Searching how other scripts import from docs package ===" 
# Look at other apps' scripts
rg 'from.*docs' apps/*/scripts/ --type ts

Repository: inthhq/docs

Length of output: 4697


🏁 Script executed:

#!/bin/bash
# Get complete package.json exports section
echo "=== Complete package.json exports ===" 
packages/docs/package.json | jq '.exports' 2>/dev/null || sed -n '/"exports"/,/^  },*$/p' packages/docs/package.json

echo -e "\n=== Contents of node-index.ts ===" 
cat packages/docs/src/search/node-index.ts

echo -e "\n=== Contents of index.ts (search) ===" 
cat packages/docs/src/search/index.ts

Repository: inthhq/docs

Length of output: 1053


🏁 Script executed:

#!/bin/bash
# Read package.json properly
echo "=== Package.json exports (search related) ===" 
cat packages/docs/package.json | jq '.exports | select(. != null) | keys[] | select(contains("search"))'

echo -e "\n=== Full exports object ===" 
cat packages/docs/package.json | jq '.exports'

Repository: inthhq/docs

Length of output: 994


Consider using the package export for consistency with Node.js deployments.

The package exports ./search/node to ./dist/search/node-index.js, which this script could import via @inth/docs/search/node. This exercises the actual published package export rather than relying on relative paths to source files.

Note: Other scripts in this app use relative imports (e.g., ../../../packages/docs/src/convert/index.ts), so this would be inconsistent within the monorepo. Use this approach only if the team standardizes on package imports for docs utilities.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/scripts/search-generate.ts` at line 9, Replace the relative
source import with the package export to exercise the published build: change
the import that currently pulls generateSearchIndex from
"../../../packages/docs/src/search/node-index.ts" to import {
generateSearchIndex } from "@inth/docs/search/node"; update any tooling or
TypeScript config if needed so the package resolution works at runtime; ensure
the symbol generateSearchIndex is imported from the package export and
tests/scripts still run.

Comment on lines +8 to +16
function textFromChildren(children: HeadingProps["children"]): string {
if (typeof children === "string" || typeof children === "number") {
return String(children);
}
if (Array.isArray(children)) {
return children.map(textFromChildren).join(" ");
}
return "";
}
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

React element children may produce empty IDs.

The textFromChildren helper handles strings, numbers, and arrays, but returns an empty string for React elements (e.g., <h1><code>API</code></h1> or <h1><span>Hello</span></h1>). This could result in headings without anchor IDs.

Consider extracting text from React elements with a children prop:

🛠️ Proposed enhancement
 function textFromChildren(children: HeadingProps["children"]): string {
   if (typeof children === "string" || typeof children === "number") {
     return String(children);
   }
   if (Array.isArray(children)) {
     return children.map(textFromChildren).join(" ");
   }
+  if (children && typeof children === "object" && "props" in children) {
+    return textFromChildren((children as { props?: { children?: unknown } }).props?.children);
+  }
   return "";
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/src/mdx-components.tsx` around lines 8 - 16, The
textFromChildren function currently only handles strings, numbers, and arrays
and returns "" for React elements, which leads to empty heading IDs; update
textFromChildren (used for HeadingProps["children"]) to detect React
elements/objects and, when an object with a props property exists, recurse into
element.props.children (also handle null/undefined/boolean children) so text is
extracted from nested elements like <code> or <span>; keep existing behavior for
strings/numbers/arrays and ensure the function always returns a trimmed string
suitable for generating anchor IDs.

Comment on lines +65 to +70
return streamDocsAnswer({
index: docsSearchIndex,
query,
model: process.env.DOCS_SEARCH_MODEL ?? DEFAULT_MODEL,
productName: "@inth/docs",
}).response;
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

Sources metadata is discarded.

The streamDocsAnswer function returns both response and sources, but only response is returned to the client. This means clients cannot display source citations alongside the streamed answer.

If this is intentional (e.g., sources are rendered client-side from the search results), this is fine. Otherwise, consider returning sources via a header or separate endpoint.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/src/routes/api/docs/ask.ts` around lines 65 - 70, The handler
currently returns only the response from streamDocsAnswer and discards sources;
update the endpoint to include the returned sources so clients can display
citations — call streamDocsAnswer(...) and capture both response and sources,
then return an object containing both (e.g., { response, sources }) or, if
streaming/compatibility requires, serialize sources into a response header
(e.g., "x-doc-sources") before returning; look for the call to streamDocsAnswer
and the variable docsSearchIndex to locate the code to change.

Comment on lines +139 to +145
const response = await fetch("/api/docs/ask", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query: trimmedQuery }),
});
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

Consider adding AbortController for answer stream cancellation.

Unlike the search request, the /api/docs/ask POST doesn't use an AbortController. If the user starts a new query or navigates away while streaming, the previous stream continues in the background. For a smoother UX and resource cleanup, consider aborting in-flight answer requests when starting a new one.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/src/routes/search.tsx` around lines 139 - 145, The POST to
"/api/docs/ask" (the fetch call creating `const response = await
fetch("/api/docs/ask", { ... body: JSON.stringify({ query: trimmedQuery }) })`)
lacks an AbortController, so previous answer streams keep running; create an
AbortController per query, pass controller.signal into fetch, store the
controller (e.g., in a local variable or React ref) and call controller.abort()
before starting a new request or when unmounting/navigating to cancel any
in-flight answer stream and cleanup resources.

Comment on lines +159 to +168
while (true) {
const chunk = await reader.read();
if (chunk.done) {
break;
}
setAnswer((current) => current + decoder.decode(chunk.value));
}
setAnswer((current) => current + decoder.decode());
setAnswerStatus("idle");
}
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

Missing error handling in streaming loop.

If reader.read() throws during streaming (e.g., network disconnect), the error propagates unhandled and leaves answerStatus stuck in "streaming" state. Wrap the streaming loop in try-catch:

🛡️ Suggested fix
     setAnswerStatus("streaming");
     const reader = response.body.getReader();
     const decoder = new TextDecoder();
-    while (true) {
-      const chunk = await reader.read();
-      if (chunk.done) {
-        break;
+    try {
+      while (true) {
+        const chunk = await reader.read();
+        if (chunk.done) {
+          break;
+        }
+        setAnswer((current) => current + decoder.decode(chunk.value));
       }
-      setAnswer((current) => current + decoder.decode(chunk.value));
+      setAnswer((current) => current + decoder.decode());
+      setAnswerStatus("idle");
+    } catch {
+      setAnswerStatus("error");
+      setError("Answer streaming failed.");
     }
-    setAnswer((current) => current + decoder.decode());
-    setAnswerStatus("idle");
📝 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
while (true) {
const chunk = await reader.read();
if (chunk.done) {
break;
}
setAnswer((current) => current + decoder.decode(chunk.value));
}
setAnswer((current) => current + decoder.decode());
setAnswerStatus("idle");
}
try {
while (true) {
const chunk = await reader.read();
if (chunk.done) {
break;
}
setAnswer((current) => current + decoder.decode(chunk.value));
}
setAnswer((current) => current + decoder.decode());
setAnswerStatus("idle");
} catch {
setAnswerStatus("error");
setError("Answer streaming failed.");
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/src/routes/search.tsx` around lines 159 - 168, The streaming
loop that calls reader.read() can throw and currently leaves answerStatus stuck
at "streaming"; wrap the entire read loop in a try-catch (surround the
while(true) { const chunk = await reader.read(); ... } block) to catch any
errors, call setAnswerStatus("error") (or set to "idle" in a finally) and
optionally append an error message via setAnswer, and ensure resources are
cleaned (e.g., close reader) — update references in this file around
reader.read(), setAnswer(...), setAnswerStatus(...) and decoder.decode(...) to
implement the try-catch/finally handling.

maxOutputTokens: options.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
timeout: options.timeout ?? DEFAULT_TIMEOUT,
providerOptions: options.providerOptions,
onError: () => undefined,
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

Silent error suppression may hinder debugging.

The onError: () => undefined callback silently discards streaming errors. While this prevents server crashes, it also makes it difficult to diagnose issues in production. Consider at minimum logging the error:

🔧 Suggested improvement
-    onError: () => undefined,
+    onError: ({ error }) => {
+      console.error("[streamDocsAnswer] Stream error:", error);
+    },
📝 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
onError: () => undefined,
onError: ({ error }) => {
console.error("[streamDocsAnswer] Stream error:", error);
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/search/ai.ts` at line 68, Replace the silent onError
handler that returns undefined with one that logs the error for observability:
locate the onError callback (onError: () => undefined) in
packages/docs/src/search/ai.ts and change it to capture the error parameter and
write a non-throwing log (e.g., console.error or the existing logger) including
context like "AI stream error" and the error object, so errors are recorded but
still don't crash the process.

Comment on lines +554 to +560
for (const posting of postings) {
const chunk = index.chunks.find(
(candidate) => candidate.id === posting.chunkId
);
if (!chunk) {
continue;
}
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

O(n²) chunk lookup in search loop.

index.chunks.find() is called inside the posting iteration loop (and again at line 581), resulting in O(postings × chunks) complexity. For large indices with many terms and chunks, this could become a performance bottleneck.

Consider building a Map<string, DocsSearchChunk> lookup at the start of searchDocs:

⚡ Performance optimization
 export function searchDocs(
   index: DocsSearchIndex,
   query: string,
   options: SearchDocsOptions = {}
 ): DocsSearchResult[] {
   const queryTokens = tokenize(query);
   if (queryTokens.length === 0 || index.chunks.length === 0) {
     return [];
   }

+  const chunkById = new Map(index.chunks.map((c) => [c.id, c]));
   const scores = new Map<string, number>();
   // ... later in the loop:
-      const chunk = index.chunks.find(
-        (candidate) => candidate.id === posting.chunkId
-      );
+      const chunk = chunkById.get(posting.chunkId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/search/search.ts` around lines 554 - 560, The loop in
searchDocs calls index.chunks.find(...) for each posting (and again later),
causing O(postings × chunks) work; create a Map keyed by chunk.id (e.g., const
chunkMap = new Map(index.chunks.map(c => [c.id, c]))) once at the start of
searchDocs and replace index.chunks.find(...) calls with
chunkMap.get(posting.chunkId) (and similarly where chunk lookups repeat) so
lookups become O(1) and avoid the quadratic behavior.

Comment on lines +734 to +773
export function createMemoryRateLimiter(
options: MemoryRateLimiterOptions
): RateLimiter {
const entries = new Map<string, { count: number; resetAt: number }>();
const now = options.now ?? Date.now;

return {
check(identifier: string): RateLimitResult {
const currentTime = now();
const existing = entries.get(identifier);
if (!existing || existing.resetAt <= currentTime) {
const resetAt = currentTime + options.windowMs;
entries.set(identifier, { count: 1, resetAt });
return {
allowed: true,
limit: options.limit,
remaining: Math.max(0, options.limit - 1),
resetAt,
};
}

if (existing.count >= options.limit) {
return {
allowed: false,
limit: options.limit,
remaining: 0,
resetAt: existing.resetAt,
};
}

existing.count += 1;
return {
allowed: true,
limit: options.limit,
remaining: Math.max(0, options.limit - existing.count),
resetAt: existing.resetAt,
};
},
};
}
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

In-memory rate limiter entries are never cleaned up.

The entries Map grows indefinitely as new client identifiers are added, but expired entries are never removed. For long-running servers with many unique clients, this could cause memory growth.

Consider adding periodic cleanup or a max-size eviction policy:

🧹 Optional cleanup suggestion
 export function createMemoryRateLimiter(
   options: MemoryRateLimiterOptions
 ): RateLimiter {
   const entries = new Map<string, { count: number; resetAt: number }>();
   const now = options.now ?? Date.now;
+  const MAX_ENTRIES = 10_000;

   return {
     check(identifier: string): RateLimitResult {
       const currentTime = now();
+
+      // Lazy cleanup of expired entries when approaching limit
+      if (entries.size > MAX_ENTRIES) {
+        for (const [key, entry] of entries) {
+          if (entry.resetAt <= currentTime) {
+            entries.delete(key);
+          }
+        }
+      }
+
       const existing = entries.get(identifier);

This is a minor concern since the documentation already notes that production should use shared storage (Redis/KV), but worth considering for demo/development scenarios.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/search/search.ts` around lines 734 - 773, The in-memory Map
entries in createMemoryRateLimiter never get removed causing unbounded growth;
update createMemoryRateLimiter to purge expired keys from entries: when
check(identifier) runs, if an existing entry is expired remove it before
creating the new entry (use entries.delete(identifier)), and additionally add an
optional periodic cleanup (e.g., a timer started inside createMemoryRateLimiter
that iterates entries and deletes those with resetAt <= now()) or a simple
max-size eviction policy to trim the Map; reference functions/vars:
createMemoryRateLimiter, entries, check, resetAt, MemoryRateLimiterOptions.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/docs-smoke/content/docs/index.mdx`:
- Around line 31-34: The docs intro still says “has five package surfaces” but
you added the new package "@inth/docs/search"; update the intro sentence in
apps/docs-smoke/content/docs/index.mdx (the sentence that mentions package
surfaces) to say “six package surfaces” and ensure any adjacent counts or
pluralization remain consistent with the added "@inth/docs/search" entry.

In `@apps/docs-smoke/content/docs/search.mdx`:
- Line 37: Summary: The word "markdown" should be capitalized to "Markdown" for
consistency. Update the text string "Run conversion first, then generate the
search index from markdown:" to "Run conversion first, then generate the search
index from Markdown:" in the docs content (the line containing that sentence in
apps/docs-smoke/content/docs/search.mdx) so the proper noun "Markdown" is used
consistently.

In `@apps/docs-smoke/tests/e2e/smoke.e2e.ts`:
- Around line 141-144: The test parses JSON from the response to
request.get("/api/docs/ask") without first asserting the HTTP success status,
which masks endpoint failures as JSON parse errors; update the test around
answerConfigResponse and answerConfig to assert the response is OK (e.g., check
answerConfigResponse.ok or use your test framework's status assertion) before
calling .json(), and include a descriptive message so failures surface as an
endpoint-status issue rather than JSON parse noise.
🪄 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: e72530fb-c49f-4eed-9a8a-ffb3e14c6959

📥 Commits

Reviewing files that changed from the base of the PR and between f960bb7 and 0dd0d1d.

⛔ Files ignored due to path filters (1)
  • apps/docs-smoke/src/generated/docs-search-index.json is excluded by !**/generated/**
📒 Files selected for processing (7)
  • apps/docs-smoke/content/docs/index.mdx
  • apps/docs-smoke/content/docs/meta.json
  • apps/docs-smoke/content/docs/search.mdx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/routeTree.gen.ts
  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📜 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/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
  • apps/docs-smoke/src/routeTree.gen.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/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
  • apps/docs-smoke/src/routeTree.gen.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/docs-smoke/src/routes/docs/search.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/docs-smoke/src/routes/docs/search.tsx
🧠 Learnings (35)
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/app/**/page.tsx : Place page components in appropriate route segments within the docs app

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/app/**/*.{ts,tsx} : Use Next.js App Router conventions in the docs app

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/app/**/page.tsx : Place page components in appropriate route segments

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When managing URL search params, load the router-core search-params skill file from node_modules/tanstack/router-core/skills/router-core/search-params/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When adding new routes and pages, load the router-core skill file from node_modules/tanstack/router-core/skills/router-core/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/app/**/* : Use Next.js App Router conventions

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/app/**/{layout,page}.tsx : Keep layouts and pages separate in the docs app directory

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/src/components/**/*.tsx : Place reusable components in `src/components` within the docs app

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When adding auth and route guards, load the router-core auth-and-guards skill file from node_modules/tanstack/router-core/skills/router-core/auth-and-guards/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When fetching data in route loaders, load the router-core data-loading skill file from node_modules/tanstack/router-core/skills/router-core/data-loading/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When building API endpoints and server routes, load the start-core server-routes skill file from node_modules/tanstack/start-client-core/skills/start-core/server-routes/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When setting up navigation and links between pages, load the router-core navigation skill file from node_modules/tanstack/router-core/skills/router-core/navigation/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-04-18T15:01:52.690Z
Learnt from: CR
Repo: inthhq/docs PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-18T15:01:52.690Z
Learning: Applies to **/*.{jsx,tsx} : Use Server Components for async data fetching instead of async Client Components in Next.js

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
📚 Learning: 2025-12-01T15:20:25.102Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/open-graph.mdc:0-0
Timestamp: 2025-12-01T15:20:25.102Z
Learning: Applies to src/**/route.{ts,tsx} : Read dynamic content from `request.nextUrl.searchParams` and pass into pure component props rather than fetching during render

Applied to files:

  • apps/docs-smoke/src/routes/docs/search.tsx
  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2025-12-01T15:20:25.102Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/open-graph.mdc:0-0
Timestamp: 2025-12-01T15:20:25.102Z
Learning: Applies to src/**/route.{ts,tsx} : Use whole pixel dimensions for text and key visual lines; consider `pointScaleFactor: 2` for high-DPI crispness

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Include integration tests for component combinations

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2026-03-19T22:57:45.114Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:45.114Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Include unit tests for components, integration tests for component combinations, accessibility tests, and error boundary tests

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:57:45.114Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:45.114Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Document components with purpose and use case, required and optional props, context requirements, example usage, and common pitfalls

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{tsx,jsx} : For each component, documentation should include: purpose and use case, required and optional props, context requirements, example usage, and common pitfalls

Applied to files:

  • apps/docs-smoke/src/lib/docs.ts
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/**/*.mdx : Use MDX for documentation pages in the docs app

Applied to files:

  • apps/docs-smoke/content/docs/search.mdx
📚 Learning: 2026-03-19T22:57:19.913Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:19.913Z
Learning: Applies to apps/docs/**/*.mdx : Use MDX for documentation pages

Applied to files:

  • apps/docs-smoke/content/docs/search.mdx
📚 Learning: 2026-01-31T11:24:55.220Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/docs-rules.mdc:0-0
Timestamp: 2026-01-31T11:24:55.220Z
Learning: Applies to apps/docs/**/*.mdx : Include code examples where appropriate in MDX documentation pages of the docs app

Applied to files:

  • apps/docs-smoke/content/docs/search.mdx
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Test accessibility features in Elements package component tests

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.test.{ts,tsx} : Test accessibility features

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Test with screen readers in Elements package component tests

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Include integration tests for Elements package components

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2025-07-23T12:51:43.299Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/global-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:43.299Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Include accessibility tests for components

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:58:00.031Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-19T22:58:00.031Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Write comprehensive unit tests with clear test case names describing what is being tested

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2025-07-23T12:51:10.961Z
Learnt from: CR
Repo: consentdotio/monorepo PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2025-07-23T12:51:10.961Z
Learning: Applies to packages/elements/**/*.test.{ts,tsx} : Include integration tests

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T22:57:33.411Z
Learnt from: CR
Repo: consentdotio/GDPRish PR: 0
File: .cursor/rules/elements-rules.mdc:0-0
Timestamp: 2026-03-19T22:57:33.411Z
Learning: Applies to packages/elements/**/*.{test,spec}.{ts,tsx} : Test error boundaries in Elements package component tests

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-02-20T14:21:19.208Z
Learnt from: CR
Repo: consentdotio/dsar PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2026-02-20T14:21:19.208Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Avoid done callbacks in async tests - use async/await instead

Applied to files:

  • apps/docs-smoke/tests/e2e/smoke.e2e.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When implementing type safety patterns, load the router-core type-safety skill file from node_modules/tanstack/router-core/skills/router-core/type-safety/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-03-19T16:08:15.951Z
Learnt from: CR
Repo: consentdotio/c-ray PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-19T16:08:15.951Z
Learning: When implementing code splitting, load the router-core code-splitting skill file from node_modules/tanstack/router-core/skills/router-core/code-splitting/SKILL.md

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2025-12-17T14:48:41.563Z
Learnt from: CR
Repo: consentdotio/c15t-docs PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-12-17T14:48:41.563Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't import next/document outside of pages/_document.jsx in Next.js projects

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
📚 Learning: 2026-04-16T03:27:04.353Z
Learnt from: CR
Repo: inthhq/dsar PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-16T03:27:04.353Z
Learning: Applies to **/index.{js,jsx,ts,tsx} : Avoid barrel files (index files that re-export everything)

Applied to files:

  • apps/docs-smoke/src/routeTree.gen.ts
🪛 LanguageTool
apps/docs-smoke/content/docs/search.mdx

[uncategorized] ~37-~37: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...st, then generate the search index from markdown: ```ts import { generateSearchIndex } ...

(MARKDOWN_NNP)

🔇 Additional comments (3)
apps/docs-smoke/content/docs/meta.json (1)

5-5: Good nav metadata update.

Adding "search" keeps the docs page registry aligned with the new /docs/search page.

apps/docs-smoke/src/routes/docs/search.tsx (1)

6-12: Route wiring looks correct.

createFileRoute("/docs/search") cleanly maps to the MDX page component with no extra complexity.

apps/docs-smoke/src/lib/docs.ts (1)

7-11: Nice synchronization between route types and demo nav entries.

Both new paths are reflected in DemoRoute["to"] and demoRoutes, reducing drift risk.

Also applies to: 25-29, 45-49

Comment on lines +31 to +34
"@inth/docs/search": {
type: "runtime",
description: "Static local search, source-grounded answer context, request guards, and AI SDK streaming helpers.",
},
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

Update package-surface count in the intro.

After adding @inth/docs/search, the sentence at Line 8 (“has five package surfaces”) is stale and should be updated to six.

📝 Suggested doc fix
-`@inth/docs` has five package surfaces:
+`@inth/docs` has six package surfaces:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/content/docs/index.mdx` around lines 31 - 34, The docs intro
still says “has five package surfaces” but you added the new package
"@inth/docs/search"; update the intro sentence in
apps/docs-smoke/content/docs/index.mdx (the sentence that mentions package
surfaces) to say “six package surfaces” and ensure any adjacent counts or
pluralization remain consistent with the added "@inth/docs/search" entry.


## Build the Index

Run conversion first, then generate the search index from markdown:
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

Capitalize “Markdown” for consistency.

At Line 37, use “Markdown” (proper noun) for documentation style consistency.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~37-~37: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...st, then generate the search index from markdown: ```ts import { generateSearchIndex } ...

(MARKDOWN_NNP)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/content/docs/search.mdx` at line 37, Summary: The word
"markdown" should be capitalized to "Markdown" for consistency. Update the text
string "Run conversion first, then generate the search index from markdown:" to
"Run conversion first, then generate the search index from Markdown:" in the
docs content (the line containing that sentence in
apps/docs-smoke/content/docs/search.mdx) so the proper noun "Markdown" is used
consistently.

Comment on lines +141 to +144
const answerConfigResponse = await request.get("/api/docs/ask");
const answerConfig = (await answerConfigResponse.json()) as {
enabled: boolean;
};
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

Assert /api/docs/ask success before parsing JSON.

Add an explicit ok() assertion so failures surface as endpoint-status issues instead of JSON parse noise.

✅ Suggested test hardening
   const answerConfigResponse = await request.get("/api/docs/ask");
+  expect(answerConfigResponse.ok()).toBe(true);
   const answerConfig = (await answerConfigResponse.json()) as {
     enabled: boolean;
   };
📝 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
const answerConfigResponse = await request.get("/api/docs/ask");
const answerConfig = (await answerConfigResponse.json()) as {
enabled: boolean;
};
const answerConfigResponse = await request.get("/api/docs/ask");
expect(answerConfigResponse.ok()).toBe(true);
const answerConfig = (await answerConfigResponse.json()) as {
enabled: boolean;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs-smoke/tests/e2e/smoke.e2e.ts` around lines 141 - 144, The test
parses JSON from the response to request.get("/api/docs/ask") without first
asserting the HTTP success status, which masks endpoint failures as JSON parse
errors; update the test around answerConfigResponse and answerConfig to assert
the response is OK (e.g., check answerConfigResponse.ok or use your test
framework's status assertion) before calling .json(), and include a descriptive
message so failures surface as an endpoint-status issue rather than JSON parse
noise.

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