Guidance for Claude Code working in this repository.
Read .claude/shared/hard-rules.md before doing anything — including when the user gives you a specific command or script to run. Hard rules are non-negotiable and override convenience shortcuts, user phrasing, and script names. If a suggested script violates a hard rule, flag it and use the correct alternative.
For release/publish tasks specifically: always query the registry to verify actual published state before drawing conclusions. Use per-package release scripts (not changeset publish) to ensure --tag latest is always respected.
Use the
/release-frameworkcommand. Type/release-frameworkin Claude Code — it automates the full workflow below (detect changed packages, create changeset, version, build, publish, verify) with no manual steps.
When the user asks to bump a version and/or release a package, execute these steps in order. No deviations.
1. Verify registry state first
pnpm view <pkg> version --registry=https://sonatype.nosi.cv/repository/igrp/Confirm the current published version before doing anything else.
2. Apply versions from changesets
pnpm version:changesetsThis consumes pending .changeset/*.md files, bumps package.json versions, and regenerates CHANGELOG.md. Commit the result.
3. Build (respect dependency order)
Build only the affected package(s). Order: next-auth → next-types → design-system → next-ui → next.
- Single package: use its specific build script (e.g.
pnpm build:next-ui) - All framework packages:
pnpm build:framework
4. Release via per-package script
# from the repo root — example for next-ui:
pnpm --filter @igrp/framework-next-ui releaseEach package's release script runs pnpm publish --registry=... --tag latest. Never use changeset publish or pnpm release:publish — they use --tag beta in pre-release mode.
5. Verify the publish landed
pnpm view <pkg> version --registry=https://sonatype.nosi.cv/repository/igrp/Confirm the new version is live before reporting success.
igrp-framework-nextjs is a pnpm workspace monorepo that publishes the IGRP Framework — a set of React/Next.js packages used to build IGRP applications — plus reference templates. Published artifacts go to the internal NOSi Sonatype registry.
Requires Node ≥ 22 and pnpm. Workspaces: packages/**, templates/**, apps/**.
packages/
design-system/ → @igrp/igrp-framework-react-design-system (client UI library)
design-system-storybook/ → Storybook + visual/regression tests for the DS
framework/
next-auth/ → @igrp/framework-next-auth (NextAuth wrappers, OIDC, session, middleware)
next-types/ → @igrp/framework-next-types (shared TS types; depends on next-auth types)
next-ui/ → @igrp/framework-next-ui (client template chrome)
next/ → @igrp/framework-next (server entry: IGRPLayout, IGRPRootLayout, igrpBuildConfig, API client)
template-migrator/ → @igrp/template-migrator (CLI `igrp-migrate`; applies template migrations to consumer apps; drift gate)
templates/
demo-v1/ → @igrp/framework-next-template (canonical reference template — the only one in the repo)
scripts/ → repo utilities (e.g. migrate-primitive-names.mjs)
Each package/template has its own CLAUDE.md with package-specific expertise. Claude Code auto-loads the nearest one when you edit files inside that directory.
@.claude/shared/hard-rules.md
@.claude/shared/dependency-order.md
@.claude/shared/commands.md
@.claude/shared/three-layer-ui.md
The distinction is load-bearing — mixing layers incorrectly produces inconsistent UI and breaks form wiring.
@igrp/framework-next— server-side entry.IGRPRootLayout,IGRPLayout,igrpBuildConfig, and the access-management API client (igrpGetAccessClient,igrpGetAccessClientConfig). A template's root layout/page wires these together.@igrp/framework-next-ui— client-side template chrome: header, sidebar, menus, nav-user, breadcrumbs, command search, theme selector, auth carousel/form,IGRPRootProviders,IGRPSessionProvider.@igrp/framework-next-auth— NextAuth.js wrappers with multiple entry points (./server,./client,./session,./jwt,./middleware,./config,./sanitize,./oidc,./providers,./types). Respect these entry points instead of reaching intodist/.@igrp/framework-next-types— shared TS types. Depends on@igrp/framework-next-authfor session/JWT types.
demo-v1 is the canonical example of how to consume the framework:
- Middleware (
src/middleware.ts) validates the NextAuth session, bypasses public/login/logout/API routes, honorsIGRP_PREVIEW_MODE/AUTH_PROVIDER=none, and sanitizescallbackUrlto prevent login loops and open-redirects. - Root layout (
src/app/layout.tsx) wraps the app inIGRPRootLayout+ providers. - IGRP layout (
src/app/(igrp)/layout.tsx) runs auth checks, loads session, rendersIGRPLayoutwith header/sidebar around the route group. - Config builder (
src/igrp.template.config.ts) usesigrpBuildConfigto assemble layout + API + toaster + session config, and swaps in mock data when bypass is on. - Server actions (
src/actions/igrp/) fetch layout + session server-side;api/auth/*holds NextAuth routes.
Critical env constraint: when NEXT_PUBLIC_BASE_PATH is set, NEXTAUTH_URL must include both the basePath and /api/auth (e.g. http://localhost:3000/apps/template/api/auth). NextAuth treats NEXTAUTH_URL as the API root, not the app root — getting this wrong produces a login loop with a growing nested callbackUrl chain.
@.claude/shared/preview-mode.md
@.claude/shared/tailwind-v4.md
@.claude/shared/ui-rules.md
Inside templates/demo-v1/**/*.{ts,tsx}, the template's agent rules are canonical in templates/demo-v1/.agents/ — rules/ui.md (design-system hard rules) and rules/permissions.md (permission gating). AGENTS.md, .cursor/, .trae/ and .github/ are thin bridges that point there; rule content belongs in .agents/rules/, never in a bridge. .github/copilot-instructions.md is generated (pnpm --filter @igrp/framework-next-template agents:sync) because Copilot cannot follow references.
The full component reference is the skill at templates/demo-v1/.agents/skills/igrp-design-system/SKILL.md. That subtree is not committed — the zip script injects it from plugins/igrp/skills/design-system/, which is the copy to edit in this repo. Treat it as authoritative and load only the sub-files you need.
| Entry point | Key exports |
|---|---|
./server |
getServerSession, getServerSessionStrict (throws on missing session), NextAuth factory |
./client |
useSession, useSafeSession, signIn, signOut, SessionProvider |
./config |
IGRPAuthConfigError, withIGRPAuth(), edge-safe config builder |
./oidc |
refreshOidcAccessToken, revokeOidcSession |
./providers |
createAuthProviderFromEnv, IGRP_AUTH_PROVIDER_ID |
./middleware |
withAuth |
./jwt |
JWT helpers/types |
./session |
Session shape (includes accessToken) |
./sanitize |
Redirect URL sanitization |
./types |
AuthProviderId enum |
Source: packages/framework/next-auth/src/ — server.ts, client.ts, config.ts, middleware.ts, oidc.ts, providers.ts, etc.
IGRPConfigArgs, IGRPLayoutConfigArgs, IGRPConfigClient, IGRPMenuType, IGRPApplicationType, IGRPResourceType, IGRPHeaderDataArgs, IGRPNotificationArgs, IGRPSidebarDataArgs, IGRPMockDataAsync, IGRPToasterPosition, IGRPPackageJson
Source: packages/framework/next-types/src/types/ — igrp.ts, access-management.ts, header.ts, sidebar.ts, globals.ts
| Entry point | Key exports |
|---|---|
. (root) |
IGRPRootLayout, IGRPLayout, IGRPGlobalLoading, igrpBuildConfig, igrpGetAccessClient, igrpGetAccessClientConfig |
./errors |
IgrpError, IgrpConfigError, IgrpAuthConfigError, IgrpLayoutDataError |
./app-error |
App-level error boundary |
./logger |
Logger utility |
./actions |
Server actions (fetchMenusAction, fetchCurrentUserAction, …) + ActionResult<T> |
./client |
useLayoutData hook |
Source: packages/framework/next/src/ — igrp-layout.tsx, igrp-root-layout.tsx, lib/build.ts, lib/api-client.ts, lib/api-config.ts, errors.ts
Providers: IGRPRootProviders, IGRPSessionProvider, IGRPActiveThemeProvider, IGRPNestedProviders
Header: IGRPTemplateHeader, IGRPHeaderSkeleton, IGRPHeaderError
Sidebar: IGRPTemplateSidebar, IGRPSidebarSkeleton, IGRPSidebarError
Nav: IGRPTemplateMenus, IGRPTemplateNavUser, IGRPTemplateAppSwitcher, IGRPTemplateBreadcrumbs
Search/settings: IGRPTemplateCommandSearch, IGRPTemplateThemeSelector, IGRPTemplateModeSwitcher
Auth: IGRPAuthCarousel, IGRPAuthForm
Error: IGRPGlobalError, IGRPSegmentError, IGRPLayoutErrorBoundary, IGRPTemplateNotFound
Misc: IGRPSessionWatcher, IGRPTemplateNotifications, IGRPTemplateLoading
Source: packages/framework/next-ui/src/ — providers/, components/templates/, components/auth/, components/errors/
Horizon (IGRP* — always first choice):
Forms: IGRPForm, IGRPInput, IGRPSelect, IGRPCheckbox, IGRPSwitch, IGRPTextarea
Data: IGRPDataTable (pagination, filter, sort, row actions)
Charts: IGRPAreaChart, IGRPBarChart, IGRPLineChart, IGRPPieChart, IGRPRadarChart, IGRPRadialBarChart
Cards: IGRPCard, IGRPCardDetails, IGRPStatsCard
Calendar: IGRPCalendarSingle, IGRPCalendarRange, IGRPCalendarMultiple
Dialogs: IGRPModalDialog, IGRPAlertDialog
Other: IGRPMenuNavigation, IGRPChat, IGRPAccordion, IGRPTabs, IGRPAvatar, IGRPBadge, IGRPButton, IGRPAlert, IGRPCommand
Primitives (Radix + CVA — only when Horizon is too opinionated):
Button, Card, Input, Textarea, Badge, Avatar, Separator, Dialog, AlertDialog, Drawer, Popover, Sheet, Breadcrumb, NavigationMenu, Pagination, Tabs, Sidebar, Form, Field, Label, Checkbox, RadioGroup, Select, InputOTP, Slider, Switch, Toggle, Accordion, Collapsible, CommandDialog, DropdownMenu, Table, Progress, Skeleton, Spinner, HoverCard, Tooltip, ContextMenu, Carousel, ScrollArea, Chart*
Custom (domain-specific): IGRPUserAvatar, IGRPStatsCardMini, IGRPStatsCardTopBorderColored, IGRPStatusBanner
Utilities: cn(), formatChartValue, getChartHeight, IGRP_CHART_COLORS
Source: packages/design-system/src/components/ — horizon/, primitives/, custom/
| Package | Build | React Compiler | Tailwind prebuild |
|---|---|---|---|
framework-next-auth |
tsup | no | no |
framework-next-types |
tsc -b |
n/a (types only) | no |
design-system |
SWC + Babel | yes | yes |
framework-next-ui |
SWC + Babel | yes | yes |
framework-next |
SWC + Babel | yes | no |
template-migrator |
tsup (+ tsx scripts/pack.ts prebuild) |
no | no |
SWC+Babel pipeline: build:swc → build:babel (React Compiler pass) → build:types (emit .d.ts). Escape hatch when the React Compiler misbehaves: build:without_reactcompiler.
There is no root test script — tests live in two packages and use Vitest:
pnpm --filter @igrp/template-migrator test— runsvitest run(migration logic). Itsreleasescript runscheck:driftfirst (tsx scripts/check-drift.ts), which fails the publish iftemplates/demo-v1has drifted from the shipped migrations.packages/design-system-storybook—test:vitest(component) andtest-storybook(Playwright snapshots; Storybook must be running).
Run a single test: pnpm --filter @igrp/template-migrator exec vitest run path/to/file.test.ts -t "test name" (drop run for watch mode). The -t flag filters by test/describe name.