Skip to main content
Tips Claude Code · Pelajaran 8 dari 13 · 13 menit

Claude Code Headless: claude -p dengan Gerbang Lulus/Gagal

Menjalankan Claude Code headless dengan claude -p: format output, mode izin, allowedTools dan dontAsk, plus skrip gerbang verifikasi yang menutup loop.

Oleh Matthews WongDitinjau pada Dicek dengan Claude Code v2.1.301

Poin utama

Mode headless menjalankan Claude Code tanpa orang di depan keyboard: claude -p menerima prompt, bekerja, mencetak hasil, lalu keluar dengan status code. Karena tidak ada yang bisa menjawab prompt, Anda menyetujui di muka sejumlah kecil tool, dan memberi Claude skrip yang keluar dengan 0 atau 1 agar hasilnya terbukti.

Setelah pelajaran ini, Anda bisa

  • Menjalankan claude -p dengan format output, allowlist tool, dan mode izin yang tepat.
  • Menjaga run tanpa pengawasan tetap dalam batas tool, giliran, dan biaya yang sempit.
  • Menutup loop dengan skrip lulus/gagal, dan tahu kapan perlu membuat rencana dulu.
Lihat cara kerjanya

Ikuti diagramnya langkah demi langkah

Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.

Satu run tanpa pengawasan, ditutup oleh gerbang

Penjadwal menjalankan claude -p dengan allowlist; Claude mengedit, menjalankan skrip gerbang yang gagal, memperbaiki, mengulang sampai exit 0, dan status keluar proses kembali ke penjadwal.

Ditinjau pada 17 Oktober 2026 · Claude Code v2.1.301

Langkah 1 dari 7. Penjadwal menjalankan claude -p, yang tidak bisa bertanya apa pun ke Anda. Tool yang disetujui di muka dan skrip gerbang menggantikan orang yang biasanya menyetujui dan memeriksa.

Tombol: ← → langkah · Home / End · K putar atau jeda

  1. Penjadwal menjalankan claude -p, yang tidak bisa bertanya apa pun ke Anda. Tool yang disetujui di muka dan skrip gerbang menggantikan orang yang biasanya menyetujui dan memeriksa.

  2. Job mengirim prompt beserta flag: mode izin, allowlist, dan batas giliran. Karena tidak ada orang, semua pilihan harus ada di command line.

    claude -p "Fix the staged post, then run the validator" --permission-mode dontAsk --allowedTools "Edit" "Bash(node scripts/validate-staged-post.mjs *)" --max-turns 12 --output-format json
  3. Di mode dontAsk, setiap call yang biasanya memunculkan prompt akan ditolak. Hanya pembacaan, Edit yang ada di allowlist, dan satu perintah validator yang berjalan. Sisanya tidak pernah dieksekusi.

    --permission-mode dontAskTebak langkah berikutnya Claude mencoba perintah yang tidak ada di --allowedTools, di bawah dontAsk. Apa yang terjadi? (Run macet menunggu jawaban · Call ditolak, tanpa prompt · Tetap berjalan, karena tidak ada yang mengawasi)
  4. Perintah validator cocok dengan allowlist, jadi berjalan. Ia memeriksa post yang di-stage dan keluar dengan exit 1 beserta daftar FAIL, karena pekerjaannya belum selesai.

    console.error("FAIL:\n - " + errs.join("\n - ")); process.exit(1);
  5. Teks kegagalan kembali ke Claude. Ia mengedit apa yang disebut daftar lalu menjalankan gerbang lagi. Loop berakhir saat gerbang lulus atau --max-turns habis.

    Tebak langkah berikutnya Gerbang mencetak FAIL dan keluar dengan exit 1. Apa yang dilakukan Claude berikutnya? (Berhenti dan tetap melaporkan sukses · Membaca kegagalan, memperbaikinya, lalu mengulang · Bertanya ke Anda di terminal)
  6. Pada pengulangan, validator keluar dengan exit 0. Claude menulis jawaban akhirnya, dan claude -p mencetaknya sebagai satu objek JSON dengan field result dan session id.

    claude -p "..." --output-format json | jq -r ".result"
  7. Proses keluar dengan 0 saat sukses dan non-nol saat run gagal, sehingga penjadwal bisa bercabang. Artinya run selesai, bukan pekerjaannya benar: jalankan gerbang itu sendiri sekali lagi.

    claude -p ... ; echo $?

Apa yang dilakukan claude -p, dan apa yang dicetaknya?

Flag -p (atau --print) menjalankan Claude Code secara non-interaktif: menerima prompt, mengerjakan, mencetak jawaban, lalu keluar. Ia keluar dengan kode 0 saat sukses dan non-nol saat run gagal, jadi skrip bisa bercabang berdasarkan statusnya. Kegagalan di dalam run, misalnya autentikasi hilang, dicetak sebagai hasil di stdout. Ia juga membaca stdin, jadi cat build-error.txt yang di-pipe ke claude -p bisa dipakai, sampai batas 10 MB.

Baca selengkapnya (2)

--output-format memilih bentuknya. text, yang default, adalah teks biasa. json adalah satu objek berisi hasil, session id, metadata, dan perkiraan biaya, yang bisa dibaca jq: jq -r .result. stream-json adalah satu event JSON per baris, dan butuh --verbose, serta opsional --include-partial-messages, untuk men-stream token. Tambahkan --json-schema agar output mengikuti skema Anda di field structured_output. Untuk melanjutkan, --continue membuka percakapan terakhir dan --resume menerima session id.

Untuk skrip dan CI, dokumentasi merekomendasikan --bare, dan menyebut ia akan menjadi default untuk -p di rilis mendatang. Ia melewati penemuan otomatis hooks, skills, subagent, plugin, MCP server, auto memory, dan CLAUDE.md, sehingga run berperilaku sama di setiap mesin. Harganya, ia tidak pernah membaca login Anda: set ANTHROPIC_API_KEY, atau sediakan apiKeyHelper, dan berikan konteks yang dibutuhkan lewat flag seperti --append-system-prompt atau --mcp-config.

Bagaimana menjaga run tanpa pengawasan tetap dalam batas aman?

Karena tidak ada yang menyetujui, batasnya Anda tentukan di awal. --allowedTools membiarkan tool tertentu berjalan tanpa prompt, memakai sintaks aturan izin: Bash(git diff *) mengizinkan perintah apa pun yang diawali git diff. Spasi sebelum bintang itu penting, karena Bash(git diff*) juga akan cocok dengan git diff-index. --permission-mode menetapkan dasarnya: acceptEdits menulis file bebas, auto meminta classifier meninjau aksi, dan dontAsk menolak apa pun yang akan memunculkan prompt, yang disebut dokumentasi berguna untuk CI yang terkunci.

Baca selengkapnya (2)

Anggaran membatasi dampak sebuah loop. --max-turns membatasi jumlah giliran agentic dan keluar dengan error saat batas tercapai, dan --max-budget-usd menghentikan run begitu perkiraan biaya mencapai jumlah itu; keduanya hanya untuk mode print. --permission-prompts none (v2.1.259 ke atas) menolak apa pun yang akan memunculkan prompt dan memberi tahu Claude agar tidak mencobanya lagi. --dangerously-skip-permissions melewati semua pemeriksaan, dan dokumentasi menyarankan memakainya hanya di container atau VM tanpa akses internet.

Satu jebakannya adalah trust. Run claude -p tidak pernah menampilkan dialog workspace trust, jadi aturan permissions.allow dari .claude/settings.json proyek tidak berlaku, dan Claude Code mencetak peringatan ke stderr. Sebaliknya, hooks di settings proyek dan server di .mcp.json tetap dimuat. Di repositori yang bukan buatan Anda, mulai dengan --bare atau --setting-sources user.

Bagaimana verification loop menutup run, dan kapan perlu membuat rencana dulu?

Claude berhenti saat pekerjaan tampak selesai. Panduan best practices menyatakannya lugas: tanpa pemeriksaan yang bisa dijalankannya, tampak selesai adalah satu-satunya sinyal, dan Andalah yang menjadi verification loop-nya. Beri ia sesuatu yang mengembalikan lulus atau gagal, seperti test suite, exit code build, atau skrip validator, dan ia akan bekerja, menjalankan pemeriksaan, membaca hasilnya, lalu mengulang sampai lulus. Itulah beda, dalam bahasa dokumentasi, antara sesi yang Anda awasi dan sesi yang bisa Anda tinggalkan.

Baca selengkapnya (2)

Anda bisa menegakkan gerbang itu lebih keras dengan tiga cara: memintanya di prompt, menambah hook Stop yang menahan giliran berakhir sampai sebuah skrip lulus, atau menambah subagent verifikasi agar agen yang bekerja bukan yang menilai. Apa pun pilihannya, minta Claude menunjukkan bukti, output dan perintahnya, bukan sekadar mengatakan lulus. Dan anggap exit code claude -p hanya berarti run sudah selesai. Jalankan ulang gerbang di CI, karena gerbang yang tidak Anda jalankan adalah klaim, bukan hasil.

Plan mode adalah separuh lainnya, untuk saat Anda belum yakin pendekatannya. Claude meneliti dan menulis rencana tetapi tidak mengedit source sampai Anda menyetujuinya, dan --permission-mode plan memulai run di mode itu; dokumentasi mencatat flag yang sama berfungsi dengan -p. Jika Anda bisa menjelaskan diff-nya dalam satu kalimat, lewati rencana. Dokumentasi tidak menyebut cara menyetujui rencana secara non-interaktif, jadi pakai run plan headless untuk mengeluarkan rencana, dan lakukan edit di run terpisah.

Cara situs ini melakukannya

Konfigurasi nyata di balik pelajaran ini

Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.

.claude/agents/blog-post-author.md:70-78
# Self-validation — do not skip

```bash
node scripts/validate-staged-post.mjs /tmp/blog-staging/agent-<slug>
```

Fix everything it reports, including warnings about content keys defined but not
referenced. Re-run until it prints `PASS`. If a cover does not exist yet the
image check will fail — say so in your report rather than inventing one.

Verification loop milik repo ini sendiri, yang ditulis ke dalam sebuah subagent. Agent tidak boleh melapor sebelum skrip mencetak PASS. Ia diperintahkan memperbaiki apa yang dilaporkan skrip lalu menjalankannya lagi, yaitu loop yang dijelaskan pelajaran ini. Saat ini ia berjalan di sesi interaktif, tetapi instruksi yang sama bisa dipakai di prompt claude -p.

scripts/validate-staged-post.mjs:1-3
// Validate ONE staged post folder + its component before the big merge.
// Usage: node scripts/validate-staged-post.mjs /tmp/blog-staging/agent-3
// Mirrors merge-blog-staging.mjs per-post checks + SEO rules. Exit 0 = PASS.

Gerbang yang baik menyatakan kontraknya di baris-baris pertama: apa yang diperiksa, cara memanggilnya, dan arti exit status-nya. Exit 0 berarti PASS. Satu fakta itulah yang membuat loop, hook, atau job CI bisa memperlakukan skrip ini sebagai vonis.

scripts/validate-staged-post.mjs:196-200
if (warns.length) console.log("WARN:\n - " + warns.join("\n - "));
if (errs.length) {
  console.error("FAIL:\n - " + errs.join("\n - "));
  process.exit(1);
}

Sisi yang gagal. Semua error dikumpulkan dulu lalu dicetak sebagai daftar di stderr, kemudian skrip keluar dengan 1. Daftar semua masalah sekaligus memberi Claude to-do lengkap untuk edit berikutnya, bukan satu kegagalan per run, sementara warning masuk ke stdout tanpa mengubah exit code.

Yang sering rusak

Kesalahan yang benar-benar membuang waktu

  • Bash(git diff*) tanpa spasi

    Spasi sebelum bintang membuatnya cocok sebagai awalan atas kata perintah yang utuh. Tanpa spasi, Bash(git diff*) juga mengizinkan git diff-index dan apa pun yang kebetulan diawali huruf yang sama. Tulis Bash(git diff *).

  • Aturan allow proyek yang tidak pernah berlaku

    Run claude -p tidak menampilkan dialog trust, jadi permissions.allow di .claude/settings.json repo diabaikan dan peringatan masuk ke stderr. Berikan allowlist lewat command line dengan --allowedTools.

  • --bare tanpa API key

    Mode bare tidak pernah membaca kredensial OAuth atau keychain, jadi login langganan tidak bekerja di dalamnya. Set ANTHROPIC_API_KEY atau apiKeyHelper. Ia juga melewati CLAUDE.md, hooks, dan MCP server, sehingga skrip yang bergantung padanya harus memberikannya secara eksplisit.

  • Menganggap exit code 0 sebagai bukti

    Status nol hanya berkata run selesai. Ia tidak berkata perubahannya benar. Jalankan gerbang yang sama sendiri setelah job, supaya kelulusan datang dari skrip, bukan dari ringkasan Claude sendiri.

  • Loop tanpa batas atas

    Gerbang yang mustahil lulus membuat run terus membaca, memperbaiki, dan mengulang. Set --max-turns dan --max-budget-usd. Batas giliran keluar dengan error saat tercapai, dan SIGTERM mengakhiri run dengan exit code 143 tanpa hasil untuk giliran yang belum selesai.

Mode izin untuk run claude -p
defaultHanya pembacaanMeninjau setiap aksi
acceptEditsPembacaan, edit file, perintah filesystem umumKode yang akan Anda tinjau nanti
planPembacaan; edit diblokir sampai rencana disetujuiMendapat rencana, bukan edit
autoSebagian besar aksi, ditinjau classifierTugas panjang tanpa campur tangan
dontAskPembacaan dan tool yang disetujui di muka; sisanya ditolakCI yang terkunci
bypassPermissionsSemuanyaHanya di dalam container atau VM
Coba sendiri

Lakukan di komputer Anda sendiri

Anda butuh Claude Code, jq, dan satu repositori latihan berisi proyek TypeScript. Mulai dari read-only, lalu beri satu run allowlist yang sempit dan sebuah gerbang. Jalankan pada kode yang boleh dibuang.

  1. Jalankan pertanyaan read-only dan ambil teksnya saja dari JSON.

    claude -p "What does this project do? Answer in two sentences." --output-format json | jq -r ".result"
  2. Tulis gerbangnya dulu. Yang ini gagal dengan exit non-nol sampai proyek lolos type-check.

    npx tsc --noEmit
    echo "gate exit: $?"
  3. Serahkan gerbang itu ke run yang terkunci. Ia boleh mengedit file dan menjalankan type-checker saja, paling banyak 12 giliran.

    claude -p "Fix the type errors, then run npx tsc --noEmit until it passes." --permission-mode dontAsk --allowedTools "Edit" "Bash(npx tsc *)" --max-turns 12 --output-format json > run.json
  4. Baca jawabannya, lalu jalankan gerbang sendiri dan bercabang berdasarkan exit status-nya. Jangan percaya ringkasan begitu saja.

    jq -r ".result" run.json
    if npx tsc --noEmit; then echo "gate passed"; else echo "gate failed"; exit 1; fi
  5. Ajukan pertanyaan perencanaan di plan mode untuk melihat edit tetap diblokir. Lalu bandingkan dengan prompt yang sama di bawah dontAsk.

    claude -p "Plan how to add input validation to the form. Do not edit files." --permission-mode plan
Kuis singkat

Tiga pertanyaan sebelum lanjut

Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.

  1. 1.Run claude -p dengan --permission-mode dontAsk mencoba perintah Bash yang tidak ada di --allowedTools. Apa yang terjadi?
  2. 2.Mengapa menulis Bash(git diff *) dengan spasi sebelum bintang?
  3. 3.Job CI Anda berakhir dengan claude -p keluar 0. Apa yang sebenarnya Anda ketahui?
Pikirkan dulu

Jawab dalam hati, lalu cek

  1. 1.Kapan Anda memilih --output-format stream-json dibanding json?Lihat jawabannya

    Jawaban:Saat Anda ingin event begitu terjadi, misalnya progres atau token, bukan satu objek di akhir. Ia butuh --verbose, dan --include-partial-messages menambah potongan per token. Baris terakhir adalah pesan result berisi teks akhir, biaya, dan data sesi.

  2. 2.Mengapa instruksi agent di repo ini menyuruh menjalankan ulang sampai mencetak PASS?Lihat jawabannya

    Jawaban:Karena sekali jalan validator hanya mendaftar masalah. Loop perbaiki, ulang, dan baca hasil itulah yang mengubah daftar tadi menjadi pekerjaan selesai, dan exit status skrip adalah sinyal yang tidak bisa dibantah oleh Claude.

  3. 3.Job claude -p terjadwal tidak boleh menyentuh hooks atau .mcp.json yang di-commit di sebuah repositori. Apa yang Anda berikan?Lihat jawabannya

    Jawaban:--bare, yang tidak membaca hooks, skills, plugin, atau server .mcp.json dari proyek. Alternatifnya --setting-sources user, atau entri disabledMcpjsonServers untuk satu server. Lalu berikan yang memang dibutuhkan lewat --settings dan --mcp-config.

Pelajari lebih dalam

Bahasan mendalam terkait di situs ini