recovery-feature-scaffold
Scaffold complete features for the Steps to Recovery app including database schema, encrypted storage, offline sync, React Query hooks, screens, and tests. Use when adding new data models (gratitude lists, resentments, daily inventory), creating new journaling features, building step work tools, or implementing any new feature requiring SQLite + Supabase sync.
Best use case
recovery-feature-scaffold is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Scaffold complete features for the Steps to Recovery app including database schema, encrypted storage, offline sync, React Query hooks, screens, and tests. Use when adding new data models (gratitude lists, resentments, daily inventory), creating new journaling features, building step work tools, or implementing any new feature requiring SQLite + Supabase sync.
Teams using recovery-feature-scaffold should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/recovery-feature-scaffold/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How recovery-feature-scaffold Compares
| Feature / Agent | recovery-feature-scaffold | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Scaffold complete features for the Steps to Recovery app including database schema, encrypted storage, offline sync, React Query hooks, screens, and tests. Use when adding new data models (gratitude lists, resentments, daily inventory), creating new journaling features, building step work tools, or implementing any new feature requiring SQLite + Supabase sync.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Recovery Feature Scaffold
Generate complete, production-ready features for Steps to Recovery with one command.
## Quick Start
```bash
# From project root
cd .claude/skills/recovery-feature-scaffold/scripts
node scaffold.js <FeatureName>
# Example - creates a complete gratitude list feature
node scaffold.js GratitudeList
```
This generates **9 files** in under 1 second with all boilerplate wired up.
This generates:
- Database migration (SQLite + Supabase)
- TypeScript types
- Encrypted storage hooks (useFeature.ts)
- CRUD operations with React Query
- List screen + Detail/Edit screen
- Navigation updates
- Test file
## What Gets Generated
```
feature: GratitudeList
apps/mobile/src/
├── features/gratitude-list/
│ ├── types.ts # TypeScript interfaces
│ ├── hooks/
│ │ └── useGratitude.ts # React Query + encryption
│ ├── screens/
│ │ ├── GratitudeListScreen.tsx
│ │ └── GratitudeDetailScreen.tsx
│ ├── components/
│ │ └── GratitudeCard.tsx
│ └── __tests__/
│ └── gratitude.test.ts
├── lib/
│ └── database/
│ └── migrations/
│ └── 007_add_gratitude_list.sql
└── navigation/
└── AppNavigator.tsx # Auto-updated
supabase/migrations/
└── 007_add_gratitude_list.sql # RLS policies included
```
## Manual Scaffolding (No Script)
Follow this 5-step workflow when script isn't available:
### Step 1: Define Types
Create `src/features/<feature>/types.ts`:
```typescript
export interface GratitudeItem {
id: string;
user_id: string;
encrypted_content: string;
category: 'people' | 'things' | 'experiences' | 'other';
created_at: string;
updated_at: string;
}
export interface CreateGratitudeInput {
content: string;
category: GratitudeItem['category'];
}
export interface UpdateGratitudeInput {
id: string;
content?: string;
category?: GratitudeItem['category'];
}
```
### Step 2: Create Database Migration
SQLite migration (`src/lib/database/migrations/XXX_add_feature.sql`):
```sql
-- SQLite migration
CREATE TABLE IF NOT EXISTS gratitude_items (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
encrypted_content TEXT NOT NULL,
category TEXT CHECK (category IN ('people', 'things', 'experiences', 'other')),
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_gratitude_user ON gratitude_items(user_id);
CREATE INDEX IF NOT EXISTS idx_gratitude_created ON gratitude_items(created_at DESC);
```
Supabase migration (`supabase/migrations/XXX_add_feature.sql`):
```sql
-- Supabase table
CREATE TABLE IF NOT EXISTS public.gratitude_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
encrypted_content TEXT NOT NULL,
category TEXT CHECK (category IN ('people', 'things', 'experiences', 'other')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
-- RLS Policies
ALTER TABLE public.gratitude_items ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can only access their own gratitude items"
ON public.gratitude_items FOR ALL
USING (auth.uid() = user_id);
-- Trigger for updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_gratitude_items_updated_at
BEFORE UPDATE ON public.gratitude_items
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
```
### Step 3: Create Encrypted Hooks
`src/features/<feature>/hooks/useFeature.ts`:
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useDatabase } from '../../../contexts/DatabaseContext';
import { encryptContent, decryptContent } from '../../../utils/encryption';
import { generateUUID } from '../../../utils/uuid';
import type { GratitudeItem, CreateGratitudeInput, UpdateGratitudeInput } from '../types';
const FEATURE_KEY = 'gratitude-items';
export function useGratitudeItems() {
const { db, userId } = useDatabase();
return useQuery({
queryKey: [FEATURE_KEY],
queryFn: async (): Promise<GratitudeItem[]> => {
if (!db) throw new Error('Database not initialized');
const items = await db.getAllAsync<GratitudeItem>(
'SELECT * FROM gratitude_items WHERE user_id = ? ORDER BY created_at DESC',
userId,
);
// Decrypt content for display
return Promise.all(
items.map(async (item) => ({
...item,
content: await decryptContent(item.encrypted_content),
})),
);
},
enabled: !!db && !!userId,
});
}
export function useCreateGratitude() {
const { db, userId } = useDatabase();
const queryClient = useQueryClient();
const { enqueueSync } = useSyncQueue();
return useMutation({
mutationFn: async (input: CreateGratitudeInput): Promise<GratitudeItem> => {
if (!db) throw new Error('Database not initialized');
const id = generateUUID();
const now = Date.now();
const encrypted = await encryptContent(input.content);
await db.runAsync(
`INSERT INTO gratitude_items (id, user_id, encrypted_content, category, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)`,
id,
userId,
encrypted,
input.category,
now,
now,
);
const item: GratitudeItem = {
id,
user_id: userId!,
encrypted_content: encrypted,
category: input.category,
created_at: now.toString(),
updated_at: now.toString(),
};
// Queue for sync
await enqueueSync('gratitude_items', id, 'INSERT', item);
return item;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [FEATURE_KEY] });
},
});
}
export function useUpdateGratitude() {
const { db } = useDatabase();
const queryClient = useQueryClient();
const { enqueueSync } = useSyncQueue();
return useMutation({
mutationFn: async (input: UpdateGratitudeInput): Promise<void> => {
if (!db) throw new Error('Database not initialized');
const now = Date.now();
const updates: string[] = [];
const values: (string | number)[] = [];
if (input.content) {
updates.push('encrypted_content = ?');
values.push(await encryptContent(input.content));
}
if (input.category) {
updates.push('category = ?');
values.push(input.category);
}
updates.push('updated_at = ?');
values.push(now);
values.push(input.id);
await db.runAsync(`UPDATE gratitude_items SET ${updates.join(', ')} WHERE id = ?`, ...values);
// Queue for sync
await enqueueSync('gratitude_items', input.id, 'UPDATE', { id: input.id });
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [FEATURE_KEY] });
},
});
}
export function useDeleteGratitude() {
const { db } = useDatabase();
const queryClient = useQueryClient();
const { enqueueSync } = useSyncQueue();
return useMutation({
mutationFn: async (id: string): Promise<void> => {
if (!db) throw new Error('Database not initialized');
await db.runAsync('DELETE FROM gratitude_items WHERE id = ?', id);
// Queue for sync
await enqueueSync('gratitude_items', id, 'DELETE');
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [FEATURE_KEY] });
},
});
}
```
### Step 4: Create Screens
`src/features/<feature>/screens/FeatureListScreen.tsx`:
```typescript
import { useGratitudeItems, useDeleteGratitude } from '../hooks/useGratitude';
import { GratitudeCard } from '../components/GratitudeCard';
import { EmptyState } from '../../../components/EmptyState';
import { Button } from '../../../components/ui/Button';
export function GratitudeListScreen({ navigation }): React.ReactElement {
const { data: items, isLoading } = useGratitudeItems();
const deleteMutation = useDeleteGratitude();
if (isLoading) return <LoadingScreen />;
return (
<View className="flex-1 bg-slate-900">
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<GratitudeCard
item={item}
onPress={() => navigation.navigate('GratitudeDetail', { id: item.id })}
onDelete={() => deleteMutation.mutate(item.id)}
/>
)}
ListEmptyComponent={
<EmptyState
icon="Heart"
title="No Gratitude Items"
description="Start building your gratitude practice by adding your first item."
/>
}
/>
<FloatingActionButton
onPress={() => navigation.navigate('GratitudeDetail', { id: 'new' })}
/>
</View>
);
}
```
### Step 5: Add Navigation
Update `src/navigation/AppNavigator.tsx`:
```typescript
import { GratitudeListScreen } from '../features/gratitude-list/screens/GratitudeListScreen';
import { GratitudeDetailScreen } from '../features/gratitude-list/screens/GratitudeDetailScreen';
// Add to stack navigator
<Stack.Screen
name="GratitudeList"
component={GratitudeListScreen}
options={{ title: 'Gratitude' }}
/>
<Stack.Screen
name="GratitudeDetail"
component={GratitudeDetailScreen}
options={{ title: 'Gratitude Item' }}
/>
```
Update `src/navigation/types.ts`:
```typescript
export type RootStackParamList = {
// ... existing screens
GratitudeList: undefined;
GratitudeDetail: { id: string };
};
```
## Feature Templates
### Template: Simple List (Gratitude, Affirmations)
Single text field + category/tags.
### Template: Journal Entry (Daily Reflection, Step Work)
Rich text content + mood/feelings + date.
### Template: Checklist (Step Tasks, Daily Goals)
Multiple items with checkboxes + progress tracking.
### Template: Relationship (People, Sponsors)
Contact info + relationship type + notes.
## Best Practices
1. **Always encrypt sensitive content** - Use `encryptContent()` before storing
2. **Queue all mutations** - Call `enqueueSync()` after every write
3. **Invalidate queries** - Use `queryClient.invalidateQueries()` after mutations
4. **Add indexes** - Index `user_id` and `created_at` columns
5. **Test encryption** - Verify roundtrip in generated tests
## Complete Example
See [references/example-output.md](references/example-output.md) for full generated code from `node scaffold.js GratitudeList`.
## Common Feature Patterns
| Feature Type | Command | Use Case |
| ------------- | --------------------------------- | ------------------------------- |
| Simple List | `node scaffold.js GratitudeList` | Gratitude, affirmations, quotes |
| Journal Entry | `node scaffold.js DailyInventory` | Reflections, step work |
| Checklist | `node scaffold.js StepOneTasks` | Step work tasks, goals |
| Relationships | `node scaffold.js SponsorContact` | People, sponsors, contacts |
## Troubleshooting
### Migration number collision
Script auto-detects next migration number. If you have conflicts, manually rename files.
### Missing imports
Add to `tsconfig.json` paths if needed:
```json
"@/features/*": ["./src/features/*"]
```
### Supabase deploy fails
Ensure you're logged in:
```bash
npx supabase login
npx supabase link --project-ref tbiunmmvfbakwlzykpwq
```Related Skills
team-feature
Launch Agent Team for feature implementation with review gates (coders + specialized reviewers + tech lead)
solution-scaffolder
Create new .NET solutions with complete project structure, configurations, and conventions based on the ArticlesSite architecture. Guides users through interactive prompts to scaffold solutions following SOLID principles, clean architecture, and established coding standards.
scaffolder
Generates boilerplate code following loaded rules. Creates new components, modules, APIs, and features that automatically comply with your coding standards. Extracts patterns from rules and applies them consistently.
safe-feature-addition
Elite guide for shipping features without breaking production. Universal patterns for JS, TS, Python, Go, Rust, and more. Implements "Additive over Destructive" development, Unified Safety Auditing, and Feature Flags.
Recovery and Escalation Protocols
This skill should be used when encountering blockers, repeated failures (tests failing 3+ times), stuck states, error conditions, or degraded performance during any workflow phase. Provides systematic recovery procedures, escalation paths, rollback protocols, and debugging guidance to handle failures gracefully without compounding problems.
rails-admin-scaffold
Generate a full-featured CRUD admin panel for Rails 6.1+ applications with auto-detection of CSS frameworks, pagination gems, and smart field mapping
python-development-python-scaffold
You are a Python project architecture expert specializing in scaffolding production-ready Python applications. Generate complete project structures with modern tooling (uv, FastAPI, Django), type hint
project-scaffolder
Guide for setting up Claude Code infrastructure in new or existing projects
javascript-typescript-typescript-scaffold
You are a TypeScript project architecture expert specializing in scaffolding production-ready Node.js and frontend applications. Generate complete project structures with modern tooling (pnpm, Vite, N
full-stack-orchestration-full-stack-feature
Use when working with full stack orchestration full stack feature
frontend-mobile-development-component-scaffold
You are a React component architecture expert specializing in scaffolding production-ready, accessible, and performant components. Generate complete component implementations with TypeScript, tests, s
feature-slicing
Apply Feature-Sliced Design (FSD) architecture to frontend projects. Use when creating new frontend features, components, pages, or restructuring existing code. Triggers on tasks involving React/Next.js/Vue project organization, layer architecture, feature isolation, module boundaries, or when user mentions FSD, feature slicing, or scalable frontend structure.