audit-frontend
Skill16 starsChanged 3 days agoApache-2.0View on GitHub
Run a comprehensive frontend code audit of the Nuxt 3 admin interface. Checks Pinia-Colada query/mutation patterns, TypeScript strictness, component architecture, SDK freshness, i18n coverage, and Tailwind usage. Use when user says 'audit the frontend', 'check frontend code quality', 'find unused components', 'composable health check', 'pinia-colada audit', 'typescript errors', or 'frontend code review'. Do NOT use for backend API audits (use scaffold-api-endpoint) or bot UI issues (use bot-framework). Reports issues with file locations and severity levels.
We split this skill into the rules it gives an agent
21 rules. About 0 are common, 2 are distinctive, and 19 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 9
Run a comprehensive audit of the Nuxt 3 admin interface at packages/web/.
- Very specific: line 12
Default scope is all if no argument provided.
- Very specific: line 14
Before starting, read packages/web/CLAUDE.md for the full frontend conventions reference.
- Very specific: line 20
Check all files in packages/web/composables/ against these rules:
- Very specific: line 24
Every exported query composable must use defineQuery(), every mutation must use defineMutation().
- Very specific: line 49
Every useQuery must have staleTime: minutesToMilliseconds(N) (from date-fns).
- Very specific: line 61
Every SDK function call must include composable: '$fetch' — this is required by the @hey-api/client-nuxt adapter.
- Very specific: line 73
If a query uses route.params.*, it must have enabled: useRouteReady('param_name') to prevent firing before the route resolves.
- Very specific: line 80
Every mutation should call queryCache.invalidateQueries() — either inside the mutation function or in onSettled.
- Very specific: line 81
Prefer onSettled over onSuccess for cache invalidation (runs regardless of success/failure).
- Very specific: line 188
Every bg-surface-*, text-surface-*, border-surface-* class should have a dark: variant pair:
- Very specific: line 212
Check that every file directly under pages/[tenant]/service/*.vue wraps content in StructuralScreen with a NuxtPage for detail routing.
- Very specific: line 258
All composables from composables/ and composables//
- Distinctive: line 274
Check if API is accessible:
- Very specific: line 280
If accessible, compare OpenAPI spec endpoints with packages/web/sdk/client/sdk.gen.ts:
- Very specific: line 284
If not accessible, compare timestamps of SDK files vs latest packages/api/ changes:
- Very specific: line 291
Verify SDK is never manually edited — check git status for modifications in sdk/client/.
- Very specific: line 297
Read all locale files: packages/web/i18n/locales/{de,en,fr,it}.yaml
- Very specific: line 298
Use English as reference — find keys missing in de/fr/it
- Distinctive: line 300
Check for empty string values
- Very specific: line 330
Verify that surface color tokens use PrimeVue semantic names (surface-50 through surface-950) rather than Tailwind gray/slate/zinc …
≈ “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
Frontend Code Audit
Run a comprehensive audit of the Nuxt 3 admin interface at packages/web/. Scope via $ARGUMENTS: all,
composables, typescript, components, pages, sdk, i18n, tailwind.
Default scope is all if no argument provided.
Before starting, read packages/web/CLAUDE.md for the full frontend conventions reference.
Audit 1: Pinia-Colada Composables (scope: composables)
Check all files in packages/web/composables/ against these rules:
1a. defineQuery/defineMutation wrappers (ERROR)
Every exported query composable must use defineQuery(), every mutation must use defineMutation(). Bare useQuery or
useMutation in a plain exported function is an anti-pattern — it breaks singleton caching.
# Find bare useQuery not wrapped in defineQuery
grep -rn "export.*=.*(" packages/web/composables/ --include="*.ts" | grep -v defineQuery | grep -v defineMutationCross-check: if a file imports useQuery from @pinia/colada but doesn't call defineQuery, flag it.
1b. Query keys must be functions (ERROR)
Keys must be arrow functions key: () => [...], never static arrays key: [...]. Static keys don't react to route or
ref changes.
# Find static query keys (array literal without arrow function)
grep -rn "key: \[" packages/web/composables/ --include="*.ts"Correct: key: () => ['agent-instances', route.params.agent_class] Wrong:
key: ['agent-instances', route.params.agent_class]
1c. staleTime must use minutesToMilliseconds (ERROR)
Every useQuery must have staleTime: minutesToMilliseconds(N) (from date-fns). Standard is 5 minutes. Missing
staleTime means the global default applies, which may not be intentional.
# Find useQuery calls without staleTime
grep -rn "useQuery" packages/web/composables/ --include="*.ts" -A 5 | grep -v staleTimeHardcoded milliseconds like staleTime: 300000 are also wrong — use the date-fns helper.
1d. composable: '$fetch' on every SDK call (ERROR)
Every SDK function call must include composable: '$fetch' — this is required by the @hey-api/client-nuxt adapter.
Missing it causes the request to use raw fetch instead of Nuxt's $fetch, breaking auth token injection.
# Find SDK calls without composable: '$fetch'
grep -rn "await \(get\|create\|update\|delete\|post\|put\|patch\)" packages/web/composables/ --include="*.ts"Cross-check each SDK call has composable: '$fetch' in its options object.
1e. Route-dependent queries must use useRouteReady (WARNING)
If a query uses route.params.*, it must have enabled: useRouteReady('param_name') to prevent firing before the route
resolves. During route transitions, params can briefly be template placeholders like '{agent_id}'.
Reference implementation: packages/web/composables/useRouteReady.ts
1f. Mutations must invalidate query cache (WARNING)
Every mutation should call queryCache.invalidateQueries() — either inside the mutation function or in onSettled.
Prefer onSettled over onSuccess for cache invalidation (runs regardless of success/failure). Mutations should
invalidate both broad keys (['agents']) and specific keys (['agents', agentClass, agentId]).
1g. Named exports, not default (WARNING)
Composables must use export const useFoo = defineQuery(...), not export default defineQuery(...). Default exports
lose the explicit name and rely on filename inference for auto-import.
# Find default exports in composables
grep -rn "export default" packages/web/composables/ --include="*.ts"1h. Query key naming conventions (WARNING)
Keys should be kebab-case string arrays: ['agent-classes'], ['agent-instances', agentClass, agentId]. Check for:
- Snake_case keys (
['my_user']should be['my-user']) - Keys that don't include all reactive inputs used in the query function
Audit 2: TypeScript Strictness (scope: typescript)
2a. Untyped function parameters (ERROR)
Find callback parameters without type annotations (implicit any).
# Find untyped arrow function params in .vue and .ts files
grep -rn "=> {" packages/web/pages/ packages/web/components/ --include="*.vue" --include="*.ts" | grep "(event)" | grep -v ": "Common offender: const handleClick = (event) => { should be (event: MouseEvent) => {.
2b. Type imports must use import type (WARNING)
SDK types should use import type { FooDto } not import { FooDto }. Regular imports pull in runtime code; type
imports are erased at compile time.
# Find non-type imports of SDK types (Dto suffix = type)
grep -rn "import {.*Dto" packages/web/ --include="*.vue" --include="*.ts" | grep -v "import type"Exception: types re-exported as values (e.g., export type AgentClassDto = AgentClassDtoReadable) are fine.
2c. Route params must be cast (WARNING)
route.params.foo is string | string[]. Always cast to string when passing to SDK functions:
route.params.agent_id as string. Flag bare route.params.foo passed directly to functions without cast.
2d. Computed vs function naming (WARNING)
Computed refs named with get prefix (like getEmptyStateTitle = computed(...)) suggest a function but are reactive
refs. Either name them as nouns (emptyStateTitle) or make them actual functions.
2e. console.log in production code (WARNING)
grep -rn "console\.\(log\|debug\|info\)" packages/web/ --include="*.vue" --include="*.ts" \
| grep -v "node_modules" | grep -v "sdk/"console.error in global error handlers (app.vue) is acceptable. console.log in pages/components is not.
Audit 3: Component Architecture (scope: components)
3a. Unused components (WARNING)
# For each .vue file in components/, derive auto-import name and search for usage
find packages/web/components/ -name "*.vue" | while read f; do
# Agent/Card.vue → AgentCard
NAME=$(echo "$f" | sed 's|.*/components/||; s|\.vue$||; s|/||g')
USAGE=$(grep -rl "$NAME" packages/web/ --include="*.vue" --include="*.ts" | grep -v "$f" | wc -l)
[ "$USAGE" -eq 0 ] && echo "UNUSED: $f ($NAME)"
done3b. Props/emits typing pattern (WARNING)
Components should use:
withDefaults(defineProps<{...}>(), {...})for props with defaultsdefineEmits<{ eventName: [argType] }>()for typed emits (Vue 3.3+ named tuple form)defineProps<{...}>()for props without defaults
Flag components using the options API syntax (props: { ... }) or untyped defineEmits(['click']).
3c. v-model bridging pattern (INFO)
For v-model on custom components, check for the standard pattern:
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value),
})Flag cases where modelValue prop exists but the writable computed bridge is missing.
3d. Dark mode completeness (WARNING)
Every bg-surface-*, text-surface-*, border-surface-* class should have a dark: variant pair:
- Light:
bg-surface-50/100/200, Dark:dark:bg-surface-800/900/950 - Light:
text-surface-500/600/700, Dark:dark:text-surface-300/400 - Light:
border-surface-200/300, Dark:dark:border-surface-700/800
# Find surface classes without dark: companion on the same element
grep -rn "bg-surface-\(50\|100\|200\)" packages/web/components/ --include="*.vue" \
| grep -v "dark:bg-surface"Audit 4: Page Architecture (scope: pages)
4a. StructuralScreen/Column pattern (ERROR)
List pages must follow:
StructuralScreen > StructuralColumn + NuxtPageCheck that every file directly under pages/[tenant]/service/*.vue wraps content in StructuralScreen with a
NuxtPage for detail routing.
grep -rL "StructuralScreen" packages/web/pages/\[tenant\]/service/*.vue4b. tenantPath for all navigation (ERROR)
All router.push(), navigateTo(), and NuxtLink :to must use useTenantPath() (it wraps useLocalePath() and adds
the [tenant] segment). Bare paths like router.push('/service/agents') break locale and tenant prefixing.
# Find bare path navigation without tenantPath/localePath
grep -rn "router\.push\|navigateTo" packages/web/pages/ packages/web/components/ --include="*.vue" \
| grep -v tenantPath | grep -v localePath | grep -v "//.*router"Also check <NuxtLink :to=" attributes — they should wrap paths in tenantPath(). Sysadmin pages (/tenants/...) are
not tenant-scoped and use useLocalePath().
4c. NavItem isActive must be a closure (ERROR)
In hub pages (e.g., [agent_class]-[agent_id].vue), NavItem.isActive must be a function returning boolean, not a
boolean value. The SelectButton calls isActive() dynamically.
// Correct: closure
isActive: () => route.path.startsWith(tenantPath(subPath('overview')))
// Wrong: evaluated once
isActive: route.path.startsWith(tenantPath(subPath('overview')))4d. Detail pages use close-route (WARNING)
Leaf pages (inside [param]/) should pass close-route to StructuralColumn for the back button. The path should be
bare (not wrapped in tenantPath — StructuralColumn applies it internally).
4e. Unnecessary explicit imports (WARNING)
In <script setup>, these are auto-imported and must NOT be explicitly imported:
- Vue:
ref,computed,watch,onMounted,nextTick - Vue Router:
useRoute,useRouter,navigateTo - i18n:
useI18n - PrimeVue:
useConfirm,useToast - All composables from
composables/andcomposables/**/
# Find unnecessary imports in pages and components
grep -rn "import.*from 'vue'" packages/web/pages/ packages/web/components/ --include="*.vue"
grep -rn "import.*from 'vue-router'" packages/web/pages/ --include="*.vue"
grep -rn "import.*from 'vue-i18n'" packages/web/pages/ --include="*.vue"
grep -rn "import.*from 'primevue/use" packages/web/pages/ --include="*.vue"Exception: useLocalePath from '#i18n' DOES need explicit import (useTenantPath is auto-imported).
Audit 5: SDK Freshness (scope: sdk)
- Check if API is accessible:
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/api/v1/active/openapi.jsonIf accessible, compare OpenAPI spec endpoints with
packages/web/sdk/client/sdk.gen.ts:- Report: new endpoints missing from SDK, removed endpoints still in SDK
- If not accessible, compare timestamps of SDK files vs latest
packages/api/changes:
stat -c %Y packages/web/sdk/client/sdk.gen.ts
git log -1 --format=%ct packages/api/- Verify SDK is never manually edited — check git status for modifications in
sdk/client/.
Audit 6: i18n Coverage (scope: i18n)
- Read all locale files:
packages/web/i18n/locales/{de,en,fr,it}.yaml - Use English as reference — find keys missing in de/fr/it
- Find keys in de/fr/it that don't exist in en (orphaned)
- Check for empty string values
- Scan
.vuefiles for$t('...')andt('...')calls — find keys used in code but missing from en.yaml - Report coverage per language as a percentage
Note: some keys are dynamically constructed with template literals — flag them as unverifiable, not missing.
Audit 7: Tailwind and Styling (scope: tailwind)
7a. Style blocks should be minimal (WARNING)
<style> blocks are acceptable only for PrimeVue deep overrides (:deep(.p-panel-header)). Flag large style blocks
(more than 10 lines) or style blocks not using :deep() — they should use Tailwind classes instead.
# Find style blocks and count lines
grep -rn "<style" packages/web/components/ packages/web/pages/ --include="*.vue"7b. Inline styles (WARNING)
grep -rn 'style="' packages/web/components/ packages/web/pages/ --include="*.vue" | grep -v ":style"Static style="" should be Tailwind classes. Dynamic :style for computed values is acceptable.
7c. PrimeVue surface tokens (INFO)
Verify that surface color tokens use PrimeVue semantic names (surface-50 through surface-950) rather than Tailwind
gray/slate/zinc (gray-100, slate-200). PrimeVue surface tokens respect the theme.
grep -rn "text-\(gray\|slate\|zinc\|neutral\|stone\)-" packages/web/ --include="*.vue"
grep -rn "bg-\(gray\|slate\|zinc\|neutral\|stone\)-" packages/web/ --include="*.vue"Report Format
Present findings as a summary table:
| Audit Area | Status | Issues Found | Severity |
| Composables | pass/warn/fail | Count | High |
| TypeScript | pass/warn/fail | Count | High |
| Components | pass/warn | Count | Medium |
| Pages | pass/warn/fail | Count | High |
| SDK Freshness | pass/warn/fail | Count | Medium |
| i18n Coverage | pass/warn/fail | Count + % | Medium |
| Tailwind/Styling | pass/warn | Count | Low |
Then list each issue grouped by audit area with: file path, line number, issue description, suggested fix.
Examples
/audit-frontend all-- Run all 7 audits and produce summary table/audit-frontend composables-- Deep check on Pinia-Colada patterns (defineQuery, keys, staleTime, cache)/audit-frontend typescript-- Find type errors, implicit any, missing type imports/audit-frontend pages-- Check page architecture, tenantPath usage, NavItem patterns/audit-frontend i18n-- Cross-check translation keys across de/en/fr/it locales
Troubleshooting
| Issue | Resolution |
| API unreachable for SDK check | Skip SDK freshness audit, report as "skipped (API unreachable)" |
| Too many unused components | Verify auto-import naming: Agent/Card.vue becomes AgentCard |
| False positive on i18n keys | Some keys are dynamically constructed — check for template literals |
Composable uses bare useQuery | Flag as ERROR — should be wrapped in defineQuery for singleton caching |
useLocalePath flagged as unnecessary | This IS required — it must be imported from '#i18n' |
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- SecurityMindedSolutions/ai-skills★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository50 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- .github/actions/CLAUDE.mdCLAUDE.md
- arc42Skill
- 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-composableSkill
- 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.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
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.

