claudekit-cli 1.0.0 → 1.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/.github/workflows/ci.yml +2 -0
- package/CHANGELOG.md +19 -0
- package/CLAUDE.md +7 -0
- package/README.md +61 -3
- package/biome.json +3 -0
- package/dist/index.js +102 -0
- package/package.json +1 -1
- package/src/commands/version.ts +135 -0
- package/src/index.ts +11 -0
- package/src/types.ts +7 -0
- package/tests/commands/version.test.ts +297 -0
- package/.claude/agents/brainstormer.md +0 -96
- package/.claude/agents/code-reviewer.md +0 -141
- package/.claude/agents/copywriter.md +0 -108
- package/.claude/agents/database-admin.md +0 -86
- package/.claude/agents/debugger.md +0 -124
- package/.claude/agents/docs-manager.md +0 -115
- package/.claude/agents/git-manager.md +0 -60
- package/.claude/agents/journal-writer.md +0 -111
- package/.claude/agents/planner.md +0 -87
- package/.claude/agents/project-manager.md +0 -113
- package/.claude/agents/researcher.md +0 -173
- package/.claude/agents/scout.md +0 -123
- package/.claude/agents/tester.md +0 -95
- package/.claude/agents/ui-ux-designer.md +0 -206
- package/.claude/commands/bootstrap.md +0 -104
- package/.claude/commands/brainstorm.md +0 -67
- package/.claude/commands/content/enhance.md +0 -13
- package/.claude/commands/content/fast.md +0 -11
- package/.claude/commands/content/good.md +0 -13
- package/.claude/commands/cook.md +0 -19
- package/.claude/commands/debug.md +0 -10
- package/.claude/commands/design/3d.md +0 -65
- package/.claude/commands/design/describe.md +0 -13
- package/.claude/commands/design/fast.md +0 -19
- package/.claude/commands/design/good.md +0 -23
- package/.claude/commands/design/screenshot.md +0 -23
- package/.claude/commands/design/video.md +0 -23
- package/.claude/commands/docs/init.md +0 -13
- package/.claude/commands/docs/summarize.md +0 -10
- package/.claude/commands/docs/update.md +0 -21
- package/.claude/commands/fix/ci.md +0 -11
- package/.claude/commands/fix/fast.md +0 -12
- package/.claude/commands/fix/hard.md +0 -18
- package/.claude/commands/fix/logs.md +0 -16
- package/.claude/commands/fix/test.md +0 -18
- package/.claude/commands/fix/types.md +0 -10
- package/.claude/commands/git/cm.md +0 -5
- package/.claude/commands/git/cp.md +0 -4
- package/.claude/commands/integrate/polar.md +0 -42
- package/.claude/commands/plan/ci.md +0 -12
- package/.claude/commands/plan/two.md +0 -13
- package/.claude/commands/plan.md +0 -10
- package/.claude/commands/scout.md +0 -29
- package/.claude/commands/test.md +0 -7
- package/.claude/commands/watzup.md +0 -8
- package/.claude/hooks/telegram_notify.sh +0 -136
- package/.claude/send-discord.sh +0 -64
- package/.claude/settings.json +0 -7
- package/.claude/statusline.sh +0 -143
- package/.claude/workflows/development-rules.md +0 -80
- package/.claude/workflows/documentation-management.md +0 -28
- package/.claude/workflows/orchestration-protocol.md +0 -16
- package/.claude/workflows/primary-workflow.md +0 -41
- package/.opencode/agent/code-reviewer.md +0 -141
- package/.opencode/agent/debugger.md +0 -74
- package/.opencode/agent/docs-manager.md +0 -119
- package/.opencode/agent/git-manager.md +0 -60
- package/.opencode/agent/planner-researcher.md +0 -100
- package/.opencode/agent/planner.md +0 -87
- package/.opencode/agent/project-manager.md +0 -113
- package/.opencode/agent/researcher.md +0 -173
- package/.opencode/agent/solution-brainstormer.md +0 -89
- package/.opencode/agent/system-architecture.md +0 -192
- package/.opencode/agent/tester.md +0 -96
- package/.opencode/agent/ui-ux-designer.md +0 -203
- package/.opencode/agent/ui-ux-developer.md +0 -97
- package/.opencode/command/cook.md +0 -7
- package/.opencode/command/debug.md +0 -10
- package/.opencode/command/design/3d.md +0 -65
- package/.opencode/command/design/fast.md +0 -18
- package/.opencode/command/design/good.md +0 -21
- package/.opencode/command/design/screenshot.md +0 -22
- package/.opencode/command/design/video.md +0 -22
- package/.opencode/command/docs/init.md +0 -11
- package/.opencode/command/docs/summarize.md +0 -10
- package/.opencode/command/docs/update.md +0 -18
- package/.opencode/command/fix/ci.md +0 -8
- package/.opencode/command/fix/fast.md +0 -11
- package/.opencode/command/fix/hard.md +0 -15
- package/.opencode/command/fix/logs.md +0 -16
- package/.opencode/command/fix/test.md +0 -18
- package/.opencode/command/fix/types.md +0 -10
- package/.opencode/command/git/cm.md +0 -5
- package/.opencode/command/git/cp.md +0 -4
- package/.opencode/command/plan/ci.md +0 -12
- package/.opencode/command/plan/two.md +0 -13
- package/.opencode/command/plan.md +0 -10
- package/.opencode/command/test.md +0 -7
- package/.opencode/command/watzup.md +0 -8
- package/docs/code-standards.md +0 -1128
- package/docs/codebase-summary.md +0 -821
- package/docs/github-setup.md +0 -176
- package/docs/project-pdr.md +0 -739
- package/docs/system-architecture.md +0 -950
- package/docs/tech-stack.md +0 -290
- package/plans/251008-claudekit-cli-implementation-plan.md +0 -1469
- package/plans/reports/251008-from-code-reviewer-to-developer-review-report.md +0 -864
- package/plans/reports/251008-from-tester-to-developer-test-summary-report.md +0 -409
- package/plans/reports/251008-researcher-download-extraction-report.md +0 -1377
- package/plans/reports/251008-researcher-github-api-report.md +0 -1339
- package/plans/research/251008-cli-frameworks-bun-research.md +0 -1051
- package/plans/templates/bug-fix-template.md +0 -69
- package/plans/templates/feature-implementation-template.md +0 -84
- package/plans/templates/refactor-template.md +0 -82
- package/plans/templates/template-usage-guide.md +0 -58
package/docs/code-standards.md
DELETED
|
@@ -1,1128 +0,0 @@
|
|
|
1
|
-
# Code Standards and Conventions
|
|
2
|
-
# ClaudeKit CLI
|
|
3
|
-
|
|
4
|
-
**Version:** 1.0
|
|
5
|
-
**Date:** 2025-10-08
|
|
6
|
-
**Status:** Active
|
|
7
|
-
**Applies to:** TypeScript, Bun runtime
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Table of Contents
|
|
12
|
-
|
|
13
|
-
1. [Core Principles](#core-principles)
|
|
14
|
-
2. [TypeScript Standards](#typescript-standards)
|
|
15
|
-
3. [File Organization](#file-organization)
|
|
16
|
-
4. [Naming Conventions](#naming-conventions)
|
|
17
|
-
5. [Error Handling](#error-handling)
|
|
18
|
-
6. [Security Standards](#security-standards)
|
|
19
|
-
7. [Testing Standards](#testing-standards)
|
|
20
|
-
8. [Documentation Standards](#documentation-standards)
|
|
21
|
-
9. [Git Workflow](#git-workflow)
|
|
22
|
-
10. [Code Review Checklist](#code-review-checklist)
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Core Principles
|
|
27
|
-
|
|
28
|
-
### YAGNI (You Aren't Gonna Need It)
|
|
29
|
-
|
|
30
|
-
**Principle:** Only implement features when they are actually needed, not when you anticipate they might be needed.
|
|
31
|
-
|
|
32
|
-
**Examples:**
|
|
33
|
-
|
|
34
|
-
**❌ Bad - Over-engineering:**
|
|
35
|
-
```typescript
|
|
36
|
-
// Unnecessary abstraction for simple config
|
|
37
|
-
class ConfigStrategy {
|
|
38
|
-
abstract load(): Promise<Config>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
class JsonConfigStrategy extends ConfigStrategy { ... }
|
|
42
|
-
class YamlConfigStrategy extends ConfigStrategy { ... }
|
|
43
|
-
class TomlConfigStrategy extends ConfigStrategy { ... }
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**✅ Good - Simple and direct:**
|
|
47
|
-
```typescript
|
|
48
|
-
// Simple, focused implementation
|
|
49
|
-
class ConfigManager {
|
|
50
|
-
static async load(): Promise<Config> {
|
|
51
|
-
const data = await Bun.file(CONFIG_PATH).json();
|
|
52
|
-
return ConfigSchema.parse(data);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
### KISS (Keep It Simple, Stupid)
|
|
60
|
-
|
|
61
|
-
**Principle:** Favor simple, straightforward solutions over complex ones.
|
|
62
|
-
|
|
63
|
-
**Examples:**
|
|
64
|
-
|
|
65
|
-
**❌ Bad - Overly complex:**
|
|
66
|
-
```typescript
|
|
67
|
-
const processFiles = pipe(
|
|
68
|
-
filter((f: string) => !ig.ignores(f)),
|
|
69
|
-
map((f: string) => path.resolve(f)),
|
|
70
|
-
tap((f: string) => logger.debug(f)),
|
|
71
|
-
toArray()
|
|
72
|
-
);
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**✅ Good - Clear and simple:**
|
|
76
|
-
```typescript
|
|
77
|
-
const processFiles = (files: string[]) => {
|
|
78
|
-
return files
|
|
79
|
-
.filter(f => !ig.ignores(f))
|
|
80
|
-
.map(f => path.resolve(f));
|
|
81
|
-
};
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
### DRY (Don't Repeat Yourself)
|
|
87
|
-
|
|
88
|
-
**Principle:** Avoid duplicating logic; extract common patterns into reusable functions.
|
|
89
|
-
|
|
90
|
-
**Examples:**
|
|
91
|
-
|
|
92
|
-
**❌ Bad - Repeated logic:**
|
|
93
|
-
```typescript
|
|
94
|
-
// In new.ts
|
|
95
|
-
logger.info(pc.cyan('Downloading release...'));
|
|
96
|
-
const response = await fetch(url);
|
|
97
|
-
// handle response...
|
|
98
|
-
|
|
99
|
-
// In update.ts
|
|
100
|
-
logger.info(pc.cyan('Downloading release...'));
|
|
101
|
-
const response = await fetch(url);
|
|
102
|
-
// handle response...
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
**✅ Good - Extracted to reusable class:**
|
|
106
|
-
```typescript
|
|
107
|
-
// In lib/download.ts
|
|
108
|
-
class DownloadManager {
|
|
109
|
-
async downloadAsset(asset: GitHubReleaseAsset): Promise<string> {
|
|
110
|
-
logger.info(pc.cyan('Downloading release...'));
|
|
111
|
-
const response = await fetch(asset.browser_download_url);
|
|
112
|
-
// centralized download logic
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## TypeScript Standards
|
|
120
|
-
|
|
121
|
-
### Strict Mode Configuration
|
|
122
|
-
|
|
123
|
-
**Required `tsconfig.json` settings:**
|
|
124
|
-
|
|
125
|
-
```json
|
|
126
|
-
{
|
|
127
|
-
"compilerOptions": {
|
|
128
|
-
"strict": true,
|
|
129
|
-
"noImplicitAny": true,
|
|
130
|
-
"strictNullChecks": true,
|
|
131
|
-
"strictFunctionTypes": true,
|
|
132
|
-
"strictBindCallApply": true,
|
|
133
|
-
"strictPropertyInitialization": true,
|
|
134
|
-
"noImplicitThis": true,
|
|
135
|
-
"alwaysStrict": true,
|
|
136
|
-
"noUnusedLocals": true,
|
|
137
|
-
"noUnusedParameters": true,
|
|
138
|
-
"noImplicitReturns": true,
|
|
139
|
-
"noFallthroughCasesInSwitch": true
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
### Type Safety
|
|
147
|
-
|
|
148
|
-
**1. Use Zod for Runtime Validation:**
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
import { z } from 'zod';
|
|
152
|
-
|
|
153
|
-
// Define schema
|
|
154
|
-
export const NewCommandOptionsSchema = z.object({
|
|
155
|
-
dir: z.string().default('.'),
|
|
156
|
-
kit: KitType.optional(),
|
|
157
|
-
version: z.string().optional(),
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Infer TypeScript type from schema
|
|
161
|
-
export type NewCommandOptions = z.infer<typeof NewCommandOptionsSchema>;
|
|
162
|
-
|
|
163
|
-
// Validate at runtime
|
|
164
|
-
const options = NewCommandOptionsSchema.parse(rawInput);
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**Benefits:**
|
|
168
|
-
- Compile-time type checking
|
|
169
|
-
- Runtime validation
|
|
170
|
-
- Automatic type inference
|
|
171
|
-
- Clear error messages
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
**2. Avoid `any` Type:**
|
|
176
|
-
|
|
177
|
-
**❌ Bad:**
|
|
178
|
-
```typescript
|
|
179
|
-
function processData(data: any) {
|
|
180
|
-
return data.map((item: any) => item.value);
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
**✅ Good:**
|
|
185
|
-
```typescript
|
|
186
|
-
interface DataItem {
|
|
187
|
-
value: string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function processData(data: DataItem[]): string[] {
|
|
191
|
-
return data.map(item => item.value);
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**Exceptions:** Only use `any` when:
|
|
196
|
-
- Wrapping external untyped libraries
|
|
197
|
-
- Type is truly unknown and needs runtime checking
|
|
198
|
-
- Always document why `any` is necessary
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
**3. Use Type Guards:**
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
function isGitHubError(error: unknown): error is GitHubError {
|
|
206
|
-
return error instanceof GitHubError;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
await client.getRelease();
|
|
211
|
-
} catch (error) {
|
|
212
|
-
if (isGitHubError(error)) {
|
|
213
|
-
// TypeScript knows error is GitHubError here
|
|
214
|
-
logger.error(`GitHub API error: ${error.statusCode}`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
### Interface vs Type
|
|
222
|
-
|
|
223
|
-
**Use `interface` for:**
|
|
224
|
-
- Object shapes that might be extended
|
|
225
|
-
- Public APIs
|
|
226
|
-
- Class contracts
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
interface ConfigManager {
|
|
230
|
-
load(): Promise<Config>;
|
|
231
|
-
save(config: Config): Promise<void>;
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**Use `type` for:**
|
|
236
|
-
- Union types
|
|
237
|
-
- Intersection types
|
|
238
|
-
- Mapped types
|
|
239
|
-
- Type inference from Zod
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
type KitType = 'engineer' | 'marketing';
|
|
243
|
-
type AuthMethod = 'gh-cli' | 'env-var' | 'keychain' | 'prompt';
|
|
244
|
-
type NewCommandOptions = z.infer<typeof NewCommandOptionsSchema>;
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## File Organization
|
|
250
|
-
|
|
251
|
-
### Directory Structure
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
src/
|
|
255
|
-
├── index.ts # CLI entry point
|
|
256
|
-
├── types.ts # Type definitions and schemas
|
|
257
|
-
├── commands/ # Command implementations
|
|
258
|
-
│ ├── new.ts
|
|
259
|
-
│ └── update.ts
|
|
260
|
-
├── lib/ # Core libraries
|
|
261
|
-
│ ├── auth.ts
|
|
262
|
-
│ ├── github.ts
|
|
263
|
-
│ ├── download.ts
|
|
264
|
-
│ ├── merge.ts
|
|
265
|
-
│ └── prompts.ts
|
|
266
|
-
└── utils/ # Utility functions
|
|
267
|
-
├── config.ts
|
|
268
|
-
└── logger.ts
|
|
269
|
-
|
|
270
|
-
tests/ # Mirror src structure
|
|
271
|
-
├── types.test.ts
|
|
272
|
-
├── lib/
|
|
273
|
-
│ ├── auth.test.ts
|
|
274
|
-
│ └── ...
|
|
275
|
-
└── utils/
|
|
276
|
-
├── config.test.ts
|
|
277
|
-
└── logger.test.ts
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
### File Size Limits
|
|
283
|
-
|
|
284
|
-
**Hard Limit:** 500 lines per file
|
|
285
|
-
**Recommended:** < 200 lines per file
|
|
286
|
-
|
|
287
|
-
**Why:** Smaller files are:
|
|
288
|
-
- Easier to understand
|
|
289
|
-
- Easier to test
|
|
290
|
-
- Easier to maintain
|
|
291
|
-
- Better for code review
|
|
292
|
-
|
|
293
|
-
**When to split:**
|
|
294
|
-
```typescript
|
|
295
|
-
// If auth.ts gets > 200 lines, split into:
|
|
296
|
-
auth/
|
|
297
|
-
├── index.ts // Public API
|
|
298
|
-
├── manager.ts // AuthManager class
|
|
299
|
-
├── providers/
|
|
300
|
-
│ ├── gh-cli.ts // GitHub CLI provider
|
|
301
|
-
│ ├── env.ts // Environment variable provider
|
|
302
|
-
│ └── keychain.ts // Keychain provider
|
|
303
|
-
└── types.ts // Auth-specific types
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
### Import Organization
|
|
309
|
-
|
|
310
|
-
**Order:**
|
|
311
|
-
1. External dependencies
|
|
312
|
-
2. Internal absolute imports
|
|
313
|
-
3. Internal relative imports
|
|
314
|
-
4. Type-only imports
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
// 1. External dependencies
|
|
318
|
-
import { z } from 'zod';
|
|
319
|
-
import { Octokit } from '@octokit/rest';
|
|
320
|
-
|
|
321
|
-
// 2. Internal absolute imports
|
|
322
|
-
import { logger } from '@/utils/logger';
|
|
323
|
-
import { ConfigManager } from '@/utils/config';
|
|
324
|
-
|
|
325
|
-
// 3. Internal relative imports
|
|
326
|
-
import { AuthManager } from './auth';
|
|
327
|
-
import { DownloadManager } from './download';
|
|
328
|
-
|
|
329
|
-
// 4. Type-only imports
|
|
330
|
-
import type { GitHubRelease, KitConfig } from '@/types';
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
---
|
|
334
|
-
|
|
335
|
-
## Naming Conventions
|
|
336
|
-
|
|
337
|
-
### Variables and Functions
|
|
338
|
-
|
|
339
|
-
**camelCase for variables and functions:**
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
const targetDirectory = './my-app';
|
|
343
|
-
const isValid = true;
|
|
344
|
-
|
|
345
|
-
function validateToken(token: string): boolean { ... }
|
|
346
|
-
async function downloadRelease(url: string): Promise<void> { ... }
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
**Descriptive names over short names:**
|
|
350
|
-
|
|
351
|
-
**❌ Bad:**
|
|
352
|
-
```typescript
|
|
353
|
-
const t = 'ghp_token123';
|
|
354
|
-
const d = './dir';
|
|
355
|
-
const fn = async (x) => { ... };
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
**✅ Good:**
|
|
359
|
-
```typescript
|
|
360
|
-
const githubToken = 'ghp_token123';
|
|
361
|
-
const targetDirectory = './dir';
|
|
362
|
-
const downloadAsset = async (asset: GitHubReleaseAsset) => { ... };
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
### Classes and Types
|
|
368
|
-
|
|
369
|
-
**PascalCase for classes, interfaces, types, and enums:**
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
class AuthManager { ... }
|
|
373
|
-
class DownloadManager { ... }
|
|
374
|
-
|
|
375
|
-
interface GitHubClient { ... }
|
|
376
|
-
type KitType = 'engineer' | 'marketing';
|
|
377
|
-
|
|
378
|
-
enum LogLevel {
|
|
379
|
-
Debug,
|
|
380
|
-
Info,
|
|
381
|
-
Warning,
|
|
382
|
-
Error
|
|
383
|
-
}
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
---
|
|
387
|
-
|
|
388
|
-
### Constants
|
|
389
|
-
|
|
390
|
-
**UPPER_SNAKE_CASE for constants:**
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
const CONFIG_PATH = join(homedir(), '.claudekit', 'config.json');
|
|
394
|
-
const GITHUB_API_TIMEOUT = 30_000;
|
|
395
|
-
const MAX_RETRY_ATTEMPTS = 3;
|
|
396
|
-
|
|
397
|
-
// Exception: Zod schemas use PascalCase
|
|
398
|
-
const NewCommandOptionsSchema = z.object({ ... });
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
---
|
|
402
|
-
|
|
403
|
-
### Files and Directories
|
|
404
|
-
|
|
405
|
-
**kebab-case for files and directories:**
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
// Files
|
|
409
|
-
auth-manager.ts
|
|
410
|
-
download-manager.ts
|
|
411
|
-
file-merger.ts
|
|
412
|
-
|
|
413
|
-
// Directories
|
|
414
|
-
user-commands/
|
|
415
|
-
github-client/
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
**Exception:** Test files mirror source file names:
|
|
419
|
-
```typescript
|
|
420
|
-
src/lib/auth.ts → tests/lib/auth.test.ts
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
---
|
|
424
|
-
|
|
425
|
-
## Error Handling
|
|
426
|
-
|
|
427
|
-
### Custom Error Classes
|
|
428
|
-
|
|
429
|
-
**Define specific error types:**
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
export class ClaudeKitError extends Error {
|
|
433
|
-
constructor(
|
|
434
|
-
message: string,
|
|
435
|
-
public code?: string,
|
|
436
|
-
public statusCode?: number
|
|
437
|
-
) {
|
|
438
|
-
super(message);
|
|
439
|
-
this.name = 'ClaudeKitError';
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export class AuthenticationError extends ClaudeKitError {
|
|
444
|
-
constructor(message: string) {
|
|
445
|
-
super(message, 'AUTH_ERROR', 401);
|
|
446
|
-
this.name = 'AuthenticationError';
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
**Benefits:**
|
|
452
|
-
- Type-safe error handling
|
|
453
|
-
- Consistent error structure
|
|
454
|
-
- Easy to filter and handle specific errors
|
|
455
|
-
|
|
456
|
-
---
|
|
457
|
-
|
|
458
|
-
### Error Handling Patterns
|
|
459
|
-
|
|
460
|
-
**1. Try-Catch with Cleanup:**
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
async function downloadAndExtract(url: string): Promise<void> {
|
|
464
|
-
const tempDir = await createTempDir();
|
|
465
|
-
|
|
466
|
-
try {
|
|
467
|
-
const archivePath = await downloadFile(url, tempDir);
|
|
468
|
-
await extractArchive(archivePath, tempDir);
|
|
469
|
-
} catch (error) {
|
|
470
|
-
logger.error('Download failed', error);
|
|
471
|
-
throw new DownloadError('Failed to download release', error);
|
|
472
|
-
} finally {
|
|
473
|
-
// Always clean up
|
|
474
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
---
|
|
480
|
-
|
|
481
|
-
**2. Type Guards for Error Handling:**
|
|
482
|
-
|
|
483
|
-
```typescript
|
|
484
|
-
try {
|
|
485
|
-
await githubClient.getRelease(kit);
|
|
486
|
-
} catch (error) {
|
|
487
|
-
if (error instanceof GitHubError && error.statusCode === 404) {
|
|
488
|
-
logger.error('Release not found. Check kit name and version.');
|
|
489
|
-
} else if (error instanceof GitHubError && error.statusCode === 401) {
|
|
490
|
-
logger.error('Authentication failed. Check your GitHub token.');
|
|
491
|
-
} else {
|
|
492
|
-
logger.error('Unexpected error', error);
|
|
493
|
-
}
|
|
494
|
-
throw error;
|
|
495
|
-
}
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
---
|
|
499
|
-
|
|
500
|
-
**3. Result Type Pattern (Future Enhancement):**
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
// Optional: Use for operations that may fail
|
|
504
|
-
type Result<T, E = Error> =
|
|
505
|
-
| { ok: true; value: T }
|
|
506
|
-
| { ok: false; error: E };
|
|
507
|
-
|
|
508
|
-
async function tryDownload(url: string): Promise<Result<string>> {
|
|
509
|
-
try {
|
|
510
|
-
const path = await download(url);
|
|
511
|
-
return { ok: true, value: path };
|
|
512
|
-
} catch (error) {
|
|
513
|
-
return { ok: false, error: error as Error };
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
---
|
|
519
|
-
|
|
520
|
-
### User-Friendly Error Messages
|
|
521
|
-
|
|
522
|
-
**Structure:**
|
|
523
|
-
1. What went wrong
|
|
524
|
-
2. Why it happened (if known)
|
|
525
|
-
3. How to fix it
|
|
526
|
-
|
|
527
|
-
**Examples:**
|
|
528
|
-
|
|
529
|
-
**❌ Bad:**
|
|
530
|
-
```typescript
|
|
531
|
-
throw new Error('Token invalid');
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
**✅ Good:**
|
|
535
|
-
```typescript
|
|
536
|
-
throw new AuthenticationError(
|
|
537
|
-
'GitHub token is invalid or expired.\n' +
|
|
538
|
-
'Please check your token and try again.\n' +
|
|
539
|
-
'Create a new token at: https://github.com/settings/tokens'
|
|
540
|
-
);
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
---
|
|
544
|
-
|
|
545
|
-
## Security Standards
|
|
546
|
-
|
|
547
|
-
### Token Security
|
|
548
|
-
|
|
549
|
-
**1. Never Log Tokens:**
|
|
550
|
-
|
|
551
|
-
```typescript
|
|
552
|
-
// ❌ NEVER do this
|
|
553
|
-
logger.debug(`Using token: ${token}`);
|
|
554
|
-
logger.error(`Auth failed with token ${token}`);
|
|
555
|
-
|
|
556
|
-
// ✅ Always sanitize
|
|
557
|
-
logger.debug('Using token: ***');
|
|
558
|
-
logger.error('Authentication failed. Please check your token.');
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
---
|
|
562
|
-
|
|
563
|
-
**2. Token Sanitization:**
|
|
564
|
-
|
|
565
|
-
```typescript
|
|
566
|
-
const sanitize = (text: string): string => {
|
|
567
|
-
return text
|
|
568
|
-
.replace(/ghp_[a-zA-Z0-9]{36}/g, 'ghp_***')
|
|
569
|
-
.replace(/github_pat_[a-zA-Z0-9_]{82}/g, 'github_pat_***')
|
|
570
|
-
.replace(/gho_[a-zA-Z0-9]{36}/g, 'gho_***')
|
|
571
|
-
.replace(/ghu_[a-zA-Z0-9]{36}/g, 'ghu_***')
|
|
572
|
-
.replace(/ghs_[a-zA-Z0-9]{36}/g, 'ghs_***')
|
|
573
|
-
.replace(/ghr_[a-zA-Z0-9]{36}/g, 'ghr_***');
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
// Use in all logging
|
|
577
|
-
logger.error(sanitize(errorMessage));
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
|
|
582
|
-
**3. Secure Storage:**
|
|
583
|
-
|
|
584
|
-
```typescript
|
|
585
|
-
import keytar from 'keytar';
|
|
586
|
-
|
|
587
|
-
const SERVICE_NAME = 'claudekit-cli';
|
|
588
|
-
const ACCOUNT_NAME = 'github-token';
|
|
589
|
-
|
|
590
|
-
// Store token
|
|
591
|
-
await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, token);
|
|
592
|
-
|
|
593
|
-
// Retrieve token
|
|
594
|
-
const token = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
|
|
595
|
-
|
|
596
|
-
// Delete token
|
|
597
|
-
await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
---
|
|
601
|
-
|
|
602
|
-
### Input Validation
|
|
603
|
-
|
|
604
|
-
**Always validate external input with Zod:**
|
|
605
|
-
|
|
606
|
-
```typescript
|
|
607
|
-
// Command line arguments
|
|
608
|
-
const options = NewCommandOptionsSchema.parse(rawOptions);
|
|
609
|
-
|
|
610
|
-
// API responses
|
|
611
|
-
const release = GitHubReleaseSchema.parse(apiResponse);
|
|
612
|
-
|
|
613
|
-
// User input
|
|
614
|
-
const directory = z.string().min(1).parse(userInput);
|
|
615
|
-
|
|
616
|
-
// Environment variables
|
|
617
|
-
const token = GitHubTokenSchema.parse(process.env.GITHUB_TOKEN);
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
---
|
|
621
|
-
|
|
622
|
-
### Path Security
|
|
623
|
-
|
|
624
|
-
**Prevent path traversal:**
|
|
625
|
-
|
|
626
|
-
```typescript
|
|
627
|
-
function isSafePath(basePath: string, targetPath: string): boolean {
|
|
628
|
-
const resolvedBase = path.resolve(basePath);
|
|
629
|
-
const resolvedTarget = path.resolve(targetPath);
|
|
630
|
-
|
|
631
|
-
// Ensure target is within base
|
|
632
|
-
return resolvedTarget.startsWith(resolvedBase);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// Use in file operations
|
|
636
|
-
if (!isSafePath(outputDir, filePath)) {
|
|
637
|
-
throw new Error('Path traversal detected');
|
|
638
|
-
}
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
---
|
|
642
|
-
|
|
643
|
-
## Testing Standards
|
|
644
|
-
|
|
645
|
-
### Test Structure
|
|
646
|
-
|
|
647
|
-
**Use Bun Test with clear descriptions:**
|
|
648
|
-
|
|
649
|
-
```typescript
|
|
650
|
-
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
|
651
|
-
|
|
652
|
-
describe('AuthManager', () => {
|
|
653
|
-
beforeEach(async () => {
|
|
654
|
-
// Setup
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
afterEach(async () => {
|
|
658
|
-
// Cleanup
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
test('should detect GitHub CLI token', async () => {
|
|
662
|
-
// Arrange
|
|
663
|
-
const authManager = new AuthManager();
|
|
664
|
-
|
|
665
|
-
// Act
|
|
666
|
-
const { method, token } = await authManager.getToken();
|
|
667
|
-
|
|
668
|
-
// Assert
|
|
669
|
-
expect(method).toBe('gh-cli');
|
|
670
|
-
expect(token).toMatch(/^ghp_/);
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
test('should fall back to environment variable', async () => {
|
|
674
|
-
// Test implementation
|
|
675
|
-
});
|
|
676
|
-
});
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
---
|
|
680
|
-
|
|
681
|
-
### Test Coverage Requirements
|
|
682
|
-
|
|
683
|
-
**Minimum Coverage:** 80%
|
|
684
|
-
**Target Coverage:** 90%+
|
|
685
|
-
|
|
686
|
-
**What to test:**
|
|
687
|
-
- ✅ All public methods
|
|
688
|
-
- ✅ Error scenarios
|
|
689
|
-
- ✅ Edge cases
|
|
690
|
-
- ✅ Type validation
|
|
691
|
-
- ✅ Security features
|
|
692
|
-
|
|
693
|
-
**What NOT to test:**
|
|
694
|
-
- ❌ External libraries (Octokit, etc.)
|
|
695
|
-
- ❌ Trivial getters/setters
|
|
696
|
-
- ❌ Type definitions only
|
|
697
|
-
|
|
698
|
-
---
|
|
699
|
-
|
|
700
|
-
### Mocking Best Practices
|
|
701
|
-
|
|
702
|
-
```typescript
|
|
703
|
-
import { mock } from 'bun:test';
|
|
704
|
-
|
|
705
|
-
test('should handle network errors', async () => {
|
|
706
|
-
// Mock fetch to simulate network error
|
|
707
|
-
const mockFetch = mock(() => {
|
|
708
|
-
throw new Error('Network error');
|
|
709
|
-
});
|
|
710
|
-
|
|
711
|
-
globalThis.fetch = mockFetch;
|
|
712
|
-
|
|
713
|
-
await expect(downloadFile(url)).rejects.toThrow('Network error');
|
|
714
|
-
|
|
715
|
-
// Restore original fetch
|
|
716
|
-
mockFetch.mockRestore();
|
|
717
|
-
});
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
---
|
|
721
|
-
|
|
722
|
-
## Documentation Standards
|
|
723
|
-
|
|
724
|
-
### JSDoc Comments
|
|
725
|
-
|
|
726
|
-
**Required for all public APIs:**
|
|
727
|
-
|
|
728
|
-
```typescript
|
|
729
|
-
/**
|
|
730
|
-
* Downloads a GitHub release asset to the specified directory
|
|
731
|
-
*
|
|
732
|
-
* @param asset - The GitHub release asset to download
|
|
733
|
-
* @param destDir - Destination directory for the downloaded file
|
|
734
|
-
* @returns Path to the downloaded file
|
|
735
|
-
* @throws {DownloadError} If download fails or is interrupted
|
|
736
|
-
*
|
|
737
|
-
* @example
|
|
738
|
-
* ```typescript
|
|
739
|
-
* const path = await downloadManager.downloadAsset(asset, './temp');
|
|
740
|
-
* console.log(`Downloaded to: ${path}`);
|
|
741
|
-
* ```
|
|
742
|
-
*/
|
|
743
|
-
async downloadAsset(
|
|
744
|
-
asset: GitHubReleaseAsset,
|
|
745
|
-
destDir: string
|
|
746
|
-
): Promise<string> {
|
|
747
|
-
// Implementation
|
|
748
|
-
}
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
---
|
|
752
|
-
|
|
753
|
-
### Inline Comments
|
|
754
|
-
|
|
755
|
-
**When to comment:**
|
|
756
|
-
- Complex algorithms
|
|
757
|
-
- Non-obvious business logic
|
|
758
|
-
- Security considerations
|
|
759
|
-
- Performance optimizations
|
|
760
|
-
- Workarounds
|
|
761
|
-
|
|
762
|
-
**When NOT to comment:**
|
|
763
|
-
- Self-explanatory code
|
|
764
|
-
- Trivial operations
|
|
765
|
-
- Repeating what code says
|
|
766
|
-
|
|
767
|
-
**❌ Bad:**
|
|
768
|
-
```typescript
|
|
769
|
-
// Increment counter
|
|
770
|
-
counter++;
|
|
771
|
-
|
|
772
|
-
// Loop through files
|
|
773
|
-
for (const file of files) { ... }
|
|
774
|
-
```
|
|
775
|
-
|
|
776
|
-
**✅ Good:**
|
|
777
|
-
```typescript
|
|
778
|
-
// Strip top-level directory from archive to avoid nested structure
|
|
779
|
-
// GitHub releases include repo name as root dir
|
|
780
|
-
const strippedPath = relativePath.split('/').slice(1).join('/');
|
|
781
|
-
|
|
782
|
-
// Use exponential backoff to avoid rate limiting
|
|
783
|
-
await sleep(Math.pow(2, retryCount) * 1000);
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
---
|
|
787
|
-
|
|
788
|
-
## Git Workflow
|
|
789
|
-
|
|
790
|
-
### Commit Messages
|
|
791
|
-
|
|
792
|
-
**Format:** Conventional Commits
|
|
793
|
-
|
|
794
|
-
```
|
|
795
|
-
<type>(<scope>): <subject>
|
|
796
|
-
|
|
797
|
-
<body>
|
|
798
|
-
|
|
799
|
-
<footer>
|
|
800
|
-
```
|
|
801
|
-
|
|
802
|
-
**Types:**
|
|
803
|
-
- `feat`: New feature
|
|
804
|
-
- `fix`: Bug fix
|
|
805
|
-
- `docs`: Documentation only
|
|
806
|
-
- `style`: Code style (formatting, etc.)
|
|
807
|
-
- `refactor`: Code refactoring
|
|
808
|
-
- `test`: Adding or updating tests
|
|
809
|
-
- `chore`: Maintenance tasks
|
|
810
|
-
|
|
811
|
-
**Examples:**
|
|
812
|
-
|
|
813
|
-
```
|
|
814
|
-
feat(auth): add GitHub CLI token detection
|
|
815
|
-
|
|
816
|
-
Implement automatic token detection from gh CLI when available.
|
|
817
|
-
Falls back to environment variables if gh CLI is not found.
|
|
818
|
-
|
|
819
|
-
Closes #123
|
|
820
|
-
```
|
|
821
|
-
|
|
822
|
-
```
|
|
823
|
-
fix(download): handle network timeouts gracefully
|
|
824
|
-
|
|
825
|
-
Add AbortSignal timeout to prevent hanging on slow connections.
|
|
826
|
-
Show clear error message when timeout occurs.
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
```
|
|
830
|
-
docs(readme): update authentication setup guide
|
|
831
|
-
|
|
832
|
-
Add instructions for fine-grained PAT creation.
|
|
833
|
-
Include minimum required permissions.
|
|
834
|
-
```
|
|
835
|
-
|
|
836
|
-
---
|
|
837
|
-
|
|
838
|
-
### Branch Naming
|
|
839
|
-
|
|
840
|
-
**Format:** `<type>/<short-description>`
|
|
841
|
-
|
|
842
|
-
**Examples:**
|
|
843
|
-
- `feat/github-cli-auth`
|
|
844
|
-
- `fix/download-timeout`
|
|
845
|
-
- `docs/update-readme`
|
|
846
|
-
- `refactor/split-auth-providers`
|
|
847
|
-
|
|
848
|
-
---
|
|
849
|
-
|
|
850
|
-
### Pull Request Guidelines
|
|
851
|
-
|
|
852
|
-
**PR Title:** Same as commit message format
|
|
853
|
-
|
|
854
|
-
**PR Description:**
|
|
855
|
-
```markdown
|
|
856
|
-
## Summary
|
|
857
|
-
Brief description of changes
|
|
858
|
-
|
|
859
|
-
## Changes
|
|
860
|
-
- Added GitHub CLI token detection
|
|
861
|
-
- Updated authentication fallback chain
|
|
862
|
-
- Added tests for new auth method
|
|
863
|
-
|
|
864
|
-
## Testing
|
|
865
|
-
- ✅ All tests passing
|
|
866
|
-
- ✅ Manual testing with gh CLI
|
|
867
|
-
- ✅ Manual testing without gh CLI
|
|
868
|
-
|
|
869
|
-
## Checklist
|
|
870
|
-
- [x] Tests added/updated
|
|
871
|
-
- [x] Documentation updated
|
|
872
|
-
- [x] Type checking passes
|
|
873
|
-
- [x] Linting passes
|
|
874
|
-
```
|
|
875
|
-
|
|
876
|
-
---
|
|
877
|
-
|
|
878
|
-
## Code Review Checklist
|
|
879
|
-
|
|
880
|
-
### Functionality
|
|
881
|
-
- [ ] Code implements requirements correctly
|
|
882
|
-
- [ ] Edge cases handled
|
|
883
|
-
- [ ] Error scenarios covered
|
|
884
|
-
- [ ] No regressions introduced
|
|
885
|
-
|
|
886
|
-
### Code Quality
|
|
887
|
-
- [ ] Follows YAGNI, KISS, DRY principles
|
|
888
|
-
- [ ] Clean, readable code
|
|
889
|
-
- [ ] No code duplication
|
|
890
|
-
- [ ] Consistent naming conventions
|
|
891
|
-
- [ ] Files under 500 lines
|
|
892
|
-
|
|
893
|
-
### Security
|
|
894
|
-
- [ ] No token exposure
|
|
895
|
-
- [ ] Input validation present
|
|
896
|
-
- [ ] Path validation for file operations
|
|
897
|
-
- [ ] No hardcoded secrets
|
|
898
|
-
|
|
899
|
-
### Performance
|
|
900
|
-
- [ ] Efficient algorithms used
|
|
901
|
-
- [ ] Streaming for large files
|
|
902
|
-
- [ ] No memory leaks
|
|
903
|
-
- [ ] Proper async/await usage
|
|
904
|
-
|
|
905
|
-
### Testing
|
|
906
|
-
- [ ] Unit tests added/updated
|
|
907
|
-
- [ ] All tests passing
|
|
908
|
-
- [ ] Coverage meets requirements
|
|
909
|
-
- [ ] Edge cases tested
|
|
910
|
-
|
|
911
|
-
### Documentation
|
|
912
|
-
- [ ] JSDoc for public APIs
|
|
913
|
-
- [ ] Inline comments for complex logic
|
|
914
|
-
- [ ] README updated if needed
|
|
915
|
-
- [ ] CHANGELOG updated
|
|
916
|
-
|
|
917
|
-
### TypeScript
|
|
918
|
-
- [ ] No type errors
|
|
919
|
-
- [ ] Strict mode compliant
|
|
920
|
-
- [ ] No `any` without justification
|
|
921
|
-
- [ ] Zod schemas for validation
|
|
922
|
-
|
|
923
|
-
---
|
|
924
|
-
|
|
925
|
-
## Code Quality Tools
|
|
926
|
-
|
|
927
|
-
### Linting: Biome
|
|
928
|
-
|
|
929
|
-
**Configuration** (`.biome.json`):
|
|
930
|
-
```json
|
|
931
|
-
{
|
|
932
|
-
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
933
|
-
"organizeImports": {
|
|
934
|
-
"enabled": true
|
|
935
|
-
},
|
|
936
|
-
"linter": {
|
|
937
|
-
"enabled": true,
|
|
938
|
-
"rules": {
|
|
939
|
-
"recommended": true,
|
|
940
|
-
"complexity": {
|
|
941
|
-
"noExtraBooleanCast": "error",
|
|
942
|
-
"noMultipleSpacesInRegularExpressionLiterals": "error",
|
|
943
|
-
"noUselessConstructor": "error",
|
|
944
|
-
"noWith": "error"
|
|
945
|
-
},
|
|
946
|
-
"style": {
|
|
947
|
-
"noParameterAssign": "error",
|
|
948
|
-
"useConst": "error",
|
|
949
|
-
"useTemplate": "error"
|
|
950
|
-
},
|
|
951
|
-
"suspicious": {
|
|
952
|
-
"noExplicitAny": "warn",
|
|
953
|
-
"noDebugger": "error",
|
|
954
|
-
"noConsoleLog": "warn"
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
},
|
|
958
|
-
"formatter": {
|
|
959
|
-
"enabled": true,
|
|
960
|
-
"indentStyle": "space",
|
|
961
|
-
"indentWidth": 2,
|
|
962
|
-
"lineWidth": 100
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
```
|
|
966
|
-
|
|
967
|
-
---
|
|
968
|
-
|
|
969
|
-
### Type Checking
|
|
970
|
-
|
|
971
|
-
**Run before commit:**
|
|
972
|
-
```bash
|
|
973
|
-
bun run typecheck
|
|
974
|
-
```
|
|
975
|
-
|
|
976
|
-
**Zero tolerance:** No type errors allowed in production code.
|
|
977
|
-
|
|
978
|
-
---
|
|
979
|
-
|
|
980
|
-
### Testing
|
|
981
|
-
|
|
982
|
-
**Run before commit:**
|
|
983
|
-
```bash
|
|
984
|
-
bun test
|
|
985
|
-
```
|
|
986
|
-
|
|
987
|
-
**Requirements:**
|
|
988
|
-
- All tests must pass
|
|
989
|
-
- Coverage ≥ 80%
|
|
990
|
-
- No skipped tests without reason
|
|
991
|
-
|
|
992
|
-
---
|
|
993
|
-
|
|
994
|
-
## Best Practices Summary
|
|
995
|
-
|
|
996
|
-
### DO ✅
|
|
997
|
-
|
|
998
|
-
- Use TypeScript strict mode
|
|
999
|
-
- Validate all external input with Zod
|
|
1000
|
-
- Write comprehensive tests
|
|
1001
|
-
- Handle errors gracefully
|
|
1002
|
-
- Use descriptive variable names
|
|
1003
|
-
- Keep files small (< 500 lines)
|
|
1004
|
-
- Document public APIs with JSDoc
|
|
1005
|
-
- Sanitize sensitive data in logs
|
|
1006
|
-
- Use streaming for large files
|
|
1007
|
-
- Follow conventional commits
|
|
1008
|
-
|
|
1009
|
-
### DON'T ❌
|
|
1010
|
-
|
|
1011
|
-
- Use `any` type without justification
|
|
1012
|
-
- Log sensitive data (tokens, etc.)
|
|
1013
|
-
- Ignore error scenarios
|
|
1014
|
-
- Write overly complex code
|
|
1015
|
-
- Duplicate logic across files
|
|
1016
|
-
- Hardcode configuration values
|
|
1017
|
-
- Skip input validation
|
|
1018
|
-
- Leave TODO comments in production
|
|
1019
|
-
- Commit broken tests
|
|
1020
|
-
- Push without running tests
|
|
1021
|
-
|
|
1022
|
-
---
|
|
1023
|
-
|
|
1024
|
-
## Performance Guidelines
|
|
1025
|
-
|
|
1026
|
-
### Memory Efficiency
|
|
1027
|
-
|
|
1028
|
-
**Use streaming for large operations:**
|
|
1029
|
-
|
|
1030
|
-
```typescript
|
|
1031
|
-
// ✅ Good - Streaming
|
|
1032
|
-
const stream = createWriteStream(outputPath);
|
|
1033
|
-
for await (const chunk of downloadStream) {
|
|
1034
|
-
stream.write(chunk);
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
// ❌ Bad - Loading into memory
|
|
1038
|
-
const data = await fetch(url).then(r => r.arrayBuffer());
|
|
1039
|
-
await writeFile(outputPath, Buffer.from(data));
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
---
|
|
1043
|
-
|
|
1044
|
-
### Async Best Practices
|
|
1045
|
-
|
|
1046
|
-
**Use Promise.all for parallel operations:**
|
|
1047
|
-
|
|
1048
|
-
```typescript
|
|
1049
|
-
// ✅ Good - Parallel
|
|
1050
|
-
const [user, repos, stars] = await Promise.all([
|
|
1051
|
-
client.getUser(),
|
|
1052
|
-
client.getRepos(),
|
|
1053
|
-
client.getStars(),
|
|
1054
|
-
]);
|
|
1055
|
-
|
|
1056
|
-
// ❌ Bad - Sequential
|
|
1057
|
-
const user = await client.getUser();
|
|
1058
|
-
const repos = await client.getRepos();
|
|
1059
|
-
const stars = await client.getStars();
|
|
1060
|
-
```
|
|
1061
|
-
|
|
1062
|
-
---
|
|
1063
|
-
|
|
1064
|
-
**Proper error handling in async:**
|
|
1065
|
-
|
|
1066
|
-
```typescript
|
|
1067
|
-
async function processFiles(files: string[]): Promise<void> {
|
|
1068
|
-
try {
|
|
1069
|
-
await Promise.all(files.map(f => processFile(f)));
|
|
1070
|
-
} catch (error) {
|
|
1071
|
-
logger.error('Failed to process files', error);
|
|
1072
|
-
throw new ProcessingError('File processing failed', error);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
```
|
|
1076
|
-
|
|
1077
|
-
---
|
|
1078
|
-
|
|
1079
|
-
## Maintenance Guidelines
|
|
1080
|
-
|
|
1081
|
-
### Regular Tasks
|
|
1082
|
-
|
|
1083
|
-
**Weekly:**
|
|
1084
|
-
- Review and update dependencies
|
|
1085
|
-
- Run security audit (`bun audit`)
|
|
1086
|
-
- Check for deprecated APIs
|
|
1087
|
-
- Review and close stale issues
|
|
1088
|
-
|
|
1089
|
-
**Monthly:**
|
|
1090
|
-
- Review test coverage
|
|
1091
|
-
- Update documentation
|
|
1092
|
-
- Refactor complex code
|
|
1093
|
-
- Performance profiling
|
|
1094
|
-
|
|
1095
|
-
**Quarterly:**
|
|
1096
|
-
- Major dependency updates
|
|
1097
|
-
- Architecture review
|
|
1098
|
-
- Security audit
|
|
1099
|
-
- Performance benchmarking
|
|
1100
|
-
|
|
1101
|
-
---
|
|
1102
|
-
|
|
1103
|
-
### Deprecation Process
|
|
1104
|
-
|
|
1105
|
-
1. Mark as deprecated with JSDoc
|
|
1106
|
-
2. Provide migration path
|
|
1107
|
-
3. Add console warning
|
|
1108
|
-
4. Update documentation
|
|
1109
|
-
5. Remove after 2 versions
|
|
1110
|
-
|
|
1111
|
-
**Example:**
|
|
1112
|
-
|
|
1113
|
-
```typescript
|
|
1114
|
-
/**
|
|
1115
|
-
* @deprecated Use getToken() instead. Will be removed in v2.0.0
|
|
1116
|
-
*/
|
|
1117
|
-
async function fetchToken(): Promise<string> {
|
|
1118
|
-
logger.warning('fetchToken() is deprecated. Use getToken() instead.');
|
|
1119
|
-
return this.getToken();
|
|
1120
|
-
}
|
|
1121
|
-
```
|
|
1122
|
-
|
|
1123
|
-
---
|
|
1124
|
-
|
|
1125
|
-
**Document Version:** 1.0
|
|
1126
|
-
**Last Updated:** 2025-10-08
|
|
1127
|
-
**Status:** Active
|
|
1128
|
-
**Next Review:** 2025-11-08
|