diff --git a/.claude/hooks/skill-rules.json b/.claude/hooks/skill-rules.json index c983fa4b7e..086613c5d1 100644 --- a/.claude/hooks/skill-rules.json +++ b/.claude/hooks/skill-rules.json @@ -280,6 +280,53 @@ "gh pr comment" ] } + }, + "feature-toggle-developer": { + "type": "domain", + "priority": "high", + "description": "Smart router to feature toggle removal (→ feature-toggle-developer.md). Systematic toggle removal, automated cleanup detection, unused code identification", + "promptTriggers": { + "keywords": [ + "remove toggle", + "delete toggle", + "enable toggle", + "feature flag", + "feature toggle", + "remove feature flag", + "delete feature flag", + "enable feature flag", + "cleanup after toggle", + "unused code", + "orphaned code", + "defaultValue: true", + "defaultValue: false", + "FeatureFlags.", + "toggle removal" + ], + "intentPatterns": [ + "(remove|delete|eliminate).*?(toggle|feature flag|feature toggle)", + "(enable|activate|turn on).*?(toggle|feature flag|permanently)", + "(cleanup|clean up|remove unused).*?(code|component|class|file)", + "(find|identify|check for).*?(unused|orphaned|dead).*?(code|component)", + "(after|completed).*?(toggle|feature flag).*?(removal|cleanup)" + ] + }, + "fileTriggers": { + "pathPatterns": [ + "**/FeatureDescription+Flags.swift", + "**/FeatureFlags+Flags.swift", + "**/.claude/skills/feature-toggle-developer.md" + ], + "contentPatterns": [ + "FeatureFlags\\.", + "static let.*=.*FeatureDescription", + "defaultValue: true", + "defaultValue: false", + "if FeatureFlags\\.", + "guard.*FeatureFlags\\.", + "@State.*FeatureFlags" + ] + } } }, "config": { diff --git a/.claude/skills/feature-toggle-developer/SKILL.md b/.claude/skills/feature-toggle-developer/SKILL.md new file mode 100644 index 0000000000..91e1c2eb8b --- /dev/null +++ b/.claude/skills/feature-toggle-developer/SKILL.md @@ -0,0 +1,309 @@ +# Feature Toggle Developer + +**Status**: Active +**Auto-activates on**: Feature flag/toggle removal, cleanup after refactoring toggles +**Related Skills**: `code-generation-developer`, `ios-dev-guidelines`, `code-review-developer` + +## Purpose + +Guides the systematic removal of feature toggles (feature flags) from the codebase with automated cleanup detection. Ensures no orphaned code, unused components, or forgotten files remain after toggle removal. + +## When This Skill Activates + +- User mentions: "remove toggle", "delete feature flag", "enable feature toggle permanently" +- User edits: `FeatureDescription+Flags.swift` +- After completing toggle removal: helps identify cleanup opportunities + +## Feature Toggle Removal Workflow + +### Phase 1: Pre-Removal Analysis + +Before removing any toggle, gather complete information: + +1. **Find the toggle definition**: + ```bash + # Location: Modules/AnytypeCore/AnytypeCore/Utils/FeatureFlags/FeatureDescription+Flags.swift + rg "static let toggleName" --type swift + ``` + +2. **Check the defaultValue** to determine which branch to keep: + - `defaultValue: true` → Keep the TRUE branch, remove FALSE branch + - `defaultValue: false` → Keep the FALSE branch, remove TRUE branch + +3. **Search for ALL usages**: + ```bash + rg "toggleName" --type swift + ``` + +4. **Identify usage patterns**: + - Direct: `if FeatureFlags.toggleName { ... }` + - Inverted: `if !FeatureFlags.toggleName { ... }` or `guard !FeatureFlags.toggleName` + - Compound: `if FeatureFlags.toggleName && otherCondition { ... }` + - Assignment: `let value = FeatureFlags.toggleName ? a : b` + - State: `@State private var toggle = FeatureFlags.toggleName` + +5. **List affected files** and present to user for review + +### Phase 2: Toggle Removal + +Systematic removal process: + +1. **Remove conditional checks and simplify**: + + **Example 1 - Simple conditional (defaultValue: true)**: + ```swift + // BEFORE + if FeatureFlags.toggleName { + // feature code + } + + // AFTER (keep true branch) + // feature code + ``` + + **Example 2 - Ternary operator (defaultValue: true)**: + ```swift + // BEFORE + VStack(spacing: FeatureFlags.toggleName ? 8 : 0) + + // AFTER + VStack(spacing: 8) + ``` + + **Example 3 - Inverted logic (defaultValue: true)**: + ```swift + // BEFORE + guard !FeatureFlags.toggleName else { return } + oldCode() + + // AFTER (flag is true, so guard fails, remove entire block) + // [entire block deleted] + ``` + + **Example 4 - State variable (defaultValue: true)**: + ```swift + // BEFORE + @State private var toggle = FeatureFlags.toggleName + if toggle { newUI() } else { oldUI() } + + // AFTER + newUI() + // Note: @State variable removed in cleanup phase + ``` + +2. **Remove feature flag definition**: + ```swift + // Delete from: Modules/AnytypeCore/AnytypeCore/Utils/FeatureFlags/FeatureDescription+Flags.swift + static let toggleName = FeatureDescription(...) + ``` + +3. **Run code generation**: + ```bash + make generate + ``` + This updates `FeatureFlags+Flags.swift` automatically. + +4. **Verify removal**: + ```bash + rg "toggleName" --type swift # Should return no results + ``` + +### Phase 3: Automated Cleanup Detection ⭐ + +**CRITICAL**: After toggle removal, systematically check for orphaned code: + +#### 3.1 Unused State Variables + +Search for `@State` variables that were only used for the toggle: + +```bash +# Look for patterns like: @State private var someToggle = FeatureFlags.toggleName +rg "@State.*=.*FeatureFlags" --type swift +``` + +**Action**: Remove the entire `@State` variable declaration if it's no longer used. + +#### 3.2 Unused View Components + +When a toggle controlled which UI component to show, one component may now be unused: + +**Detection Pattern**: +- Toggle switched between ComponentA and ComponentB +- After removal, only one is used +- Search for the unused component's name across codebase + +**Example from vaultBackToRoots**: +```swift +// BEFORE +if !vaultBackToRootsToggle { + SpaceCardLabel(...) // This became unused +} else { + NewSpaceCardLabel(...) +} + +// AFTER +NewSpaceCardLabel(...) + +// CLEANUP: SpaceCardLabel is now unused +rg "SpaceCardLabel" --type swift # Check if used anywhere else +# If only in its own file → DELETE the file +``` + +**Action**: +1. Search for unused component name: `rg "UnusedComponentName" --type swift` +2. If only found in its definition file and comments → DELETE the file +3. Update references in comments + +#### 3.3 Unused ViewModels / Service Classes + +Toggle removal may leave entire classes unused: + +**Detection**: +```bash +# For each major component that was conditionally used: +rg "UnusedViewModel" --type swift +rg "class UnusedViewModel" --type swift +``` + +**Action**: Delete unused ViewModels, their files, and DI registrations. + +#### 3.4 Unused Imports + +After simplification, `import AnytypeCore` may only have been needed for `FeatureFlags`: + +**Detection**: +- File imports `AnytypeCore` +- Only usage was `FeatureFlags.toggleName` +- After removal, no other `AnytypeCore` usage + +**Action**: Remove unused import. + +#### 3.5 Orphaned Parameters / Properties + +Toggle-gated functionality may have parameters that are no longer needed: + +**Example**: +```swift +// BEFORE +func configure(showFeature: Bool) { + if showFeature && FeatureFlags.toggle { ... } +} + +// AFTER toggle removal +func configure(showFeature: Bool) { + if showFeature { ... } +} + +// POTENTIAL CLEANUP: Is showFeature still needed? +``` + +**Action**: Review function signatures and remove unnecessary parameters. + +#### 3.6 Test Cleanup + +Toggle removal affects tests: + +**Check**: +1. Mock objects with toggle-related properties +2. Test cases specifically for toggle behavior +3. Test setup code with toggle configurations + +**Files to check**: +```bash +rg "toggleName" AnyTypeTests/ --type swift +rg "toggleName" "Anytype/Sources/PreviewMocks/" --type swift +``` + +**Action**: Update or remove tests for deleted code paths. + +### Phase 4: Final Verification + +Before committing: + +1. **Grep verification**: + ```bash + rg "toggleName" --type swift # Should be empty + ``` + +2. **Compilation check**: + - Remind user to verify compilation in Xcode + - Claude cannot verify this due to caching + +3. **Generate updated commit message**: + ``` + IOS-XXXX Removed [toggleName] toggle + ``` + +4. **Review cleanup summary**: + - List all files modified + - List all files deleted + - Note any remaining manual checks needed + +## Cleanup Checklist Template + +Use this checklist after every toggle removal: + +```markdown +## Cleanup Verification for [toggleName] + +- [ ] Toggle definition removed from FeatureDescription+Flags.swift +- [ ] `make generate` run successfully +- [ ] All conditional usage removed +- [ ] No grep results for toggle name +- [ ] Unused @State variables removed +- [ ] Unused view components identified and deleted +- [ ] Unused ViewModels/services deleted +- [ ] Unused imports removed (especially AnytypeCore) +- [ ] Orphaned function parameters removed +- [ ] Tests updated (check AnyTypeTests/ and PreviewMocks/) +- [ ] Comments referencing old component updated +- [ ] Xcode compilation verified (by user) +``` + +## Common Patterns & Pitfalls + +### Inverted Logic + +**Watch out for** `!FeatureFlags.toggle`: + +```swift +// If defaultValue: true +if !FeatureFlags.toggle { + oldCode() // This branch NEVER runs, delete it +} +``` + +### Compound Conditions + +**Simplify** conditions properly: + +```swift +// BEFORE (defaultValue: true) +if FeatureFlags.toggle && userHasPermission { + showFeature() +} + +// AFTER +if userHasPermission { + showFeature() +} +``` + +### Guard Statements + +**Be careful** with guards: + +```swift +// BEFORE (defaultValue: true) +guard !FeatureFlags.toggle else { return } +performOldBehavior() + +// AFTER (toggle is true, guard returns, entire block is dead code) +// DELETE ENTIRE BLOCK +``` + +## Integration with Other Skills + +- **code-generation-developer**: References for `make generate` command and troubleshooting +- **ios-dev-guidelines**: Swift refactoring patterns, import management +- **code-review-developer**: Cleanup standards, ensuring no orphaned code in PRs \ No newline at end of file diff --git a/.claude/skills/skills-manager/SKILL.md b/.claude/skills/skills-manager/SKILL.md index 044334f67b..4db2dcb391 100644 --- a/.claude/skills/skills-manager/SKILL.md +++ b/.claude/skills/skills-manager/SKILL.md @@ -140,13 +140,14 @@ echo "Update space settings localization for membership tiers" | .claude/hooks/u **Location**: `.claude/skills/*/SKILL.md` -**The 6 skills**: +**The 7 skills**: 1. `ios-dev-guidelines` - Swift/iOS patterns 2. `localization-developer` - Localization 3. `code-generation-developer` - Feature flags, make generate 4. `design-system-developer` - Icons, typography, colors 5. `skills-manager` - This skill (meta!) 6. `code-review-developer` - Code review standards +7. `feature-toggle-developer` - Feature toggle removal, cleanup detection ### Configuration