@codyswann/lisa 1.0.0 → 1.0.5

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 (280) hide show
  1. package/README.md +244 -36
  2. package/all/copy-overwrite/.claude/README.md +1 -3
  3. package/all/copy-overwrite/.claude/REFERENCE.md +519 -0
  4. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +7 -7
  5. package/all/copy-overwrite/.claude/agents/test-coverage-agent.md +17 -0
  6. package/all/copy-overwrite/.claude/commands/git/commit.md +9 -5
  7. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +1 -1
  8. package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +209 -0
  9. package/all/copy-overwrite/.claude/commands/project/add-test-coverage.md +58 -0
  10. package/all/copy-overwrite/.claude/commands/project/archive.md +1 -1
  11. package/all/copy-overwrite/.claude/commands/project/complete-task.md +53 -1
  12. package/all/copy-overwrite/.claude/commands/project/debrief.md +12 -23
  13. package/all/copy-overwrite/.claude/commands/project/execute.md +33 -77
  14. package/all/copy-overwrite/.claude/commands/project/fix-linter-error.md +87 -0
  15. package/all/copy-overwrite/.claude/commands/project/implement.md +24 -28
  16. package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +30 -55
  17. package/all/copy-overwrite/.claude/commands/project/plan.md +87 -242
  18. package/all/copy-overwrite/.claude/commands/project/reduce-max-lines-per-function.md +76 -0
  19. package/all/copy-overwrite/.claude/commands/project/reduce-max-lines.md +75 -0
  20. package/all/copy-overwrite/.claude/commands/project/research.md +86 -188
  21. package/all/copy-overwrite/.claude/commands/project/review.md +19 -38
  22. package/all/copy-overwrite/.claude/commands/project/setup.md +1 -1
  23. package/all/copy-overwrite/.claude/commands/project/verify.md +62 -25
  24. package/all/copy-overwrite/.claude/commands/pull-request/review.md +25 -7
  25. package/all/copy-overwrite/.claude/commands/tasks/load.md +63 -0
  26. package/all/copy-overwrite/.claude/commands/tasks/sync.md +84 -0
  27. package/all/copy-overwrite/.claude/hooks/README.md +75 -0
  28. package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +61 -0
  29. package/all/copy-overwrite/.claude/hooks/debug-hook.sh +47 -0
  30. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +2 -0
  31. package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +95 -0
  32. package/all/copy-overwrite/.claude/{skills/coding-philosophy/SKILL.md → rules/coding-philosophy.md} +93 -70
  33. package/all/copy-overwrite/.claude/settings.json +35 -14
  34. package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +41 -9
  35. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +2 -0
  36. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +2 -0
  37. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +2 -0
  38. package/all/copy-overwrite/.safety-net.json +25 -0
  39. package/all/copy-overwrite/CLAUDE.md +8 -30
  40. package/all/copy-overwrite/HUMAN.md +517 -17
  41. package/all/create-only/.claude/rules/PROJECT_RULES.md +9 -0
  42. package/all/create-only/scripts/setup-deploy-key.sh +190 -0
  43. package/all/deletions.json +5 -0
  44. package/cdk/copy-overwrite/.github/workflows/ci.yml +142 -0
  45. package/cdk/copy-overwrite/.github/workflows/deploy.yml +59 -0
  46. package/cdk/copy-overwrite/eslint.cdk.ts +175 -0
  47. package/cdk/copy-overwrite/eslint.config.ts +51 -0
  48. package/cdk/copy-overwrite/eslint.slow.config.ts +80 -0
  49. package/cdk/copy-overwrite/knip.json +53 -0
  50. package/cdk/copy-overwrite/tsconfig.eslint.json +11 -0
  51. package/cdk/merge/package.json +17 -1
  52. package/dist/cli/index.d.ts +3 -2
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +83 -64
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/cli/prompts.d.ts +17 -3
  57. package/dist/cli/prompts.d.ts.map +1 -1
  58. package/dist/cli/prompts.js +52 -16
  59. package/dist/cli/prompts.js.map +1 -1
  60. package/dist/core/config.d.ts +13 -4
  61. package/dist/core/config.d.ts.map +1 -1
  62. package/dist/core/config.js +17 -9
  63. package/dist/core/config.js.map +1 -1
  64. package/dist/core/git-service.d.ts +40 -0
  65. package/dist/core/git-service.d.ts.map +1 -0
  66. package/dist/core/git-service.js +52 -0
  67. package/dist/core/git-service.js.map +1 -0
  68. package/dist/core/index.d.ts +3 -3
  69. package/dist/core/index.js +3 -3
  70. package/dist/core/lisa.d.ts +124 -7
  71. package/dist/core/lisa.d.ts.map +1 -1
  72. package/dist/core/lisa.js +423 -221
  73. package/dist/core/lisa.js.map +1 -1
  74. package/dist/core/manifest.d.ts +5 -1
  75. package/dist/core/manifest.d.ts.map +1 -1
  76. package/dist/core/manifest.js +22 -16
  77. package/dist/core/manifest.js.map +1 -1
  78. package/dist/detection/detector.interface.d.ts +1 -1
  79. package/dist/detection/detectors/cdk.d.ts +6 -1
  80. package/dist/detection/detectors/cdk.d.ts.map +1 -1
  81. package/dist/detection/detectors/cdk.js +16 -8
  82. package/dist/detection/detectors/cdk.js.map +1 -1
  83. package/dist/detection/detectors/expo.d.ts +6 -1
  84. package/dist/detection/detectors/expo.d.ts.map +1 -1
  85. package/dist/detection/detectors/expo.js +13 -8
  86. package/dist/detection/detectors/expo.js.map +1 -1
  87. package/dist/detection/detectors/nestjs.d.ts +7 -2
  88. package/dist/detection/detectors/nestjs.d.ts.map +1 -1
  89. package/dist/detection/detectors/nestjs.js +17 -9
  90. package/dist/detection/detectors/nestjs.js.map +1 -1
  91. package/dist/detection/detectors/npm-package.d.ts +6 -1
  92. package/dist/detection/detectors/npm-package.d.ts.map +1 -1
  93. package/dist/detection/detectors/npm-package.js +9 -4
  94. package/dist/detection/detectors/npm-package.js.map +1 -1
  95. package/dist/detection/detectors/typescript.d.ts +6 -1
  96. package/dist/detection/detectors/typescript.d.ts.map +1 -1
  97. package/dist/detection/detectors/typescript.js +12 -7
  98. package/dist/detection/detectors/typescript.js.map +1 -1
  99. package/dist/detection/index.d.ts +13 -3
  100. package/dist/detection/index.d.ts.map +1 -1
  101. package/dist/detection/index.js +17 -7
  102. package/dist/detection/index.js.map +1 -1
  103. package/dist/errors/index.d.ts +66 -2
  104. package/dist/errors/index.d.ts.map +1 -1
  105. package/dist/errors/index.js +89 -17
  106. package/dist/errors/index.js.map +1 -1
  107. package/dist/index.js +3 -3
  108. package/dist/index.js.map +1 -1
  109. package/dist/logging/console-logger.d.ts +21 -1
  110. package/dist/logging/console-logger.d.ts.map +1 -1
  111. package/dist/logging/console-logger.js +26 -6
  112. package/dist/logging/console-logger.js.map +1 -1
  113. package/dist/logging/index.d.ts +3 -3
  114. package/dist/logging/index.js +2 -2
  115. package/dist/logging/logger.interface.d.ts +1 -1
  116. package/dist/logging/silent-logger.d.ts +21 -1
  117. package/dist/logging/silent-logger.d.ts.map +1 -1
  118. package/dist/logging/silent-logger.js +20 -0
  119. package/dist/logging/silent-logger.js.map +1 -1
  120. package/dist/strategies/copy-contents.d.ts +47 -6
  121. package/dist/strategies/copy-contents.d.ts.map +1 -1
  122. package/dist/strategies/copy-contents.js +99 -49
  123. package/dist/strategies/copy-contents.js.map +1 -1
  124. package/dist/strategies/copy-overwrite.d.ts +10 -2
  125. package/dist/strategies/copy-overwrite.d.ts.map +1 -1
  126. package/dist/strategies/copy-overwrite.js +17 -9
  127. package/dist/strategies/copy-overwrite.js.map +1 -1
  128. package/dist/strategies/create-only.d.ts +10 -2
  129. package/dist/strategies/create-only.d.ts.map +1 -1
  130. package/dist/strategies/create-only.js +14 -6
  131. package/dist/strategies/create-only.js.map +1 -1
  132. package/dist/strategies/index.d.ts +17 -7
  133. package/dist/strategies/index.d.ts.map +1 -1
  134. package/dist/strategies/index.js +19 -9
  135. package/dist/strategies/index.js.map +1 -1
  136. package/dist/strategies/merge.d.ts +10 -2
  137. package/dist/strategies/merge.d.ts.map +1 -1
  138. package/dist/strategies/merge.js +21 -21
  139. package/dist/strategies/merge.js.map +1 -1
  140. package/dist/strategies/strategy.interface.d.ts +1 -1
  141. package/dist/strategies/strategy.interface.d.ts.map +1 -1
  142. package/dist/transaction/backup.d.ts +15 -1
  143. package/dist/transaction/backup.d.ts.map +1 -1
  144. package/dist/transaction/backup.js +47 -12
  145. package/dist/transaction/backup.js.map +1 -1
  146. package/dist/transaction/index.d.ts +3 -3
  147. package/dist/transaction/index.js +2 -2
  148. package/dist/transaction/transaction.d.ts +25 -2
  149. package/dist/transaction/transaction.d.ts.map +1 -1
  150. package/dist/transaction/transaction.js +25 -2
  151. package/dist/transaction/transaction.js.map +1 -1
  152. package/dist/utils/file-operations.d.ts +21 -0
  153. package/dist/utils/file-operations.d.ts.map +1 -1
  154. package/dist/utils/file-operations.js +48 -12
  155. package/dist/utils/file-operations.js.map +1 -1
  156. package/dist/utils/index.d.ts +3 -3
  157. package/dist/utils/index.js +3 -3
  158. package/dist/utils/json-utils.d.ts +12 -0
  159. package/dist/utils/json-utils.d.ts.map +1 -1
  160. package/dist/utils/json-utils.js +17 -5
  161. package/dist/utils/json-utils.js.map +1 -1
  162. package/dist/utils/path-utils.d.ts +11 -0
  163. package/dist/utils/path-utils.d.ts.map +1 -1
  164. package/dist/utils/path-utils.js +12 -1
  165. package/dist/utils/path-utils.js.map +1 -1
  166. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +5 -0
  167. package/eslint-plugin-code-organization/index.js +5 -0
  168. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +5 -0
  169. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +2 -0
  170. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +2 -0
  171. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +2 -0
  172. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +2 -0
  173. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +2 -0
  174. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +2 -0
  175. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +2 -41
  176. package/{typescript → expo}/copy-overwrite/.github/workflows/build.yml +3 -0
  177. package/expo/copy-overwrite/.github/workflows/ci.yml +36 -0
  178. package/{typescript → expo}/copy-overwrite/.github/workflows/deploy.yml +22 -26
  179. package/{typescript → expo}/copy-overwrite/.github/workflows/lighthouse.yml +4 -1
  180. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +5 -0
  181. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +5 -0
  182. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +5 -0
  183. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +5 -0
  184. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +5 -0
  185. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +6 -1
  186. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +5 -0
  187. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +5 -0
  188. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -68
  189. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +5 -3
  190. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +5 -0
  191. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +5 -0
  192. package/expo/copy-overwrite/eslint.config.ts +53 -0
  193. package/expo/copy-overwrite/eslint.expo.ts +330 -0
  194. package/expo/copy-overwrite/eslint.slow.config.ts +86 -0
  195. package/expo/copy-overwrite/knip.json +132 -0
  196. package/expo/copy-overwrite/lighthouserc.js +27 -0
  197. package/expo/copy-overwrite/tsconfig.eslint.json +25 -0
  198. package/expo/create-only/lighthouserc-config.json +6 -1
  199. package/expo/merge/package.json +16 -3
  200. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +1 -1
  201. package/{typescript → nestjs}/copy-overwrite/.github/k6/README.md +2 -2
  202. package/{typescript → nestjs}/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +3 -0
  203. package/{typescript → nestjs}/copy-overwrite/.github/k6/examples/data-driven-test.js +5 -0
  204. package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/load.js +6 -2
  205. package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/smoke.js +5 -0
  206. package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/soak.js +5 -0
  207. package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/spike.js +5 -0
  208. package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/stress.js +5 -0
  209. package/{typescript → nestjs}/copy-overwrite/.github/k6/scripts/api-test.js +5 -0
  210. package/{typescript → nestjs}/copy-overwrite/.github/k6/scripts/default-test.js +5 -0
  211. package/nestjs/copy-overwrite/.github/workflows/ci.yml +29 -0
  212. package/nestjs/copy-overwrite/.github/workflows/deploy.yml +291 -0
  213. package/{typescript → nestjs}/copy-overwrite/.github/workflows/load-test.yml +3 -0
  214. package/nestjs/copy-overwrite/eslint.config.ts +53 -0
  215. package/nestjs/copy-overwrite/eslint.nestjs.ts +178 -0
  216. package/nestjs/merge/package.json +11 -3
  217. package/package.json +34 -40
  218. package/typescript/copy-contents/.husky/pre-commit +1 -1
  219. package/typescript/copy-contents/.husky/pre-push +99 -118
  220. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +2 -0
  221. package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +3 -11
  222. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +2 -0
  223. package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +68 -0
  224. package/typescript/copy-overwrite/.claude/settings.json +79 -0
  225. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +44 -0
  226. package/typescript/copy-overwrite/.github/README.md +49 -1
  227. package/typescript/copy-overwrite/.github/dependabot.yml +3 -0
  228. package/typescript/copy-overwrite/.github/workflows/ci.yml +7 -29
  229. package/typescript/copy-overwrite/.github/workflows/claude.yml +3 -0
  230. package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +6 -4
  231. package/typescript/copy-overwrite/.github/workflows/create-issue-on-failure.yml +176 -0
  232. package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +3 -1
  233. package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +3 -1
  234. package/typescript/copy-overwrite/.github/workflows/lint-slow.yml +40 -0
  235. package/typescript/copy-overwrite/.github/workflows/quality.yml +151 -38
  236. package/typescript/copy-overwrite/.github/workflows/release.yml +3 -0
  237. package/typescript/copy-overwrite/.gitleaksignore +3 -0
  238. package/typescript/copy-overwrite/.lintstagedrc.json +6 -0
  239. package/typescript/copy-overwrite/.prettierignore +2 -1
  240. package/typescript/copy-overwrite/.yamllint +2 -0
  241. package/typescript/copy-overwrite/ast-grep/rule-tests/.gitkeep +3 -0
  242. package/typescript/copy-overwrite/ast-grep/rules/.gitkeep +3 -0
  243. package/typescript/copy-overwrite/ast-grep/utils/.gitkeep +3 -0
  244. package/typescript/copy-overwrite/{commitlint.config.js → commitlint.config.cjs} +5 -0
  245. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +5 -0
  246. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +5 -0
  247. package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +5 -0
  248. package/typescript/copy-overwrite/eslint.base.ts +430 -0
  249. package/typescript/copy-overwrite/eslint.config.ts +52 -0
  250. package/typescript/copy-overwrite/eslint.ignore.config.json +19 -2
  251. package/typescript/copy-overwrite/eslint.slow.config.ts +69 -0
  252. package/typescript/copy-overwrite/eslint.typescript.ts +142 -0
  253. package/typescript/copy-overwrite/knip.json +64 -0
  254. package/typescript/copy-overwrite/sgconfig.yml +11 -0
  255. package/typescript/copy-overwrite/tsconfig.eslint.json +9 -0
  256. package/typescript/create-only/eslint.config.local.ts +24 -0
  257. package/typescript/{copy-overwrite/eslint.thresholds.config.json → create-only/eslint.thresholds.json} +1 -1
  258. package/typescript/github-rulesets/base.json +2 -75
  259. package/typescript/merge/.claude/settings.json +160 -0
  260. package/typescript/merge/package.json +35 -34
  261. package/all/copy-overwrite/.claude/commands/rules/format-md.md +0 -72
  262. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +0 -416
  263. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +0 -316
  264. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +0 -73
  265. package/expo/copy-overwrite/eslint.config.mjs +0 -560
  266. package/lisa.sh +0 -35
  267. package/typescript/copy-overwrite/eslint.config.mjs +0 -390
  268. /package/{all/create-only/PROJECT_RULES.md → cdk/copy-overwrite/.github/workflows/.keep} +0 -0
  269. /package/{typescript → nestjs}/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +0 -0
  270. /package/{typescript → nestjs}/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +0 -0
  271. /package/{typescript → nestjs}/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +0 -0
  272. /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/load.json +0 -0
  273. /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/smoke.json +0 -0
  274. /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/soak.json +0 -0
  275. /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/spike.json +0 -0
  276. /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/stress.json +0 -0
  277. /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/normal.json +0 -0
  278. /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/relaxed.json +0 -0
  279. /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/strict.json +0 -0
  280. /package/{typescript → nestjs}/copy-overwrite/.github/workflows/k6-load-test-README.md +0 -0
@@ -0,0 +1,63 @@
1
+ ---
2
+ description: Load tasks from a project directory into the current session
3
+ argument-hint: <project-name>
4
+ allowed-tools: Read, Bash, TaskCreate, TaskUpdate, TaskList
5
+ ---
6
+
7
+ # Load Project Tasks
8
+
9
+ Load tasks from `projects/$ARGUMENTS/tasks/` into the current Claude Code session.
10
+
11
+ ## Process
12
+
13
+ ### Step 1: Validate Project
14
+
15
+ Check if the project exists:
16
+
17
+ ```bash
18
+ ls projects/$ARGUMENTS/tasks/*.json 2>/dev/null
19
+ ```
20
+
21
+ If no task files exist, report: "No tasks found in projects/$ARGUMENTS/tasks/"
22
+
23
+ ### Step 2: Set Active Project
24
+
25
+ Create the active project marker:
26
+
27
+ ```bash
28
+ echo "$ARGUMENTS" > .claude-active-project
29
+ ```
30
+
31
+ This ensures any new tasks created will sync back to this project.
32
+
33
+ ### Step 3: Load Tasks
34
+
35
+ For each JSON file in `projects/$ARGUMENTS/tasks/`:
36
+
37
+ 1. Read the task JSON file
38
+ 2. Use TaskCreate to recreate the task with:
39
+ - subject from JSON
40
+ - description from JSON
41
+ - activeForm from JSON
42
+ - metadata: `{ "project": "$ARGUMENTS" }`
43
+ 3. If the task was already completed (status: "completed"), use TaskUpdate to mark it completed
44
+
45
+ ### Step 4: Report
46
+
47
+ After loading all tasks, report:
48
+
49
+ ```
50
+ Loaded X tasks from projects/$ARGUMENTS/tasks/
51
+ - Pending: Y
52
+ - Completed: Z
53
+
54
+ Active project set to: $ARGUMENTS
55
+ New tasks will automatically sync to this project.
56
+ ```
57
+
58
+ ## Notes
59
+
60
+ - Tasks are recreated with new IDs in the current session
61
+ - The original task IDs from the project are not preserved
62
+ - Task dependencies (blocks/blockedBy) are NOT currently preserved across load/sync cycles
63
+ - Use TaskList to see the loaded tasks
@@ -0,0 +1,84 @@
1
+ ---
2
+ description: Sync current session tasks to a project directory
3
+ argument-hint: <project-name>
4
+ allowed-tools: Read, Write, Bash, TaskList, TaskGet
5
+ ---
6
+
7
+ # Sync Tasks to Project
8
+
9
+ Sync all tasks from the current session to `projects/$ARGUMENTS/tasks/`.
10
+
11
+ ## Process
12
+
13
+ ### Step 1: Validate Project
14
+
15
+ Check if the project directory exists:
16
+
17
+ ```bash
18
+ ls -d projects/$ARGUMENTS 2>/dev/null
19
+ ```
20
+
21
+ If the project doesn't exist, ask: "Project '$ARGUMENTS' doesn't exist. Create it?"
22
+
23
+ If yes, create the project structure:
24
+
25
+ ```bash
26
+ mkdir -p projects/$ARGUMENTS/tasks
27
+ ```
28
+
29
+ ### Step 2: Set Active Project
30
+
31
+ Create/update the active project marker:
32
+
33
+ ```bash
34
+ echo "$ARGUMENTS" > .claude-active-project
35
+ ```
36
+
37
+ ### Step 3: Get Current Tasks
38
+
39
+ Use TaskList to get all tasks in the current session.
40
+
41
+ ### Step 4: Sync Each Task
42
+
43
+ For each task from TaskList:
44
+
45
+ 1. Use TaskGet to get full task details
46
+ 2. Create a JSON file with the task data:
47
+
48
+ ```json
49
+ {
50
+ "id": "<task-id>",
51
+ "subject": "<subject>",
52
+ "description": "<description>",
53
+ "activeForm": "<activeForm>",
54
+ "status": "<status>",
55
+ "blocks": [],
56
+ "blockedBy": []
57
+ }
58
+ ```
59
+
60
+ 3. Write to `projects/$ARGUMENTS/tasks/<id>.json`
61
+
62
+ ### Step 5: Stage for Git
63
+
64
+ ```bash
65
+ git add projects/$ARGUMENTS/tasks/*.json
66
+ ```
67
+
68
+ ### Step 6: Report
69
+
70
+ ```
71
+ Synced X tasks to projects/$ARGUMENTS/tasks/
72
+ - Pending: Y
73
+ - In Progress: Z
74
+ - Completed: W
75
+
76
+ Files staged for commit. Run /git:commit when ready.
77
+ ```
78
+
79
+ ## Notes
80
+
81
+ - This command manually syncs all current tasks to a project
82
+ - Use this when you started work without a project context
83
+ - After syncing, the active project is set so future tasks auto-sync
84
+ - Existing task files in the project directory will be overwritten
@@ -154,6 +154,81 @@ Note: The hooks run in order. ESLint runs first to fix linting issues, then Pret
154
154
  - [Claude Code Hooks Guide](https://docs.claude.com/en/docs/claude-code/hooks-guide)
155
155
  - [Hooks Reference](https://docs.claude.com/en/docs/claude-code/hooks)
156
156
 
157
+ ### check-tired-boss.sh
158
+
159
+ **Type**: Stop hook (blocking)
160
+ **Trigger**: When Claude finishes responding
161
+ **Purpose**: Verifies Claude's response starts with "I'm tired boss" as required by CLAUDE.md
162
+
163
+ #### How it works
164
+
165
+ 1. The hook is triggered when Claude finishes responding (Stop event)
166
+ 2. Reads the transcript file to get Claude's last assistant message
167
+ 3. Checks if the response starts with "I'm tired boss"
168
+ 4. If compliant, allows the response to complete normally
169
+ 5. If non-compliant, blocks the Stop event with an error message telling Claude to read CLAUDE.md and try again
170
+
171
+ #### Why this hook exists
172
+
173
+ CLAUDE.md requires: "Always output 'I'm tired boss' before starting any task, request or anything else."
174
+
175
+ This hook enforces that rule by checking every response and forcing Claude to retry if it doesn't comply.
176
+
177
+ #### Configuration
178
+
179
+ The hook is configured in `.claude/settings.json` and runs before the notification hook:
180
+
181
+ ```json
182
+ {
183
+ "hooks": {
184
+ "UserPromptSubmit": [
185
+ {
186
+ "matcher": "",
187
+ "hooks": [
188
+ {
189
+ "type": "command",
190
+ "command": "echo 'REMINDER: Start your response with \"I'\\''m tired boss\" as required by CLAUDE.md.'",
191
+ "timeout": 1
192
+ }
193
+ ]
194
+ }
195
+ ],
196
+ "Stop": [
197
+ {
198
+ "matcher": "",
199
+ "hooks": [
200
+ {
201
+ "type": "command",
202
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-tired-boss.sh",
203
+ "timeout": 5
204
+ },
205
+ {
206
+ "type": "command",
207
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/notify-ntfy.sh",
208
+ "timeout": 5
209
+ }
210
+ ]
211
+ }
212
+ ]
213
+ }
214
+ }
215
+ ```
216
+
217
+ #### Features
218
+
219
+ - **Blocking enforcement**: Non-compliant responses are blocked with clear feedback
220
+ - **Reminder injection**: UserPromptSubmit hook reminds Claude before it responds
221
+ - **Cross-platform**: Uses POSIX-compatible shell commands (works on macOS and Linux)
222
+ - **Graceful fallback**: Uses jq for JSON parsing when available, falls back to grep/sed
223
+ - **Order-aware**: Runs before notification hook so failed attempts don't trigger "finished" notifications
224
+
225
+ #### Limitations
226
+
227
+ - The user will see the non-compliant response before Claude is forced to retry
228
+ - Works through after-the-fact enforcement rather than prevention (no PreResponse hook exists)
229
+
230
+ ---
231
+
157
232
  ### notify-ntfy.sh
158
233
 
159
234
  **Type**: Notification and Stop hooks
@@ -0,0 +1,61 @@
1
+ #!/bin/bash
2
+ # This file is managed by Lisa.
3
+ # Do not edit directly — changes will be overwritten on the next `lisa` run.
4
+ # =============================================================================
5
+ # CLAUDE.md Compliance Hook - "I'm tired boss" Verification
6
+ # =============================================================================
7
+ # Verifies Claude's response starts with "I'm tired boss" as required by
8
+ # CLAUDE.md. This is a Stop hook that blocks if Claude doesn't comply,
9
+ # forcing a retry with an error message.
10
+ #
11
+ # @see CLAUDE.md - "Always output 'I'm tired boss' before starting any task"
12
+ # =============================================================================
13
+
14
+ # Read JSON input from stdin
15
+ INPUT=$(cat)
16
+
17
+ # Extract transcript path
18
+ TRANSCRIPT_PATH=$(echo "$INPUT" | grep -o '"transcript_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
19
+
20
+ # Exit silently if no transcript available
21
+ if [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
22
+ exit 0
23
+ fi
24
+
25
+ # Extract the last assistant message from the transcript
26
+ # Use awk for cross-platform compatibility (tac not available on macOS)
27
+ LAST_ASSISTANT=$(awk '/"type"[[:space:]]*:[[:space:]]*"assistant"/{line=$0} END{if(line) print line}' "$TRANSCRIPT_PATH" 2>/dev/null)
28
+
29
+ if [ -z "$LAST_ASSISTANT" ]; then
30
+ exit 0
31
+ fi
32
+
33
+ # Extract the text content from the assistant message
34
+ # Use jq for robust JSON parsing when available, fallback to grep/sed
35
+ RESPONSE_TEXT=""
36
+ if command -v jq >/dev/null 2>&1; then
37
+ RESPONSE_TEXT=$(echo "$LAST_ASSISTANT" | jq -r '.message.content[] | select(.type == "text") | .text' 2>/dev/null | head -1)
38
+ else
39
+ # Fallback: simple regex extraction (may fail on escaped quotes)
40
+ RESPONSE_TEXT=$(echo "$LAST_ASSISTANT" | grep -o '"text"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*: *"//' | sed 's/"$//')
41
+ fi
42
+
43
+ # Exit if no text content found
44
+ if [ -z "$RESPONSE_TEXT" ]; then
45
+ exit 0
46
+ fi
47
+
48
+ # Check if response starts with "I'm tired boss" (case-sensitive)
49
+ REQUIRED_PHRASE="I'm tired boss"
50
+ if echo "$RESPONSE_TEXT" | head -1 | grep -q "^$REQUIRED_PHRASE"; then
51
+ # Compliance verified
52
+ exit 0
53
+ fi
54
+
55
+ # Non-compliant - block and provide feedback
56
+ # Output JSON to block the Stop event
57
+ cat << 'EOF'
58
+ {"decision":"block","reason":"Your response did not start with \"I'm tired boss\". Read @CLAUDE.md and try again. Every response MUST begin with this exact phrase."}
59
+ EOF
60
+
61
+ exit 0
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ ##
3
+ # Debug hook that logs all hook events when CLAUDE_DEBUG=1
4
+ # This script is a no-op when CLAUDE_DEBUG is not set or set to 0
5
+ ##
6
+
7
+ # Exit immediately if debug mode is not enabled
8
+ if [[ "${CLAUDE_DEBUG:-0}" != "1" ]]; then
9
+ exit 0
10
+ fi
11
+
12
+ # Read JSON input from stdin
13
+ INPUT=$(cat)
14
+
15
+ # Parse hook event info using jq
16
+ HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"')
17
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "N/A"')
18
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
19
+ CWD=$(echo "$INPUT" | jq -r '.cwd // "unknown"')
20
+ PERMISSION_MODE=$(echo "$INPUT" | jq -r '.permission_mode // "unknown"')
21
+
22
+ # Create debug log directory if it doesn't exist
23
+ DEBUG_LOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/debug"
24
+ mkdir -p "$DEBUG_LOG_DIR"
25
+
26
+ # Log file with session ID
27
+ LOG_FILE="$DEBUG_LOG_DIR/hooks-${SESSION_ID}.log"
28
+
29
+ # Timestamp
30
+ TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
31
+
32
+ # Log the event
33
+ {
34
+ echo "[$TIMESTAMP] Hook: $HOOK_EVENT"
35
+ echo " Tool: $TOOL_NAME"
36
+ echo " Session: $SESSION_ID"
37
+ echo " CWD: $CWD"
38
+ echo " Permission Mode: $PERMISSION_MODE"
39
+ echo " Full Input:"
40
+ echo "$INPUT" | jq '.' | sed 's/^/ /'
41
+ echo "---"
42
+ } >> "$LOG_FILE"
43
+
44
+ # Also output to stderr in verbose mode (won't affect Claude)
45
+ echo "[DEBUG] Hook fired: $HOOK_EVENT (tool: $TOOL_NAME)" >&2
46
+
47
+ exit 0
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ # This file is managed by Lisa.
3
+ # Do not edit directly — changes will be overwritten on the next `lisa` run.
2
4
  # =============================================================================
3
5
  # ntfy.sh Notification Hook for Claude Code
4
6
  # =============================================================================
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # sync-tasks.sh - Syncs Claude Code tasks to project directories
4
+ #
5
+ # This hook is triggered on PostToolUse for TaskCreate and TaskUpdate.
6
+ # It reads the task metadata to determine the project and syncs
7
+ # task JSON files to ./projects/{project}/tasks/
8
+ #
9
+ # Input (via stdin): JSON with tool_name, tool_input, tool_output
10
+ #
11
+
12
+ set -euo pipefail
13
+
14
+ # Read JSON input from stdin
15
+ INPUT=$(cat)
16
+
17
+ # Extract tool name
18
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
19
+
20
+ # Only process TaskCreate and TaskUpdate
21
+ if [[ "$TOOL_NAME" != "TaskCreate" && "$TOOL_NAME" != "TaskUpdate" ]]; then
22
+ exit 0
23
+ fi
24
+
25
+ # Try to get project from multiple sources:
26
+ # 1. Task metadata (passed in tool_input)
27
+ # 2. .claude-active-project marker file
28
+
29
+ PROJECT=""
30
+
31
+ # Check tool_input metadata for project
32
+ PROJECT=$(echo "$INPUT" | jq -r '.tool_input.metadata.project // empty')
33
+
34
+ # If no project in metadata, check marker file
35
+ if [[ -z "$PROJECT" && -f ".claude-active-project" ]]; then
36
+ PROJECT=$(cat .claude-active-project | tr -d '[:space:]')
37
+ fi
38
+
39
+ # If still no project, skip syncing
40
+ if [[ -z "$PROJECT" ]]; then
41
+ exit 0
42
+ fi
43
+
44
+ # Validate project name (kebab-case, no path traversal)
45
+ if [[ ! "$PROJECT" =~ ^[a-z0-9-]+$ ]]; then
46
+ echo "Warning: Invalid project name '$PROJECT', skipping sync" >&2
47
+ exit 0
48
+ fi
49
+
50
+ # Get task ID
51
+ TASK_ID=""
52
+ if [[ "$TOOL_NAME" == "TaskCreate" ]]; then
53
+ # For TaskCreate, ID is in tool_output
54
+ TASK_ID=$(echo "$INPUT" | jq -r '.tool_output.taskId // .tool_output.id // empty')
55
+ elif [[ "$TOOL_NAME" == "TaskUpdate" ]]; then
56
+ # For TaskUpdate, ID is in tool_input
57
+ TASK_ID=$(echo "$INPUT" | jq -r '.tool_input.taskId // empty')
58
+ fi
59
+
60
+ if [[ -z "$TASK_ID" ]]; then
61
+ exit 0
62
+ fi
63
+
64
+ # Find the task file in ~/.claude/tasks/
65
+ # Tasks are stored in ~/.claude/tasks/{session-uuid}/{id}.json
66
+ CLAUDE_TASKS_DIR="${HOME}/.claude/tasks"
67
+ TASK_FILE=""
68
+
69
+ # Get session ID from hook input (preferred - 100% accurate)
70
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
71
+
72
+ if [[ -n "$SESSION_ID" && -f "${CLAUDE_TASKS_DIR}/${SESSION_ID}/${TASK_ID}.json" ]]; then
73
+ # Use session ID directly - guaranteed correct session
74
+ TASK_FILE="${CLAUDE_TASKS_DIR}/${SESSION_ID}/${TASK_ID}.json"
75
+ else
76
+ # Fallback: find most recently modified task file with this ID
77
+ # This handles edge cases where session_id isn't available
78
+ TASK_FILE=$(find "$CLAUDE_TASKS_DIR" -name "${TASK_ID}.json" -exec stat -f '%m %N' {} \; 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
79
+ fi
80
+
81
+ if [[ -z "$TASK_FILE" || ! -f "$TASK_FILE" ]]; then
82
+ exit 0
83
+ fi
84
+
85
+ # Ensure project tasks directory exists
86
+ PROJECT_TASKS_DIR="./projects/${PROJECT}/tasks"
87
+ mkdir -p "$PROJECT_TASKS_DIR"
88
+
89
+ # Copy task file to project directory
90
+ cp "$TASK_FILE" "${PROJECT_TASKS_DIR}/${TASK_ID}.json"
91
+
92
+ # Optionally stage the file for git (non-blocking)
93
+ git add "${PROJECT_TASKS_DIR}/${TASK_ID}.json" 2>/dev/null || true
94
+
95
+ exit 0
@@ -1,13 +1,6 @@
1
- ---
2
- name: coding-philosophy
3
- description: Enforces immutable coding principles, function structure ordering, functional programming patterns, TDD, clean deletion, and YAGNI+SOLID+DRY+KISS principles for this codebase. This skill should be used when writing or reviewing TypeScript/React code in this project, particularly when creating hooks, utility functions, or components. Use this skill to ensure code follows the established patterns for immutability, proper ordering of statements, functional transformations, test-driven development, and simplicity (preferring Occam's Razor/KISS when principles conflict).
4
- ---
5
-
6
1
  # Coding Philosophy
7
2
 
8
- ## Overview
9
-
10
- This skill enforces the core coding philosophy for this project: **immutability**, **predictable structure**, **functional transformations**, **test-driven development**, **clean deletion**, and **simplicity**. All code should follow these principles to maintain consistency, testability, and clarity.
3
+ This rule enforces the core coding philosophy: **immutability**, **predictable structure**, **functional transformations**, **test-driven development**, **clean deletion**, and **simplicity**.
11
4
 
12
5
  ## Guiding Principles: YAGNI + SOLID + DRY + KISS
13
6
 
@@ -15,7 +8,7 @@ Follow these software engineering principles, **deferring to Occam's Razor/KISS
15
8
 
16
9
  ### KISS (Keep It Simple, Stupid) - The Tiebreaker
17
10
 
18
- When principles conflict, **always choose the simpler solution**. Occam's Razor applies to code: the simplest solution that works is usually correct.
11
+ When principles conflict, **always choose the simpler solution**.
19
12
 
20
13
  ```typescript
21
14
  // KISS: Simple direct approach
@@ -46,18 +39,8 @@ Extract duplication only when:
46
39
  2. The abstraction is **simpler** than the duplication
47
40
  3. The extracted code has a **clear single purpose**
48
41
 
49
- ```typescript
50
- // DRY + KISS: Extract when clearly beneficial
51
- const formatPlayerName = (first: string, last: string) => `${first} ${last}`;
52
-
53
- // Anti-pattern: Premature abstraction for 2 usages
54
- // Keep inline if simpler and only used twice
55
- ```
56
-
57
42
  ### SOLID Principles - Applied Pragmatically
58
43
 
59
- Apply SOLID when it **reduces complexity**, not dogmatically:
60
-
61
44
  | Principle | Apply When | Skip When |
62
45
  | ------------------------- | --------------------------------------------- | --------------------------------------- |
63
46
  | **S**ingle Responsibility | Function does 2+ unrelated things | Splitting adds complexity |
@@ -66,21 +49,15 @@ Apply SOLID when it **reduces complexity**, not dogmatically:
66
49
  | **I**nterface Segregation | Consumers need different subsets | Interface is already small |
67
50
  | **D**ependency Inversion | Testing requires mocking external services | Direct dependency is simpler |
68
51
 
69
- ```typescript
70
- // Good SRP: Each function has one job
71
- const validateEmail = (email: string) => EMAIL_REGEX.test(email);
72
- const formatEmail = (email: string) => email.toLowerCase().trim();
73
-
74
- // Over-applied SRP: Don't split a simple 3-line function into 3 files
75
- ```
76
-
77
52
  ### Decision Framework
78
53
 
79
54
  When unsure, ask in order:
80
- 1. **Do I need this now?** (YAGNI) If no, don't build it
81
- 2. **Is there a simpler way?** (KISS) Choose the simpler option
82
- 3. **Am I repeating myself 3+ times?** (DRY) Extract if the abstraction is simpler
83
- 4. **Does this function do one thing?** (SOLID-SRP) Split only if clearer
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
+ ---
84
61
 
85
62
  ## Core Principles
86
63
 
@@ -100,7 +77,7 @@ user.name = "New Name";
100
77
 
101
78
  All functions, hooks, and components follow a strict ordering:
102
79
 
103
- ```
80
+ ```text
104
81
  1. Variable definitions and derived state (const, useState, useMemo, useCallback)
105
82
  2. Side effects (useEffect, function calls with no return value)
106
83
  3. Return statement
@@ -123,49 +100,24 @@ users.forEach(u => names.push(u.name));
123
100
 
124
101
  **Always write failing tests before implementation code.** This is mandatory, not optional.
125
102
 
126
- ```
103
+ ```text
127
104
  TDD Cycle:
128
105
  1. RED: Write a failing test that defines expected behavior
129
106
  2. GREEN: Write the minimum code to make the test pass
130
107
  3. REFACTOR: Clean up while keeping tests green
131
108
  ```
132
109
 
133
- ```typescript
134
- // Step 1: Write the failing test FIRST
135
- describe("formatPlayerName", () => {
136
- it("should format first and last name", () => {
137
- expect(formatPlayerName("John", "Doe")).toBe("John Doe");
138
- });
139
-
140
- it("should handle empty last name", () => {
141
- expect(formatPlayerName("John", "")).toBe("John");
142
- });
143
- });
144
-
145
- // Step 2: THEN write implementation to make tests pass
146
- const formatPlayerName = (first: string, last: string): string =>
147
- last ? `${first} ${last}` : first;
148
- ```
149
-
150
- **TDD is non-negotiable because it:**
151
- - Forces you to think about the API before implementation
152
- - Ensures every feature has test coverage
153
- - Prevents over-engineering (you only write what's needed to pass tests)
154
- - Documents expected behavior
155
-
156
110
  ### 5. Clean Deletion
157
111
 
158
112
  **Delete old code completely.** No deprecation warnings, migration shims, or backward-compatibility layers unless explicitly requested.
159
113
 
160
114
  ```typescript
161
115
  // Correct: Remove the old code entirely
162
- // (Old function is gone, new function exists)
163
116
  const calculateScore = (player: Player): number => player.stats.overall;
164
117
 
165
118
  // Wrong: Keeping deprecated versions around
166
119
  /** @deprecated Use calculateScore instead */
167
120
  const getPlayerScore = (player: Player): number => calculateScore(player);
168
- const calculateScoreV2 = (player: Player): number => player.stats.overall;
169
121
  ```
170
122
 
171
123
  **Clean deletion rules:**
@@ -175,18 +127,7 @@ const calculateScoreV2 = (player: Player): number => player.stats.overall;
175
127
  - Never write migration code unless explicitly asked
176
128
  - Trust git history for recovery if needed
177
129
 
178
- **Why clean deletion:**
179
- - Reduces cognitive load (one way to do things)
180
- - Prevents confusion about which version to use
181
- - Keeps bundle size small
182
- - YAGNI: If no one is using it, delete it
183
-
184
- ## Detailed Guidelines
185
-
186
- For comprehensive examples and patterns, see the reference files:
187
-
188
- - **[references/immutable-patterns.md](references/immutable-patterns.md)** - Detailed immutable patterns with reduce, spread, and functional transformations
189
- - **[references/function-structure.md](references/function-structure.md)** - Function ordering rules and examples for hooks, utilities, and components
130
+ ---
190
131
 
191
132
  ## Quick Reference
192
133
 
@@ -244,6 +185,8 @@ if (isComplete) {
244
185
  }
245
186
  ```
246
187
 
188
+ ---
189
+
247
190
  ## Hook Structure Example
248
191
 
249
192
  ```typescript
@@ -334,6 +277,8 @@ export const calculateTeamRankings = (
334
277
  };
335
278
  ```
336
279
 
280
+ ---
281
+
337
282
  ## Anti-Patterns to Avoid
338
283
 
339
284
  ### Never use `let` for conditional assignment
@@ -403,3 +348,81 @@ useEffect(() => {
403
348
  /* ... */
404
349
  }, [value]);
405
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
+ ```