AscendKit is a local-first Swift toolkit for preparing App Store releases. It helps Apple platform teams organize release workspaces, inspect projects, lint App Store metadata, prepare screenshots, compare local metadata with App Store Connect, and execute a guarded review-submission workflow.
The project is designed for AI-assisted release work without handing raw secrets or mutable App Store state directly to an agent. AscendKit keeps release state in deterministic local files, stores only secret references, and requires explicit confirmation flags before remote App Store Connect mutations.
Current documented release: v1.9.0.
AscendKit follows Semantic Versioning. The v1 command surface is stable for 1.x: breaking workflow changes require a new major version, while compatible commands, flags, diagnostics, and documentation can continue to evolve through minor releases.
AscendKit has been used end-to-end on real iOS app release workflows covering local screenshot preparation, metadata, pricing, reviewer information, build selection, screenshot upload, and guarded App Review handoff. App Privacy publishing is currently documented as a boundary where Apple's IRIS endpoint may require App Store Connect UI or future Apple ID web-session support.
Implemented today:
- Swift Package with
AscendKitCoreand theascendkitCLI. - Durable release workspaces under
.ascendkit/releases/<release-id>. - Project intake and release doctor checks.
- Metadata templates, fastlane metadata import, linting, diffing, and ASC request planning.
- Screenshot planning, import manifests, fastlane screenshot import, configurable composition themes (5 presets + auto mode), local composition outputs, guarded ASC upload planning, native screenshot upload execution, and a high-fidelity Screenshot Studio with physical device frame registry (Dynamic Island, notch, metal-bezel vector overlays) for App Store-ready framed posters.
- App Store Connect auth profiles using secret references.
- ASC app lookup, build lookup, metadata observation, metadata apply, and guarded review handoff.
- Reviewer information, readiness checks, review handoff, and submission result persistence.
- Local IAP subscription template validation.
Out of scope for the current release:
- Binary upload.
- Archive/sign/export replacement.
- Xcode Cloud replacement.
- Deep MCP integration.
- Fully managed App Store Connect pricing/App Privacy abstractions for every Apple API edge case.
- Broad remote screenshot lifecycle management beyond guarded replace-existing deletion.
v1.0.0 release readiness is tracked in docs/v1-release-readiness.md. Post-v1 work is tracked in docs/post-v1-roadmap.md, and the longer product direction is tracked in docs/app-store-growth-copilot-roadmap.md.
- macOS 14 or later.
- Xcode command line tools for project inspection, simulator discovery, local screenshot capture, and release diagnostics.
- An App Store Connect API key for remote ASC operations.
- Optional:
fastlaneonly if you are migrating existing metadata or screenshot folders. It is not required for the core workflow. - Optional for contributors: Swift 6.1 or later when building from source.
AscendKit is designed so a developer can hand the release workflow to an AI coding agent without giving the agent raw secrets or uncontrolled App Store Connect authority.
Before copying the prompt, replace every <<...>> placeholder with a real value. If you do not know a value yet, leave it as <<...>>; the prompt instructs the agent to stop and ask for that value instead of guessing or using the placeholder literally. The safest path is to install AscendKit and run ascendkit agent prompt below because it generates a prompt with concrete app-specific values instead of template placeholders.
You are helping me prepare an Apple app for App Store submission with AscendKit.
AscendKit repository: https://github.com/rushairer/AscendKit
App project root: <<ABSOLUTE_APP_PROJECT_ROOT>>
Release id: <<RELEASE_ID_FOR_THIS_APP_VERSION>>
ASC profile name: <<ASC_PROFILE_NAME_OR_ASK_ME_TO_CREATE_ONE>>
The values wrapped in <<...>> are placeholders, not real paths or credentials. Before running commands, verify that every placeholder has been replaced with a real value. If any placeholder remains, stop and ask me for the missing value. Do not run commands with placeholder values.
First, learn AscendKit from its README and docs/agent-release-playbook.md.
Install AscendKit with Homebrew:
brew tap rushairer/ascendkit
brew install ascendkit
ascendkit --version
ascendkit version --json
Then use the installed ascendkit binary, not swift run, to drive this app from 0 to 1:
1. Inspect the app project and create a release workspace.
2. Protect the app repo from committing .ascendkit/ artifacts.
3. Run release doctor checks and report blockers.
4. Prepare or import App Store metadata.
5. Use screenshots doctor to decide whether deterministic UI-test-driven screenshots are available.
6. If UI-test screenshots are missing, generate a scaffold with screenshots scaffold-uitests, review it, and add app-specific deterministic navigation without real credentials.
7. Generate, import, compose, lint, and upload screenshots only after dry-run plans are clean.
8. Configure ASC auth through a saved profile or secret reference only; do not paste private key contents into code or prompts.
9. Observe App Store Connect state, plan metadata changes, and apply remote mutations only with explicit --confirm-remote-mutation after reviewing JSON plans.
10. Keep binary upload out of scope. Xcode Cloud handles binary upload.
11. Execute remote review submission only through AscendKit's audited pipeline: run `submit preflight --remote` to verify ASC state, then `submit execute --confirm-remote-submission` when all readiness and review plan conditions are met. If conditions are not met, stop at `submit handoff` and complete the final submission manually in App Store Connect.
Safety boundaries:
- Do not commit secrets, .ascendkit workspaces, screenshots, reviewer info, ASC identifiers, App Store Connect credentials, app binaries, or generated release artifacts.
- Do not hardcode my app-specific data into AscendKit.
- Do not use real user credentials in UI Tests or screenshots.
- If App Privacy cannot be published through the official ASC API, stop and provide a manual App Store Connect UI handoff.
Start with these commands:
APP_ROOT="<<ABSOLUTE_APP_PROJECT_ROOT>>"
RELEASE_ID="<<RELEASE_ID_FOR_THIS_APP_VERSION>>"
WORKSPACE="$APP_ROOT/.ascendkit/releases/$RELEASE_ID"
ASC_PROFILE="<<ASC_PROFILE_NAME_OR_ASK_ME_TO_CREATE_ONE>>"
case "$APP_ROOT $RELEASE_ID $ASC_PROFILE" in
*'<<'*'>>'*)
echo "Stop: replace AscendKit prompt placeholders before running release commands." >&2
exit 64
;;
esac
ascendkit intake inspect --root "$APP_ROOT" --release-id "$RELEASE_ID" --save --json
ascendkit workspace gitignore --workspace "$WORKSPACE" --fix --json
ascendkit workspace next-steps --workspace "$WORKSPACE" --json
During the work, prefer workspace next-steps --json, workspace summary --json, workspace validate-handoff --json, and workspace export-summary --json over guessing.
Finish by reporting AscendKit version, bundle id, app version, selected ASC build, metadata status, screenshot status, pricing, App Privacy status, review handoff status, remaining blockers, and validation commands run.
Generate a shorter app-specific prompt with the installed CLI:
APP_ROOT="<<ABSOLUTE_APP_PROJECT_ROOT>>"
RELEASE_ID="<<RELEASE_ID_FOR_THIS_APP_VERSION>>"
ASC_PROFILE="<<ASC_PROFILE_NAME_OR_ASK_ME_TO_CREATE_ONE>>"
case "$APP_ROOT $RELEASE_ID $ASC_PROFILE" in
*'<<'*'>>'*)
echo "Stop: replace AscendKit prompt placeholders before generating the handoff prompt." >&2
exit 64
;;
esac
ascendkit agent prompt \
--app-root "$APP_ROOT" \
--release-id "$RELEASE_ID" \
--asc-profile "$ASC_PROFILE" \
--output /tmp/ascendkit-agent-prompt.txtFor an existing AscendKit source checkout, maintainers can also run scripts/create-agent-handoff-prompt.sh with the same --app-root, --release-id, and --asc-profile values. Normal users and release agents should prefer the installed ascendkit agent prompt command.
When an AI agent works in your app project and you ask it to submit to the App Store, it may not know AscendKit exists and will try to write Python scripts calling the App Store Connect API directly. To prevent this, add the following section to your app project's CLAUDE.md:
## App Store Release
When the user asks to submit this app to the App Store, use **AscendKit** (`ascendkit` CLI, installed via `brew install rushairer/ascendkit/ascendkit`).
**Do NOT write Python/Shell scripts to call the App Store Connect API.** AscendKit handles JWT auth, metadata, screenshots, pricing, privacy, and submission readiness.
```bash
# Initialize release workspace
ascendkit intake inspect --root "$(pwd)" --release-id "app-1.0-b1" --save --json
ascendkit doctor release --workspace ".ascendkit/releases/app-1.0-b1" --json
# Prepare metadata (edit the generated file for your app)
ascendkit metadata init --workspace ".ascendkit/releases/app-1.0-b1" --locale en-US --json
ascendkit metadata lint --workspace ".ascendkit/releases/app-1.0-b1" --locale en-US --json
# Configure ASC auth
ascendkit asc auth init --workspace ".ascendkit/releases/app-1.0-b1" --profile YOUR_ASC_PROFILE --json
# Observe and apply metadata
ascendkit asc apps lookup --workspace ".ascendkit/releases/app-1.0-b1" --json
ascendkit asc metadata observe --workspace ".ascendkit/releases/app-1.0-b1" --json
ascendkit asc metadata plan --workspace ".ascendkit/releases/app-1.0-b1" --json
ascendkit asc metadata apply --workspace ".ascendkit/releases/app-1.0-b1" --confirm-remote-mutation --json
# Prepare submission
ascendkit submit readiness --workspace ".ascendkit/releases/app-1.0-b1" --json
ascendkit submit preflight --workspace ".ascendkit/releases/app-1.0-b1" --remote --json
ascendkit submit execute --workspace ".ascendkit/releases/app-1.0-b1" --confirm-remote-submission --json
ascendkit submit handoff --workspace ".ascendkit/releases/app-1.0-b1" --json
```
If `ascendkit` is not installed, tell the user to run `brew tap rushairer/ascendkit && brew install ascendkit`.
All commands support `--json`. Always run plan/observe before apply. Use `submit preflight --remote` to verify ASC state, then `submit execute --confirm-remote-submission` when all conditions are met. If conditions are not met, use `submit handoff` and complete final submission manually in App Store Connect.The full snippet is also available at docs/claude-md-snippet-for-app-projects.md. Customize the --release-id, --locale, and --profile values for your project. For the full agent operating manual, point your agent at docs/agent-release-playbook.md.
Prefer Homebrew for normal use:
brew tap rushairer/ascendkit
brew install ascendkit
ascendkit --version
ascendkit --help
ascendkit version --jsonAfter installation, run ascendkit from any app project directory. User-facing documentation assumes this installed binary.
Alternative direct installer from a source checkout or release asset:
scripts/install-ascendkit.sh --version 1.9.0
ASCENDKIT_INSTALL_DIR=/usr/local/bin scripts/install-ascendkit.shThe installer downloads the macOS universal release archive from GitHub Releases, verifies the .sha256 digest with shasum, and installs only the ascendkit CLI binary. Use it when Homebrew is unavailable or when validating a specific release asset.
Verify a published release before announcing it:
scripts/verify-release-assets.sh --version 1.9.0The verifier checks for the expected GitHub Release assets and performs a temporary installer smoke test.
Run the v1 representative app smoke against a local app project before a public release:
scripts/v1-representative-app-smoke.sh --app-root /path/to/YourAppThe smoke uses the installed ascendkit binary from PATH, creates a local .ascendkit/ release workspace in the target app, and exercises intake, doctor, metadata lint, screenshot workflow status, ASC local status, submission readiness, and agent handoff commands without remote mutations.
GitHub release archives are generated with:
scripts/package-release.sh
cd dist && shasum -a 256 -c ascendkit-*.tar.gz.sha256The archive contains only the AscendKit CLI, the installer script, LICENSE, README.md, and install instructions. It does not contain app workspaces, App Store Connect credentials, screenshots, or app binaries.
Homebrew formula maintenance:
scripts/update-homebrew-formula.sh
scripts/verify-homebrew-formula.sh --version 1.9.0
scripts/diagnose-homebrew-install.sh --version 1.9.0
scripts/sync-homebrew-tap.sh --commit --push
scripts/finalize-homebrew-release.sh --version 1.9.0 --commit --push --reinstall
scripts/v1-release-readiness.sh --version 1.9.0 --app-root /path/to/RepresentativeApp
ruby -c Formula/ascendkit.rbThe generated formula points at the GitHub Release archive for the current ascendkit --version. If the matching GitHub Release exists, the script uses the uploaded release asset digest, or downloads the published asset and computes its checksum if the digest is temporarily unavailable. It only falls back to the local package checksum before a release exists. After a release workflow succeeds, run scripts/update-homebrew-formula.sh again and commit any checksum update so Formula/ascendkit.rb matches the published asset digest. Then sync the dedicated rushairer/homebrew-ascendkit tap with scripts/sync-homebrew-tap.sh --commit --push. Maintainers should keep both formula copies aligned with every public release so users can install with brew install ascendkit.
For normal post-tag release finalization, prefer the single finalizer:
scripts/finalize-homebrew-release.sh --version 1.9.0 --commit --push --reinstallRun it only after .github/workflows/release.yml has completed for the tag. It refreshes the formula from the final GitHub Release asset digest, verifies the formula, syncs the Homebrew tap, and optionally reinstalls and diagnoses the installed Homebrew binary.
If Homebrew reports a checksum mismatch, stale formula, wrong tap, or unexpected installed version, run:
scripts/diagnose-homebrew-install.sh --version 1.9.0The diagnostic is read-only. It checks the installed ascendkit binary, universal architectures, tap remote, formula URL, formula SHA-256, and GitHub Release asset digest, then prints repair commands such as re-tapping and reinstalling from rushairer/ascendkit.
If full brew update is slow or blocked by unrelated Homebrew remotes, the diagnostic also reports whether the local rushairer/ascendkit tap checkout is behind its remote and prints a direct git -C ... pull --ff-only repair command for that tap.
For development, run from the source checkout:
swift test
swift run ascendkit --help
swift run ascendkit --version
swift run ascendkit version --jsonIf you are handing AscendKit to another AI agent, start with docs/agent-release-playbook.md. Use a short prompt plus that playbook; do not rely on a long one-off prompt as the only operating manual.
Generate a short handoff prompt from this repository when assigning a release to another agent:
scripts/create-agent-handoff-prompt.sh \
--app-root /path/to/YourApp \
--release-id app-1.0-b1 \
--asc-profile production \
--output /tmp/ascendkit-agent-prompt.txtThe prompt generator is read-only. It references the playbook, uses installed ascendkit commands, and keeps secrets, screenshots, reviewer data, binaries, and raw .ascendkit/ workspaces out of the prompt.
The typical flow is:
- Inspect the app project and create a release workspace.
- Add or import metadata.
- Prepare screenshots locally.
- Configure App Store Connect auth.
- Observe remote ASC state.
- Plan and apply metadata.
- Select a build.
- Plan and upload screenshots with explicit confirmation, or use App Store Connect UI when replacing existing screenshots.
- Add reviewer information.
- Run readiness checks.
- Run
submit preflight --remoteto verify ASC state, thensubmit execute --confirm-remote-submissionto submit for review. If conditions are not met, generate a review handoff and complete final submission manually in App Store Connect.
Set a few shell variables first:
APP_ROOT=/path/to/YourApp
RELEASE_ID=appstore-1.0
WORKSPACE="$APP_ROOT/.ascendkit/releases/$RELEASE_ID"Create the workspace:
ascendkit intake inspect \
--root "$APP_ROOT" \
--release-id "$RELEASE_ID" \
--saveCreate metadata templates, or import existing fastlane metadata:
ascendkit metadata init --workspace "$WORKSPACE" --locale en-US
ascendkit metadata import-fastlane \
--workspace "$WORKSPACE" \
--source "$APP_ROOT/fastlane/metadata"Lint metadata:
ascendkit metadata status --workspace "$WORKSPACE"
ascendkit metadata lint --workspace "$WORKSPACE" --locale en-US --jsonImport screenshots from a folder or from fastlane:
ascendkit screenshots import \
--workspace "$WORKSPACE" \
--source /path/to/screenshots
ascendkit screenshots import-fastlane \
--workspace "$WORKSPACE" \
--source "$APP_ROOT/fastlane/screenshots" \
--locales en-US,zh-HansCompose local screenshot artifacts:
ascendkit screenshots copy init --workspace "$WORKSPACE" --locale en-US
ascendkit screenshots copy refresh --workspace "$WORKSPACE" --locale en-US
ascendkit screenshots copy lint --workspace "$WORKSPACE" --locale en-US
ascendkit screenshots compose --workspace "$WORKSPACE" --mode storeReadyCopy
ascendkit screenshots compose --workspace "$WORKSPACE" --mode deviceFrame
ascendkit screenshots compose --workspace "$WORKSPACE" --mode posterdeviceFrame is a compatibility alias for the full App Store framed-poster renderer: it produces opaque marketing screenshots with a background, localized title/subtitle copy, and an inset device frame. It does not produce border-only transparent screenshots.
Run the local Xcode UI-test screenshot workflow without fastlane:
ascendkit screenshots destinations --workspace "$WORKSPACE" --json
ascendkit screenshots copy init --workspace "$WORKSPACE" --locale en-US --json
ascendkit screenshots copy refresh --workspace "$WORKSPACE" --locale en-US --json
ascendkit screenshots copy lint --workspace "$WORKSPACE" --locale en-US --json
ascendkit screenshots workflow run \
--workspace "$WORKSPACE" \
--scheme MyApp \
--mode framedPoster \
--copy "$WORKSPACE/screenshots/copy/en-US.json" \
--json
ascendkit screenshots workflow status --workspace "$WORKSPACE" --jsonSave an ASC auth profile. The profile stores only a reference to the private key file, not the key content:
ascendkit asc auth save-profile \
--name default \
--issuer-id ASC_ISSUER_ID \
--key-id ASC_KEY_ID \
--private-key-provider file \
--private-key-ref /secure/path/AuthKey_KEYID.p8Attach that profile to the release workspace:
ascendkit asc auth init \
--workspace "$WORKSPACE" \
--profile default
ascendkit asc auth check --workspace "$WORKSPACE" --jsonObserve the app and remote metadata:
ascendkit asc lookup plan --workspace "$WORKSPACE" --json
ascendkit asc apps lookup --workspace "$WORKSPACE" --json
ascendkit asc metadata observe --workspace "$WORKSPACE" --jsonPlan and upload screenshots. By default, the plan reads observed ASC screenshot sets and blocks execution when matching remote screenshots already exist. If you intend to replace existing screenshots, add --replace-existing to the plan and upload commands so AscendKit records and executes explicit screenshot deletions before uploading new assets:
ascendkit screenshots upload-plan --workspace "$WORKSPACE" --json
ascendkit screenshots upload-plan \
--workspace "$WORKSPACE" \
--replace-existing \
--json
ascendkit screenshots upload \
--workspace "$WORKSPACE" \
--replace-existing \
--confirm-remote-mutation \
--json
ascendkit screenshots upload-status --workspace "$WORKSPACE" --jsonFor targeted screenshot corrections, generate a plan with one or more --only-item PLAN_ITEM_ID values and --delete-matching-files-only. This keeps the upload plan limited to the selected local screenshots and deletes only the remote screenshots with matching file names:
ascendkit screenshots upload-plan \
--workspace "$WORKSPACE" \
--display-type APP_IPAD_PRO_3GEN_129 \
--replace-existing \
--delete-matching-files-only \
--only-item "en-US:iPadOS:APP_IPAD_PRO_3GEN_129:3:iPad Pro 13-inch (M5)-03_soundfont-framed-poster.png" \
--json
ascendkit screenshots upload \
--workspace "$WORKSPACE" \
--confirm-remote-mutation \
--jsonPlan and apply metadata changes:
ascendkit metadata diff --workspace "$WORKSPACE" --json
ascendkit asc metadata plan --workspace "$WORKSPACE" --json
ascendkit asc metadata requests --workspace "$WORKSPACE" --json
ascendkit asc metadata apply \
--workspace "$WORKSPACE" \
--confirm-remote-mutation \
--jsonSet the app price to free through the official App Store Connect API:
ascendkit asc pricing set-free --workspace "$WORKSPACE" --json
ascendkit asc pricing set-free \
--workspace "$WORKSPACE" \
--confirm-remote-mutation \
--jsonObserve builds and select a processed build:
ascendkit asc builds observe --workspace "$WORKSPACE" --json
ascendkit asc builds list --workspace "$WORKSPACE" --jsonAdd reviewer information. If no login is required, omit login credentials and leave --requires-login false:
ascendkit submit review-info set \
--workspace "$WORKSPACE" \
--first-name "Ada" \
--last-name "Lovelace" \
--email "review@example.com" \
--phone "+15555555555" \
--notes "No login required. Please launch the app and continue through onboarding." \
--requires-login falsePrepare the submission, verify remote state, and execute:
ascendkit doctor release --workspace "$WORKSPACE" --json
ascendkit submit readiness --workspace "$WORKSPACE" --json
ascendkit submit prepare --workspace "$WORKSPACE" --json
ascendkit submit review-plan --workspace "$WORKSPACE" --json
ascendkit submit preflight --workspace "$WORKSPACE" --remote --json
ascendkit submit execute --workspace "$WORKSPACE" --confirm-remote-submission --jsonWhen all readiness and review plan conditions are met, submit execute --confirm-remote-submission executes the full review submission through AscendKit's audited pipeline (build binding, compliance, privacy, review detail, age rating, review submission creation, and submit-for-review). If conditions are not met, the command records a non-executed result explaining which conditions remain unsatisfied.
Use submit handoff to generate a human-readable Markdown handoff, or submit status to check combined readiness, plan, and execution state.
All commands support --json where shown in ascendkit --help. JSON output is intended for scripts, CI, and AI-agent wrappers.
Generate safe, concrete prompts for handing an app release to an AI coding agent.
ascendkit agent prompt \
--app-root "$APP_ROOT" \
--release-id "$RELEASE_ID" \
--asc-profile "$ASC_PROFILE" \
--output /tmp/ascendkit-agent-prompt.txt
ascendkit agent prompt \
--workspace "$WORKSPACE" \
--asc-profile "$ASC_PROFILE" \
--output /tmp/ascendkit-agent-prompt.txt
ascendkit agent prompt \
--app-root "$APP_ROOT" \
--release-id "$RELEASE_ID" \
--asc-profile "$ASC_PROFILE" \
--jsonagent prompt refuses placeholder-style sample values such as <<...>>, /path/to/App, and app-1.0-b1. Use --workspace when a release workspace already exists; AscendKit derives the app root and release id from PATH/.ascendkit/releases/RELEASE_ID without reading workspace contents or writing audit records. The output prompt includes the release workspace, the selected ASC profile name, safety boundaries, and the first deterministic commands an agent should run. It does not include secrets, reviewer details, screenshots, binaries, or raw .ascendkit/ contents.
Inspect existing release workspace state.
ascendkit workspace status --workspace "$WORKSPACE"
ascendkit workspace status --workspace "$WORKSPACE" --json
ascendkit workspace summary --workspace "$WORKSPACE" --json
ascendkit workspace hygiene --workspace "$WORKSPACE" --json
ascendkit workspace gitignore --workspace "$WORKSPACE" --json
ascendkit workspace gitignore --workspace "$WORKSPACE" --fix --json
ascendkit workspace export-summary --workspace "$WORKSPACE" --output /tmp/ascendkit-summary.json --json
ascendkit workspace validate-handoff --workspace "$WORKSPACE" --export /tmp/ascendkit-summary.json --json
ascendkit workspace next-steps --workspace "$WORKSPACE" --jsonworkspace status shows which expected files exist, such as manifest, metadata, screenshots, ASC auth, readiness, and review artifacts. workspace summary reads the persisted release artifacts and emits final readiness state plus deduplicated next actions for agents, including screenshot upload recovery or ready-for-review commands when upload status has been recorded. workspace hygiene checks whether the local workspace contains release artifacts or potential secrets that must not be committed. workspace gitignore checks whether the app project's .gitignore excludes .ascendkit/; add --fix to append the rule. workspace export-summary writes a sanitized handoff JSON file that excludes raw screenshots, ASC auth, metadata, review artifacts, audit log contents, and absolute workspace paths. The export includes ascendKitVersion, handoffCommands, and safetyBoundaries so the receiving agent can verify the CLI version, generate a concrete agent prompt --workspace handoff, resume with placeholder-based commands, and preserve AscendKit's release boundaries without receiving raw workspace files. workspace validate-handoff checks whether another agent can safely take over the release workflow; release blockers are reported as warnings because they are work for the receiving agent, not handoff blockers, and the report includes handoffInstructions for the receiving agent. workspace next-steps converts summary next actions into a priority-sorted, command-oriented plan with both placeholder command values and directly copyable executableCommand values for the current workspace.
ascendkit workspace audit --workspace "$WORKSPACE"Reads the workspace audit log. Sensitive values are redacted before they are written.
ascendkit workspace list --root "$APP_ROOT"Lists known release workspaces under an app root.
Create or update the release manifest from an Apple project.
ascendkit intake inspect \
--root "$APP_ROOT" \
--release-id "$RELEASE_ID" \
--save \
--jsonUseful options:
--root PATH: app repository root.--project PATH: specific.xcodeprojor project folder.--workspace PATH: existing release workspace.--release-id ID: release workspace identifier.--save: persist the discovered manifest.
Run release hygiene checks.
ascendkit doctor release --workspace "$WORKSPACE" --jsonThe doctor checks app icon presence, entitlements, Info.plist privacy purpose strings, local metadata, screenshot state, IAP templates, and release-sensitive residue. When Xcode's GENERATE_INFOPLIST_FILE build setting is enabled, AscendKit automatically parses keys under INFOPLIST_KEY_*, strips bracket SDK conditionals, and merges them into target-level Info.plist configurations, bypassing false-positive missing-path or not-found diagnostics that previously blocked agents from proceeding.
Manage local App Store metadata.
ascendkit metadata init --workspace "$WORKSPACE" --locale en-USWrites a starter metadata JSON file for one locale.
ascendkit metadata import-fastlane \
--workspace "$WORKSPACE" \
--source "$APP_ROOT/fastlane/metadata" \
--jsonImports fastlane metadata folders into AscendKit's local metadata model.
ascendkit metadata status --workspace "$WORKSPACE" --jsonLists local metadata bundles known to the workspace.
ascendkit metadata lint --workspace "$WORKSPACE" --locale en-US --jsonChecks required fields and App Store length constraints.
ascendkit metadata diff --workspace "$WORKSPACE" --jsonCompares local metadata with observed ASC metadata saved in the workspace.
ascendkit metadata sync --workspace "$WORKSPACE" --jsonWrites observed ASC metadata (from asc metadata observe) into local source files. This is useful when a workspace was created after manual ASC submissions and local metadata is still template text. After sync, metadata diff shows 0 blocking diffs. Use --dry-run to preview without writing.
Plan, import, validate, and compose local screenshot artifacts.
Screenshot Studio (v1.9.0): The framedPoster composition mode now uses a built-in DeviceFrameRegistry that maps physical marketing device specifications — pixel dimensions, bezel width, per-device corner radius, Dynamic Island cutout size, and notch dimensions — for iPhone 6.7" (1290x2796), iPhone 6.5" (1242x2688), iPad Pro 13-inch (2064x2752), and Mac Desktop (2560x1600). Source screenshots are matched to device specs automatically using tolerant orientation-aware size detection. The renderer draws high-fidelity vector hardware overlays: double-stroke metal-gradient bezels, Dynamic Island / notch cutout masks, translucent Home indicator bar, and soft drop-shadow back-drops matching App Store card styling. AI Agents can invoke Screenshot Studio with:
ascendkit screenshots compose --workspace "$WORKSPACE" --mode framedPoster --jsonascendkit screenshots doctor --workspace "$WORKSPACE" --jsonChecks whether the project is ready for deterministic UI-test-driven screenshots. It reports the discovered Xcode project/workspace, app target, UI test target(s), screenshot plan state, simulator destination hints, platform support, and concrete next commands. The platform matrix marks iOS, iPadOS, and macOS as default capture paths, while tvOS, watchOS, and visionOS require discovered or explicit destinations before deterministic capture. If no UI test target exists, AscendKit keeps manual screenshot import available but tells users and AI Agents to scaffold UI Tests for repeatable screenshots.
ascendkit screenshots scaffold-uitests --workspace "$WORKSPACE" --jsonWrites a reviewable starter UI Test file under screenshots/scaffold/ plus a JSON manifest. The scaffold uses --ascendkit-screenshot-mode, ASCENDKIT_SCREENSHOT_OUTPUT_DIR, ordered screenshot names, and XCTAttachment fallback capture. The JSON manifest includes navigationPlaceholders so AI Agents can see every screen that needs app-specific deterministic navigation without parsing Swift comments. It does not edit the Xcode project; users or AI Agents should review the file, add it to the UI test target, and replace placeholder navigation comments with app-specific deterministic steps.
ascendkit screenshots plan \
--workspace "$WORKSPACE" \
--screens Home,Settings,Paywall \
--features Onboarding,Sync,Premium \
--platforms iOS \
--locales en-US,zh-Hans \
--jsonCreates a deterministic screenshot plan with coverage warnings.
ascendkit screenshots destinations --workspace "$WORKSPACE" --jsonDiscovers available local iOS simulators and recommends screenshot capture destinations for the workspace platforms.
ascendkit screenshots capture-plan \
--workspace "$WORKSPACE" \
--scheme MyApp \
--configuration Debug \
--jsonWrites screenshots/manifests/capture-plan.json with deterministic xcodebuild test commands, locale flags, result bundle paths, and ASCENDKIT_SCREENSHOT_OUTPUT_DIR environment values for UI tests. If --destination is omitted, AscendKit recommends an available local simulator. This is a local capture plan only; it does not mutate App Store Connect.
UI tests can produce screenshots in either of two supported ways:
let screenshot = XCUIScreen.main.screenshot()
// Preferred when the test runner receives AscendKit environment values.
let outputURL = URL(fileURLWithPath: ProcessInfo.processInfo.environment["ASCENDKIT_SCREENSHOT_OUTPUT_DIR"]!)
.appendingPathComponent("01-home.png")
try screenshot.pngRepresentation.write(to: outputURL, options: [.atomic])
// Robust fallback: AscendKit also imports ordered XCTest attachments from .xcresult.
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "01-home.png"
attachment.lifetime = .keepAlways
add(attachment)Attachment names must start with an ordered screenshot stem such as 01-home, 02-settings, or 03-paywall. AscendKit ignores generic attachments such as launch screenshots to avoid importing unrelated diagnostics.
ascendkit screenshots capture --workspace "$WORKSPACE" --jsonExecutes the saved capture plan locally, writes screenshots/manifests/capture-result.json, stores stdout/stderr logs under screenshots/capture/logs, imports ordered .xcresult attachments when the raw output directory is empty, and refreshes the screenshot import manifest when all capture commands succeed.
ascendkit screenshots workflow run \
--workspace "$WORKSPACE" \
--scheme MyApp \
--mode framedPoster \
--copy "$WORKSPACE/screenshots/copy/en-US.json" \
--jsonRuns the local screenshot workflow end to end: recommends local simulator destinations, writes a fresh capture plan, executes local Xcode UI tests, refreshes the import manifest, refreshes/lints the provided copy file, composes final screenshots, and writes screenshots/manifests/workflow-result.json. The default workflow composition mode is framedPoster.
ascendkit screenshots workflow status --workspace "$WORKSPACE" --jsonSummarizes capture plan, capture execution, import manifest, composition manifest, workflow result, and upload plan readiness in one report.
ascendkit screenshots copy init --workspace "$WORKSPACE" --locale en-US --json
ascendkit screenshots copy refresh --workspace "$WORKSPACE" --locale en-US --json
ascendkit screenshots copy lint --workspace "$WORKSPACE" --locale en-US --jsonGenerates, refreshes, and validates an editable framed-poster copy template at screenshots/copy/en-US.json from screenshot-plan.json. Use copy refresh after the screenshot plan changes; it preserves edited titles/subtitles for matching files and removes stale entries. Lint results are persisted to screenshots/manifests/copy-lint.json and included in workflow status. Edit titles and subtitles there before running screenshots compose --mode framedPoster, screenshots compose --mode deviceFrame, or screenshots workflow run --mode framedPoster.
ascendkit screenshots readiness \
--workspace "$WORKSPACE" \
--source /path/to/screenshots \
--jsonValidates whether a screenshot source folder can be imported.
ascendkit screenshots import --workspace "$WORKSPACE" --source /path/to/screenshotsImports screenshots from a user-provided folder into a manifest.
ascendkit screenshots import-fastlane \
--workspace "$WORKSPACE" \
--source "$APP_ROOT/fastlane/screenshots" \
--locales en-US,zh-HansImports fastlane-style screenshots.
ascendkit screenshots compose --workspace "$WORKSPACE" --mode storeReadyCopy
ascendkit screenshots compose --workspace "$WORKSPACE" --mode poster
ascendkit screenshots compose --workspace "$WORKSPACE" --mode deviceFrame
ascendkit screenshots compose --workspace "$WORKSPACE" --mode framedPoster --copy "$WORKSPACE/screenshots/copy/en-US.json"Composition modes:
storeReadyCopy: organize imported images for upload.poster: render local poster-style PNG artifacts.deviceFrame: compatibility alias forframedPoster; renders a complete App Store marketing screenshot, not a border-only frame.framedPoster: render App Store-sized PNG artifacts with an opaque background, title/subtitle copy, and an inset high-fidelity device frame while preserving the source screenshot dimensions. Uses the built-inDeviceFrameRegistryto resolve physical hardware specifications (exact pixel size, corner radius, bezel width, Dynamic Island, and notch) by matching source image dimensions with tolerant orientation-aware detection. Supported device frames include iPhone 6.7" (1290x2796, Dynamic Island), iPhone 6.5" (1242x2688, notch), iPad Pro 13-inch (2064x2752), and Mac Desktop (2560x1600).
Optional framed poster copy file. If --copy is omitted, AscendKit first reads default locale copy assets from screenshots/copy/<locale>.json, then falls back to screenshot-plan.json screen names and purposes as inferred title/subtitle copy:
{
"items": [
{
"locale": "en-US",
"platform": "iOS",
"fileName": "01-today.png",
"title": "Choose Three",
"subtitle": "Set a calm focus for today"
}
]
}ascendkit screenshots coverage --workspace "$WORKSPACE" --json
ascendkit screenshots upload-plan \
--workspace "$WORKSPACE" \
--display-type APP_IPHONE_67 \
--jsonSummarizes local screenshot coverage by locale, platform, and upload display type without making network requests.
Creates a dry-run App Store Connect screenshot upload plan from imported or composed artifacts. This is the native upload foundation; it does not mutate ASC yet.
The plan includes observed remote screenshot sets from asc metadata observe and reports a blocking finding when a matching locale/display type already has screenshots, preventing accidental duplicates.
ascendkit screenshots upload-plan \
--workspace "$WORKSPACE" \
--display-type APP_IPHONE_67 \
--replace-existing \
--jsonPlans explicit deletion of matching remote screenshots before upload. This still does not mutate ASC; it only records remoteScreenshotsToDelete in the upload plan.
ascendkit screenshots upload \
--workspace "$WORKSPACE" \
--replace-existing \
--confirm-remote-mutation \
--jsonExecutes native screenshot upload through App Store Connect by optionally deleting planned remote screenshots, creating or reusing screenshot sets, reserving screenshots, uploading ASC asset parts, and committing checksums. This command mutates ASC only with --confirm-remote-mutation.
If screenshots upload-plan has findings, execution refuses to proceed.
Transient ASC and asset-upload requests are retried with bounded backoff. If one screenshot fails after execution starts, AscendKit records the failure in failedItems and continues with remaining screenshots when possible.
After each commit, AscendKit polls assetDeliveryState for a bounded number of attempts and records both the final state and assetDeliveryPollAttempts for each uploaded screenshot.
Use screenshots upload-status to summarize uploaded, failed, deleted, retryable screenshot items, asset delivery states, pending processing, failed delivery, ready-for-review-checks state, and deterministic recovery commands without making network requests.
Configure App Store Connect credentials without storing private key contents.
ascendkit asc auth save-profile \
--name production \
--issuer-id ASC_ISSUER_ID \
--key-id ASC_KEY_ID \
--private-key-provider file \
--private-key-ref /secure/path/AuthKey_KEYID.p8Profiles are saved under ~/.ascendkit/profiles/asc/ with owner-only permissions.
ascendkit asc auth profiles --jsonLists saved auth profiles with redacted IDs.
ascendkit asc auth init --workspace "$WORKSPACE" --profile production
ascendkit asc auth check --workspace "$WORKSPACE" --jsonWrites and validates the workspace auth config.
Supported secret providers:
file: read a private key from a local file path.env: read a secret from an environment variable.
keychain references may appear in older local profiles, but new CLI auth commands only accept file and env until a fully verified Keychain resolver is added.
Plan and perform ASC app lookup.
ascendkit asc lookup plan --workspace "$WORKSPACE" --jsonWrites the planned read-only ASC lookup shape.
ascendkit asc apps lookup --workspace "$WORKSPACE" --jsonUses the official ASC API to find the app from the release manifest bundle ID.
asc lookup apps is retained as a compatibility alias:
ascendkit asc lookup apps --workspace "$WORKSPACE" --jsonObserve or import App Store Connect build candidates.
ascendkit asc builds observe --workspace "$WORKSPACE" --jsonFetches remote ASC builds for the selected app.
ascendkit asc builds list --workspace "$WORKSPACE" --jsonPrints currently saved build candidates.
ascendkit asc builds import \
--workspace "$WORKSPACE" \
--id BUILD_ID \
--version 1.0 \
--build 7 \
--state processed \
--jsonImports a build candidate manually. This is useful for deterministic tests or when remote observation is unavailable.
Observe, plan, and apply App Store metadata.
ascendkit asc metadata import \
--workspace "$WORKSPACE" \
--file /path/to/observed-state.json \
--jsonImports previously observed ASC metadata state.
ascendkit asc metadata observe --workspace "$WORKSPACE" --jsonFetches current ASC metadata for the app/version.
ascendkit asc metadata plan --workspace "$WORKSPACE" --jsonBuilds a dry-run mutation plan from local metadata and observed ASC state.
ascendkit asc metadata requests --workspace "$WORKSPACE" --jsonBuilds grouped JSON:API request plans from the mutation plan.
ascendkit asc metadata apply \
--workspace "$WORKSPACE" \
--confirm-remote-mutation \
--json
ascendkit asc metadata status --workspace "$WORKSPACE" --jsonApplies remote metadata mutations. The confirmation flag is required by design.
Use asc metadata status after observe/diff/apply to summarize whether metadata is ready for review planning, including stale diff and release-notes-only cases.
Plan or apply App Store pricing without fastlane.
ascendkit asc pricing set-free --workspace "$WORKSPACE" --jsonFinds the free app price point for the base territory and writes asc/pricing-result.json without mutating remote state.
ascendkit asc pricing set-free \
--workspace "$WORKSPACE" \
--base-territory USA \
--confirm-remote-mutation \
--jsonCreates an App Store Connect appPriceSchedules resource that sets the app to free. This uses the official ASC API and does not depend on fastlane.
Record or attempt App Privacy publication state.
ascendkit asc privacy set-not-collected \
--workspace "$WORKSPACE" \
--confirm-remote-mutation \
--json
ascendkit asc privacy status --workspace "$WORKSPACE" --jsonAttempts to publish App Privacy as Data Not Collected and records the result in asc/privacy-status.json. The status JSON includes readyForSubmission and nextActions so agents can make the final readiness decision without parsing prose. If Apple rejects API-key auth for the App Privacy endpoint, complete App Privacy in App Store Connect UI and then record the manual handoff:
ascendkit asc privacy confirm-manual \
--workspace "$WORKSPACE" \
--data-not-collected \
--jsonPrepare and execute App Review submission.
ascendkit submit review-info init --workspace "$WORKSPACE"Writes an editable reviewer-info template.
ascendkit submit review-info set \
--workspace "$WORKSPACE" \
--first-name "Ada" \
--last-name "Lovelace" \
--email "review@example.com" \
--phone "+15555555555" \
--notes "No login required." \
--requires-login falseWrites reviewer contact, notes, and login requirement. If login is required, pass --requires-login true, --credential-ref ENV_VAR_NAME, and --access-instructions "...". The credential reference points to an environment variable; do not place review passwords in the repository.
ascendkit submit readiness --workspace "$WORKSPACE" --jsonBuilds a checklist of release prerequisites. For framedPoster screenshot composition, including the deviceFrame alias, readiness also requires a clean screenshots/manifests/copy-lint.json report.
ascendkit submit prepare --workspace "$WORKSPACE" --jsonCreates a submission preparation summary.
ascendkit submit review-plan --workspace "$WORKSPACE" --jsonBuilds a review submission plan from local readiness, ASC state, metadata apply results, and selected build. Computes remoteSubmissionExecutionAllowed from readiness and review plan conditions.
ascendkit submit preflight --workspace "$WORKSPACE" --remote --jsonReads ASC remote state before execution: app store version state, build processing state, and existing review submissions. Writes review/preflight-remote-state.json. Use this to verify ASC state matches local assumptions before executing.
ascendkit submit status --workspace "$WORKSPACE" --jsonShows combined local readiness, review plan, and previous execution result. Read-only, idempotent.
ascendkit submit handoff --workspace "$WORKSPACE"Writes a human-readable review handoff Markdown file, including App Privacy state, readiness, next actions, and whether remote execution is allowed.
ascendkit submit execute \
--workspace "$WORKSPACE" \
--confirm-remote-submission \
--jsonWhen all readiness and review plan conditions are met (remoteSubmissionExecutionAllowed is true), executes the full review submission pipeline: attaches build, updates compliance, publishes privacy answers, upserts review detail and age rating, creates or reuses a review submission, and submits for review. The result is written to review/submission-result.json and logged in the audit trail. Existing non-submitted review submissions are reused for idempotency.
Create and validate local subscription templates.
ascendkit iap template init --workspace "$WORKSPACE" --json
ascendkit iap validate --workspace "$WORKSPACE" --jsonThis is a local validation layer. Remote IAP creation and subscription sync are not part of the current command surface.
AscendKit writes release state under:
<app-root>/.ascendkit/releases/<release-id>/
Important files:
manifest.json: discovered app/project metadata.doctor-report.json: release doctor results.metadata/source/*.json: local source metadata.metadata/localized/*.json: imported or localized metadata.metadata/lint/*.json: lint results.screenshots/manifests/*.json: screenshot import/composition manifests.screenshots/manifests/capture-plan.json: local Xcode UI-test screenshot capture command plan.screenshots/manifests/capture-result.json: local screenshot capture execution result with log paths and produced files.screenshots/manifests/workflow-result.json: local screenshot workflow result joining capture, import refresh, and composition.screenshots/manifests/upload.json: dry-run native ASC screenshot upload plan.screenshots/manifests/upload-result.json: native ASC screenshot upload execution result, including uploaded items, asset delivery state, delivery poll attempts, deleted remote screenshots, and per-item failures.asc/auth.json: ASC auth config with secret references only.asc/apps.json: ASC app lookup result.asc/observed-state.json: observed ASC metadata state.asc/metadata-plan.json: metadata mutation dry-run plan.asc/metadata-requests.json: JSON:API request plan.asc/metadata-apply-result.json: remote metadata apply result.asc/pricing-result.json: pricing plan or apply result.build/candidates.json: build candidates.review/reviewer-info.json: reviewer contact and access notes.review/submission-plan.json: planned review submission.review/submission-result.json: remote submission result.audit/events.jsonl: redacted audit log.
.ascendkit/ is ignored by default because it may contain app-specific release state.
AscendKit's core rule is: commit configuration and references, not secrets.
- Do not commit
.p8files. - Do not commit
.ascendkit/release workspaces. - Do not store private keys, passwords, or reviewer login credentials in repository files.
- Use
asc auth save-profilewithfileorenvreferences. - Prefer
--jsonoutputs for automation, but treat workspace artifacts as release-sensitive. - Review
docs/security-model.mdbefore adapting AscendKit for team or CI use.
Remote mutation commands require explicit flags:
asc metadata apply --confirm-remote-mutationscreenshots upload --confirm-remote-mutationscreenshots upload --replace-existing --confirm-remote-mutationfor planned remote screenshot replacement.submit execute --confirm-remote-submissionexecutes remote review submission when all readiness and review plan conditions are met. Usesubmit preflight --remoteto verify ASC state first.
Run this before committing:
scripts/preflight-public-release.shRecommended local security scan:
rg -n --hidden --glob '!.build/**' --glob '!.swiftpm/**' \
"(BEGIN .*PRIVATE KEY|AuthKey_|\\.p8|issuer_id|key_id|password|token|bearer)" .Release checklist:
- Keep
README.mdcommand examples aligned withascendkit --help. - Run
scripts/package-release.shand verify the.sha256file before attaching release archives. - Add tests for new command behavior before expanding remote mutation.
- Update
docs/v1-command-surface.mdanddocs/automation-boundaries.mdwhen scope changes. - Never commit real app release workspaces, screenshots, API keys, or reviewer credentials.
- Run
workspace gitignore --workspace "$WORKSPACE" --fixbefore sharing an app repo that uses AscendKit. - Use
workspace validate-handoff --workspace "$WORKSPACE" --export FILEbefore asking another agent to take over. - Use
workspace next-steps --workspace "$WORKSPACE" --jsonto give agents a command-oriented recovery plan. - Use
workspace export-summary --workspace "$WORKSPACE" --output FILEwhen handing state to another agent instead of sharing.ascendkit/. - Prefer small, deterministic command outputs that can be consumed by scripts and agents.
- After the GitHub Release workflow succeeds, run
scripts/update-homebrew-formula.shandscripts/verify-homebrew-formula.sh --version VERSION, then commit any formula checksum sync. - Before tagging any public release, complete the applicable gates in
docs/v1-release-readiness.md, runscripts/preflight-public-release.sh, verify Homebrew install from the published formula, and confirm this README's Current Status, command examples, safety boundaries, release checklist, and maintainer workflow match the tagged release. - Run
scripts/v1-representative-app-smoke.sh --app-root PATHagainst a representative app using the installed Homebrew binary. - Prefer
scripts/finalize-homebrew-release.sh --version VERSION --commit --push --reinstallafter the tag release workflow completes; otherwise manually sync the dedicated Homebrew tap withscripts/sync-homebrew-tap.sh --commit --push, then verifybrew reinstall rushairer/ascendkit/ascendkit. - Run
scripts/v1-release-readiness.sh --version VERSION --app-root PATHas the combined final v1 gate after the GitHub Release and tap are published. - If install or checksum reports differ across machines, run
scripts/diagnose-homebrew-install.sh --version VERSIONbefore changing release assets.
GitHub Actions:
.github/workflows/ci.ymlruns onmainpushes and pull requests. It runs tests, shell syntax checks, whitespace checks, release packaging, and checksum verification..github/workflows/release.ymlruns onv*tags. It runs tests, builds the CLI archive, verifies the checksum, uploads the archive plus.sha256, generatesFormula/ascendkit.rb, uploads the formula plus installer script, and verifies the published release assets with an installer smoke test.
Fastlane removal roadmap:
- Keep
import-fastlanecommands only as migration helpers. - Harden native ASC screenshot replacement with ordering sync and deeper real-world recovery checks.
- Formalize App Privacy declarations on official ASC API where available and explicit fallback paths where Apple exposes only private iris endpoints.
- Keep binary upload out of scope; Xcode Cloud remains the preferred binary delivery path.
Issues and pull requests are welcome. Good contributions usually fit one of these categories:
- Safer ASC API abstractions.
- Better release-readiness diagnostics.
- More deterministic screenshot and metadata workflows.
- Tests around edge cases in real App Store Connect behavior.
- Documentation improvements that make release automation safer.
Please keep the project local-first and secret-safe. Features that require hidden global state, plaintext credentials, or unreviewable remote mutation should be redesigned before merging.
Useful design docs:
docs/project-charter.mddocs/product-scope.mddocs/security-model.mddocs/automation-boundaries.mddocs/v1-command-surface.mddocs/release-workspace-model.mddocs/asc-api-strategy.mddocs/screenshot-pipeline.mddocs/agent-release-playbook.md
AscendKit is released under the MIT License. See LICENSE.