@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.
- package/README.md +244 -36
- package/all/copy-overwrite/.claude/README.md +1 -3
- package/all/copy-overwrite/.claude/REFERENCE.md +519 -0
- package/all/copy-overwrite/.claude/agents/skill-evaluator.md +7 -7
- package/all/copy-overwrite/.claude/agents/test-coverage-agent.md +17 -0
- package/all/copy-overwrite/.claude/commands/git/commit.md +9 -5
- package/all/copy-overwrite/.claude/commands/git/submit-pr.md +1 -1
- package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +209 -0
- package/all/copy-overwrite/.claude/commands/project/add-test-coverage.md +58 -0
- package/all/copy-overwrite/.claude/commands/project/archive.md +1 -1
- package/all/copy-overwrite/.claude/commands/project/complete-task.md +53 -1
- package/all/copy-overwrite/.claude/commands/project/debrief.md +12 -23
- package/all/copy-overwrite/.claude/commands/project/execute.md +33 -77
- package/all/copy-overwrite/.claude/commands/project/fix-linter-error.md +87 -0
- package/all/copy-overwrite/.claude/commands/project/implement.md +24 -28
- package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +30 -55
- package/all/copy-overwrite/.claude/commands/project/plan.md +87 -242
- package/all/copy-overwrite/.claude/commands/project/reduce-max-lines-per-function.md +76 -0
- package/all/copy-overwrite/.claude/commands/project/reduce-max-lines.md +75 -0
- package/all/copy-overwrite/.claude/commands/project/research.md +86 -188
- package/all/copy-overwrite/.claude/commands/project/review.md +19 -38
- package/all/copy-overwrite/.claude/commands/project/setup.md +1 -1
- package/all/copy-overwrite/.claude/commands/project/verify.md +62 -25
- package/all/copy-overwrite/.claude/commands/pull-request/review.md +25 -7
- package/all/copy-overwrite/.claude/commands/tasks/load.md +63 -0
- package/all/copy-overwrite/.claude/commands/tasks/sync.md +84 -0
- package/all/copy-overwrite/.claude/hooks/README.md +75 -0
- package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +61 -0
- package/all/copy-overwrite/.claude/hooks/debug-hook.sh +47 -0
- package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +2 -0
- package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +95 -0
- package/all/copy-overwrite/.claude/{skills/coding-philosophy/SKILL.md → rules/coding-philosophy.md} +93 -70
- package/all/copy-overwrite/.claude/settings.json +35 -14
- package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +41 -9
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +2 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +2 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +2 -0
- package/all/copy-overwrite/.safety-net.json +25 -0
- package/all/copy-overwrite/CLAUDE.md +8 -30
- package/all/copy-overwrite/HUMAN.md +517 -17
- package/all/create-only/.claude/rules/PROJECT_RULES.md +9 -0
- package/all/create-only/scripts/setup-deploy-key.sh +190 -0
- package/all/deletions.json +5 -0
- package/cdk/copy-overwrite/.github/workflows/ci.yml +142 -0
- package/cdk/copy-overwrite/.github/workflows/deploy.yml +59 -0
- package/cdk/copy-overwrite/eslint.cdk.ts +175 -0
- package/cdk/copy-overwrite/eslint.config.ts +51 -0
- package/cdk/copy-overwrite/eslint.slow.config.ts +80 -0
- package/cdk/copy-overwrite/knip.json +53 -0
- package/cdk/copy-overwrite/tsconfig.eslint.json +11 -0
- package/cdk/merge/package.json +17 -1
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +83 -64
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/prompts.d.ts +17 -3
- package/dist/cli/prompts.d.ts.map +1 -1
- package/dist/cli/prompts.js +52 -16
- package/dist/cli/prompts.js.map +1 -1
- package/dist/core/config.d.ts +13 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +17 -9
- package/dist/core/config.js.map +1 -1
- package/dist/core/git-service.d.ts +40 -0
- package/dist/core/git-service.d.ts.map +1 -0
- package/dist/core/git-service.js +52 -0
- package/dist/core/git-service.js.map +1 -0
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/core/lisa.d.ts +124 -7
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +423 -221
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/manifest.d.ts +5 -1
- package/dist/core/manifest.d.ts.map +1 -1
- package/dist/core/manifest.js +22 -16
- package/dist/core/manifest.js.map +1 -1
- package/dist/detection/detector.interface.d.ts +1 -1
- package/dist/detection/detectors/cdk.d.ts +6 -1
- package/dist/detection/detectors/cdk.d.ts.map +1 -1
- package/dist/detection/detectors/cdk.js +16 -8
- package/dist/detection/detectors/cdk.js.map +1 -1
- package/dist/detection/detectors/expo.d.ts +6 -1
- package/dist/detection/detectors/expo.d.ts.map +1 -1
- package/dist/detection/detectors/expo.js +13 -8
- package/dist/detection/detectors/expo.js.map +1 -1
- package/dist/detection/detectors/nestjs.d.ts +7 -2
- package/dist/detection/detectors/nestjs.d.ts.map +1 -1
- package/dist/detection/detectors/nestjs.js +17 -9
- package/dist/detection/detectors/nestjs.js.map +1 -1
- package/dist/detection/detectors/npm-package.d.ts +6 -1
- package/dist/detection/detectors/npm-package.d.ts.map +1 -1
- package/dist/detection/detectors/npm-package.js +9 -4
- package/dist/detection/detectors/npm-package.js.map +1 -1
- package/dist/detection/detectors/typescript.d.ts +6 -1
- package/dist/detection/detectors/typescript.d.ts.map +1 -1
- package/dist/detection/detectors/typescript.js +12 -7
- package/dist/detection/detectors/typescript.js.map +1 -1
- package/dist/detection/index.d.ts +13 -3
- package/dist/detection/index.d.ts.map +1 -1
- package/dist/detection/index.js +17 -7
- package/dist/detection/index.js.map +1 -1
- package/dist/errors/index.d.ts +66 -2
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +89 -17
- package/dist/errors/index.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/logging/console-logger.d.ts +21 -1
- package/dist/logging/console-logger.d.ts.map +1 -1
- package/dist/logging/console-logger.js +26 -6
- package/dist/logging/console-logger.js.map +1 -1
- package/dist/logging/index.d.ts +3 -3
- package/dist/logging/index.js +2 -2
- package/dist/logging/logger.interface.d.ts +1 -1
- package/dist/logging/silent-logger.d.ts +21 -1
- package/dist/logging/silent-logger.d.ts.map +1 -1
- package/dist/logging/silent-logger.js +20 -0
- package/dist/logging/silent-logger.js.map +1 -1
- package/dist/strategies/copy-contents.d.ts +47 -6
- package/dist/strategies/copy-contents.d.ts.map +1 -1
- package/dist/strategies/copy-contents.js +99 -49
- package/dist/strategies/copy-contents.js.map +1 -1
- package/dist/strategies/copy-overwrite.d.ts +10 -2
- package/dist/strategies/copy-overwrite.d.ts.map +1 -1
- package/dist/strategies/copy-overwrite.js +17 -9
- package/dist/strategies/copy-overwrite.js.map +1 -1
- package/dist/strategies/create-only.d.ts +10 -2
- package/dist/strategies/create-only.d.ts.map +1 -1
- package/dist/strategies/create-only.js +14 -6
- package/dist/strategies/create-only.js.map +1 -1
- package/dist/strategies/index.d.ts +17 -7
- package/dist/strategies/index.d.ts.map +1 -1
- package/dist/strategies/index.js +19 -9
- package/dist/strategies/index.js.map +1 -1
- package/dist/strategies/merge.d.ts +10 -2
- package/dist/strategies/merge.d.ts.map +1 -1
- package/dist/strategies/merge.js +21 -21
- package/dist/strategies/merge.js.map +1 -1
- package/dist/strategies/strategy.interface.d.ts +1 -1
- package/dist/strategies/strategy.interface.d.ts.map +1 -1
- package/dist/transaction/backup.d.ts +15 -1
- package/dist/transaction/backup.d.ts.map +1 -1
- package/dist/transaction/backup.js +47 -12
- package/dist/transaction/backup.js.map +1 -1
- package/dist/transaction/index.d.ts +3 -3
- package/dist/transaction/index.js +2 -2
- package/dist/transaction/transaction.d.ts +25 -2
- package/dist/transaction/transaction.d.ts.map +1 -1
- package/dist/transaction/transaction.js +25 -2
- package/dist/transaction/transaction.js.map +1 -1
- package/dist/utils/file-operations.d.ts +21 -0
- package/dist/utils/file-operations.d.ts.map +1 -1
- package/dist/utils/file-operations.js +48 -12
- package/dist/utils/file-operations.js.map +1 -1
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -3
- package/dist/utils/json-utils.d.ts +12 -0
- package/dist/utils/json-utils.d.ts.map +1 -1
- package/dist/utils/json-utils.js +17 -5
- package/dist/utils/json-utils.js.map +1 -1
- package/dist/utils/path-utils.d.ts +11 -0
- package/dist/utils/path-utils.d.ts.map +1 -1
- package/dist/utils/path-utils.js +12 -1
- package/dist/utils/path-utils.js.map +1 -1
- package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +5 -0
- package/eslint-plugin-code-organization/index.js +5 -0
- package/eslint-plugin-code-organization/rules/enforce-statement-order.js +5 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +2 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +2 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +2 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +2 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +2 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +2 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +2 -41
- package/{typescript → expo}/copy-overwrite/.github/workflows/build.yml +3 -0
- package/expo/copy-overwrite/.github/workflows/ci.yml +36 -0
- package/{typescript → expo}/copy-overwrite/.github/workflows/deploy.yml +22 -26
- package/{typescript → expo}/copy-overwrite/.github/workflows/lighthouse.yml +4 -1
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +6 -1
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -68
- package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +5 -3
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +5 -0
- package/expo/copy-overwrite/eslint.config.ts +53 -0
- package/expo/copy-overwrite/eslint.expo.ts +330 -0
- package/expo/copy-overwrite/eslint.slow.config.ts +86 -0
- package/expo/copy-overwrite/knip.json +132 -0
- package/expo/copy-overwrite/lighthouserc.js +27 -0
- package/expo/copy-overwrite/tsconfig.eslint.json +25 -0
- package/expo/create-only/lighthouserc-config.json +6 -1
- package/expo/merge/package.json +16 -3
- package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +1 -1
- package/{typescript → nestjs}/copy-overwrite/.github/k6/README.md +2 -2
- package/{typescript → nestjs}/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +3 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/examples/data-driven-test.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/load.js +6 -2
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/smoke.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/soak.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/spike.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/stress.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scripts/api-test.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scripts/default-test.js +5 -0
- package/nestjs/copy-overwrite/.github/workflows/ci.yml +29 -0
- package/nestjs/copy-overwrite/.github/workflows/deploy.yml +291 -0
- package/{typescript → nestjs}/copy-overwrite/.github/workflows/load-test.yml +3 -0
- package/nestjs/copy-overwrite/eslint.config.ts +53 -0
- package/nestjs/copy-overwrite/eslint.nestjs.ts +178 -0
- package/nestjs/merge/package.json +11 -3
- package/package.json +34 -40
- package/typescript/copy-contents/.husky/pre-commit +1 -1
- package/typescript/copy-contents/.husky/pre-push +99 -118
- package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +2 -0
- package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +3 -11
- package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +2 -0
- package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +68 -0
- package/typescript/copy-overwrite/.claude/settings.json +79 -0
- package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +44 -0
- package/typescript/copy-overwrite/.github/README.md +49 -1
- package/typescript/copy-overwrite/.github/dependabot.yml +3 -0
- package/typescript/copy-overwrite/.github/workflows/ci.yml +7 -29
- package/typescript/copy-overwrite/.github/workflows/claude.yml +3 -0
- package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +6 -4
- package/typescript/copy-overwrite/.github/workflows/create-issue-on-failure.yml +176 -0
- package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +3 -1
- package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +3 -1
- package/typescript/copy-overwrite/.github/workflows/lint-slow.yml +40 -0
- package/typescript/copy-overwrite/.github/workflows/quality.yml +151 -38
- package/typescript/copy-overwrite/.github/workflows/release.yml +3 -0
- package/typescript/copy-overwrite/.gitleaksignore +3 -0
- package/typescript/copy-overwrite/.lintstagedrc.json +6 -0
- package/typescript/copy-overwrite/.prettierignore +2 -1
- package/typescript/copy-overwrite/.yamllint +2 -0
- package/typescript/copy-overwrite/ast-grep/rule-tests/.gitkeep +3 -0
- package/typescript/copy-overwrite/ast-grep/rules/.gitkeep +3 -0
- package/typescript/copy-overwrite/ast-grep/utils/.gitkeep +3 -0
- package/typescript/copy-overwrite/{commitlint.config.js → commitlint.config.cjs} +5 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +5 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +5 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +5 -0
- package/typescript/copy-overwrite/eslint.base.ts +430 -0
- package/typescript/copy-overwrite/eslint.config.ts +52 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +19 -2
- package/typescript/copy-overwrite/eslint.slow.config.ts +69 -0
- package/typescript/copy-overwrite/eslint.typescript.ts +142 -0
- package/typescript/copy-overwrite/knip.json +64 -0
- package/typescript/copy-overwrite/sgconfig.yml +11 -0
- package/typescript/copy-overwrite/tsconfig.eslint.json +9 -0
- package/typescript/create-only/eslint.config.local.ts +24 -0
- package/typescript/{copy-overwrite/eslint.thresholds.config.json → create-only/eslint.thresholds.json} +1 -1
- package/typescript/github-rulesets/base.json +2 -75
- package/typescript/merge/.claude/settings.json +160 -0
- package/typescript/merge/package.json +35 -34
- package/all/copy-overwrite/.claude/commands/rules/format-md.md +0 -72
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +0 -416
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +0 -316
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +0 -73
- package/expo/copy-overwrite/eslint.config.mjs +0 -560
- package/lisa.sh +0 -35
- package/typescript/copy-overwrite/eslint.config.mjs +0 -390
- /package/{all/create-only/PROJECT_RULES.md → cdk/copy-overwrite/.github/workflows/.keep} +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/load.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/smoke.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/soak.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/spike.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/stress.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/normal.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/relaxed.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/strict.json +0 -0
- /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
|
package/all/copy-overwrite/.claude/{skills/coding-philosophy/SKILL.md → rules/coding-philosophy.md}
RENAMED
|
@@ -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
|
-
|
|
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**.
|
|
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)
|
|
81
|
-
2. **Is there a simpler way?** (KISS)
|
|
82
|
-
3. **Am I repeating myself 3+ times?** (DRY)
|
|
83
|
-
4. **Does this function do one thing?** (SOLID-SRP)
|
|
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
|
-
|
|
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
|
+
```
|