---
name: create-kmp-project
description: Use when the user wants to scaffold, create, bootstrap, or start a new Kotlin Multiplatform (KMP) or Compose Multiplatform (CMP) project from the CLI — including targets for Android, iOS, Desktop (JVM, macOS/Linux/Windows), or Web (Wasm). Covers the official `Kotlin/KMP-App-Template`, `KMP-App-Template-Native`, `multiplatform-library-template`, `Kotlin/kotlin-wasm-compose-template`, and Amper-branch variants. Also use when the user expects the Android CLI / `android create` to produce a KMP/CMP project (it cannot).
---

# create-kmp-project

> From Shubham Singh Bisht's Android helpers — https://shubhamsinghbisht.com/android-roadmap/#helpers

## Overview

There is no first-party CLI that scaffolds a KMP project. The new `android` CLI (`android create`) ships only an `empty-activity` Android template. The JetBrains KMP wizard at `kmp.jetbrains.com` is a Next.js app that posts a client-built `spec` JSON to `/generateKmtProject` — not scriptable without running its bundle.

The reliable CLI path is: **download a curated official template zip from a `Kotlin/*` GitHub repo, then rewrite the project name, package, applicationId, namespaces, and iOS bundle id in place.**

## When to Use

- User asks to create a new KMP / Kotlin Multiplatform project
- User asks to create a Compose Multiplatform (CMP) project, especially with Web/Wasm and/or Desktop (macOS/Linux/Windows) targets
- User asks to start from `KMP-App-Template`, `kotlin-wasm-compose-template`, or any JetBrains wizard template
- User expects `android create` / Android CLI to produce a KMP/CMP project
- User wants to try the Amper build flavor of the template

Do **not** use for: adding KMP/CMP to an existing Android project (different task — that's a migration, not a scaffold).

## Templates

| Choice | Zip URL | Targets | Module layout |
|---|---|---|---|
| App, Compose UI shared (default KMP) | `https://github.com/Kotlin/KMP-App-Template/archive/refs/heads/main.zip` | Android + iOS | `androidApp` + `iosApp` + `shared` |
| App, Amper build tool | `https://github.com/Kotlin/KMP-App-Template/archive/refs/heads/amper.zip` | Android + iOS | Same as above, Amper config — experimental |
| App, native UI per platform | `https://github.com/Kotlin/KMP-App-Template-Native/archive/refs/heads/main.zip` | Android + iOS | Compose for Android, SwiftUI for iOS, shared business logic |
| KMP library | `https://github.com/Kotlin/multiplatform-library-template/archive/refs/heads/main.zip` | Multi (for publishable libraries) | Library module only |
| **CMP app for Web + Desktop + Android + iOS** | `https://github.com/Kotlin/kotlin-wasm-compose-template/archive/refs/heads/main.zip` | **Wasm (Web) + Desktop (JVM) + Android + iOS** | Single `composeApp` module with `commonMain` / `wasmJsMain` / `desktopMain` / `androidMain` / `iosMain` |

## Inputs to collect

Before scaffolding, ask the user:
1. **Template choice** (one of the templates above)
2. **Project name** (e.g. `KmpDemo`) — becomes `rootProject.name`
3. **Package id** (e.g. `com.example.kmpdemo`) — becomes Android `namespace`, `applicationId`, iOS `BUNDLE_ID`, Kotlin source package
4. **Target directory** (default: the current directory; project goes into `<target>/<projectName>`)

## Procedure (App template, Compose UI shared — main case)

These commands assume macOS (BSD `sed`). On Linux use `sed -i` without `''`.

```bash
WORK=/path/to/parent           # where you'll put things
NAME=KmpDemo                   # project name
PKG=com.example.kmpdemo        # package id
SLUG=KMP-App-Template          # template slug (matches GitHub repo)
ZIP="https://github.com/Kotlin/$SLUG/archive/refs/heads/main.zip"

# 1. Download + extract
cd "$WORK"
curl -L --fail -o "$SLUG-main.zip" "$ZIP"
unzip -q "$SLUG-main.zip" -d "$WORK"
cp -R "$SLUG-main" "$NAME"
cd "$NAME"

# 2. Drop upstream-only files
rm -rf .github list.json images

# 3. Rewrite package strings across configs
sed -i '' "s/com\\.jetbrains\\.kmpapp/$PKG/g" \
  settings.gradle.kts \
  androidApp/build.gradle.kts \
  shared/build.gradle.kts \
  iosApp/Configuration/Config.xcconfig \
  iosApp/iosApp.xcodeproj/project.pbxproj

# 4. rootProject.name
sed -i '' "s/\"$SLUG\"/\"$NAME\"/g" settings.gradle.kts

# 5. iOS bundle + display name
#    Note: step 3 turns BUNDLE_ID=com.jetbrains.kmpapp.KMP-App-Template into $PKG.KMP-App-Template
#    Trim the trailing slug:
sed -i '' "s/BUNDLE_ID=$PKG\\.$SLUG/BUNDLE_ID=$PKG/" iosApp/Configuration/Config.xcconfig
sed -i '' "s/APP_NAME=KMP App/APP_NAME=$NAME/" iosApp/Configuration/Config.xcconfig

# 6. Android app_name
sed -i '' "s/>KMP App</>$NAME</" androidApp/src/main/res/values/strings.xml

# 7. Rewrite Kotlin package declarations + imports
find shared androidApp -name "*.kt" -exec \
  sed -i '' "s/com\\.jetbrains\\.kmpapp/$PKG/g" {} +

# 8. Move package directories.  Path segments derive from $PKG.
OLD_DIR=com/jetbrains/kmpapp
NEW_DIR=$(echo "$PKG" | tr '.' '/')
for base in shared/src/commonMain/kotlin shared/src/iosMain/kotlin androidApp/src/main/kotlin; do
  [ -d "$base/$OLD_DIR" ] || continue
  mkdir -p "$base/$(dirname "$NEW_DIR")"
  mv "$base/$OLD_DIR" "$base/$NEW_DIR"
  # clean up empty parents from old path
  rmdir "$base/com/jetbrains" 2>/dev/null
done
```

## Procedure (CMP / Wasm — Web + Desktop + Android + iOS)

Use this for `Kotlin/kotlin-wasm-compose-template`. It has a **single `composeApp` module** with `commonMain`, `wasmJsMain`, `desktopMain`, `androidMain`, `iosMain` source sets. Most Kotlin files have no package declaration (default package), which makes the rename smaller.

```bash
WORK=/path/to/parent
NAME=CmpDemo
PKG=com.example.cmpdemo
SLUG=kotlin-wasm-compose-template
ZIP="https://github.com/Kotlin/$SLUG/archive/refs/heads/main.zip"

cd "$WORK"
curl -L --fail -o "$SLUG-main.zip" "$ZIP"
unzip -q "$SLUG-main.zip" -d "$WORK"
cp -R "$SLUG-main" "$NAME"
cd "$NAME"

# 1. Drop upstream docs assets
rm -rf screenshots

# 2. Rewrite package strings (template uses org.example.project)
sed -i '' "s/org\\.example\\.project/$PKG/g" \
  composeApp/build.gradle.kts \
  iosApp/Configuration/Config.xcconfig \
  iosApp/iosApp.xcodeproj/project.pbxproj

# 3. rootProject.name (template name is compose-example)
sed -i '' 's/"compose-example"/"'"$NAME"'"/g' settings.gradle.kts

# 4. iOS BUNDLE_ID trim + APP_NAME
#    After step 2 the bundle becomes $PKG.composeexample — trim:
sed -i '' "s/BUNDLE_ID=$PKG\\.composeexample/BUNDLE_ID=$PKG/" iosApp/Configuration/Config.xcconfig
sed -i '' "s/APP_NAME=composeexample/APP_NAME=$NAME/" iosApp/Configuration/Config.xcconfig

# 5. Android app_name
find . -name "strings.xml" -exec sed -i '' "s/>composeexample</>$NAME</" {} +

# 6. ONE Kotlin file lives in old package: composeApp/src/androidMain/kotlin/org/example/project/MainActivity.kt
#    Move it and rewrite its package line. AndroidManifest references .MainActivity relative to
#    namespace, so once package = $PKG, the manifest still resolves.
OLD_DIR=composeApp/src/androidMain/kotlin/org/example/project
if [ -d "$OLD_DIR" ]; then
  NEW_PATH="composeApp/src/androidMain/kotlin/$(echo "$PKG" | tr '.' '/')"
  mkdir -p "$NEW_PATH"
  sed -i '' "s/^package org\\.example\\.project\$/package $PKG/" "$OLD_DIR"/*.kt
  mv "$OLD_DIR"/*.kt "$NEW_PATH/"
  rmdir "$OLD_DIR" composeApp/src/androidMain/kotlin/org/example composeApp/src/androidMain/kotlin/org 2>/dev/null
fi

# 7. Compose Resources generated-class package — derived from rootProject.name (lowercased,
#    hyphens → underscores). App.kt imports `compose_example.composeapp.generated.resources.*`,
#    which after the rename must become `<rootname-normalized>.composeapp.generated.resources.*`.
#    Compute the normalized form and rewrite imports:
RES_OLD=compose_example
RES_NEW=$(echo "$NAME" | tr '[:upper:]-' '[:lower:]_')
find composeApp/src -name "*.kt" -exec \
  sed -i '' "s/${RES_OLD}\\.composeapp\\.generated\\.resources/${RES_NEW}.composeapp.generated.resources/g" {} +
```

### Build commands for CMP template

- Web (Wasm) dev server: `./gradlew :composeApp:wasmJsBrowserDevelopmentRun`
- Desktop (macOS/JVM): `./gradlew :composeApp:run`
- Android: `./gradlew :composeApp:assembleDebug`
- iOS: open `iosApp/iosApp.xcodeproj` in Xcode

If the user only wants a subset of targets (e.g. Web + Desktop), do **not** strip the other source sets automatically — that's a significant build.gradle edit. Just tell them which gradle tasks to use; leave the others dormant.

## Verification

After every scaffold, run from the new project root:

```bash
# KMP-App-Template:
grep -rln "com.jetbrains.kmpapp" .       # must return nothing
grep -rln "KMP-App-Template" .           # only README.md + MuseumApi.kt URL allowed
grep -E "namespace|applicationId" androidApp/build.gradle.kts shared/build.gradle.kts
head -1 settings.gradle.kts
cat iosApp/Configuration/Config.xcconfig

# kotlin-wasm-compose-template (CMP):
grep -rln "org.example.project\|compose-example\|composeexample" . | grep -v README.md | grep -v LICENSE
# must be empty
grep -E "namespace|applicationId" composeApp/build.gradle.kts
head -1 settings.gradle.kts
cat iosApp/Configuration/Config.xcconfig
find composeApp/src -name "*.kt" -exec grep "^package " {} +    # only the new $PKG should appear
```

Expected:
- Zero hits for the old package strings
- For `KMP-App-Template` only: `README.md` (upstream link) and `MuseumApi.kt` (sample data URL pointing at upstream's `list.json`) keep `KMP-App-Template` — harmless; tell the user.
- `namespace = "$PKG"`, `applicationId = "$PKG"` in the relevant `*.gradle.kts`. In `KMP-App-Template` the shared module is `$PKG.shared`.
- `rootProject.name = "$NAME"`
- `BUNDLE_ID=$PKG` with no trailing slug, `APP_NAME=$NAME`

## Common Mistakes

| Mistake | Symptom | Fix |
|---|---|---|
| Forgot the iOS bundle id post-trim | Bundle id is `com.foo.bar.KMP-App-Template` | Add the targeted `s/BUNDLE_ID=$PKG\.$SLUG/BUNDLE_ID=$PKG/` |
| Used GNU `sed -i` syntax on macOS | `sed: 1: "...": invalid command code` | Use `sed -i ''` (empty backup ext) on BSD/macOS |
| Renamed `MuseumApp` / `MainActivity` classes | Template demo no longer compiles | Don't. They are template content; the user can rename them later if they want. AndroidManifest references them by `.MuseumApp` / `.MainActivity` relative to namespace — that still resolves after package change. |
| Forgot the shared module namespace | Gradle sync fails on shared lib | `shared/build.gradle.kts` uses `<pkg>.shared`, not bare `<pkg>` |
| Skipped `project.pbxproj` | Xcode build warns about stale package refs | Include `iosApp/iosApp.xcodeproj/project.pbxproj` in the sed pass |
| Tried `android create --template kmp` | No such template exists | The Android CLI only ships `empty-activity`. Do not attempt. |
| Tried to script the JetBrains web wizard via curl | 404 / requires JS-built `spec` | Skip the wizard for CLI work; download a template zip instead. |
| CMP template: assumed Kotlin sources have packages | Files in `kotlin-wasm-compose-template` are mostly in the **default package** — no `package` line, no directory to rename | The exception is `composeApp/src/androidMain/kotlin/org/example/project/MainActivity.kt` — that one needs both the package line rewrite and a directory move. Grep for `^package ` to find any stragglers. |
| CMP template: stripped Android/iOS source sets without editing gradle | Build fails: dangling dependencies / plugin config | If the user wants Web + Desktop only, leave the other source sets and just use the Web/Desktop gradle tasks. Stripping requires also editing `composeApp/build.gradle.kts`. |
| CMP template: forgot to rewrite the Compose Resources generated package | `compileKotlinDesktop` fails: `Unresolved reference 'compose_example'` and `Unresolved reference 'Res'` in `App.kt` | The generated package is `<rootProject.name>.composeapp.generated.resources` where rootProject.name is lowercased and `-` is replaced with `_`. After step 3, also rewrite the import path. Step 7 in the CMP procedure does this. Verify with: `grep -rln "compose_example" composeApp/` — must be empty. |

## Optional follow-ups

- `git init && git add -A && git commit -m "init"` inside the new project
- For the **library template** the rename surface is smaller (no `androidApp/`, no `iosApp/Configuration/Config.xcconfig`) — same logic, fewer files
- For the **Amper branch**, the `.module.yaml` files also need the `com.jetbrains.kmpapp` rewrite — include `*.yaml` in step 3
- For **`KMP-App-Template-Native`**, additionally update SwiftUI files under `iosApp/iosApp/` if they reference the old package
