For AI Coding Assistants (Claude Code, Cursor, Copilot, OpenCode, etc.) This is the canonical agent guide for the v-mapkit monorepo.
CLAUDE.mdis a symlink to this file so Claude Code finds it under its expected name — there is only one source of truth.Humans should start at
README.md. Agents should read this first, then navigate to the appropriate sub-project guide.
v-mapkit is a monorepo for Vue 3 components that wrap Apple MapKit JS — the core library plus a Nuxt 4 showcase site ("shadcn-vue for Apple Maps") and a Docus API docs site.
| Package/App | Description | Guide |
|---|---|---|
packages/v-mapkit |
Core Vue 3 component library (@geoql/v-mapkit) |
packages/v-mapkit/AGENTS.md |
apps/mapkit-cn |
Showcase site with live examples (Nuxt 4) | apps/mapkit-cn/AGENTS.md |
apps/docs |
API documentation (Docus) | apps/docs/AGENTS.md |
What are you working on?
| Task | Read This Guide |
|---|---|
| Adding/modifying Vue map components | packages/v-mapkit/AGENTS.md |
| Adding examples or showcase pages | apps/mapkit-cn/AGENTS.md |
| Writing API documentation | apps/docs/AGENTS.md |
This monorepo ships a project-pinned design skill under .agents/skills/: mapkit-cn-design pins the Apple / Modern-Minimal direction for the showcase. The generic vue-best-practices and nuxt-best-practices skills are provided by the agent host and apply per the matrix below. Load the relevant skill before writing code. Loading an irrelevant skill costs nothing; missing the relevant one produces measurably worse output.
Per-package AGENTS.md ALWAYS takes precedence over generic skills when they conflict.
Skills are GENERIC (they apply to many Vue/Nuxt projects). Per-package AGENTS.md files are PROJECT-SPECIFIC (they encode this codebase's actual conventions). Each AGENTS.md documents its Known Conflicts with the loaded skills as an explicit table — read that section first, before applying generic skill advice.
| Package | Conflicts Table |
|---|---|
packages/v-mapkit |
Known Conflicts — inject over props / useMapChild lifecycle / shallowRef for the map |
apps/mapkit-cn |
Known Conflicts — Apple direction pinned, semantic tokens only, Geist only, shadcn-vue inputs |
apps/docs |
Known Conflicts — Docus owns the theme, content (Markdown) only |
| Skill | When to Load |
|---|---|
mapkit-cn-design |
Any visual work in apps/mapkit-cn — pins the Apple / Modern-Minimal direction, token contract, font stack (Geist + Geist Mono), and the ban-list. |
nuxt-best-practices |
Any Nuxt 3/4 work in apps/mapkit-cn or apps/docs — data fetching, server routes, auto-imports, rendering modes, type organization. |
vue-best-practices |
Any Vue 3 component / composable / reactivity / Composition-API work in any package or app. |
| Package / App | Skills to Load (in order) |
|---|---|
packages/v-mapkit |
vue-best-practices |
apps/mapkit-cn |
mapkit-cn-design (FIRST, for any visual work) → nuxt-best-practices → vue-best-practices |
apps/docs |
nuxt-best-practices (Markdown content needs no skill) |
You receive a task. Before writing code:
1. Which directory does it touch?
├── packages/v-mapkit → load `vue-best-practices`
├── apps/mapkit-cn
│ ├── any visual work? → load `mapkit-cn-design` FIRST (mandatory)
│ ├── then Nuxt-specific? → load `nuxt-best-practices`
│ └── Vue component / hook? → load `vue-best-practices`
└── apps/docs
├── content / Docus only? → no skill required (Markdown only)
└── Nuxt config? → load `nuxt-best-practices`
2. Read the package's AGENTS.md for conventions, file structure, "no-no" list.
3. Then write code.
v-mapkit/
├── packages/
│ └── v-mapkit/ # Core library (@geoql/v-mapkit on npm + jsr)
│ ├── src/
│ │ ├── components/ # One folder per component (VMap, VMarkerAnnotation, …)
│ │ ├── composables/ # use-map-kit, use-map-child, use-search, …
│ │ ├── constants/ # MapKit event-name groups
│ │ ├── types/ # Shared types + mapkit-augment.d.ts
│ │ ├── symbols.ts # provide/inject InjectionKeys
│ │ ├── install.ts # Vue plugin (global registration)
│ │ └── index.ts # Public barrel export
│ └── test/ # Vitest specs + mapkit-mock
│
├── apps/
│ ├── mapkit-cn/ # Nuxt 4 showcase site ("shadcn-vue for Apple Maps")
│ │ ├── app/ # Frontend (pages, components, composables)
│ │ └── app/server/ # Registry API route (/r/[name])
│ │
│ └── docs/ # Docus documentation site
│ └── content/ # API reference Markdown
│
├── package.json # pnpm workspaces root
├── pnpm-workspace.yaml # Workspace globs (packages/*, apps/*)
├── pnpm-lock.yaml
├── release-please-config.json # Automated release config
├── AGENTS.md # This file (canonical)
└── CLAUDE.md # Symlink → AGENTS.md
This monorepo uses pnpm v11 with workspaces (packageManager: pnpm@11.5.3, Node >=24). All three projects share a single lockfile and install in one command. No Bun, no npm, no yarn.
# Install all dependencies
pnpm installRun from the repo root:
# Development
pnpm run dev:lib # Watch/dev for @geoql/v-mapkit
pnpm run dev:app # Start the mapkit-cn showcase site
pnpm run dev:docs # Start the Docus docs site
# Build
pnpm run build # Build library + showcase + docs (in order)
pnpm run build:app # Build showcase only
pnpm run build:docs # Build docs only
# Test (library only — apps have no unit tests)
pnpm run test
pnpm run test:coverage
# Lint & Format (recursive across all workspaces)
pnpm run lint # pnpm -r run lint
pnpm run format # pnpm -r run format
pnpm run format:check # pnpm -r run format:check
# Maintenance
pnpm run update:deps # pnpm dlx taze -I -r
pnpm run clean # Remove node_modules + build artifactsapps/mapkit-cn ──depends on──> packages/v-mapkit (workspace:*)
apps/docs ──────workspace member (own deps, no cross-package link)
The root and both apps centralize dependency versions in pnpm catalogs (defined in pnpm-workspace.yaml): catalog:default (root tooling + deps shared by both apps — nuxt, wrangler, @nuxtjs/plausible), catalog:app:docs, and catalog:app:mapkit-cn. Their package.json files reference versions via the catalog: protocol — bump a version once in pnpm-workspace.yaml, then pnpm install. Use workspace:* only for internal monorepo references (e.g. apps/mapkit-cn → @geoql/v-mapkit). The published library (packages/v-mapkit) is the only exception: it is consumed by npm/yarn/jsr users who do not understand pnpm protocols, so its dependency blocks MUST stay real semver (no catalog:, no workspace:*) — see the library guide.
These override per-package rules. Violating any of these = wasted work.
| # | Constraint | Why |
|---|---|---|
| 1 | TypeScript only. No .js files except configs. |
Type safety, autocompletion |
| 2 | No any type. Use unknown + narrowing, or precise MapKit types. |
Prevents silent failures |
| 3 | Vue 3 Composition API only (<script setup lang="ts">). No Options API. |
Tree-shaking, type inference |
| 4 | pnpm v11 only. No Bun, no npm, no yarn for development. | Reliable CI/CD |
| 5 | 100-line maximum per component/file. Extract logic into composables to stay under it. Hard rule, every package. | Readability, single responsibility, testability |
| 6 | workspace:* only for internal package refs; catalog: for app/root deps (versions in pnpm-workspace.yaml); real semver ONLY in the published library. |
Avoid version mismatch + keep the published library installable |
| 7 | NEVER introduce Inter, Plus Jakarta Sans, Space Grotesk, gradient-text, or raw Tailwind color utilities (bg-blue-500, etc.) in apps/mapkit-cn. |
The pinned Apple / Modern-Minimal design discipline forbids them |
| 8 | Use the MapKit toolchain truthfully. Lint/format/pack via vite-plus (vp lint, vp fmt, vp pack) — not standalone eslint/prettier/vite. |
The library build pipeline is owned by vite-plus |
No single component or file should exceed ~100 lines of code. When a file grows past that:
- Extract a composable — move imperative MapKit lifecycle, search/geocode logic, or state into
composables/use-*.ts. - Split into sub-components in the same feature folder.
- Move constants and option tables out of the
.vue/.tsbody into a composable orconstants/.
This is enforced in both the library and the showcase. The library already models it: VMap.vue delegates MapKit boot to use-map-kit.ts, and every annotation/overlay delegates its create/update/remove lifecycle to use-map-child.ts.
Releases are automated with release-please (release-please-config.json, .release-please-manifest.json, .github/workflows/release-please.yml). Conventional-commit history drives version bumps and changelog generation; merging the release PR tags the version and the pipeline publishes @geoql/v-mapkit to both npm and jsr. Do not hand-edit version numbers or run a manual npm publish.
-
Clone and install:
git clone https://github.com/geoql/v-mapkit.git cd v-mapkit pnpm install # all three workspace projects in one shot
-
Start development:
pnpm run dev:lib # If working on the library pnpm run dev:app # If working on the showcase
-
Read the appropriate guide based on what you're working on (see Quick Navigation above).
Made with care by GeoQL.