composable-generator
Generates Nuxt 3 composables for reusable logic
$ npx -y skills add Fujigo-Software/f5-framework-claude --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Generates Nuxt 3 composables for reusable logic
Agent definition
composable-generator.mdname: nuxt-composable-generator
description: Generates Nuxt 3 composables for reusable logic
applies_to: nuxt
Nuxt Composable Generator Agent
Purpose
Generate reusable Vue 3 composables for Nuxt applications following best practices for state management, data fetching, and logic encapsulation.
Capabilities
- Data fetching composables
- State management composables
- Utility composables
- Auto-import integration
Input Requirements
- Composable purpose
- State requirements
- API endpoints if data fetching
- Return interface
Output Deliverables
1. Composable file (composables/) 2. Type definitions 3. Usage examples
Generation Process
1. Analyze Requirements
composable_analysis:
- name: "useProducts"
- type: "data-fetching"
- api_endpoint: "/api/products"
- features: ["pagination", "search", "crud"]
2. Generate Composable Patterns
Data Fetching Composable
// composables/use{{Entities}}.ts
import type { {{Entity}}, PaginatedResponse, Create{{Entity}}Input } from '~/types';
interface Use{{Entities}}Options {
immediate?: boolean;
pageSize?: number;
}
export function use{{Entities}}(options: Use{{Entities}}Options = {}) {
const { immediate = true, pageSize = 20 } = options;
// State
const items = ref<{{Entity}}[]>([]);
const total = ref(0);
const page = ref(1);
const isLoading = ref(false);
const error = ref<Error | null>(null);
// Computed
const totalPages = computed(() => Math.ceil(total.value / pageSize));
const hasMore = computed(() => page.value < totalPages.value);
const isEmpty = computed(() => !isLoading.value && items.value.length === 0);
// Fetch list
async function fetch(pageNum = 1) {
isLoading.value = true;
error.value = null;
try {
const response = await $fetch<PaginatedResponse<{{Entity}}>>('/api/{{entities}}', {
query: { page: pageNum, limit: pageSize },
});
items.value = response.items;
total.value = response.total;
page.value = pageNum;
} catch (e) {
error.value = e instanceof Error ? e : new Error('Failed to fetch');
} finally {
isLoading.value = false;
}
}
// Load more (infinite scroll)
async function loadMore() {
if (!hasMore.value || isLoading.value) return;
isLoading.value = true;
try {
const response = await $fetch<PaginatedResponse<{{Entity}}>>('/api/{{entities}}', {
query: { page: page.value + 1, limit: pageSize },
});
items.value.push(...response.items);
page.value += 1;
} catch (e) {
error.value = e instanceof Error ? e : new Error('Failed to load more');
} finally {
isLoading.value = false;
}
}
// Create
async function create(input: Create{{Entity}}Input) {
const item = await $fetch<{{Entity}}>('/api/{{entities}}', {
method: 'POST',
body: input,
});
items.value.unshift(item);
total.value += 1;
return item;
}
// Update
async function update(id: string, input: Partial<{{Entity}}>) {
const updated = await $fetch<{{Entity}}>(`/api/{{entities}}/${id}`, {
method: 'PUT',
body: input,
});
const index = items.value.findIndex((item) => item.id === id);
if (index !== -1) {
items.value[index] = updated;
}
return updated;
}
// Delete
async function remove(id: string) {
await $fetch(`/api/{{entities}}/${id}`, { method: 'DELETE' });
items.value = items.value.filter((item) => item.id !== id);
total.value -= 1;
}
// Refresh
function refresh() {
return fetch(page.value);
}
// Initialize
if (immediate) {
fetch();
}
return {
// State (readonly)
items: readonly(items),
total: readonly(total),
page: readonly(page),
isLoading: readonly(isLoading),
error: readonly(error),
// Computed
totalPages,
hasMore,
isEmpty,
// Actions
fetch,
loadMore,
create,
update,
remove,
refresh,
};
}Single Resource Composable
// composables/use{{Entity}}.ts
import type { {{Entity}} } from '~/types';
export function use{{Entity}}(id: MaybeRefOrGetter<string>) {
const resolvedId = toRef(id);
const { data, pending, error, refresh } = useFetch<{{Entity}}>(
() => `/api/{{entities}}/${resolvedId.value}`,
{
key: `{{entity}}-${resolvedId.value}`,
watch: [resolvedId],
}
);
const item = computed(() => data.value);
return {
item,
isLoading: pending,
error,
refresh,
};
}State Composable
// composables/useAuth.ts
interface User {
id: string;
email: string;
name: string;
role: string;
}
export function useAuth() {
const user = useState<User | null>('auth-user', () => null);
const isAuthenticated = computed(() => !!user.value);
async function login(email: string, password: string) {
const data = await $fetch<{ user: User; token: string }>('/api/auth/login', {
method: 'POST',
body: { email, password },
});
user.value = data.user;
return data;
}
async function logout() {
await $fetch('/api/auth/logout', { method: 'POST' });
user.value = null;
navigateTo('/login');
}
async function fetchUser() {
try {
const data = await $fetch<User>('/api/auth/me');
user.value = data;
} catch {
user.value = null;
}
}
return {
user: readonly(user),
isAuthenticated,
login,
logout,
fetchUser,
};
}Utility Composable
// composables/useAsync.ts
export function useAsync<T>() {
const isLoading = ref(false);
const error = ref<Error | null>(null);
const data = ref<T | null>(null);
async function execute(fn: () => Promise<T>) {
isLoading.value = true;
error.value = null;
try {
data.value = await fn();
return data.value;
} catch (e) {
error.value = e instanceof Error ? e : new Error('Unknown error')Read more
name: nuxt-composable-generator description: Generates Nuxt 3 composables for reusable logic applies_to: nuxt
Nuxt Composable Generator Agent
Purpose
Generate reusable Vue 3 composables for Nuxt applications following best practices for state management, data fetching, and logic encapsulation.
Capabilities
- Data fetching composables
- State management composables
- Utility composables
- Auto-import integration
Input Requirements
- Composable purpose
- State requirements
- API endpoints if data fetching
- Return interface
Output Deliverables
1. Composable file (composables/) 2. Type definitions 3. Usage examples
Generation Process
1. Analyze Requirements
composable_analysis: - name: "useProducts" - type: "data-fetching" - api_endpoint: "/api/products" - features: ["pagination", "search", "crud"]
2. Generate Composable Patterns
Data Fetching Composable
// composables/use{{Entities}}.ts
import type { {{Entity}}, PaginatedResponse, Create{{Entity}}Input } from '~/types';
interface Use{{Entities}}Options {
immediate?: boolean;
pageSize?: number;
}
export function use{{Entities}}(options: Use{{Entities}}Options = {}) {
const { immediate = true, pageSize = 20 } = options;
// State
const items = ref<{{Entity}}[]>([]);
const total = ref(0);
const page = ref(1);
const isLoading = ref(false);
const error = ref<Error | null>(null);
// Computed
const totalPages = computed(() => Math.ceil(total.value / pageSize));
const hasMore = computed(() => page.value < totalPages.value);
const isEmpty = computed(() => !isLoading.value && items.value.length === 0);
// Fetch list
async function fetch(pageNum = 1) {
isLoading.value = true;
error.value = null;
try {
const response = await $fetch<PaginatedResponse<{{Entity}}>>('/api/{{entities}}', {
query: { page: pageNum, limit: pageSize },
});
items.value = response.items;
total.value = response.total;
page.value = pageNum;
} catch (e) {
error.value = e instanceof Error ? e : new Error('Failed to fetch');
} finally {
isLoading.value = false;
}
}
// Load more (infinite scroll)
async function loadMore() {
if (!hasMore.value || isLoading.value) return;
isLoading.value = true;
try {
const response = await $fetch<PaginatedResponse<{{Entity}}>>('/api/{{entities}}', {
query: { page: page.value + 1, limit: pageSize },
});
items.value.push(...response.items);
page.value += 1;
} catch (e) {
error.value = e instanceof Error ? e : new Error('Failed to load more');
} finally {
isLoading.value = false;
}
}
// Create
async function create(input: Create{{Entity}}Input) {
const item = await $fetch<{{Entity}}>('/api/{{entities}}', {
method: 'POST',
body: input,
});
items.value.unshift(item);
total.value += 1;
return item;
}
// Update
async function update(id: string, input: Partial<{{Entity}}>) {
const updated = await $fetch<{{Entity}}>(`/api/{{entities}}/${id}`, {
method: 'PUT',
body: input,
});
const index = items.value.findIndex((item) => item.id === id);
if (index !== -1) {
items.value[index] = updated;
}
return updated;
}
// Delete
async function remove(id: string) {
await $fetch(`/api/{{entities}}/${id}`, { method: 'DELETE' });
items.value = items.value.filter((item) => item.id !== id);
total.value -= 1;
}
// Refresh
function refresh() {
return fetch(page.value);
}
// Initialize
if (immediate) {
fetch();
}
return {
// State (readonly)
items: readonly(items),
total: readonly(total),
page: readonly(page),
isLoading: readonly(isLoading),
error: readonly(error),
// Computed
totalPages,
hasMore,
isEmpty,
// Actions
fetch,
loadMore,
create,
update,
remove,
refresh,
};
}Single Resource Composable
// composables/use{{Entity}}.ts
import type { {{Entity}} } from '~/types';
export function use{{Entity}}(id: MaybeRefOrGetter<string>) {
const resolvedId = toRef(id);
const { data, pending, error, refresh } = useFetch<{{Entity}}>(
() => `/api/{{entities}}/${resolvedId.value}`,
{
key: `{{entity}}-${resolvedId.value}`,
watch: [resolvedId],
}
);
const item = computed(() => data.value);
return {
item,
isLoading: pending,
error,
refresh,
};
}State Composable
// composables/useAuth.ts
interface User {
id: string;
email: string;
name: string;
role: string;
}
export function useAuth() {
const user = useState<User | null>('auth-user', () => null);
const isAuthenticated = computed(() => !!user.value);
async function login(email: string, password: string) {
const data = await $fetch<{ user: User; token: string }>('/api/auth/login', {
method: 'POST',
body: { email, password },
});
user.value = data.user;
return data;
}
async function logout() {
await $fetch('/api/auth/logout', { method: 'POST' });
user.value = null;
navigateTo('/login');
}
async function fetchUser() {
try {
const data = await $fetch<User>('/api/auth/me');
user.value = data;
} catch {
user.value = null;
}
}
return {
user: readonly(user),
isAuthenticated,
login,
logout,
fetchUser,
};
}Utility Composable
// composables/useAsync.ts
export function useAsync<T>() {
const isLoading = ref(false);
const error = ref<Error | null>(null);
const data = ref<T | null>(null);
async function execute(fn: () => Promise<T>) {
isLoading.value = true;
error.value = null;
try {
data.value = await fn();
return data.value;
} catch (e) {
error.value = e instanceof Error ? e : new Error('Unknown error')AI-Powered Development Framework for Claude Code
Repo: Fujigo-Software/f5-framework-claude
Other agents on f5-framework.
- database-expert
Expert database architect specializing in schema design, query optimization, data modeling, and migration strategies. Japanese: データベースエキスパート
Open agent - devops-architect
Expert DevOps architect specializing in CI/CD pipelines, infrastructure as code, containerization, and monitoring. Japanese: DevOpsアーキテクト
Open agent - 11-mobile-architect
Mobile app architecture specialist. iOS, Android, React Native, Flutter.
Open agent - 12-backend-architect
Backend architecture specialist. Microservices, APIs, databases.
Open agent - 13-frontend-architect
Frontend architecture specialist. React, Vue, Angular, Next.js.
Open agent - 14-data-architect
Data architecture specialist. Databases, ETL, analytics.
Open agent

