docs: add .github/copilot-instructions.md for GitHub Copilot
This commit is contained in:
@@ -0,0 +1,150 @@
|
|||||||
|
# Nerve Coding Conventions
|
||||||
|
|
||||||
|
## Language & Paradigm
|
||||||
|
|
||||||
|
### Functional-first
|
||||||
|
|
||||||
|
Use `function` + `type`, not `class` + `interface`.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good
|
||||||
|
type Signal = {
|
||||||
|
senseId: string;
|
||||||
|
value: unknown;
|
||||||
|
ts: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createSignal(senseId: string, value: unknown): Signal {
|
||||||
|
return { senseId, value, ts: Date.now() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Bad — no class, no interface
|
||||||
|
class Signal implements ISignal { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
| Rule | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `type` over `interface` | All type definitions use `type` |
|
||||||
|
| `function` over `class` | Pure functions + closures, no class |
|
||||||
|
| No `this` | Functions must not depend on `this` context |
|
||||||
|
| No inheritance | No `extends`, `implements`, `abstract` |
|
||||||
|
| Composition over inheritance | Use function composition |
|
||||||
|
| Immutability first | Use `Readonly<T>`, `as const`, avoid mutation |
|
||||||
|
| No optional properties | Use `T \| null` instead of `?:` — see below |
|
||||||
|
|
||||||
|
### Exceptions
|
||||||
|
|
||||||
|
Classes are allowed when:
|
||||||
|
- Required by a third-party library (e.g. Drizzle's `sqliteTable`)
|
||||||
|
- Error subclasses (`class NerveError extends Error`)
|
||||||
|
|
||||||
|
### No Optional Properties
|
||||||
|
|
||||||
|
Never use `?:`. All nullable fields must be explicit `T | null`.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good
|
||||||
|
type SenseConfig = {
|
||||||
|
group: string;
|
||||||
|
throttle: string | null;
|
||||||
|
timeout: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ❌ Bad
|
||||||
|
type SenseConfig = {
|
||||||
|
group: string;
|
||||||
|
throttle?: string;
|
||||||
|
timeout?: string;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
For mutually exclusive fields, use discriminated unions:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good
|
||||||
|
type ReflexConfig =
|
||||||
|
| { kind: "sense"; sense: string; interval: string | null; on: string[] | null }
|
||||||
|
| { kind: "workflow"; workflow: string; on: string[] | null };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modules & Exports
|
||||||
|
|
||||||
|
- Always named exports, never default exports
|
||||||
|
- One module = one responsibility, filename = purpose
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
| Type | Style | Example |
|
||||||
|
|------|-------|---------|
|
||||||
|
| Files | kebab-case | `signal-bus.ts` |
|
||||||
|
| Types | PascalCase | `SignalBus` |
|
||||||
|
| Functions/variables | camelCase | `createSignalBus` |
|
||||||
|
| Constants | UPPER_SNAKE | `MAX_RETRY_COUNT` |
|
||||||
|
| Generics | Single letter or descriptive | `T`, `TValue` |
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- Use `Result` type for expected failures
|
||||||
|
- `throw` only for unrecoverable bugs (programmer errors)
|
||||||
|
- No try-catch for flow control
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Async
|
||||||
|
|
||||||
|
- Always `async/await`, never `.then()` chains
|
||||||
|
|
||||||
|
## No Dynamic Import
|
||||||
|
|
||||||
|
Do NOT use `await import()` in production code. Always use static top-level `import`.
|
||||||
|
|
||||||
|
Exceptions (must include a comment):
|
||||||
|
1. `sense-runtime.ts` — user module paths known only at runtime
|
||||||
|
2. `workflow-worker.ts` — user module paths known only at runtime
|
||||||
|
|
||||||
|
Test files (`__tests__/**`) are exempt.
|
||||||
|
|
||||||
|
## Toolchain
|
||||||
|
|
||||||
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| **pnpm** | Package manager |
|
||||||
|
| **TypeScript** | Type checking (strict mode) |
|
||||||
|
| **Biome** | Lint + format (replaces ESLint + Prettier) |
|
||||||
|
| **tsup** | Bundling |
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run check # biome check (lint + format)
|
||||||
|
pnpm run format # biome format --write
|
||||||
|
pnpm run build # full build
|
||||||
|
pnpm test # run tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monorepo Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
nerve/
|
||||||
|
packages/
|
||||||
|
core/ # @nerve/core — shared types and utils
|
||||||
|
cli/ # @nerve/cli — CLI entry point
|
||||||
|
daemon/ # @nerve/daemon — engine runtime
|
||||||
|
docs/ # RFCs, conventions
|
||||||
|
```
|
||||||
|
|
||||||
|
- `core` is the shared layer; `cli` and `daemon` both depend on it
|
||||||
|
- `cli` and `daemon` must NOT depend on each other
|
||||||
|
|
||||||
|
## Commit Convention
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <description>
|
||||||
|
|
||||||
|
type: feat | fix | refactor | docs | chore | test
|
||||||
|
scope: core | cli | daemon | rfc-001 | ...
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user