@nurix/etna 0.18.10 → 0.18.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/bin/etna.js +98 -2
- package/data/harness-index.json +9 -1
- package/data/harnesses/code/CLAUDE.md +9 -10
- package/data/harnesses/code/agents/brainstorm.md +3 -3
- package/data/harnesses/code/agents/change-review.md +4 -4
- package/data/harnesses/code/agents/maintenance.md +177 -59
- package/data/harnesses/code/agents/spec-mode.md +3 -3
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/generate-ownership-index.mjs +148 -0
- package/data/harnesses/code/hooks/hooks.json +12 -0
- package/data/harnesses/code/skills/boundary/references/boundary-taxonomy.md +3 -3
- package/data/harnesses/code/skills/brainstorm/README.md +11 -9
- package/data/harnesses/code/skills/brainstorm/SKILL.md +2 -2
- package/data/harnesses/code/skills/brainstorm/harvest.md +5 -6
- package/data/harnesses/code/skills/distiller/SKILL.md +3 -3
- package/data/harnesses/code/skills/distiller/references/docs-mode.md +7 -7
- package/data/harnesses/code/skills/documentation/SKILL.md +23 -23
- package/data/harnesses/code/skills/documentation/references/templates.md +26 -64
- package/data/harnesses/etna/.prettierignore +12 -0
- package/data/harnesses/etna/.prettierrc.json +3 -0
- package/data/harnesses/etna/.vscode/settings.json +10 -0
- package/data/harnesses/etna/CLAUDE.md +151 -0
- package/data/harnesses/etna/agents/brainstorm.md +35 -0
- package/data/harnesses/etna/agents/change-review.md +251 -0
- package/data/harnesses/etna/agents/maintenance.md +240 -0
- package/data/harnesses/etna/agents/performance-review.md +223 -0
- package/data/harnesses/etna/agents/spec-mode.md +56 -0
- package/data/harnesses/etna/harness.json +11 -0
- package/data/harnesses/etna/hooks/generate-ownership-index.mjs +148 -0
- package/data/harnesses/etna/hooks/hooks.json +39 -0
- package/data/harnesses/etna/hooks/lib/redact.mjs +35 -0
- package/data/harnesses/etna/hooks/prompt-recall.mjs +155 -0
- package/data/harnesses/etna/hooks/session-reflect.mjs +285 -0
- package/data/harnesses/etna/rules/general.md +86 -0
- package/data/harnesses/etna/rules/kb.md +13 -0
- package/data/harnesses/etna/rules/nomenclature.md +41 -0
- package/data/harnesses/etna/rules/npm-cli.md +88 -0
- package/data/harnesses/etna/skills/boundary/SKILL.md +49 -0
- package/data/harnesses/etna/skills/boundary/references/boundary-taxonomy.md +796 -0
- package/data/harnesses/etna/skills/brainstorm/README.md +138 -0
- package/data/harnesses/etna/skills/brainstorm/SKILL.md +135 -0
- package/data/harnesses/etna/skills/brainstorm/harvest.md +25 -0
- package/data/harnesses/etna/skills/brainstorm/templates/backlog.md +39 -0
- package/data/harnesses/etna/skills/brainstorm/templates/build.md +37 -0
- package/data/harnesses/etna/skills/brainstorm/templates/decisions.md +29 -0
- package/data/harnesses/etna/skills/brainstorm/templates/doctrine.md +31 -0
- package/data/harnesses/etna/skills/brainstorm/templates/domain-model.md +25 -0
- package/data/harnesses/etna/skills/brainstorm/templates/glossary.md +20 -0
- package/data/harnesses/etna/skills/brainstorm/templates/milestones.md +43 -0
- package/data/harnesses/etna/skills/brainstorm/templates/non-goals.md +17 -0
- package/data/harnesses/etna/skills/brainstorm/templates/plan-doc.md +61 -0
- package/data/harnesses/etna/skills/brainstorm/templates/questions.md +36 -0
- package/data/harnesses/etna/skills/brainstorm/templates/references.md +28 -0
- package/data/harnesses/etna/skills/brainstorm/templates/rejected.md +29 -0
- package/data/harnesses/etna/skills/brainstorm/templates/state.md +1 -0
- package/data/harnesses/etna/skills/brainstorm/templates/tech.md +23 -0
- package/data/harnesses/etna/skills/brainstorm/templates/thesis.md +21 -0
- package/data/harnesses/etna/skills/distiller/SKILL.md +90 -0
- package/data/harnesses/etna/skills/distiller/references/docs-mode.md +299 -0
- package/data/harnesses/etna/skills/distiller/references/method.md +94 -0
- package/data/harnesses/etna/skills/documentation/SKILL.md +124 -0
- package/data/harnesses/etna/skills/documentation/references/examples.md +162 -0
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +308 -0
- package/data/harnesses/etna/skills/documentation/references/templates.md +853 -0
- package/data/harnesses/etna/skills/workflow-mapper/SKILL.md +451 -0
- package/data/harnesses/etna/skills/workflow-mapper/references/patterns.md +214 -0
- package/data/harnesses/hr/harness.json +1 -1
- package/data/harnesses/hr/skills/evaluator/criteria/testing.md +4 -4
- package/data/harnesses/mobile/.prettierignore +12 -0
- package/data/harnesses/mobile/.prettierrc.json +3 -0
- package/data/harnesses/mobile/.vscode/settings.json +10 -0
- package/data/harnesses/mobile/CLAUDE.md +128 -0
- package/data/harnesses/mobile/agents/brainstorm.md +35 -0
- package/data/harnesses/mobile/agents/change-review.md +251 -0
- package/data/harnesses/mobile/agents/maintenance.md +240 -0
- package/data/harnesses/mobile/agents/performance-review.md +223 -0
- package/data/harnesses/mobile/agents/spec-mode.md +56 -0
- package/data/harnesses/mobile/harness.json +11 -0
- package/data/harnesses/mobile/hooks/generate-ownership-index.mjs +148 -0
- package/data/harnesses/mobile/hooks/hooks.json +39 -0
- package/data/harnesses/mobile/hooks/lib/redact.mjs +35 -0
- package/data/harnesses/mobile/hooks/prompt-recall.mjs +155 -0
- package/data/harnesses/mobile/hooks/session-reflect.mjs +285 -0
- package/data/harnesses/mobile/rules/general.md +86 -0
- package/data/harnesses/mobile/rules/nomenclature.md +41 -0
- package/data/harnesses/mobile/rules/react-native.md +132 -0
- package/data/harnesses/mobile/rules/typescript.md +40 -0
- package/data/harnesses/mobile/skills/boundary/SKILL.md +49 -0
- package/data/harnesses/mobile/skills/boundary/references/boundary-taxonomy.md +796 -0
- package/data/harnesses/mobile/skills/brainstorm/README.md +138 -0
- package/data/harnesses/mobile/skills/brainstorm/SKILL.md +135 -0
- package/data/harnesses/mobile/skills/brainstorm/harvest.md +25 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/backlog.md +39 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/build.md +37 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/decisions.md +29 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/doctrine.md +31 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/domain-model.md +25 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/glossary.md +20 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/milestones.md +43 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/non-goals.md +17 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/plan-doc.md +61 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/questions.md +36 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/references.md +28 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/rejected.md +29 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/state.md +1 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/tech.md +23 -0
- package/data/harnesses/mobile/skills/brainstorm/templates/thesis.md +21 -0
- package/data/harnesses/mobile/skills/distiller/SKILL.md +90 -0
- package/data/harnesses/mobile/skills/distiller/references/docs-mode.md +299 -0
- package/data/harnesses/mobile/skills/distiller/references/method.md +94 -0
- package/data/harnesses/mobile/skills/documentation/SKILL.md +124 -0
- package/data/harnesses/mobile/skills/documentation/references/examples.md +162 -0
- package/data/harnesses/mobile/skills/documentation/references/style-guide.md +308 -0
- package/data/harnesses/mobile/skills/documentation/references/templates.md +853 -0
- package/data/harnesses/mobile/skills/workflow-mapper/SKILL.md +451 -0
- package/data/harnesses/mobile/skills/workflow-mapper/references/patterns.md +214 -0
- package/package.json +1 -1
- package/data/harnesses/code/agents/harness-manager.md +0 -184
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# General Standards
|
|
2
|
+
|
|
3
|
+
## Runtime Versions
|
|
4
|
+
|
|
5
|
+
- **Node.js:** 22 LTS (latest)
|
|
6
|
+
- **Package manager:** detect from lockfile (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm). Don't hardcode one.
|
|
7
|
+
- **PostgreSQL:** 17 · **Redis:** 7.x (latest)
|
|
8
|
+
|
|
9
|
+
## Recommended Packages
|
|
10
|
+
|
|
11
|
+
No alternatives. Just use these.
|
|
12
|
+
|
|
13
|
+
| Package | Version | Use | Don't use |
|
|
14
|
+
| ------------------------ | --------- | ---------------------- | -------------------------- |
|
|
15
|
+
| react | ^19.2.3 | UI components | - |
|
|
16
|
+
| vite | ^8.0.0 | Build tool, dev server | webpack, CRA |
|
|
17
|
+
| tailwindcss | ^4.1.18 | Styling | styled-components |
|
|
18
|
+
| @vitejs/plugin-react-swc | ^4.2.2 | JSX compilation | @vitejs/plugin-react |
|
|
19
|
+
| react-router-dom | ^7.11.0 | Routing | - |
|
|
20
|
+
| @reduxjs/toolkit | ^2.11.2 | State management | zustand, jotai |
|
|
21
|
+
| react-redux | ^9.2.0 | Redux React bindings | - |
|
|
22
|
+
| axios | ^1.13.2 | HTTP requests | fetch |
|
|
23
|
+
| date-fns | ^4.1.0 | Date formatting | moment, dayjs |
|
|
24
|
+
| @dnd-kit/core | ^6.3.1 | Drag and drop | react-beautiful-dnd |
|
|
25
|
+
| motion | ^12.23.26 | Animations | react-spring |
|
|
26
|
+
| sonner | ^2.0.7 | Toast notifications | react-toastify |
|
|
27
|
+
| react-window | ^2.2.3 | List virtualization | react-virtualized |
|
|
28
|
+
| nanoid | ^5.1.6 | ID generation | uuid |
|
|
29
|
+
| express | ^5.2.1 | HTTP server | fastify, koa |
|
|
30
|
+
| drizzle-orm | latest | Database ORM | prisma, sequelize, typeorm |
|
|
31
|
+
| drizzle-kit | latest | Schema tooling | - |
|
|
32
|
+
| zod | ^4.2.1 | Schema validation | yup, joi |
|
|
33
|
+
| jose | ^6.1.3 | JWT handling | jsonwebtoken |
|
|
34
|
+
| microdiff | ^1.5.0 | JSON object diffing | deep-diff, just-diff |
|
|
35
|
+
| react18-json-view | ^0.2.9 | JSON tree view | react-json-view |
|
|
36
|
+
| @tiptap/react | ^3.11.1 | Rich text editor | slate, quill, draft-js |
|
|
37
|
+
| shiki | ^3.22.0 | Syntax highlighting | prism, highlight.js |
|
|
38
|
+
| lucide-react | ^0.562.0 | Icons | react-icons, heroicons |
|
|
39
|
+
| @pdfme/generator | ^5.5.8 | PDF generation | jspdf, pdfkit |
|
|
40
|
+
| cmdk | ^1.1.1 | Command palette | kbar, ninja-keys |
|
|
41
|
+
| @monaco-editor/react | ^4.7.0 | Code editor | codemirror, ace |
|
|
42
|
+
| localforage | ^1.10.0 | Client-side storage | idb, dexie |
|
|
43
|
+
| react-markdown | ^10.1.0 | Markdown rendering | marked, markdown-it |
|
|
44
|
+
| fast-deep-equal | ^3.1.3 | Deep equality checks | lodash.isEqual |
|
|
45
|
+
| @ngneat/falso | latest | Fake data generation | @faker-js/faker, chance |
|
|
46
|
+
| pino | ^10.1.0 | Logging | winston |
|
|
47
|
+
|
|
48
|
+
## Frontend
|
|
49
|
+
|
|
50
|
+
Tailwind v4 + shadcn-based UI; conventions below.
|
|
51
|
+
|
|
52
|
+
- **Build every UI through the `frontend-design` skill** — route the brief through it instead of hand-rolling layouts; it owns the archetype, personality tokens, and `@nurix/components` wiring.
|
|
53
|
+
- **Light mode is the default** — design for the light theme as the default; dark mode is opt-in, never the default.
|
|
54
|
+
- **@nurix/\* are restricted-scope npm packages** — installed from the npm registry, auto-authed by a read token in `~/.npmrc`. Just attempt the install; the token authenticates you automatically. Don't pre-empt or second-guess it. If the install actually fails, flag it to the user — never reach for alternatives, a substitute package, or `npm login`.
|
|
55
|
+
- **@nurix/components** — the UI component library (restricted-scope npm package, pre-built); import by subpath, e.g. `@nurix/components/button`.
|
|
56
|
+
- No components.json / shadcn CLI scaffold (.tsx, not .jsx) — consume the prebuilt library; never `npx shadcn add`.
|
|
57
|
+
- Vite: `@tailwindcss/vite` + `@vitejs/plugin-react-swc`. Pin react / react-dom / react/jsx-runtime / react/jsx-dev-runtime to the app's single copy via `resolve.alias` (@nurix/\* ship nested react — keep one hook dispatcher).
|
|
58
|
+
- Tailwind v4, CSS-only (no tailwind.config.js, no PostCSS): styling runs through the `@tailwindcss/vite` plugin.
|
|
59
|
+
- CSS entry `globals.css`, imports in order — `@import "./personality.css"` (token source) → `@import "tailwindcss"` → the library's `styles.css` — then `@theme inline {…}` mapping vars to tokens.
|
|
60
|
+
- Tokens: CSS vars in `:root, :root[data-theme="light"]` + the archetype selector `[data-style="…"]` (personality.css), consumed as semantic Tailwind tokens (bg-card, text-foreground). Honor the archetype's radius/theme decisions (e.g. re-assert `--radius-*` to 0 for a flat archetype; add `@custom-variant dark` only if the project opts into dark mode, and keep light as the default). The library bundles class-variance-authority, clsx, tailwind-merge.
|
|
61
|
+
- Plus general styling rules (spacing in 2/4/6/8, object-cover, truncate, motion, color tokens — never hardcoded hex in components).
|
|
62
|
+
|
|
63
|
+
## Express 5.x
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
app.use(cors({ origin: process.env.FRONTEND_URL }));
|
|
67
|
+
app.use(compression());
|
|
68
|
+
app.use(express.json({ limit: '10mb' }));
|
|
69
|
+
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- Middleware order: body parsers → custom middleware → routes → error handlers (centralized, last). Auth via middleware. Use Express Router for modular routes.
|
|
73
|
+
|
|
74
|
+
## API Response Format
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"status": 1,
|
|
79
|
+
"message": "Message sent successfully",
|
|
80
|
+
"data": { "delivered": [{ "user@example.com": 2 }] }
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Error: `{ "status": 0, "message": "Error description", "data": null }`
|
|
85
|
+
|
|
86
|
+
HTTP codes: `200`, `201` (created), `400`, `401` (not authenticated), `403` (not authorized), `404`, `500`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Nomenclature Guidelines
|
|
2
|
+
|
|
3
|
+
## Naming Guidelines
|
|
4
|
+
|
|
5
|
+
- Use descriptive names that reveal intent, purpose, and usage
|
|
6
|
+
- Names should explain why something exists and how it's used
|
|
7
|
+
- Favor clarity over brevity
|
|
8
|
+
- Maintain consistency within the codebase
|
|
9
|
+
- Avoid abbreviations unless they're universally understood
|
|
10
|
+
|
|
11
|
+
## Naming Styles
|
|
12
|
+
|
|
13
|
+
- Variables : Use nouns or noun phrases. Examples: `userData`, `firstName`, `paymentStatus`, `configOptions`
|
|
14
|
+
- Functions & Methods : Use verbs or verb phrases that describe the action. Examples: `calculateTotal`, `getUserInfo`, `validateInput`, `sendNotification`
|
|
15
|
+
- Boolean Variables : Prefix with is**, has**, can**, or should**. Examples: `isValid`, `hasPermission`, `canEdit`, `shouldRefresh`
|
|
16
|
+
- Arrays : Use plural nouns. Examples: `users`, `productList`, `errorMessages`, `activeConnections`
|
|
17
|
+
- Classes & Components : Use singular nouns or noun phrases. Examples: `RequestHandler`, `UserProfile`, `PaymentProcessor`, `DataValidator`
|
|
18
|
+
- Constants : Use uppercase with meaningful names. Examples: `DEFAULT_TIMEOUT`, `API_BASE_URL`, `MAX_RETRY_COUNT`, `COLOR_PRIMARY`
|
|
19
|
+
- Event Handlers : Use Prefix with handle or on. Examples: `handleSubmit`, `onUserLogin`, `handleInputChange`, `onPageLoad`
|
|
20
|
+
- Interfaces & Types : Use Consider prefixing with I for interfaces (if preferred). Examples: `IUserData`, `IConfigOptions`, `AuthResponse`, `RequestParams`
|
|
21
|
+
- Promises & Async Functions : Use Consider using future tense or suffixes like Async. Examples: `fetchUserData`, `getUserProfileAsync`, `loadConfigAsync`, `processPayment`
|
|
22
|
+
- File & Folder Structure : Use lowercase with descriptive names. Group by feature or function type. Example: `auth/login_service.js`
|
|
23
|
+
|
|
24
|
+
## Casing Styles
|
|
25
|
+
|
|
26
|
+
- camelCase : Used in Variables, function names, method names, property names. Examples: `userData`, `calculateTotal`, `firstName`, `isValidEmail`
|
|
27
|
+
- PascalCase : Used in Class names, component names, type definitions. Examples: `UserProfile`, `DataService`, `AuthProvider`, `RequestHandler`
|
|
28
|
+
- snake_case : Used in File names, folder names. Examples: `user_service`, `api_utils`, `auth_middleware`, `data_models`
|
|
29
|
+
- UPPER_SNAKE_CASE : Used in Constants, environment variables. Examples: `MAX_RETRY_COUNT`, `API_KEY`, `DEFAULT_TIMEOUT`, `COLOR_PRIMARY`
|
|
30
|
+
- kebab-case : Used in URLs, CSS class names, HTML IDs. Examples: `nav-container`, `user-profile`, `button-primary`, `form-wrapper`
|
|
31
|
+
|
|
32
|
+
## Database
|
|
33
|
+
|
|
34
|
+
Naming for D1 tables, columns, indexes, and their Drizzle mirrors. (For column **types** — PK shape, timestamp columns — see the Cloudflare D1 Schema rule, not here; this section governs names only.)
|
|
35
|
+
|
|
36
|
+
- **Tables** : snake_case. Entity / hub tables use the plural noun for the thing they hold. Examples: `users`, `apps`, `devices`, `services`.
|
|
37
|
+
- **Junction (M:N) tables** : name a many-to-many join `<entityA>_<entityB>_map` — **always**, even when the join carries its own columns (a `role`, a secret, a status). The `_map` suffix is the signal that a row is a relationship, not an entity; extra attributes on the join do **not** earn it a standalone noun. Use the **singular** stem of each side, owning/anchor entity first (mirror the existing tables — `app_*`). Examples: `app_user_map` (apps ⨯ users), `app_device_map` (apps ⨯ devices), `app_service_map` (apps ⨯ services).
|
|
38
|
+
- ❌ Don't invent a "nice" noun for a junction — no `members`, `installs`, `memberships`, `enrolments`. A reviewer should read the table name and know both sides and that it's a join.
|
|
39
|
+
- **Columns** : snake_case. Foreign keys are `<entity>_id` (`user_id`, `app_id`, `device_id`, `service_id`). Booleans follow the boolean rule (`is_*`/`has_*`); timestamps are `created_at` / `updated_at`.
|
|
40
|
+
- **Indexes** : `<table>_<col(s)>_idx`; unique indexes `<table>_<col(s)>_unique`. The composite unique on a junction reads `<table>_<colA>_<colB>_unique` — e.g. `app_user_map_app_user_unique`, `app_device_map_device_app_unique`.
|
|
41
|
+
- **Drizzle mirror** : the exported table object is the camelCase of the snake_case table name (`app_user_map` → `appUserMap`); column TS properties are the camelCase of the column (`created_at` → `createdAt`, `service_key` → `serviceKey`). The snake_case string passed to `sqliteTable("…")` and every index name must match `seed/init.sql` exactly (the G1 schema-parity boundary).
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# React Native + Expo
|
|
2
|
+
|
|
3
|
+
How mobile apps are built. Distilled from a hand-crafted reference app — the gold standard for *style*. This rule is **scale-aware**: a few-screen app uses the small-app form, a feature/auth-heavy app grows into the full form. Where a section offers both, the trigger to graduate is stated.
|
|
4
|
+
|
|
5
|
+
## 1. Stack & packages
|
|
6
|
+
|
|
7
|
+
- **Expo SDK** managed workflow, **config-as-code** (`app.config.ts`, never `app.json`), **expo-router** for navigation, **TypeScript strict**.
|
|
8
|
+
- New Architecture **on** by default; turn it **off** only for a legacy native module that breaks under it — document *why* at the top of `app.config.ts`.
|
|
9
|
+
- `react` / `react-dom` versions must match. Run `npx expo install <pkg>` (not a bare add) for any native/Expo module so the SDK-compatible version is picked.
|
|
10
|
+
|
|
11
|
+
| Need | Use | Not |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| HTTP | native `fetch` wrapped in a `request<T>()` util | axios |
|
|
14
|
+
| Navigation | `expo-router` (file-based) | bare React Navigation at the root |
|
|
15
|
+
| Storage (sensitive) | `expo-secure-store` | AsyncStorage |
|
|
16
|
+
| Storage (prefs/cache) | `@react-native-async-storage/async-storage` | secure-store |
|
|
17
|
+
| State (non-trivial) | `@reduxjs/toolkit` + `react-redux` | zustand, jotai |
|
|
18
|
+
| Styling | `StyleSheet.create` + token object | nativewind, tamagui, styled |
|
|
19
|
+
| Toasts | `react-native-toast-message` | custom |
|
|
20
|
+
| Haptics | `expo-haptics` behind one dispatcher | scattered call sites |
|
|
21
|
+
|
|
22
|
+
## 2. Structure & naming
|
|
23
|
+
|
|
24
|
+
- **Path alias `@/*` → `./src/*`** (in `tsconfig.json`). Import via `@/...`, never deep-relative (`../../..`). Sibling/child relative imports are fine.
|
|
25
|
+
- **Every file starts with a path + one-line purpose comment.** One-line comment above each function.
|
|
26
|
+
- **No barrel/index re-export files.** Import the source directly: `import { ThemedButton } from "@/components/ThemedButton"`.
|
|
27
|
+
- **Naming:** components & screens `PascalCase.tsx`; hooks `useXxx.ts`; services/utils/reducers/types `camelCase.ts`; route files follow expo-router (`index.tsx`, `[id].tsx`, `_layout.tsx`).
|
|
28
|
+
|
|
29
|
+
**Directory layout** — `app/` is routes only; everything else under `src/`:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
app/ # expo-router routes (screens) ONLY — see §3
|
|
33
|
+
src/
|
|
34
|
+
components/ # presentational primitives + shared UI (PascalCase)
|
|
35
|
+
hooks/ # useXxx
|
|
36
|
+
services/ # api client, auth storage, background tasks, notifications
|
|
37
|
+
state/ | reducers/+store/ # see §4 (scale decides)
|
|
38
|
+
types/ # shared TS contracts
|
|
39
|
+
utils/ # pure helpers (haptics, deepLink, schemas, ...)
|
|
40
|
+
constants/ # Colors.ts, routes.ts, toast_config.tsx
|
|
41
|
+
navigation/ # custom navigators / BackButton (only if you outgrow Stack/Tabs)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **Lean form:** for a handful of screens, group by domain instead of by type — e.g. `src/{data,player,state,ui}` — keeping `app/` routes-only. Graduate to the by-type layout above once you have >~3 cross-cutting concerns (auth + tabs + notifications + ...).
|
|
45
|
+
|
|
46
|
+
## 3. Expo Router & navigation
|
|
47
|
+
|
|
48
|
+
- **Routes live in `app/`** and contain screen logic; **presentational components live in `src/components/`**. A route file may hold its own data/business logic — strict container/presentational split is not required, but pure UI primitives stay pure.
|
|
49
|
+
- `experiments.typedRoutes: true`. Navigate with `router.push()` / `router.replace()` (use `replace` for auth/redirect transitions); reserve `<Link>` for static in-content links. Read params with `useLocalSearchParams()`.
|
|
50
|
+
- **Route groups** `(auth)` / `(tabs)` to partition without affecting the URL. `index.tsx` may be pure redirect logic, not a screen.
|
|
51
|
+
- **Root `_layout.tsx` is the provider tree**, composed outside-in in this order (include only what the app needs):
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
ErrorBoundary → Redux Provider → HydrationGate → GestureHandlerRootView
|
|
55
|
+
→ SafeAreaProvider → StatusBar → <Stack/Tabs> → Toast
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A lean app collapses this to `StatusBar → Stack` plus a one-time `useEffect` bootstrap.
|
|
59
|
+
- Header styling via `screenOptions` on the `Stack`; per-screen title via `<Stack.Screen options={{ title }}>`.
|
|
60
|
+
- For a bottom-tab app needing a custom header/drawer, `createBottomTabNavigator` inside a `(tabs)/_layout.tsx` is acceptable over expo-router `Tabs` when you need fine layout control.
|
|
61
|
+
|
|
62
|
+
## 4. State
|
|
63
|
+
|
|
64
|
+
**Scale rule:**
|
|
65
|
+
|
|
66
|
+
- **Trivial** (no auth, few screens) — **plain TS modules** under `src/state/`: a module owns one concern, exposes async get/set functions, persists to AsyncStorage with swallowed try/catch, and may cache in a module-level variable.
|
|
67
|
+
- **Non-trivial** (auth, preloaded config, cross-screen data) — **Redux Toolkit**:
|
|
68
|
+
- Slices in `src/reducers/`, store in `src/store/`. Combine slices in one root reducer.
|
|
69
|
+
- **Persist with listener middleware** (debounced ~300ms), **not** `redux-persist`.
|
|
70
|
+
- **Dual storage:** sensitive state (tokens) → `expo-secure-store`; UI prefs → AsyncStorage.
|
|
71
|
+
- **Hydrate before first render:** async hydrate thunks dispatched by a `HydrationGate` that shows a spinner until rehydrated — prevents a flash of unauthenticated UI.
|
|
72
|
+
- Store absolute expiries (`accessExpiresAt`), computed at login from the `expiresIn` the server returns.
|
|
73
|
+
|
|
74
|
+
## 5. Data fetching / networking
|
|
75
|
+
|
|
76
|
+
- **Native `fetch`**, wrapped once: a `request<T>(method, endpoint, body?, opts?)` util in `src/services/api.ts`, and a `useRequest<T>()` hook returning `{ loading, response, error, makeRequest }`.
|
|
77
|
+
- Base URL and auth token are read **at call time** (from Redux state or the settings module), not baked into a client instance. Inject `Authorization: Bearer <token>` only when `authorized: true`.
|
|
78
|
+
- Per-call `onSuccess` / `onFail` callbacks; no global interceptor. Surface failures as toasts or inline error UI.
|
|
79
|
+
- **Every fetched surface renders three states: loading, empty, error** (with a Retry affordance). Caching is in-memory at the data-module level for a lean app; Redux slices serve as the cache for the full app.
|
|
80
|
+
- Types: a `RequestMethod` union and a generic `ApiResponse<T>`; endpoint-specific call wrappers live next to their screen (`app/<feature>/api.ts`).
|
|
81
|
+
|
|
82
|
+
## 6. Styling & theming
|
|
83
|
+
|
|
84
|
+
- `StyleSheet.create` for static styles; inline only for dynamic (theme) values. **No** utility-CSS or styled-components libs.
|
|
85
|
+
- **Color tokens in one place** (`src/constants/Colors.ts`) — components reference tokens, never raw hex.
|
|
86
|
+
- **Theming is a hook, not a Context provider.** `useTheme()` returns `{ colors, colorScheme, isDark }`, resolving **user preference → device `useColorScheme()` → light fallback**. The preference lives in state (e.g. `customize.theme`: `"light" | "dark" | "system"`), so theme survives restart and there's no provider to thread.
|
|
87
|
+
|
|
88
|
+
> **Lean form:** a flat single-mode token object is fine until dark mode is a requirement; adopt the light/dark `Colors` + `useTheme()` shape when it is.
|
|
89
|
+
|
|
90
|
+
## 7. Native modules & permissions
|
|
91
|
+
|
|
92
|
+
- **Wrap each `expo-*` module behind a typed helper** — never sprinkle raw calls. Canonical example: one `triggerHaptic(type)` dispatcher with named exports (`triggerSuccessHaptic`, `triggerTabChangeHaptic`, ...) over `expo-haptics`.
|
|
93
|
+
- **Platform-specific code via filename suffixes** — `Foo.native.tsx` / `Foo.web.tsx` / `Foo.tsx` — preferred over imperative `Platform.select()`. Use `Platform.OS` checks for small inline branches only.
|
|
94
|
+
- Declare Android/iOS permissions and usage strings in `app.config.ts`. Request at point-of-use with a clear pre-prompt where the platform allows.
|
|
95
|
+
- Background work (location, audio) runs through a registered task (`expo-task-manager` / a playback service) so it survives the UI being unmounted — register it in the **app entry** before the router mounts.
|
|
96
|
+
|
|
97
|
+
## 8. Reusable primitives
|
|
98
|
+
|
|
99
|
+
- **`Themed{View,Text,Button}`** as the base layer: extend the native props and add `lightColor?` / `darkColor?`; `ThemedText` takes `type?: "default" | "title" | "subtitle" | "link"`; `ThemedButton` takes `variant?: "contained" | "outlined" | "text"`, `size?`, `fullWidth?`.
|
|
100
|
+
- **`EdgeToEdgeScreen` / `EdgeToEdgeTabScreen` / `EdgeToEdgeTabBar`** own safe-area inset handling so screens never compute insets inline.
|
|
101
|
+
- Loading/feedback primitives (`DotLoader`, `CircularProgress`, `PullToRefreshHint`) are shared, not re-implemented per screen.
|
|
102
|
+
|
|
103
|
+
## 9. TypeScript
|
|
104
|
+
|
|
105
|
+
- **`interface`** for external contracts (API shapes, component props); **`type`** for unions and aliases (`type RequestMethod = "get" | ...`).
|
|
106
|
+
- Shared domain types in `src/types/`; component prop types extend native prop types (`TextProps & { ... }`).
|
|
107
|
+
- **Never weaken `strict` or `noUncheckedIndexedAccess`.** A framework base (`expo/tsconfig.base`) may be extended but not relaxed. Generics for `request<T>` / `useRequest<T>`; no implicit `any` on external data. (See the `typescript` rule for the workspace-wide policy.)
|
|
108
|
+
|
|
109
|
+
## 10. Config & environment
|
|
110
|
+
|
|
111
|
+
- `app.config.ts` (TS) is the single source: scheme, deep-link intent filters, permissions, plugins, `userInterfaceStyle: "automatic"`, `experiments.typedRoutes: true`, splash/icon.
|
|
112
|
+
- Client-readable env via **`EXPO_PUBLIC_*`** only, read through `expo-constants` or `process.env`. **Never commit `.env`**; ship `.env.example`. Secrets/keystore creds stay out of git.
|
|
113
|
+
- Build profiles in `eas.json` (`development` / `preview` / `production`). Local config plugins (`./plugins/withXxx.js`) for native edits the SDK doesn't expose (e.g. Android Auto).
|
|
114
|
+
|
|
115
|
+
## 11. Errors, loading & UX
|
|
116
|
+
|
|
117
|
+
- **`ErrorBoundary` class component wraps the whole app.** On crash it logs, clears *session* state but **preserves user preferences**, and shows an apology + recover affordance.
|
|
118
|
+
- **Toasts** via `react-native-toast-message` with a custom `toast_config.tsx` (success/error/warning variants, `topOffset` below the header). Fire inline: `Toast.show({ type, text1, text2 })`.
|
|
119
|
+
- Gate first paint: hide the splash only after fonts load + state hydrates (`expo-splash-screen`).
|
|
120
|
+
- **Lists:** `FlatList` typed with `ListRenderItemInfo<T>`, `ListEmptyComponent` for empty, `onEndReached` + pagination for infinite scroll, `refreshing`/`onRefresh` for pull-to-refresh.
|
|
121
|
+
- Safe-area through the `EdgeToEdge*` wrappers (§8), not ad-hoc insets.
|
|
122
|
+
|
|
123
|
+
## 12. Web target (`expo start --web`)
|
|
124
|
+
|
|
125
|
+
- Web needs `react-dom`, `react-native-web`, `@expo/metro-runtime`. Add a `"web": "expo start --web"` script.
|
|
126
|
+
- `react-native-track-player` runs on web only with its peer **`shaka-player`** installed; without it the bundle fails to resolve and the page is blank.
|
|
127
|
+
- **Native-only features have no web equivalent** — background audio, lock-screen controls, Android Auto. Guard them behind `Platform.OS` so the web build renders, and treat web as a layout/navigation preview, not a playback/Auto test surface.
|
|
128
|
+
|
|
129
|
+
## 13. Formatting
|
|
130
|
+
|
|
131
|
+
- Prettier: `tabWidth: 4`, `singleQuote: false` (double quotes), `bracketSameLine: true`, `printWidth: 120`, trailing commas.
|
|
132
|
+
- No commented-out code; comments explain *why*, not *what*.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# TypeScript
|
|
2
|
+
|
|
3
|
+
TypeScript conventions. Compiles under `strict: true` and `noUncheckedIndexedAccess: true`.
|
|
4
|
+
|
|
5
|
+
## Type Safety
|
|
6
|
+
|
|
7
|
+
- Never `any` to silence the checker. Unknown value → type `unknown`, then narrow (type guard / `zod` parse).
|
|
8
|
+
- Never weaken strictness. No `// @ts-nocheck`; `// @ts-expect-error` only with a same-line reason.
|
|
9
|
+
- Infer locals & return types; annotate at boundaries (exports, props, contracts).
|
|
10
|
+
|
|
11
|
+
## Generics
|
|
12
|
+
|
|
13
|
+
Shared libraries (`@nurix/components`, etc.) are generic on purpose — that's what types `cell(row)`, checks `slug` against `keyof TData`, and ties `data`/`columns` together. Keep the safety; never widen to `any` to dodge it. State the row type **once per file**, then let inference carry it:
|
|
14
|
+
|
|
15
|
+
- **Bind a repeated factory once** (preferred): `const col = defineColumn<BriefRow>;` → `col({...})`. No `<T>` per call; `row` stays typed. Canonical: `modules/briefs/BriefsListPage.tsx`.
|
|
16
|
+
- **Infer JSX type args from props:** `<Table data={rows} columns={columns} />`, not `<Table<BriefRow> …>`.
|
|
17
|
+
- **Anchor with a collection annotation** when nothing else can: `const columns: ColumnDefinition<BriefRow>[] = [...]`.
|
|
18
|
+
|
|
19
|
+
Don't repeat `fn<Type>(…)` / `<Comp<Type>>` at every call site — cosmetic only (same safety, same emitted JS).
|
|
20
|
+
|
|
21
|
+
> Monorepo TS policy (`tsconfig.base.json` + thin per-package `extends`, source-first internal packages, `peerDependencies`, pnpm `catalog:`) lives in [`nustack.md`](nustack.md) §2 — it travels with package boundaries and publishing.
|
|
22
|
+
|
|
23
|
+
## Consuming `@nurix/*` packages
|
|
24
|
+
|
|
25
|
+
`@nurix/*` packages publish to npm with **`restricted`** access — installing needs read rights to the `nurix` org. (Publishing one is [`nustack.md`](nustack.md) §3.)
|
|
26
|
+
|
|
27
|
+
- **Auth** — `npm login` once (the npm user must be a `nurix` member), **or** drop a read token in `.npmrc` (the only CI option; with it you don't need `npm login`):
|
|
28
|
+
```ini
|
|
29
|
+
@nurix:registry=https://registry.npmjs.org/
|
|
30
|
+
//registry.npmjs.org/:_authToken=${NPM_READ_TOKEN}
|
|
31
|
+
```
|
|
32
|
+
A `403` on install means auth failed — check `npm whoami` (local) or the `NPM_READ_TOKEN` secret (CI). The read token only needs `Read` on the `nurix` scope.
|
|
33
|
+
- **Install** — `npm install @nurix/<name>` (or `pnpm add` / `yarn add`).
|
|
34
|
+
- **Import by subpath, never the barrel** — bundlers tree-shake subpaths reliably; root-barrel imports less so. Available subpaths are the package's `exports` keys.
|
|
35
|
+
```tsx
|
|
36
|
+
import { Button } from "@nurix/components/button";
|
|
37
|
+
import { useMobile } from "@nurix/components/hooks/use-mobile";
|
|
38
|
+
```
|
|
39
|
+
- **Styles** — packages that ship CSS need their stylesheet imported **once** at the app entry: `import "@nurix/components/styles.css";`. Unstyled components are almost always a missing styles import. Tailwind v4 doesn't scan `node_modules`, but the shipped stylesheet is precompiled — no `@source` directive needed unless you consume the source via a workspace link.
|
|
40
|
+
- **CLIs** ship a `bin` — run directly: `npx @nurix/etna@latest` (or `npm install -g`).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: boundary
|
|
3
|
+
description: Boundary diagnostic for implementation plans. Given a plan file, reads the source files the plan touches, identifies the application boundaries they participate in — the cross-cutting contracts (db schema, wire envelope, auth, events, env/config, …) where editing one side silently breaks the other — and reports whether the planned changes would violate any of them, severity-ranked, with suggested plan amendments. Read-only; the report lands in chat. Can also run against a diff or named code area instead of a plan. Triggers on "boundary check", "check this plan", "what could break", "impact analysis", "/boundary".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Boundary Diagnostic
|
|
7
|
+
|
|
8
|
+
You diagnose whether a plan's changes would break an application boundary.
|
|
9
|
+
|
|
10
|
+
A boundary is a contract between two pieces of code (or code and infra) that share an assumption with **no compile-time link** between them: the column a raw SQL string names, the JSON envelope a client unwraps, the event string a dispatcher parses, the env var a deploy script writes. Changing one side ships green and breaks the other silently. Your job is to find those edges in the plan's path _before_ execution.
|
|
11
|
+
|
|
12
|
+
[`references/boundary-taxonomy.md`](references/boundary-taxonomy.md) is the catalog of boundary types (~170 across persistence, wire/API, frontend, auth/tenancy, async/messaging, config/build, runtime/infra), with per-type severities and safe-change checklists. Read it first — it tells you what to look for; the code tells you what exists.
|
|
13
|
+
|
|
14
|
+
## Run
|
|
15
|
+
|
|
16
|
+
**Input:** a plan path (given, or the newest in `docs/history/plans/`) — or a diff / named code area for non-plan work. Everything below is identical either way; a diff's hunks stand in for the plan's steps.
|
|
17
|
+
|
|
18
|
+
1. **Build the touch list.** Parse the plan's Proposed Changes: files to be created or modified, entities/state added or changed, API changes, anything the author flags as a cross-module contract. Note missing subsections.
|
|
19
|
+
|
|
20
|
+
2. **Identify the boundaries in the touched code.** Read each touched file (and its immediate imports). Against the taxonomy, list the contracts this code participates in — the schema it queries, the envelope it emits, the events it publishes or parses, the auth checks it sits behind, the env it reads.
|
|
21
|
+
|
|
22
|
+
3. **Trace the other side.** A boundary has two sides; the danger lives on the one the plan _doesn't_ mention. For each contract, grep the shared name or shape across the repo — the column name in other SQL strings, the response field in client unwrappers, the event kind in subscribers, the route in fetch calls and scripts, the env key in deploy tooling. Record consumers as `file:line`.
|
|
23
|
+
|
|
24
|
+
4. **Judge each intersection.** Does the planned change alter a shape a traced consumer depends on, break an invariant the code establishes (append-only vocabularies, idempotency keys, fail-closed checks, ordering guarantees), or add a new cross-module edge? Severity:
|
|
25
|
+
- **CRITICAL** — can corrupt persisted data, or open an auth/money path.
|
|
26
|
+
- **HIGH** — silent cross-module breakage that would ship green.
|
|
27
|
+
- **MEDIUM** — drift-prone coupling (string-matched, duplicated constants).
|
|
28
|
+
- **LOW** — smell. Loud, immediately-visible breakage caps at HIGH.
|
|
29
|
+
|
|
30
|
+
5. **Report in chat:**
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
# Boundary check — {plan}
|
|
34
|
+
|
|
35
|
+
Touched files read: {N} · Boundaries identified: {N}
|
|
36
|
+
Violations: {N} ({critical}/{high}/{medium}/{low})
|
|
37
|
+
|
|
38
|
+
| # | Boundary | Planned change | Other side (file:line) | Severity |
|
|
39
|
+
|
|
40
|
+
## Suggested plan amendments
|
|
41
|
+
## Missing plan subsections
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- **Read-only.** You never edit the plan, the code, or any doc. The user folds your amendments into the plan.
|
|
47
|
+
- **Verify in source.** Never flag from plan text alone — read both sides of every boundary you report, and cite both as `file:line`.
|
|
48
|
+
- **No consumer, no finding.** An intersection with no traced other side isn't a violation; at most note it as a new edge the plan introduces.
|
|
49
|
+
- **A clean report is valid.** Zero violations means say so — don't pad.
|