Scripts
Helper scripts are located in the MobileApp/scripts/ directory. Run all scripts from the MobileApp/ directory.
Refactor Package
Renames the app's package / applicationId / iOS bundle ID and display name across the whole project — Kotlin packages and directories, Gradle files, Firebase/Google-services configs, iOS Info.plist / project.pbxproj, GitHub publish workflows, the helper scripts, and package references in the docs/guidelines (READMEs, AGENTS.md, skills/, AiGuidelines/) — covering both the dotted id (com.x.y) and the slashed path (com/x/y) forms.
# Full refactor — Kotlin packages + applicationId + bundle ID + app name:
./scripts/refactor_package.sh --app-id com.example.newapp --app-name NewApp
# IDs + app name only, keep Kotlin packages/dirs (fewer merge conflicts when
# spinning multiple apps off one base):
./scripts/refactor_package.sh --app-id com.example.newapp --app-name NewApp --skip-package-rename
Arguments & options:
--app-id <id>(required) — new AndroidapplicationId/ iOS bundle ID (e.g.com.example.newapp).--app-name <name>(required) — new app display name.--skip-package-rename— keep Kotlin packages/directories; only update IDs, Firebase refs, and app name. Default: off (packages are renamed).--old-app-id <id>/--old-app-name <name>— override the values being replaced. By default these are auto-detected from the project (old id from the androidAppnamespace, old name fromrootProject.nameinsettings.gradle.kts), so re-refactoring an already-renamed project just works without passing them. If detection can't find them, the script aborts (rather than guessing a default) and asks you to pass these flags.-y,--yes— skip the confirmation prompt (required for non-interactive/CI use).
Positional
<newAppId> <newAppName>are still accepted as a fallback, but the named flags above are preferred.
Notes:
- Edits files in place and is irreversible — commit or back up first. The script prints a plan and asks for confirmation unless
-yis passed. - Idempotent — re-running with the same args is a no-op once applied.
- Replace
google-services.json/GoogleService-Info.plistwith configs for the new app ID after running. - Requires
perl(preinstalled on macOS and CI Linux).
Update Version
Automatically increments the version code and optionally updates the version name for both Android and iOS in one go.
# Auto-increment patch version (e.g., 1.0.0 → 1.0.1) and bump version code:
./scripts/update_version.sh
# Set a specific version name:
./scripts/update_version.sh -v "2.0.0"
What it does:
- Reads current Android
versionCodeandversionNamefromandroidApp/build.gradle.kts - Increments Android
versionCodeby 1 - Reads and updates iOS
CURRENT_PROJECT_VERSIONandMARKETING_VERSIONinproject.pbxproj - Updates iOS
Info.plist(CFBundleVersionandCFBundleShortVersionString) - If no
-vflag is provided, the patch version (last number) is auto-incremented
Note: Make sure the script has executable permission: chmod +x scripts/update_version.sh
Generate Android Keystore
Generates a signed keystore file and its properties file for Play Store publishing.
# Using person name only:
./scripts/generate_android_keystore.sh "Your Name" ""
# Using organization only:
./scripts/generate_android_keystore.sh "" "YourCompany"
# Using both:
./scripts/generate_android_keystore.sh "Your Name" "YourCompany"
What it does:
- Generates a keystore at
distribution/android/keystore/keystore.jks - Creates
distribution/android/keystore/keystore.propertieswith auto-generated secure passwords - Default key validity is 10,000 days
Create Local Data Layer
Scaffolds a Room 3 entity + DAO end-to-end in commonMain.
./scripts/make_local.sh Note
What it does:
- Creates the domain model in
commonMain/.../domain/model/(skipped if it already exists). - Creates the
@EntitywithtoEntity()/toModel()extension-function mappers. - Creates the
@Daowith the standard CRUD surface (getById,getByIdFlow,getAllFlow,upsert,delete,deleteAll). - Registers the entity in
@Database(entities = [...])onAppDatabase. - Adds the abstract DAO accessor on
AppDatabase. - Registers
single { get<AppDatabase>().<name>Dao() }inDatabaseModule.kt.
Notes:
- Idempotent — re-running for the same model skips files / wiring already in place.
- Insertion points in
AppDatabase.ktandDatabaseModule.ktare marked with// Add new ... — make_local.sh inserts here.comments. Leave those alone. - After scaffolding: edit the generated entity to add real columns (and update the mappers), then bump
@Database(version = ...)and add aMigrationif you've already shipped.
Create KMP Module
Scaffolds a new Kotlin Multiplatform library module with the convention plugin applied.
# Create module in default libs/ directory:
./scripts/create_module.sh my-module
# Create module in a custom directory:
./scripts/create_module.sh my-module custom-dir
# Create module with a custom namespace:
./scripts/create_module.sh my-module libs com.example.mymodule
What it does:
- Creates the module directory with
build.gradle.ktsusing theconfigure-kmp-library-moduleconvention plugin - Sets up
commonMainsource directory with the correct package structure - Adds the module to
settings.gradle.ktsautomatically
Generate ASO Metadata
Uses OpenAI to generate optimized App Store and Play Store metadata (titles, descriptions, keywords) for your app.
# Generate from an app idea:
./scripts/generate_aso_metadata.sh --idea "AI photo editor"
# Generate from a PRD file:
./scripts/generate_aso_metadata.sh --idea-file AiGuidelines/project/prd.md --store ios
# Generate with target keywords and multiple locales:
./scripts/generate_aso_metadata.sh --idea "OCR scanner" --keywords "document scanner,ocr app" --locales "en-US,es-ES"
# Translate existing metadata to other locales:
./scripts/generate_aso_metadata.sh --base-locale "en-US" --locales "es-ES,fr-FR"
What it does:
- Generates ASO-optimized metadata for iOS (name, subtitle, keywords, description) and/or Android (title, short description, full description)
- Outputs files to
distribution/ios/appstore_metadata/anddistribution/android/playstore_metadata/ - Supports multiple locales in parallel
- Can translate existing metadata to new locales
Options:
--idea "<text>"— Short app idea or description--idea-file <file>— Path to PRD or idea document--base-locale <path>— Existing locale folder for translation mode--keywords "<k1,k2>"— Target ASO keywords--locales "<l1,l2>"— Comma-separated locales (default:en-US)--store ios|android|both— Target store (default:both)
Requires: OPENAI_API_KEY set in local.properties, ~/credentials/credentials.txt, or in the script itself.
Generate Store Screenshots
Renders every @Preview @StoreScreenshot composable into upload-ready PNGs at App Store / Play Store pixel sizes — framed in pure Compose, no Fastlane / ImageMagick / Ruby toolchain.
./scripts/generate_store_screenshots.sh
What it does:
- Runs
:shared:generateStoreScreenshotswith-PgenerateStoreScreenshots=trueso the gatedStoreScreenshotGeneratorTestactually executes. - Roborazzi captures each
@Preview @StoreScreenshotat the dimensions declared in itsStoreDeviceenum value. - Output:
distribution/store_screenshots/<locale>/<device>/<tag>_<methodName>.png.
Add a new screenshot by writing a @Preview @StoreScreenshot @Composable function anywhere under com.kotlinfoundation.kmpstarterkit.*. See Store Screenshots for the authoring pattern.