This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a pnpm monorepo with the following structure:
smartquotes/
├── apps/
│ └── web/ # Next.js website
├── examples/
│ ├── eslint/ # ESLint plugin demo
│ └── vercel-ai-sdk/ # Vercel AI SDK integration demo
├── packages/
│ └── smartquotes/ # Main npm package
└── python/ # Python package (standalone)
Always run commands at the monorepo root (not filtered to a single package) to catch cross-package issues.
pnpm install # Install all workspace dependencies
pnpm build # Build all packages
pnpm test # Test all packages
pnpm check-types # Type check all packages
pnpm lint # Lint all packagespnpm --filter smartquote build # Build with tsup
pnpm --filter smartquote test # Run all tests once
pnpm --filter smartquote test:watch # Run tests in watch mode
pnpm --filter smartquote bench # Run performance benchmarks
pnpm --filter smartquote lint # ESLint on src/
pnpm --filter smartquote check-types # TypeScript type checking
pnpm --filter smartquote docs # Generate API docs with TypeDocOr from within the package directory:
cd packages/smartquote
pnpm build
pnpm testpnpm --filter web dev # Start Next.js dev server
pnpm --filter web build # Build for productioncd python
pip install -e . # Install in development mode
pytest # Run tests
ruff check . # Lint
mypy smartquotes # Type checkProvides smart quote conversion utilities and an ESLint plugin, exported via three entry points:
smartquotes(main): Core conversion functions fromsrc/index.tssmartquotes/eslint: ESLint plugin fromsrc/eslint/index.tssmartquotes/ai-sdk: Vercel AI SDK integration fromsrc/ai-sdk/index.ts
SmartQuote- Constants using Unicode escapes (critical: LLMs normalize smart quotes to straight quotes, so always use\u201Cetc.)smartQuotes()- Context-aware batch conversionsmartQuoteMarkdown()- Markdown-aware conversion that preserves code blocks via placeholder extraction
Streaming API (for AI responses):
smartQuoteTransform(options?)- Returns a genericTransformStreamfor structured stream partssmartQuoteAsyncIterable(source, options?)- Wraps anAsyncIterable<string>for plain text streams- Both accept
{ disableMarkdown?: boolean }- settrueto convert quotes even inside code blocks
smartQuoteTransform- Typed asStreamTextTransform<ToolSet>for Vercel AI SDK v5+- Re-exports
SmartQuoteandsmartQuotesfor convenience - Requires
ai@>=5.0.0peer dependency
smart-quotes-rule.ts- Rule targeting JSX/TSX files only- Converts JSXText content (always user-facing)
- Converts allowlisted props only (placeholder, title, alt, aria-label, etc.)
- Auto-fixable; escapes smart double quotes in JSX attribute fixes
Python port of the core conversion logic:
smartquotes.convert_to_smart_quotes()- Batch conversionsmartquotes.smart_quote_markdown()- Markdown-aware conversionsmartquotes.SmartQuote- Quote character constants
- Quote constants must use Unicode escapes (
\u201C) not literal smart quotes - LLMs will normalize them - The markdown processor extracts code blocks to placeholders in order: fenced → inline → indented
- ESLint rule uses allowlist for props (not denylist) to avoid false positives on non-user-facing strings
- Streaming API buffers trailing single quotes to detect apostrophes across chunk boundaries (flush with
transform.flush()) - Options use
disableMarkdown(defaultfalse) notmarkdown- boolean options should default tofalse
pnpm-workspace.yamldefines workspace packages- Examples use
workspace:*protocol to reference the smartquotes package - Root
package.jsonis private and contains shared dev dependencies (husky, commitlint) - After changing exports in
packages/smartquote, runpnpm --filter smartquote buildbeforepnpm check-types- dependent packages import fromdist/, not source
This repo uses Conventional Commits enforced by commitlint.
Do NOT include Co-Authored-By lines for Claude/AI in commit messages.
Format:
type(scope?): subject
body?
footer?
Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
Examples:
feat: add streaming API for AI responses
fix: handle apostrophes at chunk boundaries
docs: update README with Vercel AI SDK example
refactor: extract isOpeningContext helper
test: add benchmark for smartQuoteTransformGit hooks (via husky):
pre-commit: runs lint, type check, and test across all packagescommit-msg: validates commit message format
Releases are automated via Release Please:
- Push commits to main with conventional commit messages
- Release Please automatically creates/updates a "Release PR"
- Merging the PR creates a GitHub release and triggers npm publish
Version bumps are determined by commit types:
fix:→ patch (0.1.0 → 0.1.1)feat:→ minor (0.1.0 → 0.2.0)feat!:orBREAKING CHANGE:→ major (0.1.0 → 1.0.0)
npm publishing uses trusted publishing (OIDC) - no tokens required.
CI (.github/workflows/ci.yml) runs on push/PR to main:
- Commitlint validation
- Node.js: lint, type check, test, build
- Python: ruff, mypy, pytest
Other workflows:
release-please.yml: Creates release PRs and publishes to npm via trusted publishing