10
CLI `@emsifa/wilayah`
@emsifa/wilayah adalah paket CLI npm untuk data wilayah Indonesia. Versi publish-nya 0.1.1 (cli/package.json:2-31), binary-nya bernama wilayah dan menunjuk ke ./dist/index.js (cli/package.json:6-82). CLI ini dibangun di atas citty: defineCommand mendefinisikan perintah, runMain menjalankannya (cli/src/index.ts:23).
Saat ini hanya satu subcommand yang benar-benar terdaftar, yaitu skill (cli/src/index.ts:11-134). Padahal string description paket masih menjual tiga kemampuan sekaligus: download, scaffold, dan skill (cli/package.json:45). Dua nama pertama belum punya handler sama sekali.
Halaman ini membahas mekanika CLI-nya: isi paket, opsi, penentuan target, dan penulisan berkas. Isi SKILL.md yang dipasang dibahas terpisah di Skill untuk AI Agent, sedangkan tab CLI di landing dibahas di Website dan Playground.
Registri perintah dan status
| Perintah | Status di subCommands | Bukti |
|---|---|---|
wilayah skill | terdaftar | skill: skillCommand (cli/src/index.ts:11-134) |
wilayah download | belum terdaftar | hanya muncul di string description (cli/package.json:45) dan di tab landing yang masih comingSoon (web/src/components/sections/CliSection.tsx:206) |
wilayah scaffold | belum terdaftar | tab landing dengan comingSoon: true (web/src/components/sections/CliSection.tsx:367) |
| tanpa subcommand | mencetak usage | blok console.log di run (cli/src/index.ts:19-418) |
Bentuk perintah yang direncanakan sudah ditulis lengkap di README root, tetapi seluruhnya diberi label Soon (README.md:547-5599):
npx @emsifa/wilayah download --format csv --output ./wilayah.csv
npx @emsifa/wilayah download --format sql --output ./wilayah.sql
npx @emsifa/wilayah download --format json --output ./wilayah.json
npx @emsifa/wilayah scaffold --orm laravel
npx @emsifa/wilayah scaffold --orm prisma
npx @emsifa/wilayah scaffold --orm drizzle
Detail kecil yang mudah menjebak: run milik parent tetap dieksekusi citty walaupun sebuah subcommand dipanggil, sehingga ada guard firstNonFlag yang mengembalikan fungsi lebih awal (cli/src/index.ts:14-1810). Teks help-nya pun tidak sinkron dengan kode: ia hanya menawarkan --agent <claude|codex|opencode|all> (cli/src/index.ts:2611), sementara parseAgents menerima claude, codex, opencode, antigravity, kiro, others, dan all (cli/src/utils/paths.ts:83-111). Nomor versi juga berbeda: meta.version masih 0.1.0 (cli/src/index.ts:5-13) sementara package.json sudah 0.1.1 (cli/package.json:2-3).
Isi paket dan cara build
| Aspek | Nilai | Bukti |
|---|---|---|
bin | wilayah → ./dist/index.js | cli/package.json:6-82 |
files | hanya dist | cli/package.json:9-1114 |
engines.node | >=18 | cli/package.json:12-1415 |
build | tsdown | cli/package.json:36-4016 |
prebuild | mkdir -p assets && cp ../SKILL.md ./assets/SKILL.md | cli/package.json:3917 |
| dependency runtime | @clack/prompts, citty, consola, pathe, picocolors | cli/package.json:41-4718 |
| bundling | noExternal: [/./] dan copy: ["assets"] | cli/tsdown.config.ts:14-1619 |
Semua dependency di-bundle ke satu berkas keluaran supaya npx @emsifa/wilayah tetap jalan walau isi paket hanya dist (cli/tsdown.config.ts:14-1619). Karena assets ikut di-copy, SKILL.md yang dipakai saat instalasi berakhir di dist/assets/SKILL.md.
graph LR
root["SKILL.md di root repo"] --> pre["prebuild: cp ../SKILL.md ./assets/SKILL.md"]
pre --> assets["cli/assets/SKILL.md"]
assets --> bundle["dist/assets/SKILL.md"]
bundle --> loader["loadSkillContent()"]
loader --> candidates["candidates: here/assets, cwd/assets, ../SKILL.md"]
candidates --> content["skillContent"]
loader --> fallback["/home/emsifa/Dev/personal/data-wilayah/SKILL.md"]
content --> write["ensureDirForFile + writeFile"]
write --> target["target dari AGENT_REGISTRY atau --target"]
style root fill:#2d7d46,color:#fff
style bundle fill:#8b5cf6,color:#fff
style loader fill:#f59e0b,color:#000
style fallback fill:#dc2626,color:#fff
style write fill:#336791,color:#fff
style target fill:#336791,color:#fff
%% fig: Asal konten SKILL.md sampai ditulis ke target
%% desc: prebuild menyalin SKILL.md ke assets, tsdown ikut membundelnya, lalu loadSkillContent mencoba beberapa candidate path sebelum menulis ke target.loadSkillContent mencoba beberapa candidate berurutan dan hanya menerima berkas yang memuat string Wilayah Indonesia (cli/src/utils/skill-source.ts:5-2720).
⚠️ Candidate terakhir di
skill-source.tsadalah path absolut milik mesin penulis,/home/emsifa/Dev/personal/data-wilayah/SKILL.md(cli/src/utils/skill-source.ts:2721). Bila seluruh candidate gagal, fungsi melemparCould not find SKILL.md source. Tried: ...(cli/src/utils/skill-source.ts:49-5122). Jadi jalur cadangan itu tidak menolong pengguna lain.
Alur interaktif wilayah skill
Mode interaktif hanya aktif bila stdin dan stdout sama-sama TTY, tidak ada --no-interactive, dan --agent tidak diisi (cli/src/commands/skill.ts:21-2823). Begitu --agent hadir, CLI langsung masuk mode non-interaktif (cli/src/commands/skill.ts:88-9524).
stateDiagram-v2
[*] --> DeteksiMode
DeteksiMode --> Interaktif: TTY tanpa --agent
DeteksiMode --> NonInteraktif: ada --no-interactive atau bukan TTY
Interaktif --> PilihAgent: p.multiselect agen
PilihAgent --> Dibatalkan: p.isCancel
PilihAgent --> InputPathLain: others ikut dipilih
PilihAgent --> PilihScope: hanya RealAgent
InputPathLain --> Dibatalkan: p.isCancel atau path bukan .md
InputPathLain --> PilihScope: path kustom diterima
InputPathLain --> SiapTulis: tanpa RealAgent
PilihScope --> VarianOpenCode: global dan opencode
PilihScope --> SiapTulis: project
VarianOpenCode --> SiapTulis
NonInteraktif --> Gagal: parseAgents melempar error
NonInteraktif --> SiapTulis: parseAgents lolos
SiapTulis --> DryRun: --dry-run
SiapTulis --> CekBerkas
CekBerkas --> Tulis: berkas belum ada
CekBerkas --> Tanya: berkas ada tanpa --yes
CekBerkas --> Gagal: berkas ada di mode non-interaktif
Tanya --> Tulis: Overwrite atau Append
Tanya --> Dilewati: Skip atau p.isCancel
Tulis --> [*]
DryRun --> [*]
Dilewati --> [*]
Dibatalkan --> [*]
Gagal --> [*]
classDef jalan fill:#2d7d46,color:#fff
classDef gagal fill:#dc2626,color:#fff
classDef konfigurasi fill:#f59e0b,color:#000
classDef khusus fill:#8b5cf6,color:#fff
classDef penyimpanan fill:#336791,color:#fff
class SiapTulis jalan
class Gagal,Dibatalkan,Dilewati gagal
class DeteksiMode,PilihAgent,InputPathLain,PilihScope,VarianOpenCode konfigurasi
class CekBerkas,Tanya,DryRun khusus
class Tulis penyimpanan
%% fig: State machine pemilihan agent dan scope instalasi
%% desc: Deteksi mode menentukan jalur interaktif atau non-interaktif, lalu pilihan agent dan scope diselesaikan sebelum berkas target diperiksa dan ditulis.Urutan prompt di kode sama dengan urutan di README: checklist agent, path kustom bila Others dicentang, scope Project atau Global, varian OpenCode bila relevan, lalu keputusan untuk berkas yang sudah ada (cli/README.md:37-4425). Checklist-nya memakai p.multiselect dengan enam opsi dan required: true (cli/src/commands/skill.ts:110-15626). Bila Others dipilih, p.text memvalidasi path agar berakhir .md, dan path relatif diselesaikan terhadap cwd sementara awalan ~/ diganti homedir() (cli/src/commands/skill.ts:158-18327). Prompt scope serta varian OpenCode hanya muncul bila ada RealAgent, karena kombinasi Others saja membuat scope tidak relevan dan dipaku ke project (cli/src/commands/skill.ts:185-22528).
Tahap akhir merakit daftar target: hasil getSkillTargets untuk agent registry, ditambah customTarget bila ada, dan CLI berhenti dengan No targets resolved kalau daftarnya kosong (cli/src/commands/skill.ts:266-28529). Pada --dry-run, target hanya dicetak lalu fungsi return sebelum satu berkas pun ditulis (cli/src/commands/skill.ts:287-29430).
Opsi baris perintah
| Opsi | Tipe dan default | Perilaku | Bukti |
|---|---|---|---|
--agent | string | daftar dipisah koma; all diekspansi ke ALL_AGENTS | cli/src/utils/paths.ts:83-9731 |
--target, --out | string, alias out | path kustom untuk others | cli/src/commands/skill.ts:57-6132 |
--global | boolean, default false | install ke ~ dan bukan ./ | cli/src/commands/skill.ts:62-6633 |
--yes, -y | boolean, default false | lewati konfirmasi | cli/src/commands/skill.ts:67-7234 |
--dry-run | boolean, default false | cetak target tanpa menulis | cli/src/commands/skill.ts:73-7735 |
--cwd | string | override direktori proyek lewat resolve | cli/src/commands/skill.ts:78-8136 |
--no-interactive | boolean, default false | paksa mode non-interaktif | cli/src/commands/skill.ts:82-8637 |
Daftar yang sama terdokumentasi di cli/README.md:58-6638, dan contoh pemakaian CI-nya di cli/README.md:46-5639.
Penentuan target per agent
AGENT_REGISTRY menyimpan path project dan global untuk lima agent nyata (cli/src/utils/paths.ts:22-5740). getSkillTargets lalu menempelkan path relatif itu ke cwd untuk scope project dan ke home untuk scope global (cli/src/utils/paths.ts:59-8141).
| Agent | Project (relatif cwd) | Global (relatif ~) |
|---|---|---|
claude | .claude/skills/wilayah-indonesia/SKILL.md | .claude/skills/wilayah-indonesia/SKILL.md |
codex | .codex/skills/wilayah-indonesia/SKILL.md | .codex/skills/wilayah-indonesia/SKILL.md |
opencode | .opencode/skills/wilayah-indonesia/SKILL.md | .config/opencode/skills/... (varian xdg) atau .opencode/skills/... (varian legacy) |
antigravity | .agents/skills/wilayah-indonesia/SKILL.md | .gemini/config/skills/wilayah-indonesia/SKILL.md |
kiro | .kiro/skills/wilayah-indonesia/SKILL.md | .kiro/skills/wilayah-indonesia/SKILL.md |
Untuk claude, codex, dan kiro, string project dan global kebetulan identik sehingga yang membedakan hanyalah basis path-nya (cli/src/utils/paths.ts:22-5740). OpenCode adalah satu-satunya agent yang punya variants, dan varian hanya dipakai ketika scope global (cli/src/utils/paths.ts:72-7542). Konstanta direktori dan nama berkasnya dipusatkan sebagai SKILL_DIR dan SKILL_FILE (cli/src/utils/paths.ts:9-1243).
Menulis berkas: overwrite, append, atau skip
Setiap target melewati exists() lebih dulu, dan ensureDirForFile membuat direktori induk secara rekursif sebelum penulisan (cli/src/utils/fs.ts:5-1644). Perilaku ketika berkas sudah ada bergantung pada mode dan pada apakah target itu target kustom, yang dikenali lewat target === customTarget (cli/src/commands/skill.ts:296-30345).
| Kondisi | Yang terjadi | Bukti |
|---|---|---|
| target kustom, interaktif, berkas ada | p.select menawarkan Append, Overwrite, Skip; Skip atau p.isCancel menaikkan skipped | cli/src/commands/skill.ts:306-33046 |
| target registry, interaktif, berkas ada | p.confirm dengan initialValue: false; jawaban negatif melewati berkas | cli/src/commands/skill.ts:337-34647 |
non-interaktif, berkas ada, tanpa --yes | File exists: ${target} — use --yes to overwrite lalu process.exit(1) | cli/src/commands/skill.ts:348-35148 |
| selain itu | writeFile(target, skillContent, "utf-8") | cli/src/commands/skill.ts:362-37149 |
Mode Append menambahkan blok yang dibungkus penanda <!-- wilayah-indonesia:start --> dan <!-- wilayah-indonesia:end --> (cli/src/commands/skill.ts:30-4550). Penanda itu membuat operasi idempoten: bila penanda sudah ada, isi di antaranya diganti, bukan diduplikasi (cli/src/commands/skill.ts:33-4151). Perilaku ini juga dijelaskan di cli/README.md:68-7152.
⚠️
--yesberarti overwrite, bukan append. Untuk target kustom yang sudah ada, jalur tulis biasa tetap memanggilwriteFilesehingga seluruh isi berkas lama hilang (cli/src/commands/skill.ts:362-37149). README menegaskanOthershanya bisa append lewat mode interaktif (cli/README.md:7153). Di CI,AGENTS.mdyang sudah berisi aturan proyek akan tertimpa penuh.
Di akhir, mode interaktif memanggil p.outro dengan ringkasan jumlah berkas terpasang atau jumlah yang dilewati, sedangkan mode non-interaktif memakai consola.info (cli/src/commands/skill.ts:374-38654).
Pesan kegagalan yang perlu dikenali
| Situasi | Pesan | Bukti |
|---|---|---|
non-interaktif tanpa --agent | `Missing --agent. Non-interactive mode requires --agent <claude | codex |
--agent others tanpa --target | Missing --target. When using --agent others, you must specify --target <path> (...) | cli/src/commands/skill.ts:240-24356 |
| nama agent tidak dikenal | Unknown agent "<nama>". Valid: claude, codex, opencode, antigravity, kiro, others, all | cli/src/utils/paths.ts:104-10857 |
| daftar agent kosong | No valid agents selected | cli/src/commands/skill.ts:235-23858 |
| target tidak terbentuk | No targets resolved | cli/src/commands/skill.ts:282-28559 |
SKILL.md gagal dimuat | Failed to load SKILL.md: <pesan> | cli/src/commands/skill.ts:257-26460 |
Rangkaian perintah untuk membangun dan menguji CLI dari source ada di cli/README.md:73-8161; semuanya memakai node cli/dist/index.js dengan --dry-run agar tidak menulis berkas.