| name | create-repository |
| description | Scaffold a new KOIN Repository โ domain `<Name>Repository` interface, data `<Name>RepositoryImpl` class with `@Inject constructor()`, and the corresponding `@Binds` entry in `data/.../di/repository/BindsRepositoryModule.kt`. Use whenever the user asks to add a Repository, create a Repository pair, "domain interface + data impl + Hilt ๋ฐ์ธ๋ฉ", or "repository ๋ง๋ค์ด์ค" (e.g. "Library Repository ๋ง๋ค์ด์ค", "create LibraryRepository", "/create-repository Library", "domain ์ ์ธํฐํ์ด์ค + data ์ impl + Hilt ๋ฑ๋ก"). Trigger this skill even when the user does not say "skill" โ any request that boils down to "new Repository in the KOIN clean-architecture stack" should use it. Defaults to the modern `@Binds` DI style (BindsRepositoryModule), not the legacy `@Provides` (RepositoryModule). Does NOT generate data sources, API services, mappers, domain models, or use cases โ those are out of scope and require their own follow-up scaffolding. |
Role
You are the dedicated KOIN Repository scaffolder. Given a feature noun (e.g. Library, Reservation, Menu), produce a three-piece change that compiles cleanly:
domain/src/main/java/in/koreatech/koin/domain/repository/<Name>Repository.kt โ interface
data/src/main/java/in/koreatech/koin/data/repository/<Name>RepositoryImpl.kt โ class
- One added
@Binds method (plus 2 imports) in data/src/main/java/in/koreatech/koin/data/di/repository/BindsRepositoryModule.kt
Do not create methods, data-source dependencies, mappers, request DTOs, response DTOs, domain models, or use cases. The user adds those separately (or via other skills) once the repository contract is being filled in.
Why this exists
KOIN already has 31 domain repository interfaces and 31 data impls. Each new pair is a tiny but error-prone copy-paste: get the package backticks right, get the @Inject constructor() right, register the Hilt binding in the right module out of two competing modules, keep import order ktlint-clean. This skill removes the rote.
Inputs
The user's invocation typically looks like:
/create-repository Library โ name positional
- "Library Repository ๋ง๋ค์ด์ค"
- "create LibraryRepository in domain and the impl in data with Hilt binding"
- "์ ๊ท repository ์ถ๊ฐ: ์ด๋ฆ์ reservation"
Extract the noun as <Name>:
- Strip a trailing
Repository if the user typed it (LibraryRepository โ Library).
- Strip a leading
feature/ if the user typed it (feature/library โ library).
- Convert to multi-word PascalCase:
library โ Library, reservation_v2 โ ReservationV2, lost-and-found / lostAndFound โ LostAndFound, coop-shop โ CoopShop (see domain/repository/CoopShopRepository.kt, OwnerChangePasswordRepository.kt). If the input is a single concatenated token like lostandfound with no word boundaries, ask one short clarification rather than guessing โ KOIN repositories all use multi-word PascalCase, so Lostandfound would be wrong.
If the resulting <Name> is ambiguous (e.g., user gives "owner change password"), state the proposed name and ask once before writing.
DI Style โ always @Binds, never @Provides
The repo has two co-existing DI modules:
BindsRepositoryModule.kt โ modern @Binds style (37 lines, holds 4 newest repos: Callvan, Timetable, Bus, FirebaseMessaging).
RepositoryModule.kt โ legacy @Provides style (308 lines, holds the bulk of historical repos).
New repositories MUST be added to BindsRepositoryModule.kt. The @Provides form was historically used because the codebase predates @Binds-friendly impls; it is no longer the preferred path and produces noisier code. Do not even ask the user.
Workflow
Step 1 โ Validate the name
- Compute
<Name> in PascalCase.
- Run two parallel Greps to confirm uniqueness:
Grep for interface <Name>Repository under domain/src/main/java/in/koreatech/koin/domain/repository/
Grep for class <Name>RepositoryImpl under data/src/main/java/in/koreatech/koin/data/repository/
- If either exists, refuse and report which file already has it. Do not overwrite.
Step 2 โ Write the interface and impl (parallel Writes)
Render both files in a single message:
| Path | Source |
|---|
domain/src/main/java/in/koreatech/koin/domain/repository/<Name>Repository.kt | references/Repository.kt.tmpl with {{NAME}} substituted |
data/src/main/java/in/koreatech/koin/data/repository/<Name>RepositoryImpl.kt | references/RepositoryImpl.kt.tmpl with {{NAME}} substituted |
Both files use backtick-escaped in in the package declaration (per CLAUDE.md). Do not skip the backticks โ in is a Kotlin keyword.
Step 3 โ Register the Hilt binding (Edit, not Write)
Edit data/src/main/java/in/koreatech/koin/data/di/repository/BindsRepositoryModule.kt with two surgical edits, then let ktlint reorder imports.
3a. Add two imports
The existing import block is alphabetically ordered. Append both new lines anywhere in the import block (the simplest is right before import javax.inject.Singleton) โ :data:ktlintFormat will reorder them later.
import `in`.koreatech.koin.data.repository.<Name>RepositoryImpl
import `in`.koreatech.koin.domain.repository.<Name>Repository
Use Edit anchored on import javax.inject.Singleton so the placement is deterministic.
3b. Add the @Binds method
Anchor on the last @Binds method's closing line in the file and insert a new block immediately after it. Read the file first to find the actual last method โ do not assume bindsCallvanRepository is still last, since this skill itself appends new methods on each run. As of writing the file ends like:
@Binds
@Singleton
abstract fun bindsCallvanRepository(callvanRepositoryImpl: CallvanRepositoryImpl): CallvanRepository
}
Insert before the final }:
@Binds
@Singleton
abstract fun binds<Name>Repository(<name>RepositoryImpl: <Name>RepositoryImpl): <Name>Repository
Where <name> is <Name> with the first letter lowercased (parameter name convention in this file: callvanRepositoryImpl, timetableRepositoryImpl). Note: bindsBusV2Repository is an outlier โ its parameter is named busV2RepositoryImpl even though the impl class is BusRepositoryImpl (a leftover from an incomplete v2 โ Bus rename). Do not mirror this anomaly; follow the rule against <Name>, not the bus example.
The method format is single-line signature when it fits on one line (mirrors bindsTimetableRepository, bindsCallvanRepository). If the line exceeds ktlint's max width, fall back to the multi-line form (mirrors bindsFirebaseMessagingRepository):
@Binds
@Singleton
abstract fun binds<Name>Repository(
<name>RepositoryImpl: <Name>RepositoryImpl
): <Name>Repository
3c. Idempotency
Before applying 3a and 3b, Grep BindsRepositoryModule.kt for binds<Name>Repository. If present, skip both edits and report.
Step 4 โ Format
Run:
./gradlew :data:ktlintFormat -q
This fixes import order in BindsRepositoryModule.kt (your appended imports are inserted near javax.inject.Singleton; ktlint will move them into alphabetical position). It also formats the new files.
If ktlintFormat fails, the scaffold has a syntax issue โ fix it and rerun. Do not commit broken code.
Step 5 โ Verify the build graph
./gradlew :data:assembleDebug -q
Hilt processes @Binds at compile time, so this proves the binding is well-formed (the impl is @Inject-able, return type matches the interface, no duplicate binding). If you see [Dagger/DuplicateBindings], you accidentally registered a binding that already exists in RepositoryModule.kt โ investigate the legacy module before retrying.
Output to the user
Concise checklist after success:
โ
<Name>Repository ์์ฑ ์๋ฃ
์์ฑ๋ ํ์ผ:
- domain/src/main/java/in/koreatech/koin/domain/repository/<Name>Repository.kt
- data/src/main/java/in/koreatech/koin/data/repository/<Name>RepositoryImpl.kt
์์ ๋ ํ์ผ:
- data/.../di/repository/BindsRepositoryModule.kt
- import: <Name>RepositoryImpl, <Name>Repository
- @Binds: binds<Name>Repository(...)
๊ฒ์ฆ:
- :data:ktlintFormat โ
- :data:assembleDebug โ
๋ค์:
- ์ธํฐํ์ด์ค์ suspend fun ์๊ทธ๋์ฒ ์ถ๊ฐ (์คํจ ๊ฐ๋ฅ ํธ์ถ์ Result<T>)
- Impl ์ @Inject constructor() ์ RemoteDataSource / LocalDataSource ์ฃผ์
- mapper, request/response DTO, ๋๋ฉ์ธ ๋ชจ๋ธ์ ๋ณ๋ ์์
What NOT to do
- Do NOT add methods to the interface. The TODO comment is the spec โ user fills in.
- Do NOT add constructor dependencies to the Impl. Empty constructor is the spec; user injects data sources after creating them.
- Do NOT touch
RepositoryModule.kt. Even if the user explicitly asks for @Provides style, refuse and explain @Binds is the chosen direction (mention all 4 recent repos used @Binds).
- Do NOT create RemoteDataSource, LocalDataSource, API service, or any DTOs. Those are separate concerns with their own scaffolding paths.
- Do NOT create domain models or use cases. Those are wired separately.
- Do NOT commit. Stop after
assembleDebug.
Reference files
references/Repository.kt.tmpl โ domain interface skeleton
references/RepositoryImpl.kt.tmpl โ data impl skeleton
The BindsRepositoryModule.kt edit is described inline above (no separate template โ it's a 2-import + 1-method surgical insert).