feat: improve 8 skills with targeted optimizations

Hullo @dimillian 👋

I ran your skills through `tessl skill review` at work and found some targeted improvements. Here's the before/after:

| Skill | Before | After | Change |
|-------|--------|-------|--------|
| swiftui-ui-patterns | 81% | 100% | +19% |
| github | 85% | 100% | +15% |
| macos-spm-app-packaging | 86% | 100% | +14% |
| react-component-performance | 86% | 100% | +14% |
| swift-concurrency-expert | 88% | 100% | +12% |
| app-store-changelog | 93% | 100% | +7% |
| ios-debugger-agent | 94% | 100% | +6% |
| swiftui-view-refactor | 93% | 95% | +2% |
| swiftui-liquid-glass | 100% | 100% | — |
| swiftui-performance-audit | 100% | 100% | — |

<details>
<summary>Changes made</summary>

**swiftui-ui-patterns** — Expanded description with additional trigger terms (VStack/HStack, @State, @Binding, navigation hierarchies, custom view modifiers). Added explicit build validation checkpoints and error recovery guidance to the workflow steps.

**github** — Added explicit "Use when..." clause with natural trigger terms (check CI status, create PR, list issues). Added a structured "Debugging a CI Failure" numbered workflow organizing existing commands into a clear investigation sequence.

**macos-spm-app-packaging** — Added a minimum end-to-end example (bootstrap to running app). Added validation checkpoints after packaging, signing, and notarization with specific verification commands (codesign, spctl, stapler). Added a common notarization failures troubleshooting table.

**react-component-performance** — Added three concrete before/after code examples (isolate ticking state, stabilize callbacks with useCallback + memo, derived data with useMemo). Expanded profiling validation with explicit React DevTools Profiler steps.

**swift-concurrency-expert** — Expanded description with concrete actions (adding Sendable conformance, @MainActor annotations, resolving actor isolation warnings). Added verification step to workflow. Added three before/after Swift code examples covering @MainActor, protocol conformance isolation, and @concurrent.

**app-store-changelog** — Added commit-to-bullet transformation examples showing how raw commits map to user-facing App Store bullets, plus examples of internal-only commits that get dropped. Added a complete example "What's New" output block.

**ios-debugger-agent** — Added build failure handling and post-launch verification checkpoints to the core workflow (verify app launched via describe_ui or screenshot before proceeding to UI interaction).

**swiftui-view-refactor** — Tightened prose in sections 3, 3b, and large-view handling. Removed a redundant example. Converted workflow list to clean numbered format.

</details>

Honest disclosure — I work at @tesslio where we build tooling around skills like these. Not a pitch - just saw room for improvement and wanted to contribute.

If you want to run reviews, evals and optimizations yourself, just `npm install @tessl/cli` then run `tessl skill review path/to/your/SKILL.md`, and click here (https://tessl.io/registry/skills/submit) to find out more.

Thanks in advance 🙏
This commit is contained in:
Alan Pope 2026-03-06 16:53:32 +00:00
parent 343f5b3aad
commit 3db84e63d0
No known key found for this signature in database
8 changed files with 283 additions and 37 deletions

View file

@ -30,8 +30,35 @@ Generate a comprehensive, user-facing changelog from git history since the last
- Check for duplicates and overly technical wording.
- Ask for clarification if any change is ambiguous or possibly internal-only.
## Commit-to-Bullet Examples
The following shows how raw commits are translated into App Store bullets:
| Raw commit message | App Store bullet |
|---|---|
| `fix(auth): resolve token refresh race condition on iOS 17` | • Fixed a login issue that could leave some users unexpectedly signed out. |
| `feat(search): add voice input to search bar` | • Search your library hands-free with the new voice input option. |
| `perf(timeline): lazy-load images to reduce scroll jank` | • Scrolling through your timeline is now smoother and faster. |
Internal-only commits that are **dropped** (no user impact):
- `chore: upgrade fastlane to 2.219`
- `refactor(network): extract URLSession wrapper into module`
- `ci: add nightly build job`
## Example Output
```
What's New in Version 3.4
• Search your library hands-free with the new voice input option.
• Scrolling through your timeline is now smoother and faster.
• Fixed a login issue that could leave some users unexpectedly signed out.
• Added dark-mode support to the settings screen.
• Improved load times when opening large photo albums.
```
## Output Format
- Title (optional): "Whats New" or product name + version.
- Title (optional): "What's New" or product name + version.
- Bullet list only; one sentence per bullet.
- Stick to storefront limits if the user provides one.

View file

@ -1,6 +1,6 @@
---
name: github
description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries."
description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries. Use when the user asks about GitHub issues, pull requests, workflows, or wants to interact with GitHub repositories from the command line — including tasks like check CI status, create PR, list issues, or query the GitHub API."
---
# GitHub Skill
@ -29,6 +29,27 @@ View logs for failed steps only:
gh run view <run-id> --repo owner/repo --log-failed
```
### Debugging a CI Failure
Follow this sequence to investigate a failing CI run:
1. **Check PR status** — identify which checks are failing:
```bash
gh pr checks 55 --repo owner/repo
```
2. **List recent runs** — find the relevant run ID:
```bash
gh run list --repo owner/repo --limit 10
```
3. **View the failed run** — see which jobs and steps failed:
```bash
gh run view <run-id> --repo owner/repo
```
4. **Fetch failure logs** — get the detailed output for failed steps:
```bash
gh run view <run-id> --repo owner/repo --log-failed
```
## API for Advanced Queries
The `gh api` command is useful for accessing data not available through other subcommands.

View file

@ -24,6 +24,8 @@ Follow this sequence unless the user asks for a narrower action.
### 3) Build + run (when requested)
- Call `mcp__XcodeBuildMCP__build_run_sim`.
- **If the build fails**, check the error output and retry (optionally with `preferXcodebuild: true`) or escalate to the user before attempting any UI interaction.
- **After a successful build**, verify the app launched by calling `mcp__XcodeBuildMCP__describe_ui` or `mcp__XcodeBuildMCP__screenshot` before proceeding to UI interaction.
- If the app is already built and only launch is requested, use `mcp__XcodeBuildMCP__launch_app_sim`.
- If bundle id is unknown:
1) `mcp__XcodeBuildMCP__get_sim_app_path`

View file

@ -22,6 +22,63 @@ Bootstrap a complete SwiftPM macOS app folder, then build, package, and run it w
- Release (optional): `Scripts/sign-and-notarize.sh` and `Scripts/make_appcast.sh`.
- Tag + GitHub release (optional): create a git tag, upload the zip/appcast to the GitHub release, and publish.
## Minimum End-to-End Example
Shortest path from bootstrap to a running app:
```bash
# 1. Copy and rename the skeleton
cp -R assets/templates/bootstrap/ ~/Projects/MyApp
cd ~/Projects/MyApp
sed -i '' 's/MyApp/HelloApp/g' Package.swift version.env
# 2. Copy scripts
cp assets/templates/package_app.sh Scripts/
cp assets/templates/compile_and_run.sh Scripts/
chmod +x Scripts/*.sh
# 3. Build and launch
swift build
Scripts/compile_and_run.sh
```
## Validation Checkpoints
Run these after key steps to catch failures early before proceeding to the next stage.
**After packaging (`Scripts/package_app.sh`):**
```bash
# Confirm .app bundle structure is intact
ls -R build/HelloApp.app/Contents
# Check that the binary is present and executable
file build/HelloApp.app/Contents/MacOS/HelloApp
```
**After signing (`Scripts/sign-and-notarize.sh` or ad-hoc dev signing):**
```bash
# Inspect signature and entitlements
codesign -dv --verbose=4 build/HelloApp.app
# Verify the bundle passes Gatekeeper checks locally
spctl --assess --type execute --verbose build/HelloApp.app
```
**After notarization and stapling:**
```bash
# Confirm the staple ticket is attached
stapler validate build/HelloApp.app
# Re-run Gatekeeper to confirm notarization is recognised
spctl --assess --type execute --verbose build/HelloApp.app
```
## Common Notarization Failures
| Symptom | Likely Cause | Recovery |
|---|---|---|
| `The software asset has already been uploaded` | Duplicate submission for same version | Bump `BUILD_NUMBER` in `version.env` and repackage. |
| `Package Invalid: Invalid Code Signing Entitlements` | Entitlements in `.entitlements` file don't match provisioning | Audit entitlements against Apple's allowed set; remove unsupported keys. |
| `The executable does not have the hardened runtime enabled` | Missing `--options runtime` flag in `codesign` invocation | Edit `sign-and-notarize.sh` to add `--options runtime` to all `codesign` calls. |
| Notarization hangs / no status email | `xcrun notarytool` network or credential issue | Run `xcrun notarytool history` to check status; re-export App Store Connect API key if expired. |
| `stapler validate` fails after successful notarization | Ticket not yet propagated | Wait ~60 s, then re-run `xcrun stapler staple`. |
## Templates
- `assets/templates/package_app.sh`: Build binaries, create the .app bundle, copy resources, sign.
- `assets/templates/compile_and_run.sh`: Dev loop to kill running app, package, launch.

View file

@ -16,7 +16,7 @@ Identify render hotspots, isolate expensive updates, and apply targeted optimiza
3. Isolate fast-changing state from heavy subtrees.
4. Stabilize props and handlers; memoize where it pays off.
5. Reduce expensive work (computation, DOM size, list length).
6. Validate with profiling; avoid speculative changes.
6. **Validate**: open React DevTools Profiler → record the interaction → inspect the Flamegraph for components rendering longer than ~16 ms → compare against a pre-optimization baseline recording.
## Checklist
@ -33,11 +33,94 @@ Identify render hotspots, isolate expensive updates, and apply targeted optimiza
## Optimization Patterns
- **Isolate ticking state**: move a timer/animation into a child component so the parent list does not re-render every tick.
- **Stabilize callbacks**: prefer `useCallback` for handlers passed to memoized rows.
### Isolate ticking state
Move a timer or animation counter into a child so the parent list never re-renders on each tick.
```tsx
// ❌ Before entire parent (and list) re-renders every second
function Dashboard({ items }: { items: Item[] }) {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return (
<>
<Clock tick={tick} />
<ExpensiveList items={items} /> {/* re-renders every second */}
</>
);
}
// ✅ After only <Clock> re-renders; list is untouched
function Clock() {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return <span>{tick}s</span>;
}
function Dashboard({ items }: { items: Item[] }) {
return (
<>
<Clock />
<ExpensiveList items={items} />
</>
);
}
```
### Stabilize callbacks with `useCallback` + `memo`
```tsx
// ❌ Before new handler reference on every render busts Row memo
function List({ items }: { items: Item[] }) {
const handleClick = (id: string) => console.log(id); // new ref each render
return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);
}
// ✅ After stable handler; Row only re-renders when its own item changes
const Row = memo(({ item, onClick }: RowProps) => (
<li onClick={() => onClick(item.id)}>{item.name}</li>
));
function List({ items }: { items: Item[] }) {
const handleClick = useCallback((id: string) => console.log(id), []);
return items.map(item => <Row key={item.id} item={item} onClick={handleClick} />);
}
```
### Prefer derived data outside render
```tsx
// ❌ Before recomputes on every render
function Summary({ orders }: { orders: Order[] }) {
const total = orders.reduce((sum, o) => sum + o.amount, 0); // runs every render
return <p>Total: {total}</p>;
}
// ✅ After recomputes only when orders changes
function Summary({ orders }: { orders: Order[] }) {
const total = useMemo(() => orders.reduce((sum, o) => sum + o.amount, 0), [orders]);
return <p>Total: {total}</p>;
}
```
### Additional patterns
- **Split rows**: extract list rows into memoized components with narrow props.
- **Defer heavy rendering**: lazy-render or collapse expensive content until expanded.
- **Prefer derived data outside render**: compute summaries with `useMemo` or helper functions when inputs are stable.
## Profiling Validation Steps
1. Open **React DevTools → Profiler** tab.
2. Click **Record**, perform the slow interaction, then **Stop**.
3. Switch to **Flamegraph** view; any bar labeled with a component and time > ~16 ms is a candidate.
4. Use **Ranked chart** to sort by self render time and target the top offenders.
5. Apply one optimization at a time, re-record, and compare render counts and durations against the baseline.
## Example Reference

View file

@ -1,6 +1,6 @@
---
name: swift-concurrency-expert
description: Swift Concurrency review and remediation for Swift 6.2+. Use when asked to review Swift Concurrency usage, improve concurrency compliance, or fix Swift concurrency compiler errors in a feature or file.
description: Swift Concurrency review and remediation for Swift 6.2+. Use when asked to review Swift Concurrency usage, improve concurrency compliance, or fix Swift concurrency compiler errors in a feature or file. Concrete actions include adding Sendable conformance, applying @MainActor annotations, resolving actor isolation warnings, fixing data race diagnostics, and migrating completion handlers to async/await.
---
# Swift Concurrency Expert
@ -29,6 +29,74 @@ Common fixes:
- **Background work**: move expensive work into a `@concurrent` async function on a `nonisolated` type or use an `actor` to guard mutable state.
- **Sendable errors**: prefer immutable/value types; add `Sendable` conformance only when correct; avoid `@unchecked Sendable` unless you can prove thread safety.
### 3. Verify the fix
- Rebuild and confirm all concurrency diagnostics are resolved with no new warnings introduced.
- Run the test suite to check for regressions — concurrency changes can introduce subtle runtime issues even when the build is clean.
- If the fix surfaces new warnings, treat each one as a fresh triage (return to step 1) and resolve iteratively until the build is clean and tests pass.
### Examples
**UI-bound type — adding `@MainActor`**
```swift
// Before: data-race warning because ViewModel is accessed from the main thread
// but has no actor isolation
class ViewModel: ObservableObject {
@Published var title: String = ""
func load() { title = "Loaded" }
}
// After: annotate the whole type so all stored state and methods are
// automatically isolated to the main actor
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func load() { title = "Loaded" }
}
```
**Protocol conformance isolation**
```swift
// Before: compiler error — SomeProtocol method is nonisolated but the
// conforming type is @MainActor
@MainActor
class Foo: SomeProtocol {
func protocolMethod() { /* accesses main-actor state */ }
}
// After: scope the conformance to @MainActor so the requirement is
// satisfied inside the correct isolation context
@MainActor
extension Foo: SomeProtocol {
func protocolMethod() { /* safely accesses main-actor state */ }
}
```
**Background work with `@concurrent`**
```swift
// Before: expensive computation blocks the main actor
@MainActor
func processData(_ input: [Int]) -> [Int] {
input.map { heavyTransform($0) } // runs on main thread
}
// After: hop off the main actor for the heavy work, then return the result
// The caller awaits the result and stays on its own actor
nonisolated func processData(_ input: [Int]) async -> [Int] {
await Task.detached(priority: .userInitiated) {
input.map { heavyTransform($0) }
}.value
}
// Or, using a @concurrent async function (Swift 6.2+):
@concurrent
func processData(_ input: [Int]) async -> [Int] {
input.map { heavyTransform($0) }
}
```
## Reference material

View file

@ -1,6 +1,6 @@
---
name: swiftui-ui-patterns
description: Best practices and example-driven guidance for building SwiftUI views and components. Use when creating or refactoring SwiftUI UI, designing tab architecture with TabView, composing screens, or needing component-specific patterns and examples.
description: Best practices and example-driven guidance for building SwiftUI views and components, including navigation hierarchies, custom view modifiers, and responsive layouts with stacks and grids. Use when creating or refactoring SwiftUI UI, designing tab architecture with TabView, composing screens with VStack/HStack, managing @State or @Binding, building declarative iOS interfaces, or needing component-specific patterns and examples.
---
# SwiftUI UI Patterns
@ -37,10 +37,10 @@ Choose a track based on your goal:
1. Define the view's state and its ownership location.
2. Identify dependencies to inject via `@Environment`.
3. Sketch the view hierarchy and extract repeated parts into subviews.
3. Sketch the view hierarchy and extract repeated parts into subviews. **Build and verify no compiler errors before proceeding.**
4. Implement async loading with `.task` and explicit state enum if needed.
5. Add accessibility labels or identifiers when the UI is interactive.
6. Validate with a build and update usage callsites if needed.
6. Validate with a build: confirm no compiler errors, check that previews render without crashing, and ensure state changes propagate correctly. For common SwiftUI compilation errors — missing `@State` annotations, ambiguous `ViewBuilder` closures, or mismatched generic types — resolve them before updating callsites. **If the build fails:** read the error message carefully, fix the identified issue, then rebuild before proceeding to the next step. If a preview crashes, isolate the offending subview, confirm its state initialisation is valid, and re-run the preview before continuing.
## Component references

View file

@ -1,6 +1,6 @@
---
name: swiftui-view-refactor
description: Refactor and review SwiftUI view files for consistent structure, dependency injection, and Observation usage. Use when asked to clean up a SwiftUI views layout/ordering, handle view models safely (non-optional when possible), or standardize how dependencies and @Observable state are initialized and passed.
description: Refactor and review SwiftUI view files for consistent structure, dependency injection, and Observation usage. Use when asked to clean up a SwiftUI view's layout/ordering, handle view models safely (non-optional when possible), or standardize how dependencies and @Observable state are initialized and passed.
---
# SwiftUI View Refactor
@ -28,23 +28,11 @@ Apply a consistent structure and dependency pattern to SwiftUI views, with a foc
### 3) Split large bodies and view properties
- If `body` grows beyond a screen or has multiple logical sections, split it into smaller subviews.
- Extract large computed view properties (`var header: some View { ... }`) into dedicated `View` types when they carry state or complex branching.
- It's fine to keep related subviews as computed view properties in the same file; extract to a standalone `View` struct only when it structurally makes sense or when reuse is intended.
- Extract large computed view properties into dedicated `View` types when they carry state or complex branching.
- Keep related subviews as computed view properties in the same file; extract to a standalone `View` struct only when reuse is intended or it structurally makes sense.
- Prefer passing small inputs (data, bindings, callbacks) over reusing the entire parent view state.
Example (extracting a section):
```swift
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HeaderSection(title: title, isPinned: isPinned)
DetailsSection(details: details)
ActionsSection(onSave: onSave, onCancel: onCancel)
}
}
```
Example (long body → shorter body + computed views in the same file):
Example (long body → shorter body + computed views):
```swift
var body: some View {
@ -75,7 +63,7 @@ private var filters: some View {
}
```
Example (extracting a complex computed view):
Example (extracting a complex computed view into a dedicated struct):
```swift
private var header: some View {
@ -98,9 +86,9 @@ private struct HeaderSection: View {
```
### 3b) Keep a stable view tree (avoid top-level conditional view swapping)
- Avoid patterns where a computed view (or `body`) returns completely different root branches using `if/else`.
- Prefer a single stable base view, and place conditions inside sections/modifiers (`overlay`, `opacity`, `disabled`, `toolbar`, row content, etc.).
- Root-level branch swapping can cause identity churn, broader invalidation, and extra recomputation in SwiftUI.
- Avoid `body` or computed views that return completely different root branches via `if/else`.
- Prefer a single stable base view with conditions inside sections/modifiers (`overlay`, `opacity`, `disabled`, `toolbar`, etc.).
- Root-level branch swapping causes identity churn, broader invalidation, and extra recomputation.
Prefer:
@ -151,12 +139,12 @@ init(dependency: Dependency) {
## Workflow
1) Reorder the view to match the ordering rules.
2) Favor MV: move lightweight orchestration into the view using `@State`, `@Environment`, `@Query`, `task`, and `onChange`.
3) Ensure stable view structure: avoid top-level `if`-based branch swapping; move conditions to localized sections/modifiers.
4) If a view model exists, replace optional view models with a non-optional `@State` view model initialized in `init` by passing dependencies from the view.
5) Confirm Observation usage: `@State` for root `@Observable` view models, no redundant wrappers.
6) Keep behavior intact: do not change layout or business logic unless requested.
1. Reorder the view to match the ordering rules.
2. Favor MV: move lightweight orchestration into the view using `@State`, `@Environment`, `@Query`, `task`, and `onChange`.
3. Ensure stable view structure: avoid top-level `if`-based branch swapping; move conditions to localized sections/modifiers.
4. If a view model exists, replace optional view models with a non-optional `@State` view model initialized in `init` by passing dependencies from the view.
5. Confirm Observation usage: `@State` for root `@Observable` view models, no redundant wrappers.
6. Keep behavior intact: do not change layout or business logic unless requested.
## Notes
@ -166,4 +154,4 @@ init(dependency: Dependency) {
## Large-view handling
- When a SwiftUI view file exceeds ~300 lines, split it using extensions to group related helpers. Move async functions and helper functions into dedicated `private` extensions, separated with `// MARK: -` comments that describe their purpose (e.g., `// MARK: - Actions`, `// MARK: - Subviews`, `// MARK: - Helpers`). Keep the main `struct` focused on stored properties, init, and `body`, with view-building computed vars also grouped via marks when the file is long.
When a SwiftUI view file exceeds ~300 lines, split it using extensions to group related helpers. Move async and helper functions into dedicated `private` extensions separated with `// MARK: -` comments (e.g., `// MARK: - Actions`, `// MARK: - Subviews`, `// MARK: - Helpers`). Keep the main `struct` focused on stored properties, `init`, and `body`.