scaffold-composable
Skill16 starsChanged 3 days agoApache-2.0View on GitHub
Generate Pinia-Colada composables for a backend API resource. Creates query and mutation composables following the established defineQuery/defineMutation patterns with SDK integration. Use when user says 'create a composable', 'scaffold composable', 'add query composable', 'generate useQuery hook', 'create mutation composable', 'Pinia-Colada setup', or 'add API composable for resource'. Do NOT use for full pages with composables (use scaffold-frontend-page) or composable code audits (use audit-frontend). Takes a resource name as argument.
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 1 is distinctive, and 14 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
14
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Read the frontend scope guide: packages/web/CLAUDE.md
- Very specific: line 15
If unsure about Pinia-Colada API (defineQuery, defineMutation, useQueryCache, staleTime, etc.), use mcp__context7__query-docs with library …
- Very specific: line 36
If SDK functions don't exist yet, warn the user to run /generate-sdk first.
- Distinctive: line 49
Only create files for SDK operations that actually exist.
- Very specific: line 143
Ensure composable files are in packages/web/composables/{resource}/ for Nuxt auto-import
- Very specific: line 144
Verify every SDK call includes composable: '$fetch'
- Very specific: line 145
Verify query keys use arrow functions: key: () => [...], not static arrays
- Very specific: line 146
Verify mutations call queryCache.invalidateQueries() with the correct list key
- Very specific: line 147
Verify staleTime: minutesToMilliseconds(5) is set on all queries
- Very specific: line 172
{ composable: '$fetch' }: Always pass this to SDK calls (uses Nuxt's $fetch) - Very specific: line 174
staleTime: Use minutesToMilliseconds(5) for standard resources - Very specific: line 175
enabled: Use useTenantReady() for tenant-scoped queries, useRouteReady() when a query only depends on route params (defined at … - Very specific: line 177
Cache invalidation: Call queryCache.invalidateQueries({ key: ['{resource}s'] }) after mutations
- Very specific: line 179
Exports: Always wrap in defineQuery() or defineMutation() (Pinia-Colada composable factories)
- Very specific: line 180
Types: Import DTO types from @core/sdk/client, never define manually
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Scaffold a New Composable
Generate Pinia-Colada composables for a backend API resource. The resource name should be provided via $ARGUMENTS.
Before You Start
Read the frontend scope guide: packages/web/CLAUDE.md
If unsure about Pinia-Colada API (defineQuery, defineMutation, useQueryCache, staleTime, etc.), use
mcp__context7__query-docs with library ID /posva/pinia-colada to look up current documentation.
Study an existing composable directory for reference:
- Simple query:
packages/web/composables/agent/useAgentInstances.ts - Query with route params:
packages/web/composables/agent/useAgentInstance.ts - Mutation with cache invalidation:
packages/web/composables/agent/useCreateAgentInstance.ts - Delete mutation:
packages/web/composables/agent/useDeleteAgentInstance.ts
Step 1: Check SDK Availability
Find the SDK functions and types for the resource in packages/web/sdk/client/. Identify:
- GET list: e.g.,
getAll{Resource}s - GET single: e.g.,
get{Resource} - POST create: e.g.,
create{Resource} - PUT update: e.g.,
update{Resource} - DELETE: e.g.,
delete{Resource} - DTO types: e.g.,
Full{Resource}Dto,Create{Resource}Request
If SDK functions don't exist yet, warn the user to run /generate-sdk first.
Step 2: Create Composable Directory
packages/web/composables/{resource}/
├── use{Resource}s.ts # List query (GET all)
├── use{Resource}.ts # Single item query (GET by ID)
├── useCreate{Resource}.ts # Create mutation (POST)
├── useUpdate{Resource}.ts # Update mutation (PUT)
└── useDelete{Resource}.ts # Delete mutation (DELETE)Only create files for SDK operations that actually exist.
Tenant-scoped resources (everything under pages/[tenant]/service/) take tenant_id as a path parameter, put
['tenant', tenantId, ...] at the start of every query key, and gate enabled with
useTenantReady(...routeParamNames) (see composables/agent/useAgentInstances.ts). Omit the tenant parts only for
resources that are not tenant-scoped.
Step 3: List Query Pattern
import { type Full<Resource>Dto, getAll<Resource>s } from '@core/sdk/client'
import { useQuery } from '@pinia/colada'
import { minutesToMilliseconds } from 'date-fns'
export const use<Resource>s = defineQuery(() => {
const { tenantId } = useTenant()
const { data: <resource>s, isPending: <resource>sAreLoading } = useQuery<Full<Resource>Dto[]>({
key: () => ['tenant', tenantId.value, '<resource>s'],
staleTime: minutesToMilliseconds(5),
enabled: useTenantReady(),
query: async () => {
return await getAll<Resource>s({ composable: '$fetch', path: { tenant_id: tenantId.value! } })
},
})
return {
<resource>s,
<resource>sAreLoading,
}
})Step 4: Single Item Query Pattern (with route params)
import { type Full<Resource>Dto, get<Resource> } from '@core/sdk/client'
import { useQuery } from '@pinia/colada'
import { minutesToMilliseconds } from 'date-fns'
export const use<Resource> = defineQuery(() => {
const route = useRoute()
const { tenantId } = useTenant()
const { data: <resource>, isPending: <resource>IsLoading } = useQuery<Full<Resource>Dto>({
key: () => ['tenant', tenantId.value, '<resource>s', route.params.<resource>_id as string],
staleTime: minutesToMilliseconds(5),
enabled: useTenantReady('<resource>_id'),
query: async () => {
return await get<Resource>({
composable: '$fetch',
path: { tenant_id: tenantId.value!, <resource>_id: route.params.<resource>_id as string },
})
},
})
return {
<resource>,
<resource>IsLoading,
}
})Step 5: Mutation Pattern (create/update/delete)
import { type Create<Resource>Request, create<Resource> } from '@core/sdk/client'
import { useMutation, useQueryCache } from '@pinia/colada'
export const useCreate<Resource> = defineMutation(() => {
const queryCache = useQueryCache()
const {
mutateAsync: create<Resource>Mutation,
isPending: isCreating,
error: createError,
} = useMutation({
mutation: async ({ request, tenantId }: { request: Create<Resource>Request, tenantId: string }) => {
const result = await create<Resource>({
composable: '$fetch',
path: { tenant_id: tenantId },
body: request,
})
queryCache.invalidateQueries({ key: ['tenant', tenantId, '<resource>s'] })
return result
},
})
return {
create<Resource>: create<Resource>Mutation,
isCreating,
createError,
}
})Step 6: Verify
- Ensure composable files are in
packages/web/composables/{resource}/for Nuxt auto-import - Verify every SDK call includes
composable: '$fetch' - Verify query keys use arrow functions:
key: () => [...], not static arrays - Verify mutations call
queryCache.invalidateQueries()with the correct list key - Verify
staleTime: minutesToMilliseconds(5)is set on all queries
Examples
Typical invocation: /scaffold-composable pipeline
Result: Creates composable files in packages/web/composables/pipeline/:
usePipelines.ts— list queryusePipeline.ts— single item query with route paramsuseCreatePipeline.ts— create mutation with cache invalidationuseDeletePipeline.ts— delete mutation
Troubleshooting
| Problem | Solution |
| SDK functions not found | Run /generate-sdk first to regenerate the client SDK |
| Query never resolves | Check enabled flag — use useTenantReady() (tenant-scoped) or useRouteReady() for route-dependent queries |
| Stale data after mutation | Ensure queryCache.invalidateQueries({ key: ['resources'] }) is called |
| Type errors on DTO imports | Regenerate SDK — types may be outdated |
| Composable not auto-imported | Nuxt auto-imports from composables/ — ensure file is in the right directory |
Key Conventions
{ composable: '$fetch' }: Always pass this to SDK calls (uses Nuxt's$fetch)- Query keys: Hierarchical arrays
['{resource}s'],['{resource}s', id] staleTime: UseminutesToMilliseconds(5)for standard resourcesenabled: UseuseTenantReady()for tenant-scoped queries,useRouteReady()when a query only depends on route params (defined atpackages/web/composables/useRouteReady.ts)- Cache invalidation: Call
queryCache.invalidateQueries({ key: ['{resource}s'] })after mutations - Naming:
use{Resource}s(plural list),use{Resource}(single),useCreate{Resource}(mutation) - Exports: Always wrap in
defineQuery()ordefineMutation()(Pinia-Colada composable factories) - Types: Import DTO types from
@core/sdk/client, never define manually
In this repository50 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- .github/actions/CLAUDE.mdCLAUDE.md
- arc42Skill
- audit-frontendSkill
- bot-frameworkSkill
- create-or-audit-claude-mdSkill
- create-or-audit-skillSkill
- create-or-audit-subagentSkill
- create-prSkill
- dagster-pipelinesSkill
- debug-agentSkill
- debug-frontendSkill
- debug-pipelineSkill
- design-systemSkill
- docker-devSkill
- document-decisionSkill
- document-featureSkill
- explainSkill
- generate-sdkSkill
- i18nSkill
- implement-feedback-from-prSkill
- likec4-dslSkill
- lintSkill
- merge-mainSkill
- nats-eventsSkill
- plan-issueSkill
- pr-demo-videoSkill
- primevue-lookupSkill
- rclone-guideSkill
- reflectSkill
- review-diffSkill
- scaffold-agentSkill
- scaffold-api-endpointSkill
- scaffold-api-repositorySkill
- scaffold-api-serviceSkill
- scaffold-bot-handlerSkill
- scaffold-event-displaySkill
- scaffold-frontend-componentSkill
- scaffold-frontend-pageSkill
- scaffold-frontend-subpageSkill
- scaffold-pipelineSkill
- setup-bot-connectionSkill
- splice-issueSkill
- sync-agent-docsSkill
- test-scopeSkill
- update-docSkill
- verify-sdk-syncSkill
- write-docSkill
- write-issueSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

