This seems more or less to work:

extension TriviaPiece {
    var isSwiftLintDirective: Bool {
        switch self {
        case let .lineComment(text):
            return text.hasPrefix("// swiftlint:")
        default:
            return false
        }
    }
}

extension Trivia {
    /// Splits the trivia into two parts: everything before the last SwiftLint directive,
    /// and the directive itself + everything after.
    func splitSwiftLintDirective() -> (leadingTrivia: Trivia, swiftLintDirective: Trivia?) {
        guard let swiftLintIndex = pieces.lastIndex(where: { $0.isSwiftLintDirective }) else {
            return (self, nil)
        }
        return (Trivia(pieces: pieces[..<swiftLintIndex]), Trivia(pieces: pieces[swiftLintIndex...]))
    }
}

extension SyntaxProtocol {
    mutating func addMainActor(_ attributes: WritableKeyPath<Self, AttributeListSyntax>) {
        guard !self[keyPath: attributes].hasMainActor else {
            return
        }

        let savedTrivia = leadingTrivia.splitSwiftLintDirective()
        let indent: Trivia = {
            if let last = savedTrivia.0.pieces.last,
               case .spaces = last
            {
                return [.newlines(1), last]
            } else {
                return [.newlines(1)]
            }
        }()

        leadingTrivia = []
        self[keyPath: attributes].append(
            .attribute(
                AttributeSyntax(attributeName: IdentifierTypeSyntax(name: .identifier("MainActor")))
                    .with(self[keyPath: attributes].isEmpty ? \.trailingTrivia : \.leadingTrivia, indent)
            )
        )
        if let swiftLintDirective = savedTrivia.swiftLintDirective {
            self[keyPath: attributes].trailingTrivia = self[keyPath: attributes]
                .trailingTrivia
                .appending(swiftLintDirective)
        }
        leadingTrivia = savedTrivia.leadingTrivia
    }
}

extension AttributeListSyntax.Element {
    var isMainActor: Bool {
        "MainActor" == self.as(AttributeSyntax.self)?
            .attributeName
            .as(IdentifierTypeSyntax.self)?
            .name
            .text
    }
}

extension AttributeListSyntax {

    var hasMainActor: Bool {
        contains(where: \.isMainActor)
    }

}

Probably still some edge cases to handle, but it's working pretty well for a lot of cases.