plugins/lisa-expo-copilot/skills/reduce-complexity/SKILL.md
This skill provides strategies and patterns for reducing cognitive complexity in React components. It should be used when ESLint reports sonarjs/cognitive-complexity violations, when refactoring complex View components, or when planning how to break down large components. The skill enforces this project's Container/View pattern requirements when extracting components.
npx skillsauth add codyswanngt/lisa reduce-complexityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides systematic approaches for reducing cognitive complexity in React components while adhering to this project's Container/View pattern requirements.
sonarjs/cognitive-complexity violations (threshold: 28)Cognitive complexity increases with:
| Source | Complexity Impact | Common in View Components | | ---------------------- | ----------------- | ------------------------- | | Nested conditionals | +1 per nesting | Yes | | Ternary expressions | +1 each | Yes | | Logical operators (&&) | +1 each | Yes | | Loops (map, filter) | +1 each | Yes | | Switch/case statements | +1 per case | Rare | | Catch blocks | +1 each | No (Container only) | | Nested functions | +1 per nesting | Yes |
Before extracting code, determine the appropriate strategy:
// Helper function - no logic, just rendering
function renderSectionHeader(props: {
readonly title: string;
readonly count: number;
}) {
return (
<HStack className="justify-between">
<Text className="font-bold">{props.title}</Text>
<Text className="text-sm">({props.count})</Text>
</HStack>
);
}
FilterChipList/
├── FilterChipListContainer.tsx # Handles selection logic
├── FilterChipListView.tsx # Renders chip list
└── index.tsx # Exports Container
Run ESLint to identify the violation:
bun run lint 2>&1 | grep "cognitive-complexity"
Note: Replace
bunwith your project's package manager (npm,yarn,pnpm) as needed.
Read the file and identify:
Use the decision framework above. For View components:
| Situation | Strategy | | -------------------------- | --------------------------------- | | Repeated JSX, no logic | Helper function | | Repeated JSX, needs props | Helper function with props object | | Repeated pattern, 3+ files | Full Container/View component | | Complex section, own state | Full Container/View component | | Deeply nested ternaries | Pre-compute flags in Container |
Before refactoring, ensure test coverage exists:
# Check existing coverage
bun run test:unit --coverage --collectCoverageFrom='<file-path>'
If no tests exist, write tests that verify current behavior before refactoring.
For helper functions, see references/extraction-strategies.md.
For full components, use the Container/View pattern skill.
bun run lint 2>&1 | grep "cognitive-complexity"
bun run test:unit
Before (high complexity):
<Pressable
style={{
backgroundColor: selected.includes(item) ? colors.primary : colors.bg,
borderColor: selected.includes(item) ? colors.primary : colors.border,
}}
>
<Text style={{ color: selected.includes(item) ? "#FFF" : colors.text }}>
{item}
</Text>
</Pressable>
After (reduced complexity):
// In Container - pre-compute selection state
const itemStates = useMemo(
() =>
items.map(item => ({
item,
isSelected: selected.includes(item),
})),
[items, selected]
);
// In View - simple conditional
<Pressable style={isSelected ? styles.selected : styles.default}>
<Text style={isSelected ? styles.selectedText : styles.defaultText}>
{item}
</Text>
</Pressable>;
Before (4x repeated pattern = high complexity):
{positions.length > 0 && (
<VStack>
<Text>Positions</Text>
<HStack>{positions.map(p => <Chip key={p} ... />)}</HStack>
</VStack>
)}
{tags.length > 0 && (
<VStack>
<Text>Tags</Text>
<HStack>{tags.map(t => <Chip key={t.id} ... />)}</HStack>
</VStack>
)}
// ... repeated 2 more times
After (extract FilterChipList component):
<FilterChipList
title="Positions"
items={positions}
selectedItems={filters.positions}
onToggle={onPositionToggle}
/>
<FilterChipList
title="Tags"
items={tags}
selectedItems={filters.tags}
onToggle={onTagToggle}
/>
Before:
{
isLoading ? (
<Spinner />
) : hasError ? (
<Error />
) : isEmpty ? (
<Empty />
) : (
<Content />
);
}
After (pre-compute state in Container):
// Container
const viewState = useMemo(() => {
if (isLoading) return "loading";
if (hasError) return "error";
if (isEmpty) return "empty";
return "content";
}, [isLoading, hasError, isEmpty]);
// View - map directly
const VIEW_STATES = {
loading: <Spinner />,
error: <Error />,
empty: <Empty />,
content: <Content />,
} as const;
{
VIEW_STATES[viewState];
}
For detailed patterns and complete examples:
references/extraction-strategies.md - Helper function patterns and when to use eachreferences/refactoring-patterns.md - Step-by-step refactoring examples with before/after codedocumentation
Onboard a user to the project via its LLM Wiki. Interviews the user about themselves in relation to the project, captures that to project-scoped memory only, then gives a guided tour of what the project is and sample questions they can ask. Use when someone is new to the project or asks to be onboarded. Read-mostly — it does not open PRs or write PII into the wiki.
documentation
Migrate an existing, hand-rolled wiki implementation onto the lisa-wiki kernel — phased and compatibility-first, with a strict no-loss guarantee. Use when adopting lisa-wiki in a repo that already has its own wiki/, ingest skills, docs, or roles. Renaming things into the canonical shape is fine; losing functionality or data is not. Ends by running /doctor.
development
Health-check the LLM Wiki. Reports orphan pages, contradictions, stale claims, broken internal links, missing index/log coverage, structure-manifest violations, and secret/tenant leaks. Use periodically or before hardening a wiki. Read-only — it reports findings, it does not fix them.
testing
Ingest source material into the LLM Wiki. With an argument (URL, file path, or prompt) it ingests that one source; with no argument it runs a full ingest across every enabled non-external-write source. Routes to the right connector, then runs the ordered pipeline (source note → synthesis → index → log → verify → state → commit/PR). Use whenever new knowledge should enter the wiki.