Claude Code CLI ve Antigravity CLI: Kural Nerede Durmalı?
Bir süredir bu depoya giren commit'lerin çoğunun altında bir yapay zekâ ajanının (AI agent) imzası var. Şu an okuduğunuz yazıyı yayına alan da onlardan biri. Bu kadarı artık pek çok ekipte var.
Bu depoda farklı olan şu: ajanlar tek bir üründen çalışmıyor. Anthropic'in Claude Code CLI'ı ve Google'ın Antigravity CLI'ı — ikisi de ajanı terminalde çalıştıran birer komut satırı aracı (CLI) — aynı depoda yan yana duruyor. Hangisinden çalışırsa çalışsın ajan aynı kuralları okuyor, aynı yetenekleri (skills) kullanıyor, aynı araçları çağırıyor ve aynı yerde durduruluyor.
Bunun iki sebebi var. Birincisi karşılaştırmak: iki aracı aynı işte, aynı depoda, aynı kurallarla yan yana görmenin yerini hiçbir değerlendirme yazısı tutmuyor. İkincisi, bir mimari kurarken sorduğum soruyu geliştirme sürecine de sormak: bu parça yarın çıkarılsa ne kalır?
Cevap depoda durmalı. Bir ajanın işini iyi yapmasını sağlayan bilgi — sistemin nasıl çalıştığı, neyin nerede olduğu, hangi işin hangi sırayla yapıldığı — ajanı çalıştıran ürüne değil, depoya ait olmalı. O zaman ürün de, arkasındaki model de değiştirilebilir parçalar oluyor: bir kesinti, bir fiyat değişikliği, bir sözleşme koşulu geliştirmeyi durdurmuyor.
Bunun bir de ikinci yarısı var, ve yazının asıl konusu o: bir kurallar dosyası yol göstericidir, kısıt değildir. Ajan onu okur ve kararını verir; kararın kurala uyması modele, sürüme, bağlamın uzunluğuna göre değişebilir. Bu, yol gösteren bir kural için kabul edilebilir. Mutlaka geçerli olacak bir kural için değil — o kuralın çalıştırılabilir bir yerde durması gerekir. Bu yazı, bu depoda hangi kuralın nerede durduğunu anlatıyor.
Bilgi depoda: kurallar ve skill'ler
Depo, siteyi çalıştıran kodun yanında sitenin nasıl geliştirileceğini de taşıyor. Üç katmanı var.
Her oturumda yüklenen kurallar dosyası. Depo haritası, komutlar ve hangi dosya açık olursa olsun geçerli olan kurallar. Bilerek kısa tutuluyor — iki yüz elli satır civarı — ve ayrıntıyı aşağıdaki katmanlara bırakıyor.
Yola bağlı kurallar (path-scoped rules). Ağacın yalnızca bir bölümünü düzenlerken önemli olan kurallar ayrı dosyalarda duruyor ve her dosya hangi yollar için geçerli olduğunu kendi başında söylüyor. Claude Code CLI bir kuralı o bölümden bir dosya okunduğunda yüklüyor: stil sayfalarının kuralı stil sayfası açılınca gelir, içerik modelininki içerik aracına dokununca. Dokuz dosya, dokuz yüz satıra yakın.
Skill'ler. Bir skill, iş biçiminde bir akış: bir makaleyi yayınlamak, bir görseli yüklemek, bir yedek almak, bir dağıtımı ilerletmek. Depoda on dört tane var ve her biri bir markdown dosyası — hangi komut hangi sırayla çalışır, nerede durulup bana sorulur, hangi bilgiyi kaynağı yeniden açmadan nereden alırım.
Markdown olmasının sebebi pratik: iki CLI da okuyabiliyor, bir SDK gerektirmiyor ve değişikliği kod gibi inceleyebiliyorum. Çözdüğü şey de şu: ajan her oturuma hafızasız başlıyor. Skill olmasa bir alanın sayfada nerede göründüğünü her seferinde modeli ve şablonu açıp yeniden keşfedecekti; skill bunu bir tabloda söylüyor.
Kararların gerekçesi ise ayrı bir yerde, docs/ altında duruyor. Bir kural neden var, hangi ölçümle konmuş, hangi yapılandırma ayrıntısına dayanıyor — bunlar her oturumda yüklenmiyor, ihtiyaç duyan açıp okuyor. Kurallar dosyasını kısa tutan şey bu ayrım.
Araçlar: kararın bir kez verilmiş hâli
Bir ajan Firestore'a yazan bir betiği birkaç saniyede üretir. Sorun da burada: her seferinde yeni bir betik üretir ve her betik kararları yeniden verir. Hangi projeye bağlanacak? Dokümanın tamamını mı yazacak, yalnızca değişen alanı mı? Önce yedek alacak mı? Bu soruların cevabı oturumdan oturuma değişiyorsa ortada bir sistem yok demektir.
Araç, bu kararların bir kez verilmiş hâli. page-tool save bir makaleyi kaydederken eski hâlini history/ altına yedekliyor, sıra numarasını ve oluşturma bilgilerini dolduruyor, alt içerikleri yazıyor, etiket bağını iki yere birden işliyor ve site içi aramanın kaydını tazeliyor — yönetim panelinin kaydetme servisi ne yapıyorsa onu. İkisi bilerek birbirinin aynası: panelden kaydedilen doküman ile ajanın kaydettiği doküman aynı yoldan geçiyor.
Depodaki kendi araçlarım da komut satırı aracı; bugün yedi tane var. Altısı veriye dokunuyor — içerik, yapılandırma, medya, emülatörün test verisi (seed), llms.txt metinleri ve yedekleme; Firestore, Storage ve Auth üzerinde. Yedincisi hiçbir şey yazmıyor, yalnızca dağıtımın durumunu GitHub'dan okuyor. Hepsi aynı ortak kütüphaneyi kullanıyor; ortam seçimi, kimlik bilgisi ve fark (diff) çıktısı orada, tek yerde. Veriye dokunan altı aracın her birinin emülatöre karşı koşan kendi uçtan uca (end-to-end) testi var. Toplamı bugün yaklaşık 12 bin satır betik — 3.700'ü bu testler.
Araçların bağımlılıkları da kendi paketlerinde duruyor, sitenin fonksiyonlarınınkinden ayrı. Bir görsel işleme kütüphanesi yalnızca ajanın ihtiyacıysa dağıtım hattında hiç kurulmuyor.
Bir makale nasıl yayınlanıyor
Bu yazının yayına girişi sırayla şöyle:
Bu şemayı metin olarak okuyun
Ajan önce skill'i okuyor ve yazının etiketlerini kontrol ediyor; eksik etiket varsa önce onu oluşturuyor. Sonra kaydetme komutunu deneme koşusu olarak çalıştırıyor ve araç yalnızca farkı basıyor. Farkı ben onaylıyorum; aynı komut bu kez yazma bayrağıyla çalışıyor ve CLI onu bana ayrıca soruyor. Ardından site haritası ve llms.txt işleri kuyruğa giriyor, en sonda etiket raporu ve canlı adres doğrulanıyor.
- Skill SKILL.md
- Etiketler tag list
- Deneme koşusu dry-run
- Onay fark okunur
- Yazma --yes
- İşler jobRunner
- Doğrulama canlı adres
- Ajan skill'i okuyor ve ortamı, koleksiyonu, dili çıkarıyor.
- Etiketleri kontrol ediyor. Bu yazının “Yapay Zeka” etiketi henüz yok. Olmayan bir etikete bağlanan makaleyi araç kaydetmiyor, çünkü o bağın görüneceği etiket sayfası boş açılırdı. Önce etiket dokümanı oluşturuluyor.
- Deneme koşusu (dry-run). Ajan
savekomutunu--yesolmadan çalıştırıyor. Araç önce yükü denetliyor: başlık, içerik, liste özeti ve açıklama zorunlu. Arama sonuçlarında görünen açıklamayı hiçbir iş üretmiyor; 170 karakteri geçmeyen bir cümle olarak elle yazılıyor. Sonra araç hedef yolu ve farkı basıyor, hiçbir şey yazmıyor. - Farkı ben okuyorum ve onaylıyorum. Beta dahil her gerçek ortamda.
- Aynı komut, bu kez
--yesile. Gerçek bir ortama--yesile yazan her komutu CLI her seferinde bana soruyor; onaylayınca makale, otomatik alanlar, etiket bağı ve site içi aramanın kaydı yazılıyor. Var olan bir yazı güncelleniyorsa önce eski hâli yedekleniyor. - İşler. Site haritası ve llms.txt birer iş dokümanı olarak kuyruğa giriyor; her biri yine deneme koşusu ve onayla. llms.txt'nin tam metin dosyaları yazının kendi HTML'ini değil, onun ayrı bir dokümana aktarılmış markdown hâlini okuyor. Bu yüzden işten önce ajan o metni hazırlıyor ve onu da ben okuyup onaylıyorum. Araç işin bitmesini bekliyor ve başarısız bir iş komutun kendi hatası olarak dönüyor.
- Doğrulama. Etiket raporu ve canlı adres.
Bu akış iki CLI'da da aynı: skill aynı dosya, araç aynı betik, onay aynı adımda. Değişen, beşinci adımdaki soruyu hangi CLI'ın sorduğu.
İki CLI, tek depo
İki CLI'dan çalışan ajanın da aynı kuralları okuması, aynı skill'leri kullanması ve aynı yerde durdurulması gerekiyordu. Bu iki ayrı iş, çünkü bilgi ile izin farklı biçimde taşınıyor. Üçüncü bir katman da var: model.
Bilgi tarafı: üç sembolik bağ
Claude Code CLI kuralları CLAUDE.md'den, skill'leri .claude/skills/ altından okuyor. Antigravity CLI AGENTS.md'yi ve .agents/ altını arıyor. Kopya tutmak yerine üç sembolik bağ (symlink) var: AGENTS.md → CLAUDE.md, .agents/skills → .claude/skills, .agents/rules → .claude/rules. Üçü de depoya işlenmiş göreli bağlar; depoyu yeni klonlayan biri iki CLI için de kuralları ve skill'leri hazır buluyor. Kuralların ve skill'lerin tek kopyası var, o yüzden ayrışacak iki kopya yok.
İzin tarafı: iki aynalı katman
İzin böyle çözülemiyor, çünkü iki CLI'ın izin modeli farklı.
Claude Code CLI, ayar dosyasındaki üç listeyi okuyor: allow, ask, deny — izin ver, sor, reddet. Bugün 62, 57 ve 3 satır. Antigravity CLI ise her araç çağrısından önce bir betik (hook) çalıştırıyor ve kararı o betikten bekliyor. Aynı listeler orada bir bash betiğinin içinde, kod olarak duruyor.
İki katman birbirinin aynası ve bu ayna elle tutuluyor: birine eklenen kural diğerine de ekleniyor. Bunu kolaylaştıran şey listelerin küçük tutulması. “İzin ver” tarafı bilerek geniş — npm run * tek satır — ve çizgiyi dar listeler tutuyor: seed verisinin üzerine yazan, canlı siteye dokunan, gerçek bir ortama içerik yazan ya da depoyu değiştiren her komut ask ya da deny listesinde. Tarayıcı araçlarında da aynı ayrım var: sayfayı okuyanlar izinli, sayfada tıklayan, yazan ya da betik çalıştıranlar soruluyor.
Ortak olan kısım da var: emülatör kapalıyken test koşturmayı engelleyen ve düzenlenen markdown dosyasını anında denetleyen iki koruma betiği (guard hook) tek kopya. İki CLI'ın gönderdiği veri farklı biçimde olduğu için ikisini de okuyorlar.
Model tarafı: ürün ile model ayrı seçimler
Antigravity CLI'ın model listesi Google'ın Gemini ailesinin yanında Anthropic'in Claude Sonnet ve Opus modellerini ve OpenAI'ın açık ağırlıklı (open-weight) GPT-OSS modelini de içeriyor: üç ayrı şirketin modeli, tek araçta. Claude Code CLI Claude modelleriyle çalışıyor, ama onlara Anthropic'in kendi hizmetinin yanı sıra Amazon'un, Google Cloud'un ve Microsoft'un bulut platformları üzerinden de ulaşabiliyor. Yani ajanı çalıştıran ürün, arkasındaki model ve o modele erişilen sağlayıcı üç ayrı seçim. Depoyu bir CLI'dan ötekine taşımak model ailesini değiştirmeyi gerektirmiyor, model ailesini değiştirmek de CLI'ı. Bir sağlayıcıdaki kesinti ya da koşul değişikliği geliştirmeyi durdurmuyor; en az bir yol açık kalıyor.
Karşılaştırmayı anlamlı kılan da bu ayrım. Aynı model ailesini iki ayrı CLI'da, aynı depoda, aynı görevle çalıştırmak, bir farkın ne kadarının modelden, ne kadarının onu çalıştıran araçtan geldiğini görmeyi kolaylaştırıyor.
Bu kurulumun ayakta olduğunu iki CLI'la da uçtan uca doğruladım: aynı skill'i açıp aynı aracı aynı ortama karşı çalıştırdılar ve aynı adımda durup sordular. Bu yazıdaki davranışlar Claude Code CLI 2.1.284 ve Antigravity CLI 1.2.13 sürümlerine ait; iki araç da sık güncelleniyor ve ayrıntılar sürümden sürüme değişebilir.
Hangi kural nerede duruyor
| Kural | Durduğu yer | Zorlayan |
|---|---|---|
| Veriye dokunan her komut hedef ortamı söyler, varsayılan yok | Ortak kütüphane | Araç — komut hata verip çıkıyor |
| Bir alanı sessizce silecek kaydetme yapılmaz | Aracın save komutu | Araç — üst düzey (top-level) alanlarda |
| Olmayan etikete makale bağlanmaz | Aracın save komutu | Araç |
| Başlık, içerik, özet ve açıklama olmadan kayıt yapılmaz | Aracın save komutu | Araç |
| Depolamaya her yazma gerekçesini söyler | Medya aracı (--note) | Araç |
--yes olmadan içerik yazılmaz | Araçların varsayılanı | Araç — yalnızca farkı basıyor |
Gerçek ortama --yes ile yazma | İki izin katmanı | CLI — her seferinde bana soruluyor |
git add, commit, push, issue ve PR yazmaları | İki izin katmanı | CLI — her seferinde bana soruluyor |
npm run shell:prod-* (canlı projede Firebase functions shell), terraform destroy | İki izin katmanı | CLI — reddediliyor |
| Lint ya da testten geçmeyen kod uzak depoya çıkmaz | pre-push | Git |
Commit komutunda uzun biçimli bayrak (-m değil --message) | Kurallar dosyası | Ajanın muhakemesi |
| İçerik, sitenin ses tonuyla yazılır | Skill metni | Ajanın muhakemesi |
Son iki satır bilerek orada. Ses tonu, ne zaman durup sorulacağı, hangi işin kapsam dışı olduğu koda dökülemiyor; bunlar için elimdeki araç metin ve metin bu işi görüyor. Ayrım şu: metne yazdığım şey bir niyet, ve sonucunu geri alabileceğim yerde niyet yetiyor. Geri alamayacağım yerde kural bir aracın, bir betiğin ya da bir izin listesinin içinde duruyor.
İzin katmanının üç kararı
Bu katmanı kurarken üç şeyi hesaba katmak gerekti.
ask ile force_ask aynı şey değil. Antigravity CLI'ın izin penceresinde bir “Always Allow” seçeneği var ve betiğin döndürdüğü düz ask kararı bu önbelleğe saygı gösteriyor: bir kez tıklanmış olması, sonraki çağrıların sorulmadan geçmesine yetiyor. force_ask ise önbelleği yok sayıp her seferinde soruyor. Bu yüzden insana mutlaka sorulması gereken her liste — git ve GitHub yazmaları, canlı siteye dokunan testler, gerçek ortama içerik yazan komutlar — force_ask kullanıyor. Betikte düz ask yalnızca iki yerde kalıyor: en sondaki genel kural (catch-all) ve tarayıcıda iş yapan araçlar. İkisi de Claude Code CLI'da da yalnızca izin listesinde olmadığı için sorulan şeyler; zararsız bir şeye verilmiş “Always Allow”un geçerli kalması isteniyor. Claude Code CLI'da bu ayrım gerekmiyor: ask listesindeki bir komut, “don't ask again” denmiş olsa bile her seferinde soruluyor.
Betik, komut, ağ ve MCP çağrılarında her yolda bir karar döndürüyor. Boş cevap geçersiz sayılıyor ve çağrıyı sebepsiz engelliyor. Hiç cevap vermemek daha tehlikeli: karar CLI'ın terminal politikasına (terminal execution policy) kalıyor ve o politika always-proceed ise komut sorulmadan çalışıyor. Betik bu yüzden açık bir ask ile bitiyor — listelerde adı geçmeyen her şey soruluyor. Claude Code CLI'da aynı duruşu varsayılan izin modu veriyor: yerleşik salt okunur komutlar dışında, allow listesinde olmayan her komut soruluyor. İki katman aynı yere (deny-by-default) iki ayrı yoldan varıyor.
Betik macOS'un getirdiği bash 3.2'de çalışıyor. Bash 4'e ait tek bir yazım orada sözdizimi hatası ve betiğin geri kalanını sessizce devre dışı bırakır. Yazım bilerek 3.2'nin bildiği biçimlerle sınırlı.
Sınırlar
- Ayna elle tutuluyor. İki katmanın eş kaldığını denetleyen bir test yok; eşlik, bir kural eklerken iki dosyayı birden açma disiplinine ve iki listenin düzenli olarak yan yana karşılaştırılmasına dayanıyor.
- Alan koruması yalnızca üst düzeye bakıyor.
seodururkenseo.keywords'ün kaybolması ondan geçmez. Asıl koruma canlı dokümanı çekip yerinde düzenlemek — o da skill'in kuralı, yani metin. - Araçların testleri yerelde koşuyor, CI'da değil. Bilerek: CI onların bağımlılıklarını kurmuyor, çünkü her dağıtımda indirilecek bir yük olurdu.
pre-pushbir araç değiştiğinde bu testleri kendisi koşturuyor. - İzin listeleri komutun metnini eşliyor; bir güvenlik sınırı (sandbox) değiller. Aynı program alışılmadık bir biçimde çağrılırsa — bir kabuk sarmalayıcısının içinden, başka bir yoldan — liste onu tanımayabilir. Varsayılan modda iki katmanda da tanınmayan biçim yasaklanmıyor ama soruluyor; yani kaçan şey sessizce çalışmıyor, önüme geliyor.
- Katman ajanı durduruyor, kullanıcıyı değil. Depoya işlenen izinler varsayılanı tanımlıyor. Her geliştirici kendi makinesinde yerel bir dosyayla izin listesini genişletebiliyor ya da CLI'ı soruları bir sınıflandırıcıya devreden (auto) veya tümden atlayan (bypass) bir modda başlatabiliyor. Bu bilinçli: kararı veren insanın elini bağlamak değil, ajanın onsuz karar vermesini önlemek amaçlanıyor.
Kapanış
Bu sistemde ajanı çalıştıran araç da, arkasındaki model de değiştirilebilir parçalar olarak kuruldu: ne yaptıkları yazılı, nerede durdukları belli, yerlerine başkası konabilir.
Yapay zekâ ile kod yazıyorsanız deneyebileceğiniz iki şey var. Birincisi: kurallar dosyanızı açın ve her maddenin yanına, o kuralı çiğneyen komutu kimin durdurduğunu yazın. Cevabı “ajanın muhakemesi” olan maddelerden geri alamayacağınız sonuç doğuranlar, bir araca ya da izin listesine taşınmayı bekliyor. İkincisi: ajanınızı çalıştıran aracı bir günlüğüne başka biriyle değiştirin. Ne kadarı çalışmaya devam ediyorsa, bilginizin o kadarı depoda demek.
Bu dökümü kendi deponuz için birlikte çıkarmak isterseniz iletişim sayfasından yazın; kurallar dosyanız ve izin ayarlarınız başlamak için yeterli.
Bu yazı bir serinin üçüncüsü. Sistemin neden kurulduğu ve sekiz yılın faturası: GCP Maliyeti: Bir Kararın 8 Yıllık Faturası. Nasıl çalıştığı: Ziyaret Ettiğiniz Mimari: Bu Site GCP'de Nasıl Çalışıyor?
Bu eser Creative Commons Alıntı-Gayriticari-Türetilemez 4.0 Uluslararası Lisansı ile lisanslanmıştır.
Alıntı ve veri madenciliği kısıtlamalarına dair detaylar için lütfen Kullanım Şartları mızı inceleyin.