xiaohongshu
xiaohongshu
common-conventions
TMNL codebase conventions for file organization, barrel exports, naming patterns, comments, and module structure. The meta-skill for consistency.
Full skill instructions
TMNL maintains strict conventions for code organization, naming, and documentation. This skill codifies patterns observed across the codebase to ensure consistency.
Mandatory Conventions (Non-negotiable):
When: Creating a new library at src/lib/{feature}/
src/lib/{feature}/
├── index.ts # Barrel export (required)
├── types.ts # TypeScript types (UI props, configs)
├── schemas/ # Effect Schema definitions
│ └── index.ts
├── services/ # Effect.Service implementations
│ ├── index.ts
│ └── {Feature}Service.ts
├── atoms/ # effect-atom definitions
│ └── index.ts
├── hooks/ # React hooks
│ └── use{Feature}.ts
├── components/ # React components (if UI-heavy)
│ └── {Component}.tsx
├── machines/ # XState machines (if stateful)
│ └── {feature}-machine.ts
├── v1/, v2/ # Version directories (for evolution)
└── CLAUDE.{feature}.md # Agent handoff documentation
| Directory | When to Create |
|---|---|
services/ | Multiple Effect services, or single complex service |
service.ts (root) | Single simple service (e.g., src/lib/commands/service.ts) |
atoms/ | effect-atom state management |
hooks/ | Multiple React hooks |
use{Feature}.tsx (root) | Single hook |
schemas/ | Domain types requiring validation |
types.ts | UI props, configs, non-validated types |
machines/ | XState state machines |
v1/, v2/ | Major version evolution (not breaking changes) |
When: Every src/lib/{feature}/ directory requires a barrel export.
/**
* {Feature} Library
*
* {Brief description}
*
* @example
* ```tsx
* import { Component, useFeature } from '@/lib/{feature}'
* ```
*/
// ═══════════════════════════════════════════════════════════════════════════
// CORE EXPORTS
// ═══════════════════════════════════════════════════════════════════════════
export { MainComponent } from './components/Main'
export { FeatureService } from './services'
// ═══════════════════════════════════════════════════════════════════════════
// SCHEMAS
// ═══════════════════════════════════════════════════════════════════════════
export * from './schemas'
// ═══════════════════════════════════════════════════════════════════════════
// ATOMS (effect-atom)
// ═══════════════════════════════════════════════════════════════════════════
export {
stateAtom,
configAtom,
operationsAtom,
} from './atoms'
// ═══════════════════════════════════════════════════════════════════════════
// REACT HOOKS
// ═══════════════════════════════════════════════════════════════════════════
export { useFeature, useFeatureState } from './hooks'
// ═══════════════════════════════════════════════════════════════════════════
// TYPES
// ═══════════════════════════════════════════════════════════════════════════
export type { FeatureConfig, FeatureState } from './types'
Use box-drawing characters for major sections:
// ═══════════════════════════════════════════════════════════════════════════
// SECTION NAME (all caps)
// ═══════════════════════════════════════════════════════════════════════════
Use simple dashes for subsections:
// ───────────────────────────────────────────────────────────────────────────
// Subsection name
// ───────────────────────────────────────────────────────────────────────────
For modules with v1/v2:
// Default exports from v1
export * from './v1'
// Explicit v2 namespace
export * as v2 from './v2'
Usage:
import { Slider } from '@/lib/slider' // v1 (default)
import { Slider } from '@/lib/slider/v2' // v2 (explicit)
| Pattern | Example | Usage |
|---|---|---|
{noun}Atom | resultsAtom | State atom |
{noun}sAtom | layersAtom | Collection atom |
{verb}Atom | computedAtom | Derived/computed atom |
{feature}OpsAtom | layerOpsAtom | Operation atoms (mutations) |
{verb}Op | searchOp, clearOp | Individual operation |
| Pattern | Example | Usage |
|---|---|---|
{Feature}Service | DataManagerService | Effect.Service class |
{Feature}ServiceShape | ChannelServiceShape | Interface type |
{Behavior}Behavior | LinearBehavior, DecibelBehavior | Strategy pattern |
| Pattern | Example | Usage |
|---|---|---|
use{Feature} | useSlider | Main feature hook |
use{Feature}Value | useSliderValue | Read-only value hook |
use{Feature}State | useMinibufferState | State + updater |
use{Feature}Ops | useOverlayOps | Operations only |
useAtom{Feature} | useAtomValue | Atom-specific |
| Pattern | Example | Usage |
|---|---|---|
{Name}.tsx | Slider.tsx | Component file |
{Name}Props | SliderProps | Props interface |
{Name}Context | DataGridContext | Context type |
{Name}Provider | CommandProvider | Context provider |
| Pattern | Example | Usage |
|---|---|---|
{feature}Machine | minibufferMachine | XState machine |
{Feature}Machine | MinibufferMachine | Type alias |
{Feature}Actor | MinibufferActor | ActorRef type |
/**
* {Module Name} — {Brief description}
*
* {Longer description if needed}
*
* @example
* ```tsx
* // Usage example
* ```
*/
For non-obvious design decisions:
// ARCHITECTURAL NOTE:
// CommandProvider lives here (commands/), not in minibuffer/.
// Minibuffer is a generic prompt engine. Commands USES minibuffer, not the reverse.
// TODO(prime): Migrate to v2 API after EPOCH-0003
// FIXME: This breaks when input is empty
// HACK: Workaround for WSLg rendering bug
types.ts// src/lib/slider/v1/types.ts
export interface SliderProps {
value: number
onChange: (value: number) => void
min?: number
max?: number
}
export interface SliderConfig {
min: number
max: number
step: number
defaultValue: number
}
schemas/// src/lib/overlays/schemas/core.ts
import { Schema } from 'effect'
export const OverlayId = Schema.String.pipe(
Schema.brand('OverlayId')
)
export type OverlayId = typeof OverlayId.Type
export const OverlayOpened = Schema.TaggedStruct('OverlayOpened', {
id: OverlayId,
timestamp: Schema.DateFromSelf,
})
| Pattern | Example | When |
|---|---|---|
__tests__/ directory | src/lib/stx/__tests__/ | Multiple test files |
.test.ts suffix | service.test.ts | Single test file |
.bun.test.ts suffix | eventlog-integration.bun.test.ts | Bun-specific |
// src/lib/feature/__tests__/feature.test.ts
describe('FeatureService', () => {
describe('operation', () => {
it('does X when Y', () => { ... })
it('fails with Z when W', () => { ... })
})
})
Use @effect/vitest with it.effect():
import { describe, it } from '@effect/vitest'
describe('MyService', () => {
it.effect('returns data', () =>
Effect.gen(function* () {
const service = yield* MyService
const result = yield* service.getData()
expect(result).toBeDefined()
}).pipe(Effect.provide(MyService.Default))
)
})
Use Registry.make():
it('atom updates', () => {
const r = Registry.make()
expect(r.get(counterAtom)).toBe(0)
r.set(counterAtom, 1)
expect(r.get(counterAtom)).toBe(1)
})
| File | Purpose | Audience |
|---|---|---|
CLAUDE.{feature}.md | Agent handoff | AI assistants |
README.md | User-facing docs | Developers |
ARCHITECTURE.md | Deep design analysis | Architects |
AGENTS.{feature}.md | Agent-specific notes | AI assistants |
# {Feature} — Claude Context
## Overview
{Brief description}
## Key Files
- `service.ts` — Main service implementation
- `atoms/index.ts` — Reactive state
## Patterns Used
- Effect.Service<>() for DI
- Atom.runtime() for state
## Gotchas
- Don't use X because Y
- Always Z before W
## Related Skills
- effect-patterns
- effect-atom-integration
// src/lib/commands/service.ts
/**
* TMNL Commands — Effect Service
*/
// ───────────────────────────────────────────────────────────────────────────
// Atoms (Reactive State)
// ───────────────────────────────────────────────────────────────────────────
export const commandsAtom = Atom.make<ReadonlyMap<string, Command>>(new Map())
// ───────────────────────────────────────────────────────────────────────────
// Service Implementation
// ───────────────────────────────────────────────────────────────────────────
export class CommandService extends Effect.Service<CommandService>()('app/CommandService', {
effect: Effect.gen(function* () {
// ...
}),
}) {}
src/lib/overlays/services/
├── index.ts # Re-exports all services
├── OverlayRegistry.ts # Registry service
├── PortHub.ts # Port management
└── EventDispatcher.ts # Event dispatch
// WRONG — Unorganized exports
export * from './components'
export * from './hooks'
export * from './types'
export * from './atoms'
// CORRECT — Sectioned exports
// ═══════════════════════════════════════════════════════════════════════════
// COMPONENTS
// ═══════════════════════════════════════════════════════════════════════════
export { Slider } from './components/Slider'
// ═══════════════════════════════════════════════════════════════════════════
// HOOKS
// ═══════════════════════════════════════════════════════════════════════════
export { useSlider } from './hooks/useSlider'
// WRONG — Domain type without Schema
// src/lib/feature/types.ts
export interface UserEvent {
_tag: 'UserCreated'
id: string
name: string
}
// CORRECT — Domain type with Schema
// src/lib/feature/schemas/events.ts
export const UserCreated = Schema.TaggedStruct('UserCreated', {
id: Schema.String,
name: Schema.NonEmptyString,
})
// WRONG — Mixed naming styles
export const user_state_atom = Atom.make(...) // snake_case
export const UseUserHook = () => { ... } // PascalCase for hook
export const userservice = Effect.Service() // no separator
// CORRECT — Consistent camelCase with type suffix
export const userStateAtom = Atom.make(...)
export const useUser = () => { ... }
export const UserService = Effect.Service()
// WRONG — No documentation
export const searchOp = runtimeAtom.fn<string>()(...)
// CORRECT — JSDoc on public exports
/**
* Search operation. Triggers search with given query.
*
* @param query - Search query string
* @returns Effect that updates resultsAtom
*/
export const searchOp = runtimeAtom.fn<string>()(...)
When creating src/lib/{feature}/:
index.ts with JSDoc header and sectioned exportstypes.ts for non-domain types (UI props, configs)schemas/ for domain types using Effect Schemaservices/ (or service.ts) with Effect.Service<>()atoms/index.ts with Atom definitionshooks/ with React hooks// ═══════...CLAUDE.{feature}.md for agent handoffAtom.make<T>() over Effect.Ref<T> for React@effect/vitest for services, Registry.make() for atoms| Convention | Best Example | File |
|---|---|---|
| Barrel file | Overlays | src/lib/overlays/index.ts |
| Service pattern | Commands | src/lib/commands/service.ts |
| Atom organization | Slider | src/lib/slider/v1/atoms/index.ts |
| Schema usage | Commands | src/lib/commands/types.ts |
| Hook naming | Commands | src/lib/commands/useCommandWire.tsx |
| Version strategy | Slider | src/lib/slider/index.ts |
| Test organization | STX | src/lib/stx/__tests__/ |
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience