Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .claude/agents/codebase-analyzer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: codebase-analyzer
description: Comprehensive analysis using parallel exploration tasks.
tools: Read, Glob, Grep, Task
---

Orchestrate parallel analysis:
1. Spawn explore-resources, explore-models, explore-tests tasks
2. Aggregate findings
3. Report: patterns, relationships, gaps

Output: Summary, resources found, patterns identified, recommendations.
9 changes: 9 additions & 0 deletions .claude/agents/explore-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
name: explore-models
description: Search src/models/ for TypeScript type definitions.
tools: Read, Glob, Grep
---

Search `src/models/` for interfaces and types.
Look for: `export interface`, `export type`, `extends ListQueryParams`
Return: interface names, file paths, key properties.
9 changes: 9 additions & 0 deletions .claude/agents/explore-resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
name: explore-resources
description: Search src/resources/ for API resource classes and methods.
tools: Read, Glob, Grep
---

Search `src/resources/` for resource classes extending `Resource`.
Look for: `public methodName(`, `/v3/grants/`, `interface.*Params`
Return: file paths, line numbers, method signatures.
9 changes: 9 additions & 0 deletions .claude/agents/explore-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
name: explore-tests
description: Search tests/ for Jest test patterns and mocks.
tools: Read, Glob, Grep
---

Search `tests/` for test files (*.spec.ts).
Look for: `describe('`, `it('should`, `mockResolvedValue`
Return: test file, describe blocks, test case names.
13 changes: 13 additions & 0 deletions .claude/agents/parallel-explore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
name: parallel-explore
description: Parallel codebase search. Use for broad searches across multiple directories.
tools: Read, Glob, Grep, Task
---

Spawn 3-5 parallel Task agents to search different areas:
- Task 1: `src/resources/`
- Task 2: `src/models/`
- Task 3: `tests/`
- Task 4: Root configs

Use `subagent_type: Explore`. Aggregate results into unified summary.
9 changes: 9 additions & 0 deletions .claude/agents/quick-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
name: quick-search
description: Fast keyword search across codebase using grep.
tools: Grep, Glob
---

Use Grep for content, Glob for file patterns.
Return results as: `file/path.ts:123 - line preview`
Keep responses concise.
8 changes: 8 additions & 0 deletions .claude/commands/add-endpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Add Endpoint: $ARGUMENTS

Format: `{resource} {method}` (e.g., "calendars getAvailability")

1. Add request/response types to `src/models/{resource}.ts`
2. Add method to `src/resources/{resource}.ts`
3. Add tests to `tests/resources/{resource}.spec.ts`
4. Run `make test-resource NAME={resource}`
9 changes: 9 additions & 0 deletions .claude/commands/add-resource.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Add Resource: $ARGUMENTS

1. Create `src/models/{name}.ts` with interfaces
2. Create `src/resources/{name}.ts` extending Resource
3. Register in `src/nylas.ts`
4. Create `tests/resources/{name}.spec.ts`
5. Run `make ci`

Use templates in `.claude/shared/` as reference.
7 changes: 7 additions & 0 deletions .claude/commands/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Build SDK

```bash
make build
```

Output: `lib/esm/`, `lib/cjs/`, `lib/types/`
10 changes: 10 additions & 0 deletions .claude/commands/explore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Parallel Explore

Search for: $ARGUMENTS

Spawn parallel Task agents (subagent_type: Explore) to search:
- src/resources/
- src/models/
- tests/

Aggregate and summarize results.
5 changes: 5 additions & 0 deletions .claude/commands/fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Fix Code

```bash
make lint-fix && make format
```
8 changes: 8 additions & 0 deletions .claude/commands/review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Review Code

Check: $ARGUMENTS

1. TypeScript: camelCase props, proper types, JSDoc comments
2. Resources: extends Resource, uses _list/_find/_create/_update/_destroy
3. Tests: mocks setup, assertions correct
4. Run: `make lint` and `make test`
14 changes: 14 additions & 0 deletions .claude/commands/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Run Tests

Target: $ARGUMENTS

```bash
# No args = all tests
make test

# Resource name (e.g., "calendars")
make test-resource NAME={name}

# File path
make test-single FILE={path}
```
40 changes: 40 additions & 0 deletions .claude/docs/adding-resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Adding New Resources

## Step 1: Create Model
Copy `.claude/shared/model-template.ts` to `src/models/{name}.ts`

Replace placeholders:
- `{{ResourceName}}` → PascalCase (e.g., `Calendar`)
- `{{resourceName}}` → camelCase (e.g., `calendar`)

Add to `src/models/index.ts` exports.

## Step 2: Create Resource
Copy `.claude/shared/resource-template.ts` to `src/resources/{name}.ts`

Key patterns:
- Extend `Resource` class
- Use `_list`, `_find`, `_create`, `_update`, `_destroy` helpers
- Define path: `/v3/grants/${identifier}/{resources}`
- Accept `Overrides` as last parameter

## Step 3: Register Resource
In `src/nylas.ts`:
1. Import the resource class
2. Add public property: `public {name}: {Name}s;`
3. Initialize in constructor: `this.{name} = new {Name}s(this.apiClient);`

## Step 4: Create Tests
Copy `.claude/shared/test-template.spec.ts` to `tests/resources/{name}.spec.ts`

Test patterns:
- Mock APIClient with `jest.mock('../../src/apiClient')`
- Test each public method
- Verify request parameters
- Test with/without overrides

## Step 5: Verify
```bash
make test-resource NAME={name}
make ci
```
42 changes: 42 additions & 0 deletions .claude/docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Architecture

## Entry Point
`src/nylas.ts:15` - Nylas class constructor instantiates all resources with configured APIClient.

## API Client
`src/apiClient.ts` handles:
- HTTP requests with fetch
- Bearer token authentication
- Error response handling (NylasApiError)
- JSON serialization/deserialization
- Request/response logging

## Resource Pattern
All resources extend `src/resources/resource.ts`:

| Method | HTTP | Returns |
|--------|------|---------|
| `_list()` | GET | Async iterator with pagination |
| `_find()` | GET | Single resource |
| `_create()` | POST | Created resource |
| `_update()` | PUT | Updated resource |
| `_updatePatch()` | PATCH | Patched resource |
| `_destroy()` | DELETE | Deletion response |
| `_getRaw()` | GET | Binary data |
| `_getStream()` | GET | ReadableStream |

## Models Structure
`src/models/` contains TypeScript interfaces:
- Resource objects: `Calendar`, `Event`, `Message`, etc.
- Request types: `Create{Resource}Request`
- Query params: `List{Resource}QueryParams`, `Find{Resource}QueryParams`
- Responses: `NylasResponse<T>`, `NylasListResponse<T>`

## API Paths
- Grant-scoped: `/v3/grants/{grantId}/{resource}`
- App-scoped: `/v3/{resource}`

## Build Output
- `lib/esm/` - ES modules (import/export)
- `lib/cjs/` - CommonJS (require)
- `lib/types/` - TypeScript declarations
44 changes: 44 additions & 0 deletions .claude/docs/testing-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Testing Guide

## Setup
Tests use Jest with `jest-fetch-mock`. Mock setup in `tests/setupTests.ts`.

## Mocking APIClient
```typescript
import APIClient from '../../src/apiClient';
jest.mock('../../src/apiClient');
```

## Test Structure
```typescript
describe('ResourceName', () => {
let apiClient: jest.Mocked<APIClient>;
let resource: Resources;

beforeEach(() => {
apiClient = new APIClient({ apiKey: 'test' }) as jest.Mocked<APIClient>;
resource = new Resources(apiClient);
});

describe('list', () => {
it('calls API with correct parameters', async () => {
apiClient.request.mockResolvedValue({ data: [] });
await resource.list({ identifier: 'grant-id' });
expect(apiClient.request).toHaveBeenCalledWith({
method: 'GET',
path: '/v3/grants/grant-id/resources',
overrides: {}
});
});
});
});
```

## Coverage
Threshold: 80% functions, lines, statements. Run `make test` for coverage report.

## Commands
- `make test` - All tests with coverage
- `make test-resource NAME=calendars` - Single resource
- `make test-watch` - Watch mode
- `make test-verbose` - Verbose output
22 changes: 22 additions & 0 deletions .claude/hooks/post-tool-use.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# Post-tool hook - Auto-format code after edits
set -e

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')

# Auto-format TypeScript/JavaScript files after editing
if [ "$TOOL_NAME" = "Edit" ] || [ "$TOOL_NAME" = "Write" ]; then
FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')

# Format .ts/.js files in src/ or tests/
if echo "$FILE" | grep -qE '\.(ts|js)$' && echo "$FILE" | grep -qE '(src|tests)/'; then
if [ -f "$FILE" ]; then
npx prettier --write "$FILE" 2>/dev/null || true
npx eslint --fix "$FILE" 2>/dev/null || true
fi
fi
fi

exit 0
55 changes: 55 additions & 0 deletions .claude/hooks/pre-tool-use.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash
# Pre-tool hook - validates tool calls before execution
# Exit: 0 = allow, 2 = block

set -e

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')

block() { echo "$1" >&2; exit 2; }

# Bash command restrictions
if [ "$TOOL_NAME" = "Bash" ]; then
CMD=$(echo "$TOOL_INPUT" | jq -r '.command // empty')

# Block git write operations
echo "$CMD" | grep -qE 'git\s+(commit|push|add|merge|rebase|reset)' && \
block "BLOCKED: Git write operations not allowed. User handles manually."

# Block dangerous rm
echo "$CMD" | grep -qE 'rm\s+(-rf|-fr)\s+(/|~|\.|src|lib|tests)' && \
block "BLOCKED: Dangerous rm command."

# Block npm publish without dry-run
echo "$CMD" | grep -qE 'npm\s+publish' && ! echo "$CMD" | grep -qE '--dry-run' && \
block "BLOCKED: Use 'npm publish --dry-run' first."
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-portable regex patterns fail on BSD grep

Medium Severity

The grep patterns on lines 18, 22, and 26 use \s to match whitespace, which is a Perl-compatible regex extension not supported by POSIX extended regex. On macOS with BSD grep, \s doesn't match whitespace characters, so the safety checks for blocking git write operations, dangerous rm commands, and npm publish will silently fail. Using [[:space:]] instead would be portable across both GNU and BSD grep.

Fix in Cursor Fix in Web

fi

# File protection
if [ "$TOOL_NAME" = "Edit" ] || [ "$TOOL_NAME" = "Write" ]; then
FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')

echo "$FILE" | grep -qE '\.env' && block "BLOCKED: Cannot edit .env files."
echo "$FILE" | grep -qE 'package-lock\.json$' && block "BLOCKED: Use npm commands for package-lock.json."
echo "$FILE" | grep -qE 'src/version\.ts$' && block "BLOCKED: Auto-generated. Run 'make build'."
echo "$FILE" | grep -qE 'src/models/index\.ts$' && block "BLOCKED: Auto-generated. Run 'make build'."
fi

# New file size limit (500 lines max for NEW files only)
if [ "$TOOL_NAME" = "Write" ]; then
FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')

# Only check if file doesn't exist (new file)
if [ ! -f "$FILE" ]; then
CONTENT=$(echo "$TOOL_INPUT" | jq -r '.content // empty')
LINE_COUNT=$(echo "$CONTENT" | wc -l)

if [ "$LINE_COUNT" -gt 500 ]; then
block "BLOCKED: New files cannot exceed 500 lines ($LINE_COUNT lines). Split into smaller modules."
fi
fi
fi

exit 0
3 changes: 3 additions & 0 deletions .claude/hooks/user-prompt-submit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
# User prompt hook - minimal, CLAUDE.md handles context
exit 0
Loading