@codyswann/lisa 1.66.3 → 1.67.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.
Files changed (26) hide show
  1. package/all/copy-overwrite/.claude/rules/base-rules.md +137 -0
  2. package/package.json +1 -1
  3. package/plugins/lisa/.claude-plugin/plugin.json +1 -10
  4. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  5. package/plugins/lisa-expo/.claude-plugin/plugin.json +2 -14
  6. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -12
  8. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -12
  9. package/plugins/src/base/.claude-plugin/plugin.json +0 -9
  10. package/plugins/src/expo/.claude-plugin/plugin.json +2 -13
  11. package/plugins/src/rails/.claude-plugin/plugin.json +0 -11
  12. package/plugins/src/typescript/.claude-plugin/plugin.json +0 -11
  13. package/plugins/lisa/rules/README.md +0 -240
  14. package/plugins/lisa-rails/rules/lisa.md +0 -42
  15. package/plugins/lisa-typescript/rules/lisa.md +0 -37
  16. package/plugins/src/base/rules/README.md +0 -240
  17. package/plugins/src/base/rules/coding-philosophy.md +0 -428
  18. package/plugins/src/base/rules/verfication.md +0 -594
  19. package/plugins/src/expo/rules/expo-verification.md +0 -261
  20. package/plugins/src/rails/rules/lisa.md +0 -42
  21. package/plugins/src/rails/rules/rails-conventions.md +0 -176
  22. package/plugins/src/typescript/rules/lisa.md +0 -37
  23. /package/{plugins/lisa → all/copy-overwrite/.claude}/rules/coding-philosophy.md +0 -0
  24. /package/{plugins/lisa/rules/verfication.md → all/copy-overwrite/.claude/rules/verification.md} +0 -0
  25. /package/{plugins/lisa-expo → expo/copy-overwrite/.claude}/rules/expo-verification.md +0 -0
  26. /package/{plugins/lisa-rails → rails/copy-overwrite/.claude}/rules/rails-conventions.md +0 -0
@@ -1,240 +0,0 @@
1
- # Claude Code Configuration
2
-
3
- This directory contains Claude Code configuration files that customize AI-assisted development workflows.
4
-
5
- ## Directory Structure
6
-
7
- ```
8
- .claude/
9
- ├── settings.json # Main Claude Code settings
10
- ├── settings.local.json # Local overrides (gitignored)
11
- ├── settings.local.json.example # Template for local settings
12
- ├── agents/ # Custom agent definitions
13
- ├── hooks/ # Automation hooks
14
- ├── rules/ # Always-on governance rules
15
- └── skills/ # Skill definitions (colon-namespaced)
16
- ```
17
-
18
- ## Settings
19
-
20
- ### Environment Variables
21
-
22
- | Variable | Default | Description |
23
- |----------|---------|-------------|
24
- | `BASH_DEFAULT_TIMEOUT_MS` | 1800000 (30 min) | Default timeout for bash commands |
25
- | `BASH_MAX_TIMEOUT_MS` | 7200000 (2 hours) | Maximum allowed timeout |
26
-
27
- ### Hooks
28
-
29
- | Event | Hook | Purpose |
30
- |-------|------|---------|
31
- | `SessionStart` | `install-pkgs.sh` | Install dependencies when session starts |
32
- | `PostToolUse` | `format-on-edit.sh` | Auto-format files after Write/Edit operations |
33
- | `Notification` | `notify-ntfy.sh` | Send notifications via ntfy.sh |
34
- | `Stop` | `notify-ntfy.sh` | Notify when session ends |
35
-
36
- ## Plugins
37
-
38
- The `enabledPlugins` section in `settings.json` references Claude Code plugins. These extend Claude Code functionality with additional capabilities.
39
-
40
- ### Plugin Sources
41
-
42
- | Source | Description | Registration |
43
- |--------|-------------|--------------|
44
- | `claude-plugins-official` | Official Anthropic plugins | Built-in, no registration needed |
45
- | `cc-marketplace` | Community marketplace | Available at [Claude Code Marketplace](https://marketplace.claude.ai) |
46
-
47
- ### Enabled Plugins
48
-
49
- | Plugin | Source | Purpose |
50
- |--------|--------|---------|
51
- | `typescript-lsp` | `claude-plugins-official` | TypeScript language server integration |
52
- | `safety-net` | `cc-marketplace` | Backup and safety features |
53
- | `code-simplifier` | `claude-plugins-official` | Code complexity reduction suggestions |
54
- | `code-review` | `claude-plugins-official` | Automated code review capabilities |
55
- | `playwright` | `claude-plugins-official` | Playwright test integration |
56
-
57
- ### Installing Plugins
58
-
59
- 1. **Official plugins** are available by default in Claude Code
60
- 2. **Marketplace plugins** can be installed via the Claude Code settings UI
61
- 3. **Third-party plugins** require following their installation instructions
62
-
63
- ### Plugin Availability
64
-
65
- Not all plugins may be available in all Claude Code installations:
66
-
67
- - Some plugins require specific Claude Code versions
68
- - Marketplace plugins require marketplace access
69
- - Enterprise installations may restrict available plugins
70
-
71
- If a plugin is not available, Claude Code will ignore it gracefully.
72
-
73
- ## Local Settings Override
74
-
75
- Create `settings.local.json` to override settings for your local environment:
76
-
77
- ```json
78
- {
79
- "env": {
80
- "CUSTOM_API_KEY": "your-key-here"
81
- },
82
- "hooks": {
83
- "PostToolUse": []
84
- },
85
- "enabledPlugins": {
86
- "playwright": false
87
- }
88
- }
89
- ```
90
-
91
- This file should be:
92
- - Added to `.gitignore`
93
- - Never committed to version control
94
- - Used for machine-specific settings
95
-
96
- ## Agents
97
-
98
- Custom agent definitions in `agents/` provide specialized AI personas for different tasks:
99
-
100
- | Agent | Purpose |
101
- |-------|---------|
102
- | `agent-architect.md` | Agent file design and optimization |
103
- | `architecture-specialist.md` | Implementation design, dependency mapping, pattern evaluation |
104
- | `git-history-analyzer.md` | Git history analysis |
105
- | `hooks-expert.md` | Claude Code hooks expertise |
106
- | `implementer.md` | Code implementation with TDD enforcement |
107
- | `learner.md` | Post-implementation learning and skill creation |
108
- | `performance-specialist.md` | N+1 queries, algorithmic complexity, memory leaks |
109
- | `product-specialist.md` | User flows, acceptance criteria, UX validation |
110
- | `quality-specialist.md` | Code correctness, coding philosophy, test coverage review |
111
- | `security-specialist.md` | Threat modeling (STRIDE), OWASP Top 10, auth/secrets review |
112
- | `skill-evaluator.md` | Skill quality assessment |
113
- | `slash-command-architect.md` | Command design |
114
- | `test-specialist.md` | Test strategy, test writing, coverage analysis |
115
- | `web-search-researcher.md` | Web research tasks |
116
-
117
- ## Skills
118
-
119
- Skills use colon-namespaced directories (e.g., `plan:create/`) and are invoked via `/skill-name` in Claude Code.
120
-
121
- ### Plan Skills
122
-
123
- | Skill | Purpose |
124
- |-------|---------|
125
- | `plan:create` | Create implementation plans from ticket URLs, file paths, or text descriptions |
126
- | `plan:implement` | Execute an existing plan with an Agent Team |
127
- | `plan:add-test-coverage` | Increase test coverage to a target threshold |
128
- | `plan:fix-linter-error` | Fix all violations of specific ESLint rules |
129
- | `plan:local-code-review` | Review local branch changes against main |
130
- | `plan:lower-code-complexity` | Reduce cognitive complexity threshold |
131
- | `plan:reduce-max-lines` | Reduce max file lines threshold |
132
- | `plan:reduce-max-lines-per-function` | Reduce max function lines threshold |
133
-
134
- ### Git Skills
135
-
136
- | Skill | Purpose |
137
- |-------|---------|
138
- | `git:commit` | Create conventional commits for current changes |
139
- | `git:commit-and-submit-pr` | Commit changes and create/update a pull request |
140
- | `git:submit-pr` | Push changes and create or update a pull request |
141
- | `git:prune` | Prune local branches deleted on remote |
142
-
143
- ### Integration Skills
144
-
145
- | Skill | Purpose |
146
- |-------|---------|
147
- | `jira:create` | Create JIRA epics, stories, and tasks |
148
- | `jira:verify` | Verify JIRA ticket meets organizational standards |
149
- | `jira:sync` | Sync plan progress to linked JIRA tickets |
150
- | `sonarqube:check` | Check SonarQube/SonarCloud quality gate failures |
151
- | `sonarqube:fix` | Fix SonarQube quality gate failures |
152
- | `pull-request:review` | Check and implement PR review comments |
153
- | `security:zap-scan` | Run OWASP ZAP baseline security scan |
154
-
155
- ### Utility Skills
156
-
157
- | Skill | Purpose |
158
- |-------|---------|
159
- | `tasks:load` | Load tasks from project directory into session |
160
- | `tasks:sync` | Export session tasks to project directory |
161
- | `skill-creator` | Guide for creating new skills |
162
- | `agent-design-best-practices` | Best practices for designing agent files |
163
- | `jsdoc-best-practices` | JSDoc documentation standards |
164
-
165
- See each skill's `SKILL.md` for detailed documentation.
166
-
167
- ## Customization
168
-
169
- ### Adding Custom Skills
170
-
171
- Skills contain implementation logic and use hyphen-separated naming:
172
-
173
- ```bash
174
- mkdir -p .claude/skills/my-namespace-my-skill
175
- cat > .claude/skills/my-namespace-my-skill/SKILL.md << 'EOF'
176
- ---
177
- name: my-namespace-my-skill
178
- description: "What this skill does"
179
- ---
180
-
181
- # My Skill
182
-
183
- Instructions for the skill...
184
- EOF
185
- ```
186
-
187
- ### Adding Custom Commands
188
-
189
- Commands are user-facing pass-throughs to skills. Directory nesting creates colon-separated names in the UI (e.g., `my-namespace/my-skill.md` becomes `/my-namespace:my-skill`):
190
-
191
- ```bash
192
- mkdir -p .claude/commands/my-namespace
193
- cat > .claude/commands/my-namespace/my-skill.md << 'EOF'
194
- ---
195
- description: "What this command does"
196
- allowed-tools: ["Skill"]
197
- argument-hint: "<arguments>"
198
- ---
199
-
200
- Use the /my-namespace-my-skill skill to do the thing. $ARGUMENTS
201
- EOF
202
- ```
203
-
204
- ### Adding Custom Agents
205
-
206
- ```bash
207
- cat > .claude/agents/my-agent.md << 'EOF'
208
- # My Agent
209
-
210
- ## Role
211
- Specialized for specific tasks...
212
-
213
- ## Capabilities
214
- - Capability 1
215
- - Capability 2
216
-
217
- ## Instructions
218
- How to behave...
219
- EOF
220
- ```
221
-
222
- ## Troubleshooting
223
-
224
- ### Hooks Not Running
225
-
226
- 1. Check file permissions: `chmod +x .claude/hooks/*.sh`
227
- 2. Verify `$CLAUDE_PROJECT_DIR` is set correctly
228
- 3. Check hook timeout settings
229
-
230
- ### Plugins Not Loading
231
-
232
- 1. Verify plugin is installed in your Claude Code installation
233
- 2. Check marketplace access if using marketplace plugins
234
- 3. Review Claude Code logs for plugin errors
235
-
236
- ### Skills Not Found
237
-
238
- 1. Ensure skill directory contains a `SKILL.md` file with correct frontmatter
239
- 2. Restart Claude Code to reload skills
240
- 3. Check for syntax errors in skill definition
@@ -1,428 +0,0 @@
1
- # Coding Philosophy
2
-
3
- This rule enforces the core coding philosophy: **immutability**, **predictable structure**, **functional transformations**, **test-driven development**, **clean deletion**, and **simplicity**.
4
-
5
- ## Guiding Principles: YAGNI + SOLID + DRY + KISS
6
-
7
- Follow these software engineering principles, **deferring to Occam's Razor/KISS whenever principles conflict**:
8
-
9
- ### KISS (Keep It Simple, Stupid) - The Tiebreaker
10
-
11
- When principles conflict, **always choose the simpler solution**.
12
-
13
- ```typescript
14
- // KISS: Simple direct approach
15
- const isAdmin = user.role === "admin";
16
-
17
- // Over-engineered: Abstraction without value
18
- const isAdmin = RoleChecker.getInstance().checkRole(user, RoleTypes.ADMIN);
19
- ```
20
-
21
- ### YAGNI (You Ain't Gonna Need It)
22
-
23
- Don't build features, abstractions, or flexibility you don't need **right now**.
24
-
25
- ```typescript
26
- // Correct: Solve today's problem
27
- const formatDate = (date: Date) => date.toISOString().split("T")[0];
28
-
29
- // Wrong: Building for hypothetical future needs
30
- const formatDate = (date: Date, format?: string, locale?: string, timezone?: string) => {
31
- // 50 lines handling cases that may never be used
32
- };
33
- ```
34
-
35
- ### DRY (Don't Repeat Yourself) - With KISS Constraint
36
-
37
- Extract duplication only when:
38
- 1. The same logic appears **3+ times**
39
- 2. The abstraction is **simpler** than the duplication
40
- 3. The extracted code has a **clear single purpose**
41
-
42
- ### SOLID Principles - Applied Pragmatically
43
-
44
- | Principle | Apply When | Skip When |
45
- | ------------------------- | --------------------------------------------- | --------------------------------------- |
46
- | **S**ingle Responsibility | Function does 2+ unrelated things | Splitting adds complexity |
47
- | **O**pen/Closed | Extension points have clear use cases | No foreseeable extensions |
48
- | **L**iskov Substitution | Using inheritance hierarchies | Using composition (preferred) |
49
- | **I**nterface Segregation | Consumers need different subsets | Interface is already small |
50
- | **D**ependency Inversion | Testing requires mocking external services | Direct dependency is simpler |
51
-
52
- ### Decision Framework
53
-
54
- When unsure, ask in order:
55
- 1. **Do I need this now?** (YAGNI) - If no, don't build it
56
- 2. **Is there a simpler way?** (KISS) - Choose the simpler option
57
- 3. **Am I repeating myself 3+ times?** (DRY) - Extract if the abstraction is simpler
58
- 4. **Does this function do one thing?** (SOLID-SRP) - Split only if clearer
59
-
60
- ---
61
-
62
- ## Core Principles
63
-
64
- ### 1. Immutability First
65
-
66
- Never mutate data. Always create new references.
67
-
68
- ```typescript
69
- // Correct - spread creates new object
70
- const updated = { ...user, name: "New Name" };
71
-
72
- // Incorrect - mutation
73
- user.name = "New Name";
74
- ```
75
-
76
- ### 2. Function Structure Ordering
77
-
78
- All functions, hooks, and components follow a strict ordering:
79
-
80
- ```text
81
- 1. Variable definitions and derived state (const, useState, useMemo, useCallback)
82
- 2. Side effects (useEffect, function calls with no return value)
83
- 3. Return statement
84
- ```
85
-
86
- ### 3. Functional Transformations
87
-
88
- Use `map`, `filter`, `reduce` instead of imperative loops and mutations.
89
-
90
- ```typescript
91
- // Correct - functional transformation
92
- const names = users.map(u => u.name);
93
-
94
- // Incorrect - imperative mutation
95
- const names = [];
96
- users.forEach(u => names.push(u.name));
97
- ```
98
-
99
- ### 4. Test-Driven Development (TDD)
100
-
101
- **Always write failing tests before implementation code.** This is mandatory, not optional.
102
-
103
- ```text
104
- TDD Cycle:
105
- 1. RED: Write a failing test that defines expected behavior
106
- 2. GREEN: Write the minimum code to make the test pass
107
- 3. REFACTOR: Clean up while keeping tests green
108
- ```
109
-
110
- ### 5. Clean Deletion
111
-
112
- **Delete old code completely.** No deprecation warnings, migration shims, or backward-compatibility layers unless explicitly requested.
113
-
114
- ```typescript
115
- // Correct: Remove the old code entirely
116
- const calculateScore = (player: Player): number => player.stats.overall;
117
-
118
- // Wrong: Keeping deprecated versions around
119
- /** @deprecated Use calculateScore instead */
120
- const getPlayerScore = (player: Player): number => calculateScore(player);
121
- ```
122
-
123
- **Clean deletion rules:**
124
- - When replacing code, delete the old version completely
125
- - Never create `V2`, `New`, or `Old` suffixed functions/variables
126
- - Never add `@deprecated` comments - just remove the code
127
- - Never write migration code unless explicitly asked
128
- - Trust git history for recovery if needed
129
-
130
- ---
131
-
132
- ## Quick Reference
133
-
134
- ### Variable Declaration
135
-
136
- | Pattern | Status | Example |
137
- | ------- | --------- | ----------------------------- |
138
- | `const` | Required | `const value = calculate();` |
139
- | `let` | Forbidden | Use ternary or reduce instead |
140
- | `var` | Forbidden | Never use |
141
-
142
- ### Array Operations
143
-
144
- | Instead of | Use |
145
- | ----------------------- | -------------------------------------------- |
146
- | `arr.push(item)` | `[...arr, item]` |
147
- | `arr.pop()` | `arr.slice(0, -1)` |
148
- | `arr.splice(i, 1)` | `arr.filter((_, idx) => idx !== i)` |
149
- | `arr.sort()` | `[...arr].sort()` |
150
- | `arr[i] = value` | `arr.map((v, idx) => idx === i ? value : v)` |
151
- | `forEach` with mutation | `reduce` or `map` |
152
-
153
- ### Object Operations
154
-
155
- | Instead of | Use |
156
- | ------------------------- | ----------------------------- |
157
- | `obj.key = value` | `{ ...obj, key: value }` |
158
- | `delete obj.key` | `({ key: _, ...rest } = obj)` |
159
- | `Object.assign(obj, ...)` | `{ ...obj, ...other }` |
160
-
161
- ### Building Lookup Objects
162
-
163
- ```typescript
164
- // Correct - reduce with spread
165
- const lookup = items.reduce(
166
- (acc, item) => ({ ...acc, [item.id]: item }),
167
- {} as Record<string, Item>
168
- );
169
-
170
- // Incorrect - forEach with Map.set
171
- const lookup = new Map();
172
- items.forEach(item => lookup.set(item.id, item));
173
- ```
174
-
175
- ### Conditional Values
176
-
177
- ```typescript
178
- // Correct - ternary expression
179
- const status = isComplete ? "done" : "pending";
180
-
181
- // Incorrect - let with reassignment
182
- let status = "pending";
183
- if (isComplete) {
184
- status = "done";
185
- }
186
- ```
187
-
188
- ---
189
-
190
- ## Hook Structure Example
191
-
192
- ```typescript
193
- export const usePlayerData = (playerId: string) => {
194
- // 1. VARIABLES & STATE (first)
195
- const [isLoading, setIsLoading] = useState(true);
196
- const { data } = useQuery(GetPlayerDocument, { variables: { playerId } });
197
-
198
- const playerName = useMemo(() => data?.player?.name ?? "Unknown", [data]);
199
-
200
- const handleRefresh = useCallback(() => {
201
- refetch();
202
- }, [refetch]);
203
-
204
- // 2. SIDE EFFECTS (second)
205
- useEffect(() => {
206
- console.log("Player loaded:", playerName);
207
- }, [playerName]);
208
-
209
- // 3. RETURN (last)
210
- return { playerName, isLoading, handleRefresh };
211
- };
212
- ```
213
-
214
- ## Container Component Example
215
-
216
- ```typescript
217
- const PlayerCardContainer: React.FC<Props> = ({ playerId }) => {
218
- // 1. VARIABLES & STATE
219
- const { data, loading } = useQuery(GetPlayerDocument, { variables: { playerId } });
220
- const { colors } = useTheme();
221
-
222
- const formattedStats = useMemo(
223
- () => data?.stats?.map(s => ({ ...s, display: formatStat(s) })) ?? [],
224
- [data?.stats]
225
- );
226
-
227
- const handlePress = useCallback(() => {
228
- router.push(`/players/${playerId}`);
229
- }, [playerId]);
230
-
231
- // 2. SIDE EFFECTS (none in this example)
232
-
233
- // 3. RETURN
234
- return (
235
- <PlayerCardView
236
- stats={formattedStats}
237
- colors={colors}
238
- loading={loading}
239
- onPress={handlePress}
240
- />
241
- );
242
- };
243
- ```
244
-
245
- ## Utility Function Example
246
-
247
- ```typescript
248
- export const calculateTeamRankings = (
249
- players: readonly Player[]
250
- ): readonly TeamRanking[] => {
251
- // 1. VARIABLES & DERIVED VALUES
252
- const validPlayers = players.filter(p => p.team && p.score != null);
253
-
254
- const teamScores = validPlayers.reduce(
255
- (acc, player) => ({
256
- ...acc,
257
- [player.team.id]: {
258
- teamId: player.team.id,
259
- totalScore: (acc[player.team.id]?.totalScore ?? 0) + player.score,
260
- count: (acc[player.team.id]?.count ?? 0) + 1,
261
- },
262
- }),
263
- {} as Record<string, { teamId: string; totalScore: number; count: number }>
264
- );
265
-
266
- const rankings = Object.values(teamScores).map(t => ({
267
- teamId: t.teamId,
268
- avgScore: t.totalScore / t.count,
269
- }));
270
-
271
- const sorted = [...rankings].sort((a, b) => b.avgScore - a.avgScore);
272
-
273
- // 2. NO SIDE EFFECTS IN PURE FUNCTIONS
274
-
275
- // 3. RETURN
276
- return sorted;
277
- };
278
- ```
279
-
280
- ---
281
-
282
- ## Anti-Patterns to Avoid
283
-
284
- ### Never use `let` for conditional assignment
285
-
286
- ```typescript
287
- // Wrong
288
- let result;
289
- if (condition) {
290
- result = valueA;
291
- } else {
292
- result = valueB;
293
- }
294
-
295
- // Correct
296
- const result = condition ? valueA : valueB;
297
- ```
298
-
299
- ### Never mutate arrays
300
-
301
- ```typescript
302
- // Wrong
303
- const items = [];
304
- data.forEach(d => items.push(transform(d)));
305
-
306
- // Correct
307
- const items = data.map(d => transform(d));
308
- ```
309
-
310
- ### Never use Map when Record suffices
311
-
312
- ```typescript
313
- // Wrong
314
- const lookup = new Map<string, User>();
315
- users.forEach(u => lookup.set(u.id, u));
316
- const user = lookup.get(userId);
317
-
318
- // Correct
319
- const lookup = users.reduce(
320
- (acc, u) => ({ ...acc, [u.id]: u }),
321
- {} as Record<string, User>
322
- );
323
- const user = lookup[userId];
324
- ```
325
-
326
- ### Never sort in place
327
-
328
- ```typescript
329
- // Wrong - mutates original
330
- const sorted = items.sort((a, b) => a.value - b.value);
331
-
332
- // Correct - creates new array
333
- const sorted = [...items].sort((a, b) => a.value - b.value);
334
- ```
335
-
336
- ### Never place useEffect before variable definitions
337
-
338
- ```typescript
339
- // Wrong
340
- useEffect(() => {
341
- /* ... */
342
- }, [value]);
343
- const value = useMemo(() => calculate(), [dep]);
344
-
345
- // Correct
346
- const value = useMemo(() => calculate(), [dep]);
347
- useEffect(() => {
348
- /* ... */
349
- }, [value]);
350
- ```
351
-
352
- ---
353
-
354
- ## Immutable Patterns Reference
355
-
356
- ### Building Lookup Objects with Reduce
357
-
358
- ```typescript
359
- const colorMap =
360
- edges?.reduce(
361
- (acc, edge) => (edge.color ? { ...acc, [edge.tagId]: edge.color } : acc),
362
- {} as Record<string, string>
363
- ) ?? {};
364
- ```
365
-
366
- ### Accumulating Multiple Properties
367
-
368
- ```typescript
369
- const teamGprAccumulator = validPlayers.reduce(
370
- (acc, player) => {
371
- const teamId = player.team?.id;
372
- if (!teamId) return acc;
373
-
374
- const existing = acc[teamId];
375
- return {
376
- ...acc,
377
- [teamId]: {
378
- teamId,
379
- teamName: player.team.name,
380
- gprSum: (existing?.gprSum ?? 0) + player.gpr,
381
- playerCount: (existing?.playerCount ?? 0) + 1,
382
- },
383
- };
384
- },
385
- {} as Record<string, { teamId: string; teamName: string; gprSum: number; playerCount: number }>
386
- );
387
- ```
388
-
389
- ### Nested Object Updates
390
-
391
- ```typescript
392
- const updated = {
393
- ...state,
394
- user: {
395
- ...state.user,
396
- profile: {
397
- ...state.user.profile,
398
- avatar: newAvatar,
399
- },
400
- },
401
- };
402
- ```
403
-
404
- ### Conditional Property Addition
405
-
406
- ```typescript
407
- const result = {
408
- ...baseObj,
409
- ...(condition && { optionalProp: value }),
410
- };
411
- ```
412
-
413
- ### Ternary Chain for Multiple Conditions
414
-
415
- ```typescript
416
- const priority = score > 90 ? "high" : score > 70 ? "medium" : "low";
417
- ```
418
-
419
- ### Readonly Types for Function Parameters
420
-
421
- ```typescript
422
- export const calculateTeamGprRank = (
423
- leaguePlayers: readonly (PlayerWithScores | null | undefined)[],
424
- myTeamId: string | null | undefined
425
- ): number | null => {
426
- // ...
427
- };
428
- ```