You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+24-7Lines changed: 24 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,6 +19,7 @@ You are a professional software engineer. All code must follow best practices: a
19
19
-`truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis
20
20
-`backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — shared retry pacing; never reimplement exponential backoff inline
21
21
-**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
22
23
23
24
## Architecture
24
25
@@ -93,7 +94,7 @@ Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export f
Extract when: 50+ lines, used in 2+ files, or has own state/logic. Keep inline when: < 10 lines, single use, purely presentational.
129
130
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
+
130
133
## API Contracts
131
134
132
135
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.
const data =awaitrequestJson(listEntitiesContract, {
293
298
query: { workspaceId },
@@ -301,7 +306,7 @@ export function useEntityList(workspaceId?: string) {
301
306
queryKey: entityKeys.list(workspaceId),
302
307
queryFn: ({ signal }) =>fetchEntities(workspaceIdasstring, signal),
303
308
enabled: Boolean(workspaceId),
304
-
staleTime: 60*1000,
309
+
staleTime: ENTITY_LIST_STALE_TIME,
305
310
placeholderData: keepPreviousData,
306
311
})
307
312
}
@@ -324,7 +329,7 @@ export const entityKeys = {
324
329
### Query Hooks
325
330
326
331
- 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
328
333
- Use `keepPreviousData` only on variable-key queries (where params change), never on static keys
329
334
330
335
```typescript
@@ -333,7 +338,7 @@ export function useEntityList(workspaceId?: string) {
333
338
queryKey: entityKeys.list(workspaceId),
334
339
queryFn: ({ signal }) =>fetchEntities(workspaceIdasstring, signal),
@@ -367,9 +372,21 @@ export function useUpdateEntity() {
367
372
}
368
373
```
369
374
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
+
370
387
## Styling
371
388
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.
@@ -385,7 +402,7 @@ On chip components (see "EMCN Components"), drive chrome through PROPS, not `cla
385
402
386
403
## EMCN Components
387
404
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()`.
389
406
390
407
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`.
391
408
@@ -463,7 +480,7 @@ New integrations are built in order: **Tools** → **Block** → **Icon** → (o
463
480
464
481
Two hard rules that the skills assume:
465
482
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`, …).
467
484
-**`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.
468
485
469
486
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