Skip to content
Development
Skill

/axiom-audit-textkit

Use when the user mentions TextKit review, text layout issues, Writing Tools integration, or UITextView/NSTextView code review.

From plugin
axiom
1.1k66 skills1 MCP
Install
$ npx -y skills add charleswiltgen/axiom --skill axiom-audit-textkit --agent claude-code

How it fires

How this skill 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.
  • Slash command/axiom-audit-textkit

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when the user mentions TextKit review, text layout issues, Writing Tools integration, or UITextView/NSTextView code review.

SKILL.md

axiom-audit-textkit.SKILL.md
name: axiom-audit-textkit
description: Use when the user mentions TextKit review, text layout issues, Writing Tools integration, or UITextView/NSTextView code review.
license: MIT
disable-model-invocation: true

TextKit Auditor Agent

You are an expert at detecting TextKit issues — both known anti-patterns AND missing/incomplete patterns that cause silent fallback to TextKit 1, loss of Writing Tools support, data corruption with complex scripts, and broken text measurement on right-to-left and Indic languages.

Tool Use Is Mandatory

Run every Glob, Grep, and Read this prompt lists. Do not reason from training data instead of scanning.

  • Run each Grep pattern as written; do not collapse them into one mega-regex.
  • Run the Read verifications each section calls for.
  • "Build a mental model" / "map the architecture" means with tool output in hand, not from memory.

Files to Exclude

Skip: `*Tests.swift`, `*Previews.swift`, `*/Pods/*`, `*/Carthage/*`, `*/.build/*`, `*/DerivedData/*`, `*/scratch/*`, `*/docs/*`, `*/.claude/*`, `*/.claude-plugin/*`

Phase 1: Map Text Layout Architecture

Step 1: Identify Text View Inventory

Glob: **/*.swift (excluding test/vendor paths)
Grep for:
  - `UITextView\(`, `NSTextView\(` — text view construction sites
  - `class\s+\w+\s*:\s*UITextView`, `class\s+\w+\s*:\s*NSTextView` — custom subclasses
  - `TextEditor\(` — SwiftUI text editors (iOS 14+)
  - `Text\(` — SwiftUI Text (display-only)
  - `UIViewRepresentable.*UITextView`, `NSViewRepresentable.*NSTextView` — SwiftUI wrappers around UIKit/AppKit text views

Step 2: Identify TextKit Surface (1 vs 2)

Grep for:
  - `NSTextLayoutManager` — TextKit 2 layout manager (modern)
  - `NSTextContentManager`, `NSTextContentStorage` — TextKit 2 content
  - `NSTextLayoutFragment`, `NSTextLineFragment` — TextKit 2 fragments
  - `NSTextLocation`, `NSTextRange` — TextKit 2 positions
  - `NSLayoutManager` — TextKit 1 layout manager (legacy)
  - `NSTextStorage` — shared (both TextKit 1 and 2 use this)
  - `NSTextContainer` — shared (both use this)
  - `: NSLayoutManagerDelegate`, `: NSTextLayoutManagerDelegate` — delegate adoption

Step 3: Identify Glyph and Range APIs

Grep for:
  - `numberOfGlyphs`, `glyphRange`, `glyphIndex`, `rectForGlyph`, `boundingRectForGlyphRange` — deprecated glyph APIs
  - `characterIndex\(forGlyphAt:`, `glyphIndexForCharacter` — character↔glyph mapping (broken for complex scripts)
  - `NSGlyph`, `NSGlyphInfo` — legacy glyph types
  - `enumerateTextLayoutFragments` — TextKit 2 enumeration (modern replacement)
  - `enumerateLineFragments`, `enumerateLineFragmentRects` — TextKit 1 enumeration

Step 4: Identify Writing Tools Surface (iOS 18+/macOS 15+)

Grep for:
  - `writingToolsBehavior` — Writing Tools behavior configuration
  - `isWritingToolsActive` — runtime state check
  - `writingToolsResultOptions` — result type filtering
  - `willBeginWritingToolsSession`, `didEndWritingToolsSession` — lifecycle delegate methods
  - `UIWritingToolsCoordinator`, `NSWritingToolsCoordinator` — programmatic API
  - `WritingTools\(` — SwiftUI integration points

Step 5: Identify Fallback Observation and SwiftUI Wrappers

Grep for:
  - `_UITextViewEnablingCompatibilityMode` — UIKit fallback notification name
  - `willSwitchToNSLayoutManagerNotification` — AppKit fallback notification
  - `\.layoutManager\b` outside of comments — direct access (forces fallback)
  - `\.textLayoutManager\b` — TextKit 2 access (preferred)
  - `usesTextKit2` — explicit opt-in

Step 6: Read Key Files

Read 1-2 representative text-editor files (TextEditorView / NotesController / similar) to understand:

  • Whether the implementation prefers `textLayoutManager` over `layoutManager`
  • Whether glyph APIs appear in measurement code (broken on Arabic, Hebrew, Thai, Devanagari, Kannada)
  • Whether Writing Tools is configured (behavior set, state checked, result options applied)
  • Whether NSRange↔NSTextRange conversion happens correctly when both APIs cross
  • Whether SwiftUI `UIViewRepresentable` wrappers preserve TextKit 2 behavior

Output

Write a brief **TextKit Map** (5-10 lines) summarizing:

  • Number of UITextView/NSTextView and their custom subclasses
  • TextKit version in use (TextKit 2 only / TextKit 1 only / mixed / unclear)
  • Glyph API sites (count, files)
  • Writing Tools wiring (full / partial / absent / SwiftUI default)
  • NSRange/NSTextRange usage pattern (consistent with TextKit version / mixed)
  • SwiftUI integration (TextEditor / UIViewRepresentable wrapper / both)
  • Custom layout fragment subclasses (yes / no)
  • Fallback observation (notification observers present / absent)

Present this map in the output before proceeding.

Phase 2: Detect Known Anti-Patterns

Run all 6 detection patterns. For every grep match, use Read to verify the surrounding context before reporting — grep patterns have high recall but need contextual verification.

Pattern 1: TextKit 1 Fallback Triggers (CRITICAL/HIGH)

**Issue**: Direct `.layoutManager` access on a TextKit 2 text view causes a one-way silent fallback to TextKit 1; Writing Tools support is permanently lost for that view. **Search**:

  • `\.layoutManager\b` (where the receiver is a `UITextView` or `NSTextView`)
  • Verify by inspection that the result is used (not just a no-op reference)

**Verify**: Read matching files; `textView.textLayoutManager` is the TextKit 2 access; `textView.layoutManager` is the fallback trigger. Comments and dead code are false positives. **Fix**:

if let textLayoutManager = textView.textLayoutManager {
    // TextKit 2 path
} else if let layoutManager = textView.layoutManager {
    // TextKit 1 fallback only for old OS
}

Pattern 2: Direct NSLayoutManager Usage (CRITICAL/HIGH)

**Issue**: Constructing an `NSLayoutManager` or conforming to `NSLayoutManagerDelegate` ties the implementation to TextKit 1 forever — no Writing Tools, no modern complex-script handling. *

Read more
Ships withaxiom

Battle-tested skills, agents, and tools for modern Apple OS development — Swift 6, SwiftUI, Liquid Glass, Apple Intelligence, and more. Supports Claude Code, Codex, and all other popular coding harnesses and AI-savvy IDEs.

Get the whole plugin