automatasaurus 0.1.0
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/LICENSE +21 -0
- package/README.md +543 -0
- package/bin/cli.js +62 -0
- package/package.json +39 -0
- package/src/commands/init.js +149 -0
- package/src/commands/status.js +68 -0
- package/src/commands/update.js +140 -0
- package/src/lib/block-merge.js +86 -0
- package/src/lib/json-merge.js +121 -0
- package/src/lib/manifest.js +60 -0
- package/src/lib/paths.js +55 -0
- package/src/lib/symlinks.js +127 -0
- package/template/CLAUDE.block.md +357 -0
- package/template/README.md +36 -0
- package/template/agents/architect/AGENT.md +167 -0
- package/template/agents/designer/AGENT.md +289 -0
- package/template/agents/developer/AGENT.md +182 -0
- package/template/agents/tester/AGENT.md +308 -0
- package/template/artifacts/discovery.md.template +193 -0
- package/template/artifacts/implementation-plan.md.template +119 -0
- package/template/commands/discovery.md +325 -0
- package/template/commands/work-all.md +274 -0
- package/template/commands/work-plan.md +169 -0
- package/template/commands/work.md +73 -0
- package/template/commands.block.md +45 -0
- package/template/commands.template.md +156 -0
- package/template/hooks/notify.sh +83 -0
- package/template/hooks/on-stop.sh +54 -0
- package/template/hooks/request-attention.sh +16 -0
- package/template/settings.json +85 -0
- package/template/skills/agent-coordination/SKILL.md +166 -0
- package/template/skills/code-review/SKILL.md +386 -0
- package/template/skills/css-standards/SKILL.md +488 -0
- package/template/skills/github-issues/SKILL.md +144 -0
- package/template/skills/github-workflow/SKILL.md +161 -0
- package/template/skills/infrastructure-standards/SKILL.md +189 -0
- package/template/skills/javascript-standards/SKILL.md +355 -0
- package/template/skills/notifications/SKILL.md +95 -0
- package/template/skills/pr-writing/SKILL.md +259 -0
- package/template/skills/project-commands/SKILL.md +100 -0
- package/template/skills/python-standards/SKILL.md +284 -0
- package/template/skills/requirements-gathering/SKILL.md +212 -0
- package/template/skills/user-stories/SKILL.md +192 -0
- package/template/skills/work-issue/SKILL.md +245 -0
- package/template/skills/workflow-orchestration/SKILL.md +271 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-workflow
|
|
3
|
+
description: Manages GitHub workflow including issues, PRs, labels, and reviews. Use when working with GitHub issues, creating PRs, or coordinating reviews.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GitHub Workflow Skill
|
|
7
|
+
|
|
8
|
+
Guidance for managing the development workflow through GitHub.
|
|
9
|
+
|
|
10
|
+
## Issue State Labels
|
|
11
|
+
|
|
12
|
+
| Label | Description | When to Apply |
|
|
13
|
+
|-------|-------------|---------------|
|
|
14
|
+
| `ready` | No blocking dependencies | Issue can be picked up |
|
|
15
|
+
| `in-progress` | Currently being implemented | Developer starts work |
|
|
16
|
+
| `blocked` | Waiting on dependencies | Has unresolved blockers |
|
|
17
|
+
| `needs-review` | PR open, awaiting reviews | PR created |
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gh issue edit {n} --add-label "in-progress" --remove-label "ready"
|
|
21
|
+
gh issue edit {n} --add-label "needs-review" --remove-label "in-progress"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Dependency Tracking
|
|
25
|
+
|
|
26
|
+
In issue body:
|
|
27
|
+
```markdown
|
|
28
|
+
## Dependencies
|
|
29
|
+
Depends on #12 (User authentication)
|
|
30
|
+
Depends on #15 (Database schema)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Parse dependencies:
|
|
34
|
+
```bash
|
|
35
|
+
gh issue view {n} --json body --jq '.body' | grep -oE "Depends on #[0-9]+" | grep -oE "[0-9]+"
|
|
36
|
+
gh issue view {dep} --json state --jq '.state'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Agent Identification
|
|
40
|
+
|
|
41
|
+
**All GitHub interactions must include an agent identifier header.**
|
|
42
|
+
|
|
43
|
+
Format: `**[Agent Name]**` at the start of every issue body, PR description, and comment.
|
|
44
|
+
|
|
45
|
+
## Standardized Review Comments
|
|
46
|
+
|
|
47
|
+
Since all agents share the same GitHub user, reviews use standardized comments:
|
|
48
|
+
|
|
49
|
+
**Approval:**
|
|
50
|
+
```
|
|
51
|
+
✅ APPROVED - [Role]
|
|
52
|
+
[Summary]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Changes Requested:**
|
|
56
|
+
```
|
|
57
|
+
❌ CHANGES REQUESTED - [Role]
|
|
58
|
+
[Issues to address]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Required Reviews Per PR
|
|
62
|
+
|
|
63
|
+
| Review | When Required |
|
|
64
|
+
|--------|---------------|
|
|
65
|
+
| Architect | Always |
|
|
66
|
+
| Designer | If UI changes |
|
|
67
|
+
| Tester | Always |
|
|
68
|
+
|
|
69
|
+
## Review Templates
|
|
70
|
+
|
|
71
|
+
### Architect
|
|
72
|
+
```bash
|
|
73
|
+
gh pr comment {n} --body "**[Architect]**
|
|
74
|
+
|
|
75
|
+
✅ APPROVED - Architect
|
|
76
|
+
|
|
77
|
+
Clean architecture. LGTM."
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Designer
|
|
81
|
+
```bash
|
|
82
|
+
gh pr comment {n} --body "**[Designer]**
|
|
83
|
+
|
|
84
|
+
✅ APPROVED - Designer
|
|
85
|
+
|
|
86
|
+
UI looks good. Accessibility met."
|
|
87
|
+
|
|
88
|
+
# Or if not applicable:
|
|
89
|
+
gh pr comment {n} --body "**[Designer]** N/A - No UI changes."
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Tester
|
|
93
|
+
```bash
|
|
94
|
+
gh pr comment {n} --body "**[Tester]**
|
|
95
|
+
|
|
96
|
+
✅ APPROVED - Tester
|
|
97
|
+
|
|
98
|
+
**Tests:** All passing
|
|
99
|
+
**Manual:** Completed
|
|
100
|
+
|
|
101
|
+
Ready for merge."
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Product Owner (Merge)
|
|
105
|
+
```bash
|
|
106
|
+
gh pr comment {n} --body "**[Product Owner]**
|
|
107
|
+
|
|
108
|
+
All required reviews complete:
|
|
109
|
+
- ✅ Architect
|
|
110
|
+
- ✅ Tester
|
|
111
|
+
|
|
112
|
+
Proceeding with merge."
|
|
113
|
+
|
|
114
|
+
gh pr merge {n} --squash --delete-branch
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## PR Approval Verification
|
|
118
|
+
|
|
119
|
+
Before merge:
|
|
120
|
+
1. `✅ APPROVED - Architect` present
|
|
121
|
+
2. `✅ APPROVED - Tester` present
|
|
122
|
+
3. `✅ APPROVED - Designer` present (if UI)
|
|
123
|
+
4. No outstanding `❌ CHANGES REQUESTED`
|
|
124
|
+
5. CI/tests passing
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
gh pr view {n} --comments # Check for approval comments
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Labels
|
|
131
|
+
|
|
132
|
+
### Priority
|
|
133
|
+
| Label | Description |
|
|
134
|
+
|-------|-------------|
|
|
135
|
+
| `priority:high` | Work first |
|
|
136
|
+
| `priority:medium` | Normal |
|
|
137
|
+
| `priority:low` | Work last |
|
|
138
|
+
|
|
139
|
+
### Type
|
|
140
|
+
| Label | Description |
|
|
141
|
+
|-------|-------------|
|
|
142
|
+
| `feature` | New functionality |
|
|
143
|
+
| `bug` | Bug fix |
|
|
144
|
+
| `enhancement` | Improvement |
|
|
145
|
+
|
|
146
|
+
## Common Commands
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Issues
|
|
150
|
+
gh issue list --state open --label "ready"
|
|
151
|
+
gh issue view {n}
|
|
152
|
+
gh issue edit {n} --add-label "in-progress"
|
|
153
|
+
|
|
154
|
+
# PRs
|
|
155
|
+
gh pr create --title "..." --body "..."
|
|
156
|
+
gh pr view {n} --comments
|
|
157
|
+
gh pr merge {n} --squash --delete-branch
|
|
158
|
+
|
|
159
|
+
# Milestones
|
|
160
|
+
gh issue edit {n} --milestone "v1.0"
|
|
161
|
+
```
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: infrastructure-standards
|
|
3
|
+
description: Infrastructure, cloud, and DevOps standards. Use when setting up new projects, configuring cloud resources, or working with IaC.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Infrastructure Standards
|
|
7
|
+
|
|
8
|
+
## New Project Preferences
|
|
9
|
+
|
|
10
|
+
When starting new projects, prefer:
|
|
11
|
+
|
|
12
|
+
- **Terraform** for Infrastructure as Code
|
|
13
|
+
- **GCP** (Google Cloud Platform) by default, unless otherwise specified
|
|
14
|
+
- **Serverless** architectures that are free when idle (Cloud Run, Cloud Functions)
|
|
15
|
+
- **Docker Compose** for local development
|
|
16
|
+
|
|
17
|
+
## Terraform
|
|
18
|
+
|
|
19
|
+
### Project Structure
|
|
20
|
+
```
|
|
21
|
+
infrastructure/
|
|
22
|
+
├── main.tf # Main configuration
|
|
23
|
+
├── variables.tf # Input variables
|
|
24
|
+
├── outputs.tf # Output values
|
|
25
|
+
├── providers.tf # Provider configuration
|
|
26
|
+
├── terraform.tfvars # Variable values (gitignored for secrets)
|
|
27
|
+
└── modules/ # Reusable modules
|
|
28
|
+
└── api/
|
|
29
|
+
├── main.tf
|
|
30
|
+
├── variables.tf
|
|
31
|
+
└── outputs.tf
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Best Practices
|
|
35
|
+
- Use remote state (GCS bucket for GCP)
|
|
36
|
+
- Lock state files to prevent concurrent modifications
|
|
37
|
+
- Use workspaces or separate state files for environments
|
|
38
|
+
- Tag all resources consistently
|
|
39
|
+
- Use modules for reusable infrastructure
|
|
40
|
+
|
|
41
|
+
### GCP Provider Setup
|
|
42
|
+
```hcl
|
|
43
|
+
terraform {
|
|
44
|
+
required_version = ">= 1.0"
|
|
45
|
+
|
|
46
|
+
required_providers {
|
|
47
|
+
google = {
|
|
48
|
+
source = "hashicorp/google"
|
|
49
|
+
version = "~> 5.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
backend "gcs" {
|
|
54
|
+
bucket = "project-terraform-state"
|
|
55
|
+
prefix = "terraform/state"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
provider "google" {
|
|
60
|
+
project = var.project_id
|
|
61
|
+
region = var.region
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## GCP Services
|
|
66
|
+
|
|
67
|
+
### Preferred Services (Free When Idle)
|
|
68
|
+
|
|
69
|
+
| Use Case | Service | Notes |
|
|
70
|
+
|----------|---------|-------|
|
|
71
|
+
| APIs/Backend | Cloud Run | Scale to zero, pay per request |
|
|
72
|
+
| Background Jobs | Cloud Functions | Event-driven, scale to zero |
|
|
73
|
+
| Scheduled Tasks | Cloud Scheduler + Cloud Functions | Cron-like scheduling |
|
|
74
|
+
| Database | Cloud SQL (small) or Firestore | Firestore has free tier |
|
|
75
|
+
| Storage | Cloud Storage | Pay per use |
|
|
76
|
+
| Auth | Firebase Auth | Free tier generous |
|
|
77
|
+
|
|
78
|
+
### Cloud Run Example
|
|
79
|
+
```hcl
|
|
80
|
+
resource "google_cloud_run_service" "api" {
|
|
81
|
+
name = "api"
|
|
82
|
+
location = var.region
|
|
83
|
+
|
|
84
|
+
template {
|
|
85
|
+
spec {
|
|
86
|
+
containers {
|
|
87
|
+
image = "gcr.io/${var.project_id}/api:latest"
|
|
88
|
+
|
|
89
|
+
resources {
|
|
90
|
+
limits = {
|
|
91
|
+
memory = "512Mi"
|
|
92
|
+
cpu = "1"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
metadata {
|
|
99
|
+
annotations = {
|
|
100
|
+
"autoscaling.knative.dev/minScale" = "0" # Scale to zero
|
|
101
|
+
"autoscaling.knative.dev/maxScale" = "10"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Docker Compose (Local Dev)
|
|
109
|
+
|
|
110
|
+
### Standard Structure
|
|
111
|
+
```yaml
|
|
112
|
+
# docker-compose.yml
|
|
113
|
+
version: "3.8"
|
|
114
|
+
|
|
115
|
+
services:
|
|
116
|
+
api:
|
|
117
|
+
build: ./api
|
|
118
|
+
ports:
|
|
119
|
+
- "8000:8000"
|
|
120
|
+
volumes:
|
|
121
|
+
- ./api:/app
|
|
122
|
+
environment:
|
|
123
|
+
- DATABASE_URL=postgresql://postgres:postgres@db:5432/app
|
|
124
|
+
depends_on:
|
|
125
|
+
- db
|
|
126
|
+
|
|
127
|
+
web:
|
|
128
|
+
build: ./web
|
|
129
|
+
ports:
|
|
130
|
+
- "3000:3000"
|
|
131
|
+
volumes:
|
|
132
|
+
- ./web:/app
|
|
133
|
+
- /app/node_modules
|
|
134
|
+
environment:
|
|
135
|
+
- VITE_API_URL=http://localhost:8000
|
|
136
|
+
|
|
137
|
+
db:
|
|
138
|
+
image: postgres:15
|
|
139
|
+
ports:
|
|
140
|
+
- "5432:5432"
|
|
141
|
+
environment:
|
|
142
|
+
- POSTGRES_PASSWORD=postgres
|
|
143
|
+
- POSTGRES_DB=app
|
|
144
|
+
volumes:
|
|
145
|
+
- postgres_data:/var/lib/postgresql/data
|
|
146
|
+
|
|
147
|
+
volumes:
|
|
148
|
+
postgres_data:
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Best Practices
|
|
152
|
+
- Use named volumes for persistent data
|
|
153
|
+
- Mount source code for hot reload in development
|
|
154
|
+
- Use `.env` files for environment variables (gitignored)
|
|
155
|
+
- Include health checks for dependent services
|
|
156
|
+
- Use `depends_on` with health conditions when needed
|
|
157
|
+
|
|
158
|
+
## Environment Management
|
|
159
|
+
|
|
160
|
+
### Secret Handling
|
|
161
|
+
- Never commit secrets to git
|
|
162
|
+
- Use `.env` files locally (gitignored)
|
|
163
|
+
- Use GCP Secret Manager for production
|
|
164
|
+
- Reference secrets in Terraform:
|
|
165
|
+
|
|
166
|
+
```hcl
|
|
167
|
+
data "google_secret_manager_secret_version" "api_key" {
|
|
168
|
+
secret = "api-key"
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
resource "google_cloud_run_service" "api" {
|
|
172
|
+
# ...
|
|
173
|
+
template {
|
|
174
|
+
spec {
|
|
175
|
+
containers {
|
|
176
|
+
env {
|
|
177
|
+
name = "API_KEY"
|
|
178
|
+
value_from {
|
|
179
|
+
secret_key_ref {
|
|
180
|
+
name = google_secret_manager_secret.api_key.secret_id
|
|
181
|
+
key = "latest"
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: javascript-standards
|
|
3
|
+
description: JavaScript and TypeScript coding standards, conventions, and best practices. Use when writing, reviewing, or testing JS/TS code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# JavaScript/TypeScript Coding Standards
|
|
7
|
+
|
|
8
|
+
## New Project Preferences
|
|
9
|
+
|
|
10
|
+
When starting new frontend projects, prefer:
|
|
11
|
+
|
|
12
|
+
- **React 18** with TypeScript
|
|
13
|
+
- **Vite** for development server
|
|
14
|
+
- **Parcel** for production bundling
|
|
15
|
+
- **shadcn/ui** for components
|
|
16
|
+
- **Tailwind CSS 3** for styling
|
|
17
|
+
|
|
18
|
+
## General Preferences
|
|
19
|
+
|
|
20
|
+
- **TypeScript over JavaScript** for new code
|
|
21
|
+
- **ESM over CommonJS** (`import`/`export` not `require`)
|
|
22
|
+
- **Prettier** for formatting, **ESLint** for linting
|
|
23
|
+
- **Strict TypeScript** (`"strict": true` in tsconfig)
|
|
24
|
+
|
|
25
|
+
## Style Guide
|
|
26
|
+
|
|
27
|
+
### Formatting (Prettier defaults)
|
|
28
|
+
- Line length: 80-100 characters
|
|
29
|
+
- Single quotes for strings
|
|
30
|
+
- Semicolons: consistent (prefer with)
|
|
31
|
+
- Trailing commas: ES5 or all
|
|
32
|
+
- 2 space indentation
|
|
33
|
+
|
|
34
|
+
### Naming Conventions
|
|
35
|
+
```typescript
|
|
36
|
+
// Files: kebab-case or PascalCase for components
|
|
37
|
+
user-service.ts
|
|
38
|
+
UserProfile.tsx
|
|
39
|
+
|
|
40
|
+
// Variables and functions: camelCase
|
|
41
|
+
const activeUsers = [];
|
|
42
|
+
function getUserById(userId: string): User {}
|
|
43
|
+
|
|
44
|
+
// Classes and types: PascalCase
|
|
45
|
+
class UserService {}
|
|
46
|
+
interface UserProfile {}
|
|
47
|
+
type UserId = string;
|
|
48
|
+
|
|
49
|
+
// Constants: SCREAMING_SNAKE_CASE or camelCase
|
|
50
|
+
const MAX_RETRY_ATTEMPTS = 3;
|
|
51
|
+
const defaultTimeout = 30000; // also acceptable
|
|
52
|
+
|
|
53
|
+
// React components: PascalCase
|
|
54
|
+
function UserCard({ user }: UserCardProps) {}
|
|
55
|
+
const ProfileHeader: React.FC<Props> = () => {};
|
|
56
|
+
|
|
57
|
+
// Boolean variables: is/has/should prefix
|
|
58
|
+
const isActive = true;
|
|
59
|
+
const hasPermission = false;
|
|
60
|
+
const shouldRefetch = true;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## TypeScript
|
|
64
|
+
|
|
65
|
+
### Type Definitions
|
|
66
|
+
```typescript
|
|
67
|
+
// Prefer interfaces for objects that can be extended
|
|
68
|
+
interface User {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
name: string;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Use type for unions, intersections, mapped types
|
|
76
|
+
type UserId = string | number;
|
|
77
|
+
type UserWithPosts = User & { posts: Post[] };
|
|
78
|
+
type Readonly<T> = { readonly [K in keyof T]: T[K] };
|
|
79
|
+
|
|
80
|
+
// Avoid `any` - use `unknown` for truly unknown types
|
|
81
|
+
function parseJSON(json: string): unknown {
|
|
82
|
+
return JSON.parse(json);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Use generics for reusable types
|
|
86
|
+
function first<T>(arr: T[]): T | undefined {
|
|
87
|
+
return arr[0];
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Strict Null Checking
|
|
92
|
+
```typescript
|
|
93
|
+
// Always handle null/undefined explicitly
|
|
94
|
+
function getUser(id: string): User | null {
|
|
95
|
+
const user = users.get(id);
|
|
96
|
+
return user ?? null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Use optional chaining and nullish coalescing
|
|
100
|
+
const userName = user?.profile?.name ?? 'Anonymous';
|
|
101
|
+
|
|
102
|
+
// Non-null assertion only when you're certain
|
|
103
|
+
const element = document.getElementById('app')!;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Enums and Constants
|
|
107
|
+
```typescript
|
|
108
|
+
// Prefer const objects over enums for better tree-shaking
|
|
109
|
+
const UserRole = {
|
|
110
|
+
Admin: 'admin',
|
|
111
|
+
User: 'user',
|
|
112
|
+
Guest: 'guest',
|
|
113
|
+
} as const;
|
|
114
|
+
|
|
115
|
+
type UserRole = typeof UserRole[keyof typeof UserRole];
|
|
116
|
+
|
|
117
|
+
// If using enums, prefer string enums
|
|
118
|
+
enum Status {
|
|
119
|
+
Pending = 'pending',
|
|
120
|
+
Active = 'active',
|
|
121
|
+
Inactive = 'inactive',
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Functions
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Use arrow functions for callbacks and short functions
|
|
129
|
+
const doubled = numbers.map((n) => n * 2);
|
|
130
|
+
|
|
131
|
+
// Use function declarations for top-level/hoisted functions
|
|
132
|
+
function processUser(user: User): ProcessedUser {
|
|
133
|
+
// ...
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Default parameters over optional with fallback
|
|
137
|
+
function greet(name: string, greeting = 'Hello'): string {
|
|
138
|
+
return `${greeting}, ${name}!`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Destructure parameters for objects
|
|
142
|
+
function createUser({ email, name, role = 'user' }: CreateUserParams): User {
|
|
143
|
+
// ...
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Use rest parameters over arguments
|
|
147
|
+
function sum(...numbers: number[]): number {
|
|
148
|
+
return numbers.reduce((a, b) => a + b, 0);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Async Code
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Prefer async/await over .then() chains
|
|
156
|
+
async function fetchUserData(userId: string): Promise<UserData> {
|
|
157
|
+
const user = await fetchUser(userId);
|
|
158
|
+
const posts = await fetchPosts(userId);
|
|
159
|
+
return { user, posts };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Use Promise.all for concurrent operations
|
|
163
|
+
async function fetchAllData(userId: string): Promise<AllData> {
|
|
164
|
+
const [user, posts, settings] = await Promise.all([
|
|
165
|
+
fetchUser(userId),
|
|
166
|
+
fetchPosts(userId),
|
|
167
|
+
fetchSettings(userId),
|
|
168
|
+
]);
|
|
169
|
+
return { user, posts, settings };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Handle errors appropriately
|
|
173
|
+
async function safeOperation(): Promise<Result | null> {
|
|
174
|
+
try {
|
|
175
|
+
return await riskyOperation();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (error instanceof NetworkError) {
|
|
178
|
+
console.error('Network error:', error.message);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
throw error; // Re-throw unexpected errors
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## React Patterns
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Functional components with TypeScript
|
|
190
|
+
interface UserCardProps {
|
|
191
|
+
user: User;
|
|
192
|
+
onEdit?: (user: User) => void;
|
|
193
|
+
className?: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function UserCard({ user, onEdit, className }: UserCardProps) {
|
|
197
|
+
return (
|
|
198
|
+
<div className={className}>
|
|
199
|
+
<h2>{user.name}</h2>
|
|
200
|
+
{onEdit && <button onClick={() => onEdit(user)}>Edit</button>}
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Custom hooks
|
|
206
|
+
function useUser(userId: string) {
|
|
207
|
+
const [user, setUser] = useState<User | null>(null);
|
|
208
|
+
const [loading, setLoading] = useState(true);
|
|
209
|
+
const [error, setError] = useState<Error | null>(null);
|
|
210
|
+
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
let cancelled = false;
|
|
213
|
+
|
|
214
|
+
async function load() {
|
|
215
|
+
try {
|
|
216
|
+
const data = await fetchUser(userId);
|
|
217
|
+
if (!cancelled) setUser(data);
|
|
218
|
+
} catch (e) {
|
|
219
|
+
if (!cancelled) setError(e as Error);
|
|
220
|
+
} finally {
|
|
221
|
+
if (!cancelled) setLoading(false);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
load();
|
|
226
|
+
return () => { cancelled = true; };
|
|
227
|
+
}, [userId]);
|
|
228
|
+
|
|
229
|
+
return { user, loading, error };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Memoization
|
|
233
|
+
const ExpensiveComponent = memo(function ExpensiveComponent({ data }: Props) {
|
|
234
|
+
const processed = useMemo(() => expensiveProcess(data), [data]);
|
|
235
|
+
const handleClick = useCallback(() => onClick(data.id), [data.id, onClick]);
|
|
236
|
+
|
|
237
|
+
return <div onClick={handleClick}>{processed}</div>;
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Error Handling
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Custom error classes
|
|
245
|
+
class AppError extends Error {
|
|
246
|
+
constructor(
|
|
247
|
+
message: string,
|
|
248
|
+
public code: string,
|
|
249
|
+
public statusCode: number = 500,
|
|
250
|
+
) {
|
|
251
|
+
super(message);
|
|
252
|
+
this.name = 'AppError';
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
class NotFoundError extends AppError {
|
|
257
|
+
constructor(resource: string, id: string) {
|
|
258
|
+
super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
|
|
259
|
+
this.name = 'NotFoundError';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Type guards for error handling
|
|
264
|
+
function isAppError(error: unknown): error is AppError {
|
|
265
|
+
return error instanceof AppError;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function handleError(error: unknown): Response {
|
|
269
|
+
if (isAppError(error)) {
|
|
270
|
+
return { status: error.statusCode, message: error.message };
|
|
271
|
+
}
|
|
272
|
+
console.error('Unexpected error:', error);
|
|
273
|
+
return { status: 500, message: 'Internal server error' };
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Testing
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
281
|
+
// or: import { jest } from '@jest/globals';
|
|
282
|
+
|
|
283
|
+
describe('UserService', () => {
|
|
284
|
+
let service: UserService;
|
|
285
|
+
let mockDb: MockDatabase;
|
|
286
|
+
|
|
287
|
+
beforeEach(() => {
|
|
288
|
+
mockDb = createMockDatabase();
|
|
289
|
+
service = new UserService(mockDb);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should fetch user by id', async () => {
|
|
293
|
+
mockDb.users.get.mockResolvedValue({ id: '1', name: 'Test' });
|
|
294
|
+
|
|
295
|
+
const user = await service.getUser('1');
|
|
296
|
+
|
|
297
|
+
expect(user).toEqual({ id: '1', name: 'Test' });
|
|
298
|
+
expect(mockDb.users.get).toHaveBeenCalledWith('1');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should throw NotFoundError for missing user', async () => {
|
|
302
|
+
mockDb.users.get.mockResolvedValue(null);
|
|
303
|
+
|
|
304
|
+
await expect(service.getUser('999')).rejects.toThrow(NotFoundError);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// React Testing Library
|
|
309
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
310
|
+
|
|
311
|
+
describe('UserCard', () => {
|
|
312
|
+
it('should display user name', () => {
|
|
313
|
+
render(<UserCard user={{ id: '1', name: 'John' }} />);
|
|
314
|
+
|
|
315
|
+
expect(screen.getByText('John')).toBeInTheDocument();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should call onEdit when button clicked', () => {
|
|
319
|
+
const onEdit = vi.fn();
|
|
320
|
+
render(<UserCard user={{ id: '1', name: 'John' }} onEdit={onEdit} />);
|
|
321
|
+
|
|
322
|
+
fireEvent.click(screen.getByRole('button', { name: /edit/i }));
|
|
323
|
+
|
|
324
|
+
expect(onEdit).toHaveBeenCalledWith({ id: '1', name: 'John' });
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Project Commands
|
|
330
|
+
|
|
331
|
+
Check `.claude/commands.md` for project-specific commands. Common JS/TS commands:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Dependencies
|
|
335
|
+
npm install
|
|
336
|
+
npm ci # Clean install for CI
|
|
337
|
+
|
|
338
|
+
# Development
|
|
339
|
+
npm run dev
|
|
340
|
+
npm run build
|
|
341
|
+
|
|
342
|
+
# Linting & Formatting
|
|
343
|
+
npm run lint
|
|
344
|
+
npm run lint -- --fix
|
|
345
|
+
npm run format
|
|
346
|
+
|
|
347
|
+
# Testing
|
|
348
|
+
npm test
|
|
349
|
+
npm run test:watch
|
|
350
|
+
npm run test:coverage
|
|
351
|
+
|
|
352
|
+
# Type checking
|
|
353
|
+
npm run typecheck
|
|
354
|
+
npx tsc --noEmit
|
|
355
|
+
```
|