Lompat ke konten
dokumentasi.id
Commit 493bd1c
v1
Star762

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

10

CLI `@emsifa/wilayah`

@emsifa/wilayah adalah paket CLI npm untuk data wilayah Indonesia. Versi publish-nya 0.1.1 (cli/package.json:2-3), binary-nya bernama wilayah dan menunjuk ke ./dist/index.js (cli/package.json:6-8). CLI ini dibangun di atas citty: defineCommand mendefinisikan perintah, runMain menjalankannya (cli/src/index.ts:2).

Saat ini hanya satu subcommand yang benar-benar terdaftar, yaitu skill (cli/src/index.ts:11-13). Padahal string description paket masih menjual tiga kemampuan sekaligus: download, scaffold, dan skill (cli/package.json:4). 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

PerintahStatus di subCommandsBukti
wilayah skillterdaftarskill: skillCommand (cli/src/index.ts:11-13)
wilayah downloadbelum terdaftarhanya muncul di string description (cli/package.json:4) dan di tab landing yang masih comingSoon (web/src/components/sections/CliSection.tsx:20)
wilayah scaffoldbelum terdaftartab landing dengan comingSoon: true (web/src/components/sections/CliSection.tsx:36)
tanpa subcommandmencetak usageblok console.log di run (cli/src/index.ts:19-41)

Bentuk perintah yang direncanakan sudah ditulis lengkap di README root, tetapi seluruhnya diberi label Soon (README.md:547-559):

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-18). Teks help-nya pun tidak sinkron dengan kode: ia hanya menawarkan --agent <claude|codex|opencode|all> (cli/src/index.ts:26), 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

AspekNilaiBukti
binwilayah → ./dist/index.jscli/package.json:6-8
fileshanya distcli/package.json:9-11
engines.node>=18cli/package.json:12-14
buildtsdowncli/package.json:36-40
prebuildmkdir -p assets && cp ../SKILL.md ./assets/SKILL.mdcli/package.json:39
dependency runtime@clack/prompts, citty, consola, pathe, picocolorscli/package.json:41-47
bundlingnoExternal: [/./] dan copy: ["assets"]cli/tsdown.config.ts:14-16

Semua dependency di-bundle ke satu berkas keluaran supaya npx @emsifa/wilayah tetap jalan walau isi paket hanya dist (cli/tsdown.config.ts:14-16). 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.
Fig. 1: Asal konten SKILL.md sampai ditulis ke targetprebuild 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-27).

⚠️ Candidate terakhir di skill-source.ts adalah path absolut milik mesin penulis, /home/emsifa/Dev/personal/data-wilayah/SKILL.md (cli/src/utils/skill-source.ts:27). Bila seluruh candidate gagal, fungsi melempar Could not find SKILL.md source. Tried: ... (cli/src/utils/skill-source.ts:49-51). 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-28). Begitu --agent hadir, CLI langsung masuk mode non-interaktif (cli/src/commands/skill.ts:88-95).

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.
Fig. 2: State machine pemilihan agent dan scope instalasiDeteksi 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-44). Checklist-nya memakai p.multiselect dengan enam opsi dan required: true (cli/src/commands/skill.ts:110-156). 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-183). 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-225).

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-285). Pada --dry-run, target hanya dicetak lalu fungsi return sebelum satu berkas pun ditulis (cli/src/commands/skill.ts:287-294).

Opsi baris perintah

OpsiTipe dan defaultPerilakuBukti
--agentstringdaftar dipisah koma; all diekspansi ke ALL_AGENTScli/src/utils/paths.ts:83-97
--target, --outstring, alias outpath kustom untuk otherscli/src/commands/skill.ts:57-61
--globalboolean, default falseinstall ke ~ dan bukan ./cli/src/commands/skill.ts:62-66
--yes, -yboolean, default falselewati konfirmasicli/src/commands/skill.ts:67-72
--dry-runboolean, default falsecetak target tanpa menuliscli/src/commands/skill.ts:73-77
--cwdstringoverride direktori proyek lewat resolvecli/src/commands/skill.ts:78-81
--no-interactiveboolean, default falsepaksa mode non-interaktifcli/src/commands/skill.ts:82-86

Daftar yang sama terdokumentasi di cli/README.md:58-66, dan contoh pemakaian CI-nya di cli/README.md:46-56.

Penentuan target per agent

AGENT_REGISTRY menyimpan path project dan global untuk lima agent nyata (cli/src/utils/paths.ts:22-57). getSkillTargets lalu menempelkan path relatif itu ke cwd untuk scope project dan ke home untuk scope global (cli/src/utils/paths.ts:59-81).

AgentProject (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-57). OpenCode adalah satu-satunya agent yang punya variants, dan varian hanya dipakai ketika scope global (cli/src/utils/paths.ts:72-75). Konstanta direktori dan nama berkasnya dipusatkan sebagai SKILL_DIR dan SKILL_FILE (cli/src/utils/paths.ts:9-12).

Setiap target melewati exists() lebih dulu, dan ensureDirForFile membuat direktori induk secara rekursif sebelum penulisan (cli/src/utils/fs.ts:5-16). 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-303).

KondisiYang terjadiBukti
target kustom, interaktif, berkas adap.select menawarkan Append, Overwrite, Skip; Skip atau p.isCancel menaikkan skippedcli/src/commands/skill.ts:306-330
target registry, interaktif, berkas adap.confirm dengan initialValue: false; jawaban negatif melewati berkascli/src/commands/skill.ts:337-346
non-interaktif, berkas ada, tanpa --yesFile exists: ${target} — use --yes to overwrite lalu process.exit(1)cli/src/commands/skill.ts:348-351
selain ituwriteFile(target, skillContent, "utf-8")cli/src/commands/skill.ts:362-371

Mode Append menambahkan blok yang dibungkus penanda <!-- wilayah-indonesia:start --> dan <!-- wilayah-indonesia:end --> (cli/src/commands/skill.ts:30-45). Penanda itu membuat operasi idempoten: bila penanda sudah ada, isi di antaranya diganti, bukan diduplikasi (cli/src/commands/skill.ts:33-41). Perilaku ini juga dijelaskan di cli/README.md:68-71.

⚠️ --yes berarti overwrite, bukan append. Untuk target kustom yang sudah ada, jalur tulis biasa tetap memanggil writeFile sehingga seluruh isi berkas lama hilang (cli/src/commands/skill.ts:362-371). README menegaskan Others hanya bisa append lewat mode interaktif (cli/README.md:71). Di CI, AGENTS.md yang 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-386).

Pesan kegagalan yang perlu dikenali

SituasiPesanBukti
non-interaktif tanpa --agent`Missing --agent. Non-interactive mode requires --agent <claudecodex
--agent others tanpa --targetMissing --target. When using --agent others, you must specify --target <path> (...)cli/src/commands/skill.ts:240-243
nama agent tidak dikenalUnknown agent "<nama>". Valid: claude, codex, opencode, antigravity, kiro, others, allcli/src/utils/paths.ts:104-108
daftar agent kosongNo valid agents selectedcli/src/commands/skill.ts:235-238
target tidak terbentukNo targets resolvedcli/src/commands/skill.ts:282-285
SKILL.md gagal dimuatFailed to load SKILL.md: <pesan>cli/src/commands/skill.ts:257-264

Rangkaian perintah untuk membangun dan menguji CLI dari source ada di cli/README.md:73-81; semuanya memakai node cli/dist/index.js dengan --dry-run agar tidak menulis berkas.