plugins/src/expo/skills/directory-structure/SKILL.md
This skill enforces the project's directory structure standards when creating or moving files. Use this skill when creating new components, screens, features, hooks, utilities, or any other code files to ensure they are placed in the correct location with proper naming conventions. Also use when reviewing file placement or restructuring code.
npx skillsauth add codyswanngt/lisa directory-structureInstall 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 ensures all new code follows the documented directory structure standards. It validates file placement, enforces the Container/View pattern, and ensures proper organization of features, components, and tests.
Expo SDK 55+/56 adopts the /src convention: all application source lives
under src/, while config, assets, tests, and tooling stay at the project root.
Older projects keep source at the root. The structure below shows the /src
layout; for a root-layout project, read src/<dir> as <dir>. The @/*
TypeScript path alias points at ./src/* (or ./* for root layout), so imports
are identical either way. The validator and lint/coverage configs auto-detect a
src/ directory.
<project-root>/
├── src/ # APPLICATION SOURCE (SDK 55+/56 /src convention)
│ ├── app/ # Expo Router - THIN WRAPPERS ONLY
│ ├── features/ # Feature modules - PRIMARY CODE LOCATION
│ ├── components/ # Shared components
│ │ ├── ui/ # GlueStack UI primitives
│ │ ├── icons/ # Icon components
│ │ ├── custom/ # Custom UI components
│ │ └── [ComponentName]/ # Feature-agnostic components
│ ├── hooks/ # Global hooks (with __tests__/)
│ ├── providers/ # Global React context providers (with __tests__/)
│ ├── stores/ # Global state - Apollo reactive variables (with __tests__/)
│ ├── utils/ # Utility functions (with __tests__/)
│ ├── lib/ # Utility libraries / integrations
│ ├── types/ # Global TypeScript types
│ ├── generated/ # Auto-generated GraphQL types (DO NOT EDIT)
│ ├── config/ # Configuration files
│ └── constants/ # Global constants
├── assets/ # Static assets (fonts/, icons/, css/) — STAYS AT ROOT
├── e2e/ # End-to-end tests (fixtures/, pages/, tests/, utils/) — STAYS AT ROOT
├── scripts/ # Build and development scripts — STAYS AT ROOT
├── public/ # Public web assets — STAYS AT ROOT
├── __mocks__/ # Jest manual mocks — STAYS AT ROOT
├── docs/ # Documentation — STAYS AT ROOT
└── projects/ # Project-specific files (archive/) — STAYS AT ROOT
Each feature in features/ MUST follow this structure:
features/[feature-name]/
├── components/ # Feature-specific components
│ └── [ComponentName]/ # Each component in its own directory
│ ├── [ComponentName]Container.tsx
│ ├── [ComponentName]View.tsx
│ └── index.tsx
├── screens/ # Screen components (same pattern as components)
│ └── [ScreenName]/
│ ├── [ScreenName]Container.tsx
│ ├── [ScreenName]View.tsx
│ └── index.tsx
├── hooks/ # Feature-specific hooks
│ └── __tests__/ # Hook tests
├── stores/ # Feature state (Apollo reactive variables)
├── utils/ # Feature utilities
│ └── __tests__/ # Utility tests
├── types.ts # Feature TypeScript types
├── constants.ts # Feature constants
├── config/ # Feature configuration
└── operations.graphql # GraphQL queries/mutations
Components and screens MUST follow the Container/View pattern:
[Name]Container.tsx)[Name]View.tsx)memo()index.tsx)// PlayerCard/PlayerCardContainer.tsx
export const PlayerCardContainer = () => {
const { data, loading } = useQuery(GET_PLAYER);
const handleClick = useCallback(() => {}, []);
return <PlayerCardView data={data} loading={loading} onClick={handleClick} />;
};
// PlayerCard/PlayerCardView.tsx
export const PlayerCardView = memo(({ data, loading, onClick }: Props) => {
if (loading) return <Skeleton />;
return <Pressable onPress={onClick}>...</Pressable>;
});
// PlayerCard/index.tsx
export { PlayerCardContainer as default } from './PlayerCardContainer';
The app/ directory contains Expo Router file-based routing. Route files MUST be thin wrappers:
// app/(root)/(tabs)/players/[playerId]/index.tsx
import { Main } from "@/features/player-detail/screens/Main";
export default function PlayerDetailScreen() {
return <Main />;
}
Test files MUST be placed in __tests__/ subdirectories:
hooks/
├── usePlayer.ts
└── __tests__/
└── usePlayer.test.ts
utils/
├── formatDate.ts
└── __tests__/
└── formatDate.test.ts
*.test.ts or *.test.tsx*.spec.ts or *.spec.tsxTo validate directory structure, run the validation script:
python3 .claude/skills/directory-structure/scripts/validate_structure.py [path]
The script checks:
__tests__/ directories| File Type | Correct Location |
| ------------------- | --------------------------------------- |
| Feature component | features/[feature]/components/[Name]/ |
| Feature screen | features/[feature]/screens/[Name]/ |
| Feature hook | features/[feature]/hooks/ |
| Feature test | features/[feature]/hooks/__tests__/ |
| Global hook | hooks/ |
| Global hook test | hooks/__tests__/ |
| Shared component | components/[Name]/ |
| UI primitive | components/ui/ |
| Global utility | utils/ |
| Global utility test | utils/__tests__/ |
| Route wrapper | app/(root)/... |
| GraphQL types | generated/ (auto-generated) |
app/ - Route files are wrappers only__tests__/ subdirectorydevelopment
Prepare a machine — a fresh laptop or a throwaway container — to run coding agents, before any repository exists. Detects which of Lisa's supported agents (Claude Code, Codex, Cursor, OpenCode, Antigravity, Copilot) are already installed, asks which credential manager the machine uses (Bitwarden, 1Password, Doppler, Vault, AWS, or none), and installs only what is missing, each by its vendor's own preferred method. Idempotent, headless by default, and emits a Dockerfile for a spin-up/spin-down environment. Run it on a new machine, in a container, or before cloning anything.
tools
Provision and verify a remote execution environment for a host project — Codex Cloud today, other remote surfaces as they are added. Generates a repository-owned setup script that installs the declared toolchain, materializes secrets through lisa-secrets-access, and runs the project's own hook. Provisions by API where one exists, by driving the vendor console where one does not, and by emitting exact config otherwise — then proves the result with the same read-back regardless of which tier did the work. Use before dispatching any work with executionEnv.
tools
Bring a developer's machine in line with the toolchain the project declares. Reports every tool in remoteEnv.tools that is missing, outdated, or unpinned for this platform, and installs the missing ones into ~/.local/bin from the same pinned, checksummed entries the remote surfaces use — but only when asked. Same manifest, same pins, same installers as lisa-setup-remote-env; what differs is consent and that the pin is a floor rather than an equality. Run it on a fresh checkout, after a manifest change, or when a tool fails at the moment of use.
tools
Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly.