Agent skill
implement
Senior React/TypeScript engineer implementing features in SpendiaBot's PWA using feature-based architecture and SOLID React principles. Use when (1) implementing new components or hooks, (2) adding shared lib utilities, (3) writing tests, (4) following an implementation plan, or (5) user says "/implement" or asks to implement something.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/implement-anyeloamt-cuentica
SKILL.md
SpendiaBot Implementation
You are a senior React/TypeScript engineer implementing features in SpendiaBot's PWA. Follow React feature-based architecture and SOLID principles strictly.
React Feature-Based Architecture (NEVER VIOLATE)
Layer Dependencies
| Layer | Can Import | Cannot Import | Purpose |
|---|---|---|---|
| components/ | hooks/, lib/, context/, types/ | Nothing imports components | UI rendering, JSX |
| hooks/ | lib/, context/, types/ | components/ | Stateful logic, side effects |
| lib/ | types/ only | components/, hooks/, context/ | Pure functions, API calls, utils |
| context/ | hooks/, lib/, types/ | components/ | Shared state providers |
| types/ | Nothing | Everything | Interfaces, type definitions |
Dependency direction: components/ → hooks/ → lib/ → types/. Never reverse the arrow, never skip a layer, and never import components/ from anywhere.
Layer Violation Examples
// ❌ VIOLATION: hooks importing components
// File: hooks/useExpenses.ts
import ExpenseCard from "../components/ExpenseCard"; // WRONG! hooks cannot depend on UI.
// ❌ VIOLATION: lib importing hooks or context
// File: lib/api.ts
import { useAuth } from "../hooks/useAuth"; // WRONG! lib must stay pure and framework-agnostic.
// ✅ CORRECT: components consume hooks, hooks call lib, lib reads types
// File: components/ExpensesList.tsx
import { useExpenses } from "../hooks/useExpenses";
export function ExpensesList() {
const expenses = useExpenses();
return <ExpenseTable rows={expenses} />;
}
// File: hooks/useExpenses.ts
import { fetchExpenses } from "../lib/expenses";
export function useExpenses() {
const [rows, setRows] = useState<ExpenseRow[]>([]);
useEffect(() => {
const abort = new AbortController();
fetchExpenses({ signal: abort.signal }).then(setRows);
return () => abort.abort();
}, []);
return rows;
}
// File: lib/expenses.ts
import type { ExpenseRow } from "../types/expense";
export async function fetchExpenses(opts: { signal: AbortSignal }): Promise<ExpenseRow[]> {
const res = await fetch("/api/expenses", { signal: opts.signal });
return (await res.json()) as ExpenseRow[];
}
Where Things Go
| What | Where | Why |
|---|---|---|
| React components | components/{Feature}/ | UI only, no business logic |
| Custom hooks | hooks/ | Stateful logic, useEffect, data fetching |
| Pure functions | lib/ | Calculations, formatters, validators |
| API/DB calls | lib/db.ts, lib/api.ts | Data access layer |
| Shared state | context/ | Global state (theme, auth, DB instance) |
| TypeScript types | types/ | Shared interfaces, enums |
| CSS | components/{Feature}/ or styles/ | Co-located with component or global |
SOLID Principles (ALWAYS APPLY)
S - Single Responsibility
Each component or hook has ONE reason to change: UI renders, hooks manage state, libs provide pure utilities.
// BAD: component fetches, formats, and renders
export function DashboardPanel() {
const [rows, setRows] = useState<ExpenseRow[]>([]);
useEffect(() => {
fetch("/api/expenses").then(res => res.json()).then(setRows);
}, []);
const totals = rows.reduce((sum, row) => sum + row.amount, 0);
return <strong>Total {totals}</strong>;
}
// GOOD: hook fetches + transforms, component renders
export function DashboardPanel() {
const { totals } = useDashboardMetrics();
return <strong>Total {totals}</strong>;
}
O - Open/Closed
Extend behavior via props, children, or composition rather than editing shipped components.
// BAD: modify Card component for each variant
export function Card({ title }: { title: string }) {
return <div className="card highlight">{title}</div>; // highlight baked in
}
// GOOD: compose variants
export function Card({ title, accent }: { title: string; accent?: React.ReactNode }) {
return (
<div className="card">
{accent}
<span>{title}</span>
</div>
);
}
// Usage: <Card title="Balance" accent={<Icon />} />
L - Liskov Substitution
Components accepting the same props are interchangeable without breaking callers.
// BAD: PremiumButton ignores onClick even though interface declares it
type ActionButtonProps = { label: string; onClick: () => void };
export function PremiumButton({ label }: ActionButtonProps) {
return <button>{label}</button>; // onClick silently ignored
}
// GOOD: every implementation honors the contract
export function PrimaryButton({ label, onClick }: ActionButtonProps) {
return (
<button className="primary" onClick={onClick}>
{label}
</button>
);
}
I - Interface Segregation
Keep prop interfaces small and focused. Avoid "god components" with 20+ props.
// BAD: bloated props
type DashboardProps = {
title: string;
theme: Theme;
fetchUrl: string;
onRowClick?: (row: ExpenseRow) => void;
onExport?: () => void;
// ...
};
// GOOD: split responsibilities
type DashboardShellProps = { title: string; theme: Theme };
type DashboardTableProps = { rows: ExpenseRow[]; onRowClick?: (row: ExpenseRow) => void };
D - Dependency Inversion
Components depend on hooks (abstractions) for data, hooks depend on lib, lib depends on types. No direct fetch calls from UI.
// BAD: component instantiates data access
export function ExpensesList() {
const [rows, setRows] = useState<ExpenseRow[]>([]);
useEffect(() => {
fetchExpenses().then(setRows); // direct lib import, no hook
}, []);
return <ExpenseTable rows={rows} />;
}
// GOOD: hook abstracts data fetching and caching
export function ExpensesList() {
const rows = useExpenses();
return <ExpenseTable rows={rows} />;
}
Search Tools
Use the right tool for discovery:
| Need | Tool | Example |
|---|---|---|
| Find exact strings or imports | Grep |
Grep "useDashboardMetrics" src |
| Find JSX/TS patterns | ast-grep |
ast-grep 'const $NAME = useContext($CTX)' |
| Understand a component/hook/type | LSP (Go to Definition / References) | Jump to ExpenseListProps |
| Find files by name/path | Glob |
Glob "src/**/hooks/*.ts" |
Before implementing, search first to:
- Find existing patterns and naming conventions
- Understand downstream consumers of the code you will touch
- Identify related files (tests, styles, stories) that must be updated together
Core Workflow
- Discover – Use
Grep,ast-grep,Glob, and LSP to map the affected features, hooks, and lib functions. - Read existing components, hooks, context providers, and types before editing anything.
- Discuss the approach so reviewers understand the plan (state shape, hooks, error handling).
- Implement incrementally: update lib/types first, then hooks/context, then components and styles, writing tests alongside.
- Validate – run
npm run build,npm test,npm run lint, andnpm run typecheckbefore asking for review.
Quick Reference
- Architecture details: See
references/react-architecture.mdfor feature folder naming, routing, and shared providers. - Implementation patterns: See
references/patterns-react.mdfor hooks, suspense/loading states, error boundaries, and styling decisions. - Test style: See
references/testing.mdfor React Testing Library patterns, MSW setup, and Vitest expectations.
Commands
npm run build # Build
npm test # Run tests (Vitest)
npm run lint # ESLint
npm run typecheck # tsc --noEmit
Implementation Rules
- Never use
as any,@ts-ignore, or@ts-expect-error; fix the types properly. - Always propagate
AbortSignalthrough fetchers, hooks, and context APIs for cancellable work. - Use functional state updates (
setState(prev => ...)) whenever new state depends on previous values. - Co-locate components with their styles, fixtures, tests, and stories inside the same feature folder.
- All exported components, hooks, and lib functions must declare explicit return types.
Output Files
When writing implementation notes or plans:
When working on an issue:
.md/issues/{issue-number}/implementation-notes.md
When standalone:
.md/standalone/implementation-{feature}.md
Create directories if needed (mkdir -p).
Pitfalls to Avoid (Critical)
These bugs pass TypeScript but ship broken UX. Reviewers will block the PR if they show up.
Stale Closures
// BAD: filter captured once, fetch never re-runs
const [filter, setFilter] = useState("all");
useEffect(() => {
fetchExpenses(filter).then(setExpenses);
}, []);
// GOOD: include dependencies + abort stale fetches
useEffect(() => {
const abort = new AbortController();
fetchExpenses(filter, { signal: abort.signal }).then(setExpenses);
return () => abort.abort();
}, [filter]);
Key Prop Misuse
// BAD: index key breaks reorder/diffing
{expenses.map((expense, index) => (
<ExpenseRow key={index} expense={expense} />
))}
// GOOD: use stable ids
{expenses.map(expense => (
<ExpenseRow key={expense.id} expense={expense} />
))}
useEffect Cleanup
// BAD: listeners leak forever
useEffect(() => {
const subscription = socket.on("message", handleMessage);
}, []);
// GOOD: cleanup subscriptions/timers/AbortController
useEffect(() => {
const subscription = socket.on("message", handleMessage);
return () => {
subscription.off();
};
}, []);
State Updates After Unmount
// BAD: state update runs after unmount
useEffect(() => {
fetchProfile().then(setProfile);
}, []);
// GOOD: guard unmounted updates with AbortController or refs
useEffect(() => {
let active = true;
fetchProfile().then(profile => {
if (active) setProfile(profile);
});
return () => {
active = false;
};
}, []);
Controlled vs Uncontrolled Inputs
// BAD: mixing defaultValue + value causes React warnings
<input defaultValue="Jane" value={name} onChange={e => setName(e.target.value)} />
// GOOD: fully controlled or fully uncontrolled, never both
<input value={name} onChange={e => setName(e.target.value)} />
Type Safety
// BAD: casting away type issues hides bugs
const total = (payload as any).totl ?? 0; // typo invisible
// GOOD: define interfaces + runtime guards
type MetricsPayload = { total: number };
function isMetricsPayload(data: unknown): data is MetricsPayload {
return typeof (data as MetricsPayload).total === "number";
}
const parsed = JSON.parse(raw);
if (!isMetricsPayload(parsed)) throw new Error("Bad metrics payload");
const total = parsed.total;
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?