From apple-dev
Use when the user says 'code audit', 'audit code', '代码审计', 'report card', 'security scan', or wants a comprehensive code quality assessment. Scans for security issues, concurrency safety, accessibility gaps, performance anti-patterns, and SwiftUI anti-patterns in iOS/macOS Swift projects.
npx claudepluginhub n0rvyn/indie-toolkit --plugin apple-devThis skill uses the workspace's default tool permissions.
Comprehensive code quality audit for iOS/macOS Swift projects across 5 categories.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Designs, implements, and audits WCAG 2.2 AA accessible UIs for Web (ARIA/HTML5), iOS (SwiftUI traits), and Android (Compose semantics). Audits code for compliance gaps.
Comprehensive code quality audit for iOS/macOS Swift projects across 5 categories.
Ask user or auto-detect:
.swift files under project rootExclude: Pods/, .build/, DerivedData/, *.generated.swift, test targets (unless user requests test audit).
Record file count for final summary.
Grep for patterns:
"sk-[a-zA-Z0-9]{20,}" — API keyspassword\s*[:=]\s*"[^"]{8,}" — hardcoded passwords (exclude test fixtures and placeholders like "password123" in test targets)AKIA[0-9A-Z]{16} — AWS access keys-----BEGIN.*PRIVATE KEY----- — embedded private keysapi[_-]?key\s*[:=]\s*"[^"]{8,}" — generic API keys[2.1] Hardcoded secrets — 🔴 {pattern} at {file:line} / ✅ none
Grep for:
http:// in URL strings (excluding localhost, 127.0.0.1, 0.0.0.0)allowsArbitraryLoads.*true in Info.plistServerTrust without certificate pinningURLSession custom configurations without TLS minimum version[2.2] Network security — 🔴/🟡 {issue} at {file:line} / ✅ clean
Grep for:
"\(.*)" near SELECT|INSERT|UPDATE|DELETENSPredicate(format: with \( interpolation (should use %@ arguments)WKWebView loading user-provided URLs without scheme validation[2.3] Input validation — 🔴 {issue} at {file:line} / ✅ clean
Check storage patterns:
UserDefaults storing tokens/passwords/keys → 🔴 (should use Keychain)SecItem, KeychainAccess, or custom)[2.4] Sensitive storage — 🔴 UserDefaults for secrets at {file:line} / ✅ Keychain used
Grep for:
Sendable conformance@unchecked Sendable usage → 🟡 flag for manual reviewvar properties in Sendable types without synchronization[3.1] Sendable — 🟡 @unchecked Sendable at {file:line} / 🔴 non-Sendable crossing at {file:line} / ✅ clean
Grep for:
DispatchQueue.main.async in SwiftUI context (should use @MainActor)@MainActor that update UI-bound propertiesnonisolated on properties that access mutable state[3.2] Actor isolation — 🟡/🔴 {issue} at {file:line} / ✅ clean
Grep for:
var properties on non-actor classes accessed from multiple Task {} blocksTask.detached capturing mutable variables[3.3] Data races — 🔴 {issue} at {file:line} / ✅ none detected
Grep for:
Image( without .accessibilityLabel (unless .accessibilityHidden(true)).onTapGesture, .gesture(TapGesture) without accessibility traits[4.1] Accessibility labels — 🟡 missing label at {file:line} / ✅ covered
Check for:
.accessibilityElement(children: .combine) on logical groups.accessibilityAction for custom gestures.accessibilityValue for stateful controls (toggles, sliders, steppers)[4.2] VoiceOver — 🟡 {issue} at {file:line} / ✅ adequate
Grep for:
.font(.system(size: — hardcoded font sizes break Dynamic Type → 🟡.frame(height: on text containers → 🟡.minimumScaleFactor on critical text[4.3] Dynamic Type — 🟡 fixed font size at {file:line} / ✅ dynamic fonts used
Grep for:
DispatchQueue.main.sync — deadlock risk → 🔴Thread.sleep / usleep on main thread → 🔴Data(contentsOf:), String(contentsOfFile:)) in body or @MainActor context[5.1] Main thread — 🔴 blocking at {file:line} / ✅ clean
Grep for:
@ObservedObject used where @StateObject is appropriate (object recreated every parent redraw)@State initialized with expensive computation (runs every view init)body that could benefit from extraction[5.2] Redraws — 🟡 {issue} at {file:line} / ✅ clean
Grep for:
self strongly in long-lived contexts (NotificationCenter.addObserver, Timer.scheduledTimer, stored closures)[weak self] in escaping closures stored as propertiesTimer.publish without cancellation in onDisappear/task[5.3] Memory leaks — 🔴 strong self in {context} at {file:line} / ✅ clean
Grep for:
.onAppear used for async work → 🟡 (should use .task for auto-cancellation).task { } with network calls that don't check Task.isCancelled → 🟡[6.1] Task cancellation — 🟡 .onAppear async at {file:line} / ✅ .task used
Check for:
@ObservedObject for owned objects → should be @StateObject@StateObject in non-View types → invalid@State for reference types without @Observable@EnvironmentObject without .environmentObject() in preview providers[6.2] Property wrappers — 🟡 {issue} at {file:line} / ✅ correct
Check for:
NavigationView (deprecated) → use NavigationStack → 🟡NavigationPath[6.3] Navigation — 🟡 deprecated NavigationView at {file:line} / ✅ modern patterns
Write report to .claude/reviews/audit-{YYYY-MM-DD}.md:
# Code Audit Report
**Date**: {YYYY-MM-DD}
**Scope**: {N} files audited
**Commit**: {HEAD SHA short}
## 🔴 Critical ({N})
- [{category}] {issue}
File: {file:line}
Evidence: {code snippet}
Fix: {specific recommendation}
## 🟡 Warning ({N})
- [{category}] {issue}
File: {file:line}
Evidence: {code snippet}
Fix: {specific recommendation}
## 🟢 Clean ({N} categories)
- {category}: {summary}
## Summary
| Category | 🔴 | 🟡 | 🟢 |
|----------|-----|-----|-----|
| Security | X | Y | Z |
| Concurrency | X | Y | Z |
| Accessibility | X | Y | Z |
| Performance | X | Y | Z |
| SwiftUI | X | Y | Z |
| **Total** | **X** | **Y** | **Z** |
After report is written:
git rev-parse HEAD > .claude/last-audit-commit
This enables the staleness reminder in dev-workflow/hooks/suggest-skills.sh. After 20+ commits, users running /commit, /write-plan, or /write-dev-guide will see a reminder to re-run /code-audit.
file:line and a code snippet. No vague warnings..claude/reviews/audit-{date}.md.claude/last-audit-commit updated with current HEAD SHA