Token导航 LogoToken导航

Agent Skill · 开发

appmigrationkit(appmigrationkit 命令行)

appmigrationkit 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

appmigrationkit 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在开发类的 21,746 个 Skill 里排第 2,702。

总安装 15,227周安装 622GitHub Stars 524下载 4,876核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:appmigrationkit(appmigrationkit 命令行)
来源仓库:https://github.com/dpearson2699/swift-ios-skills
仓库路径:skills/appmigrationkit
安装命令:
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill appmigrationkit
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill appmigrationkit

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

用于处理 GitHub 仓库、Issue 和 Pull Request。适合整理代码协作信息和仓库状态。appmigrationkit 属于开发类 Skill,可作为该场景下的辅助能力补充。通过 GitHub 安装,需确认权限范围。可能执行命令或访问网络,建议评估操作边界。

适用于 Codex、Claude、Cursor 和 Gemini CLI。

来源仓库内路径:skills/appmigrationkit

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/dpearson2699/swift-ios-skills --skill appmigra…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

appmigrationkit 怎么安装?

在终端执行 npx skills add https://github.com/dpearson2699/swift-ios-skills --skill appmigrationkit。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

appmigrationkit 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

appmigrationkit 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

appmigrationkit 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

appmigrationkit 用的人多吗?

公开数据是累计安装 15,227 次,近一周安装 622 次,下载 4,876 次,GitHub 524 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

appmigrationkit 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
appmigrationkit

appmigrationkit 命令行 · 本页

15,227524需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

AppMigrationKit

One-time cross-platform data transfer for app resources. Enables apps to export data from one platform (e.g., Android) and import it on iOS during device setup or onboarding. iOS 26+ / iPadOS 26+ / Swift 6.3.

Beta-sensitive. AppMigrationKit is new in iOS 26 and may change before GM. Re-check current Apple documentation before relying on specific API details.

AppMigrationKit uses an app extension model. The system orchestrates the transfer between devices. The app provides an extension conforming to export and import protocols, and the system calls that extension at the appropriate time. The app itself never manages the network connection between devices.

Contents

Architecture Overview

AppMigrationKit operates through three layers:

  1. App extension -- An AppMigrationExtension conforming type that the system invokes during migration. It handles data export and import.
  2. System orchestration -- The OS manages the device-to-device session, transport, and scheduling. The extension does not control when it runs.
  3. Containing app -- After migration completes, the app checks MigrationStatus.importStatus on first launch to determine whether migration occurred and whether it succeeded.

Key types:

TypeRole
AppMigrationExtensionProtocol for the app extension entry point
ResourcesExportingWithOptionsProtocol for exporting files via archiver
ResourcesExportingSimplified export protocol (no custom options)
ResourcesImportingProtocol for importing files on the destination
ResourcesArchiverStreams files into the export archive
MigrationDataContainerAccess to the containing app's data directories
MigrationStatusCheck import result from the containing app
MigrationPlatformIdentifies the other device's platform (e.g., .android)
MigrationAppIdentifierIdentifies the source app by store and bundle ID
AppMigrationTesterTest-only actor for validating export/import logic

Setup and Entitlements

Entitlement

The app extension requires the com.apple.developer.app-migration.data-container-access entitlement. Its value is a single-element string array containing the bundle identifier of the containing app:

<key>com.apple.developer.app-migration.data-container-access</key>
<array>
    <string>com.example.myapp</string>
</array>

No other values are valid. This entitlement grants the extension read access to the containing app's data container during export and write access during import.

Extension Target

Add a new App Extension target to the Xcode project. The extension conforms to one or more of the migration protocols (ResourcesExportingWithOptions, ResourcesExporting, ResourcesImporting).

App Migration Extension

The extension entry point conforms to AppMigrationExtension. During migration, the system prevents launching the containing app and its other extensions to ensure exclusive data access.

Accessing the Data Container

The extension accesses the containing app's files through appContainer:

import AppMigrationKit

struct MyMigrationExtension: ResourcesExportingWithOptions, ResourcesImporting {
    // Access the containing app's directories
    let container = appContainer

    // container.bundleIdentifier     -- app's bundle ID
    // container.containerRootDirectory -- root of the app container
    // container.documentsDirectory    -- Documents/
    // container.applicationSupportDirectory -- Application Support/
}

MigrationDataContainer provides containerRootDirectory, documentsDirectory, and applicationSupportDirectory as URL values pointing into the containing app's sandbox.

Exporting Resources

Conform to ResourcesExportingWithOptions (or ResourcesExporting for no custom options) to package files for transfer. The system calls exportResources(to:request:) with a ResourcesArchiver and a MigrationRequestWithOptions.

Declaring Export Properties

struct MyMigrationExtension: ResourcesExportingWithOptions {
    typealias OptionsType = MigrationDefaultSupportedOptions

    var resourcesSizeEstimate: Int {
        // Return estimated total bytes of exported data
        calculateExportSize()
    }

    var resourcesVersion: String {
        "1.0"
    }

    var resourcesCompressible: Bool {
        true  // Let the system compress during transport
    }
}
  • resourcesSizeEstimate -- Estimated total bytes. The system uses this for progress UI and free-space checks.
  • resourcesVersion -- Format version string. The import side receives this to handle versioned data formats.
  • resourcesCompressible -- When true, the archiver may compress files during transport.

Implementing Export

func exportResources(
    to archiver: sending ResourcesArchiver,
    request: MigrationRequestWithOptions<MigrationDefaultSupportedOptions>
) async throws {
    let docsDir = appContainer.documentsDirectory

    // Check destination platform if needed
    if request.destinationPlatform == .android {
        // Platform-specific export logic
    }

    // Append files one at a time -- make continuous progress
    let userDataURL = docsDir.appending(path: "user_data.json")
    try await archiver.appendItem(at: userDataURL)

    // Append with a custom archive path
    let settingsURL = docsDir.appending(path: "settings.plist")
    try await archiver.appendItem(at: settingsURL, pathInArchive: "preferences/settings.plist")

    // Append a directory
    let photosDir = docsDir.appending(path: "photos")
    try await archiver.appendItem(at: photosDir, pathInArchive: "media/photos")
}

The archiver streams files incrementally. Call appendItem(at:pathInArchive:) repeatedly as each resource is ready. The system may terminate the extension if it appears hung, so avoid long gaps between append calls.

Cancellation

ResourcesArchiver handles task cancellation automatically by throwing cancellation errors. Do not catch these errors -- doing so causes the system to kill the extension.

Migration Platform

MigrationRequestWithOptions exposes destinationPlatform as a MigrationPlatform value. Use this to tailor exported data:

if request.destinationPlatform == .android {
    // Export in a format the Android app expects
}

MigrationPlatform provides .android as a static constant. Custom platforms can be created with MigrationPlatform("customPlatform").

Importing Resources

Conform to ResourcesImporting to receive transferred files on the destination device. The system calls importResources(at:request:) after app installation but before the app is launchable.

struct MyMigrationExtension: ResourcesImporting {
    func importResources(
        at importedDataURL: URL,
        request: ResourcesImportRequest
    ) async throws {
        let sourceVersion = request.sourceVersion
        let sourceApp = request.sourceAppIdentifier

        // sourceApp.platform        -- e.g., .android
        // sourceApp.bundleIdentifier -- source app's bundle ID
        // sourceApp.storeIdentifier  -- e.g., .googlePlay

        // Copy imported files into the app container
        let docsDir = appContainer.documentsDirectory

        let userData = importedDataURL.appending(path: "user_data.json")
        if FileManager.default.fileExists(atPath: userData.path()) {
            try FileManager.default.copyItem(
                at: userData,
                to: docsDir.appending(path: "user_data.json")
            )
        }
    }
}

Error Handling During Import

On import error, the system clears the containing app's data container to prevent partial state. However, app group containers are not cleared. The import implementation should clear any app group containers before writing imported content:

func importResources(
    at importedDataURL: URL,
    request: ResourcesImportRequest
) async throws {
    // Clear shared app group data first
    let groupURL = FileManager.default.containerURL(
        forSecurityApplicationGroupIdentifier: "group.com.example.myapp"
    )
    if let groupURL {
        try? FileManager.default.removeItem(at: groupURL.appending(path: "shared_data"))
    }

    // Then import
    try await performImport(from: importedDataURL)
}

Source App Identifier

ResourcesImportRequest provides sourceAppIdentifier as a MigrationAppIdentifier with three properties:

  • platform -- The source device's platform (e.g., .android)
  • bundleIdentifier -- The source app's bundle identifier
  • storeIdentifier -- The app store (e.g., .googlePlay)

Migration Status

After migration completes, the containing app checks the result on first launch:

import AppMigrationKit

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    if let status = MigrationStatus.importStatus {
        switch status {
        case .success:
            showMigrationSuccessUI()
            MigrationStatus.clearImportStatus()
        case .failure(let error):
            showMigrationFailureUI(error: error)
            MigrationStatus.clearImportStatus()
        }
    }
    return true
}
  • MigrationStatus.importStatus is nil if no migration occurred.
  • Call clearImportStatus() after handling the result to prevent showing the notification on subsequent launches.
  • The enum has two cases: .success and .failure(any Error).

Progress Tracking

The import side exposes a Progress object via resourcesImportProgress. The system uses this to display transfer progress to the user. Update completedUnitCount incrementally during import:

var resourcesImportProgress: Progress {
    Progress(totalUnitCount: 100)
}

func importResources(
    at importedDataURL: URL,
    request: ResourcesImportRequest
) async throws {
    let progress = resourcesImportProgress
    let files = try FileManager.default.contentsOfDirectory(
        at: importedDataURL, includingPropertiesForKeys: nil
    )
    let increment = Int64(100 / max(files.count, 1))
    for file in files {
        try processFile(file)
        progress.completedUnitCount += increment
    }
    progress.completedUnitCount = 100
}

Testing

AppMigrationTester is a test-only actor for validating migration logic in unit tests hosted by the containing app. Do not use it in production.

import Testing
import AppMigrationKit

@Test func testExportImportRoundTrip() async throws {
    let tester = try await AppMigrationTester(platform: .android)

    // Export
    let result = try await tester.exportController.exportResources(
        request: nil, progress: nil
    )
    #expect(result.exportProperties.uncompressedBytes > 0)

    // Import the exported data
    try await tester.importController.importResources(
        from: result.extractedResourcesURL,
        importRequest: nil, progress: nil
    )
    try await tester.importController.registerImportCompletion(with: .success)
}

DeviceToDeviceExportProperties on the result exposes uncompressedBytes, compressedBytes (nil if not compressible), sizeEstimate, and version.

See references/appmigrationkit-patterns.md for additional test patterns.

Common Mistakes

DON'T: Catch cancellation errors from ResourcesArchiver

// WRONG -- system kills the extension if cancellation is swallowed
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
    do {
        try await archiver.appendItem(at: fileURL)
    } catch is CancellationError {
        // Swallowing this causes termination
    }
}

// CORRECT -- let cancellation propagate
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
    try await archiver.appendItem(at: fileURL)
}

DON'T: Leave long gaps between archiver append calls

// WRONG -- system may assume the extension is hung and terminate it
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
    let allFiles = gatherAllFiles()  // Takes 30 seconds
    for file in allFiles {
        try await archiver.appendItem(at: file)
    }
}

// CORRECT -- interleave file preparation with archiving
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
    for file in knownFilePaths() {
        try await archiver.appendItem(at: file)
    }
}

DON'T: Convert files to intermediate format during export

// WRONG -- may exhaust disk space creating temporary copies
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
    let converted = try convertToJSON(originalDatabase)  // Doubles disk usage
    try await archiver.appendItem(at: converted)
}

// CORRECT -- export files as-is, convert on import side if needed
func exportResources(to archiver: sending ResourcesArchiver, request: ...) async throws {
    try await archiver.appendItem(at: originalDatabase)
}

DON'T: Ignore app group containers during import error recovery

// WRONG -- system clears app container but not app groups on error
func importResources(at url: URL, request: ResourcesImportRequest) async throws {
    try writeToAppGroup(data)
    try writeToAppContainer(data)  // If this throws, app group has stale data
}

// CORRECT -- clear app group data before importing
func importResources(at url: URL, request: ResourcesImportRequest) async throws {
    try clearAppGroupData()
    try writeToAppGroup(data)
    try writeToAppContainer(data)
}

DON'T: Forget to clear import status after handling it

// WRONG -- migration UI shows every launch
if let status = MigrationStatus.importStatus {
    showMigrationResult(status)
    // Missing clearImportStatus()
}

// CORRECT
if let status = MigrationStatus.importStatus {
    showMigrationResult(status)
    MigrationStatus.clearImportStatus()
}

Review Checklist

  • Extension target added with com.apple.developer.app-migration.data-container-access entitlement
  • Entitlement array contains exactly one string: the containing app's bundle identifier
  • Extension conforms to ResourcesExportingWithOptions or ResourcesExporting for export
  • Extension conforms to ResourcesImporting for import
  • resourcesSizeEstimate returns a reasonable byte estimate
  • resourcesVersion is set and will be checked on import for format compatibility
  • Export calls appendItem incrementally without long pauses
  • Cancellation errors from ResourcesArchiver are not caught
  • Import clears app group containers before writing new data
  • Containing app checks MigrationStatus.importStatus on first launch
  • clearImportStatus() called after handling the migration result
  • AppMigrationTester used in unit tests to validate export and import
  • Files are exported as-is without intermediate format conversion on the export side
  • sourceVersion from import request used to handle versioned data formats

References

平台分布

Codex

37.42%
按下载量换算1,825

Claude

29.19%
按下载量换算1,423

Cursor

20.27%
按下载量换算988

Gemini CLI

9.35%
按下载量换算456

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills