Skip to content

Commit 0111f14

Browse files
committed
docs(agents): make AGENTS.md the single source of guidelines . (*CLAUDE.md -> AGENTS.md)
1 parent a64ce49 commit 0111f14

2 files changed

Lines changed: 25 additions & 500 deletions

File tree

AGENTS.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ You are a professional software engineer. All code must follow best practices: a
1919
- `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis
2020
- `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — shared retry pacing; never reimplement exponential backoff inline
2121
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
22+
- **Type-checking**: Run `bun run type-check` (per workspace) or `bunx turbo run type-check` (all of them). Do not remove the `@typescript/native` alias from the root `devDependencies` — nothing imports it, but it is what makes a bare `tsc` resolve to the native TypeScript 7 compiler instead of the ~10x slower JavaScript TypeScript 6 one that `@typescript/typescript6` pulls in transitively. `bun run check:native-typecheck` enforces this
2223

2324
## Architecture
2425

@@ -93,7 +94,7 @@ Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export f
9394

9495
1. React/core libraries
9596
2. External libraries
96-
3. UI components (`@/components/emcn`, `@/components/ui`)
97+
3. UI components (`@sim/emcn`, `@/components/ui`)
9798
4. Utilities (`@/lib/...`)
9899
5. Stores (`@/stores/...`)
99100
6. Feature imports
@@ -127,6 +128,8 @@ export function Component({ requiredProp, optionalProp = false }: ComponentProps
127128

128129
Extract when: 50+ lines, used in 2+ files, or has own state/logic. Keep inline when: < 10 lines, single use, purely presentational.
129130

131+
Behavior-preserving render-performance idioms — lazy-init object refs, hoist closure-free values/functions to module scope, pre-index repeated lookups with `Map`/`Set`, and never mutating a shared array in place — are in `.claude/rules/sim-react-performance.md` (which also explains why `toSorted`/`toReversed` are unsafe on client render paths despite the ES2023 tsconfig lib — SWC does not polyfill prototype methods, so use `[...arr].sort()`). For the render-timing effect/state anti-patterns use the `/you-might-not-need-*` skills and verify against the running UI.
132+
130133
## API Contracts
131134

132135
Boundary HTTP request and response shapes for all routes under `apps/sim/app/api/**` live in `apps/sim/lib/api/contracts/**` (one file per resource family — `folders.ts`, `chats.ts`, `knowledge.ts`, etc.). Routes never define route-local boundary Zod schemas, and clients never define ad-hoc wire types — both sides consume the same contract.
@@ -288,6 +291,8 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query'
288291
import { requestJson } from '@/lib/api/client/request'
289292
import { listEntitiesContract, type EntityList } from '@/lib/api/contracts/entities'
290293

294+
export const ENTITY_LIST_STALE_TIME = 60 * 1000
295+
291296
async function fetchEntities(workspaceId: string, signal?: AbortSignal): Promise<EntityList> {
292297
const data = await requestJson(listEntitiesContract, {
293298
query: { workspaceId },
@@ -301,7 +306,7 @@ export function useEntityList(workspaceId?: string) {
301306
queryKey: entityKeys.list(workspaceId),
302307
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
303308
enabled: Boolean(workspaceId),
304-
staleTime: 60 * 1000,
309+
staleTime: ENTITY_LIST_STALE_TIME,
305310
placeholderData: keepPreviousData,
306311
})
307312
}
@@ -324,7 +329,7 @@ export const entityKeys = {
324329
### Query Hooks
325330

326331
- Every `queryFn` must forward `signal` for request cancellation
327-
- Every query must have an explicit `staleTime`
332+
- Every query must have an explicit `staleTime`, assigned from a named exported constant, never an inline numeric literal — a server-side prefetch hydrating the same query key must import and reuse that constant so the two never drift out of sync
328333
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
329334

330335
```typescript
@@ -333,7 +338,7 @@ export function useEntityList(workspaceId?: string) {
333338
queryKey: entityKeys.list(workspaceId),
334339
queryFn: ({ signal }) => fetchEntities(workspaceId as string, signal),
335340
enabled: Boolean(workspaceId),
336-
staleTime: 60 * 1000,
341+
staleTime: ENTITY_LIST_STALE_TIME,
337342
placeholderData: keepPreviousData, // OK: workspaceId varies
338343
})
339344
}
@@ -367,9 +372,21 @@ export function useUpdateEntity() {
367372
}
368373
```
369374

375+
## URL / Query-Param State
376+
377+
Shareable *client* view-state (active tab/panel, filters, search query, pagination, selected entity id, view mode, a deep-linked drawer/modal) lives in the URL via [`nuqs`](https://nuqs.dev) — not in a store synced with effects, and never read via `useSearchParams().get(...)` / `new URLSearchParams(window.location.search)`. Remote data stays in React Query; high-frequency / large / ephemeral / socket-synced state stays in Zustand (canvas pan/zoom, cursor, drag, resize widths, live collaborative selection).
378+
379+
Co-locate a `search-params.ts` per feature exporting the parser map (single source of truth, shared by client `useQueryStates`/`useQueryState` and server `createSearchParamsCache`). Never `import { z }` in client code for params — use nuqs parsers. Full decision framework, conventions, the debounced-input pattern, and the workflow-editor carve-out are in `.claude/rules/sim-url-state.md`.
380+
381+
## List & Menu Ordering
382+
383+
A list orders itself the way the user already reads the same things somewhere else. Resource menus (`+` attach, `@` mention, resource-tab `+`) mirror the **sidebar** top-down; a row or root **context menu** mirrors that surface's **toolbar**, left-to-right becoming top-to-bottom; tab strips mirror their nav. Platform-only entries (desktop Browser, Terminal) trail the shared set.
384+
385+
Encode the order in ONE exported constant and sort by it — never a hand-maintained literal per menu (`RESOURCE_MENU_ORDER` / `byResourceMenuOrder` in `home/components/mothership-view/components/resource-registry`). Render mixed item kinds in a single ordered pass; emitting all submenu-backed families and then all flat ones silently pins every submenu to the top no matter what the constant says. Divergence is allowed only for search ranking, user-controlled ordering, and recency. Full rule in `.claude/rules/sim-list-ordering.md`.
386+
370387
## Styling
371388

372-
Use Tailwind only, no inline styles. Use `cn()` from `@/lib/core/utils/cn` for conditional classes.
389+
Use Tailwind only, no inline styles. Use `cn()` from `@sim/emcn` for conditional classes.
373390

374391
```typescript
375392
<div className={cn('base-classes', isActive && 'active-classes')} />
@@ -385,7 +402,7 @@ On chip components (see "EMCN Components"), drive chrome through PROPS, not `cla
385402

386403
## EMCN Components
387404

388-
Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`.
405+
Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`.
389406

390407
The chip family is the canonical UI chrome and is progressively replacing the legacy EMCN primitives — always reach for the chip equivalent: `ChipInput` over `Input`, `ChipTextarea` over `Textarea`, `ChipModal`/`ChipModalField` over `Modal`, `ChipSelect`/`ChipCombobox` (searchable) or `ChipDropdown` (simple menu-select) over `Select`/`Combobox`, `ChipSwitch` over `Switch`, `ChipDatePicker` over a raw date field, `Chip`/`ChipLink` for pill buttons/links, `ChipTag` for inline tags/badges. For context/action menus the canonical control is `DropdownMenu` (not a chip, but the standard menu — not a hand-rolled popover). Components OWN their chrome (single source of truth) — consumers pass props, not class overrides. Authoring rules in `.claude/rules/emcn-components.md`; consumer rules in `.claude/rules/sim-styling.md`.
391408

@@ -463,7 +480,7 @@ New integrations are built in order: **Tools** → **Block** → **Icon** → (o
463480

464481
Two hard rules that the skills assume:
465482

466-
- **Tool IDs are `snake_case`** (`service_action`) and must be registered in `tools/registry.ts`; blocks register in `blocks/registry.ts` (alphabetically).
483+
- **Tool IDs are `snake_case`** (`service_action`) and must be registered in `tools/registry.ts`; blocks register in `blocks/registry-maps.ts` — the `BLOCK_REGISTRY` config map and `BLOCK_META_REGISTRY` catalog-meta map (alphabetically). `blocks/registry.ts` holds only the accessor functions (`getBlock`, `getAllBlocks`, …).
467484
- **`tools.config.tool` runs during serialization (before variable resolution)** — never do `Number()` or other type coercions there, or dynamic references like `<Block.output>` are destroyed. Put all type coercions in `tools.config.params`, which runs during execution after variables resolve.
468485

469486
For the full authoring instructions — SubBlock property tables, `condition`/`dependsOn`/`required`/`mode`/`canonicalParamId` syntax, required block metadata (`integrationType`, `tags`, `authMode`, `docsLink`, `{Service}BlockMeta`), file-input/`normalizeFileInput` patterns, and checklists — use the skills: `/add-integration` (end-to-end), `/add-tools`, `/add-block`, `/add-trigger`.

0 commit comments

Comments
 (0)