Extensions
pi uzantılar oluşturabilir. Kullanım durumunuz için bir tane oluşturmasını isteyin.
Extensions pi'nin davranışını genişleten TypeScript modüllerdir. Yaşam döngüsü olaylarına abone olabilirler, LLM tarafından çağrılabilen özel araçları kaydedebilirler, komutlar ekleyebilirler ve daha fazlasını yapabilirler.
/reload için yerleştirme: Otomatik keşif için uzantıları
~/.pi/agent/extensions/(genel) veya.pi/extensions/(proje-yerel) içine yerleştirin.pi -e./path.ts'yi yalnızca hızlı testler için kullanın. Otomatik keşfedilen konumlardaki Extensions,/reloadile çalışırken yeniden yüklenebilir.
Temel yetenekler:
- Özel araçlar - LLM'nin
pi.registerTool()aracılığıyla arayabileceği araçları kaydedin - Olay müdahalesi - Araç çağrılarını engelleyin veya değiştirin, bağlam ekleyin, sıkıştırmayı özelleştirin
- Kullanıcı etkileşimi - Kullanıcılara
ctx.uiaracılığıyla bilgi verin (seç, onayla, gir, bildir) - Özel kullanıcı arayüzü bileşenleri - Karmaşık etkileşimler için
ctx.ui.custom()aracılığıyla klavye girişine sahip tam TUI bileşenler - Özel komutlar -
/mycommandgibi komutlarıpi.registerCommand()aracılığıyla kaydedin - Oturum kalıcılığı -
pi.appendEntry()aracılığıyla yeniden başlatıldıktan sonra hayatta kalan mağaza durumu - Özel oluşturma - Araç çağrılarının/sonuçlarının ve mesajlarının TUI'de nasıl görüneceğini kontrol edin
Örnek kullanım durumları:
- İzin kapıları (
rm -rf,sudovb.'den önce onaylayın) - Git kontrol noktası oluşturma (her fırsatta saklama, dalda geri yükleme)
- Yol koruması (blok
.env,node_modules/'ye yazar) - Özel sıkıştırma (konuşmayı kendi tarzınızda özetleyin)
- Konuşma özetleri (bkz.
summarize.tsörneği) - Etkileşimli araçlar (sorular, sihirbazlar, özel diyaloglar)
- Durum bilgisi olan araçlar (yapılacaklar listeleri, bağlantı havuzları)
- Harici entegrasyonlar (dosya izleyicileri, web kancaları, CI tetikleyicileri)
- Beklerken oynanan oyunlar (bkz.
snake.tsörneği)
Çalışan uygulamalar için examples/extensions/'e bakın.
İçindekiler
- Quick Start
- Extension Locations
- Available Imports
- Writing an Extension
- Events
- ExtensionContext
- ExtensionCommandContext
- ExtensionAPI Methods
- State Management
- Custom Tools
- Custom UI
- Error Handling
- Mode Behavior
- Examples Reference
Hızlı Başlangıç
~/.pi/agent/extensions/my-extension.ts oluştur:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
export default function (pi: ExtensionAPI) {
// React to events
pi.on("session_start", async (_event, ctx) => {
ctx.ui.notify("Extension loaded!", "info");
});
pi.on("tool_call", async (event, ctx) => {
if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
const ok = await ctx.ui.confirm("Dangerous!", "Allow rm -rf?");
if (!ok) return { block: true, reason: "Blocked by user" };
}
});
// Register a custom tool
pi.registerTool({
name: "greet",
label: "Greet",
description: "Greet someone by name",
parameters: Type.Object({
name: Type.String({ description: "Name to greet" }),
}),
async execute(toolCallId, params, signal, onUpdate, ctx) {
return {
content: [{ type: "text", text: `Hello, ${params.name}!` }],
details: {},
};
},
});
// Register a command
pi.registerCommand("hello", {
description: "Say hello",
handler: async (args, ctx) => {
ctx.ui.notify(`Hello ${args || "world"}!`, "info");
},
});
}--extension (veya -e) bayrağıyla test edin:
pi -e ./my-extension.tsUzantı Konumları
Güvenlik: Extensions tüm sistem izinlerinizle çalıştırın ve isteğe bağlı kod çalıştırabilir. Yalnızca güvendiğiniz kaynaklardan yükleyin.
Extensions güvenilir konumlardan otomatik olarak keşfedilir. Proje yerel .pi/extensions girişleri yalnızca projeye güvenildikten sonra yüklenir.
| Konum | Kapsam |
|---|---|
~/.pi/agent/extensions/*.ts |
Küresel (tüm projeler) |
~/.pi/agent/extensions/*/index.ts |
Genel (alt dizin) |
.pi/extensions/*.ts |
Proje-yerel |
.pi/extensions/*/index.ts |
Proje-yerel (alt dizin) |
settings.json aracılığıyla ek yollar:
{
"packages": [
"npm:@foo/bar@1.0.0",
"git:github.com/user/repo@v1"
],
"extensions": [
"/path/to/local/extension.ts",
"/path/to/local/extension/dir"
]
}Uzantıları npm veya git aracılığıyla pi paketleri olarak paylaşmak için, bkz. packages.md.
Mevcut İçe Aktarmalar
| Paket | Amaç |
|---|---|
@earendil-works/pi-coding-agent |
Uzantı türleri (ExtensionAPI, ExtensionContext, etkinlikler) |
typebox |
Takım parametreleri için şema tanımları |
@earendil-works/pi-ai |
Yapay zeka yardımcı programları (Google uyumlu numaralandırmalar için StringEnum) |
@earendil-works/pi-tui |
TUI özel işleme için bileşenler |
npm bağımlılıklar da işe yarar. Uzantınızın yanına (veya bir ana dizine) package.json ekleyin, npm install komutunu çalıştırın; node_modules/'den yapılan içe aktarmalar otomatik olarak çözümlenir.
pi install (npm veya git) ile kurulan dağıtılmış pi paketleri için çalışma zamanı depoları dependencies'de olmalıdır. Paket kurulumunda varsayılan olarak üretim kurulumları (npm install --omit=dev) kullanılır, dolayısıyla devDependencies çalışma zamanında kullanılamaz; npmCommand yapılandırıldığında git paketleri sarmalayıcılarla uyumluluk için düz install kullanır.
Node.js yerleşikler (node:fs, node:path, vb.) de mevcuttur.
Uzantı Yazma
Bir uzantı, ExtensionAPI alan bir varsayılan fabrika işlevini dışa aktarır. Fabrika senkron veya asenkron olabilir:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default function (pi: ExtensionAPI) {
// Subscribe to events
pi.on("event_name", async (event, ctx) => {
// ctx.ui for user interaction
const ok = await ctx.ui.confirm("Title", "Are you sure?");
ctx.ui.notify("Done!", "info");
ctx.ui.setStatus("my-ext", "Processing..."); // Footer status
ctx.ui.setWidget("my-ext", ["Line 1", "Line 2"]); // Widget above editor (default)
});
// Register tools, commands, shortcuts, flags
pi.registerTool({ ... });
pi.registerCommand("name", { ... });
pi.registerShortcut("ctrl+x", { ... });
pi.registerFlag("my-flag", { ... });
}Extensions jiti aracılığıyla yüklenir, dolayısıyla TypeScript derleme olmadan çalışır.
Fabrika Promise değerini döndürürse pi, başlatmaya devam etmeden önce bunu bekler. Bu, eşzamansız başlatmanın session_start'den önce, resources_discover'den önce ve pi.registerProvider() aracılığıyla kuyruğa alınan sağlayıcı kayıtları temizlenmeden önce tamamlandığı anlamına gelir.
Eşzamansız fabrika işlevleri
Uzaktan yapılandırmayı getirme veya kullanılabilir modelleri dinamik olarak keşfetme gibi tek seferlik başlatma işleri için eşzamansız fabrika kullanın.
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default async function (pi: ExtensionAPI) {
const response = await fetch("http://localhost:1234/v1/models");
const payload = (await response.json()) as {
data: Array<{
id: string;
name?: string;
context_window?: number;
max_tokens?: number;
}>;
};
pi.registerProvider("local-openai", {
baseUrl: "http://localhost:1234/v1",
apiKey: "$LOCAL_OPENAI_API_KEY",
api: "openai-completions",
models: payload.data.map((model) => ({
id: model.id,
name: model.name ?? model.id,
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: model.context_window ?? 128000,
maxTokens: model.max_tokens ?? 4096,
})),
});
}Bu model, getirilen modellerin normal başlatma sırasında ve pi --list-models'ye kadar kullanılabilir olmasını sağlar.
Uzun ömürlü kaynaklar ve kapatma
Uzantı fabrikaları hiçbir zaman oturum başlatmayan çağrılarda çalışabilir. İşlemler, yuvalar, dosya izleyiciler veya zamanlayıcılar gibi arka plan kaynaklarını fabrikadan başlatmayın.
Arka plan kaynağının başlatılmasını session_start tarihine veya kaynağa ihtiyaç duyan komut/araç/olayına kadar erteleyin. Başlattığınız oturum kapsamlı kaynakları kapatmak için idempotent bir session_shutdown işleyici kaydedin.
Uzatma Stilleri
Tek dosya - küçük uzantılar için en basiti:
~/.pi/agent/extensions/
└── my-extension.tsindex.ts içeren dizin - çoklu dosya uzantıları için:
~/.pi/agent/extensions/
└── my-extension/
├── index.ts # Entry point (exports default function)
├── tools.ts # Helper module
└── utils.ts # Helper moduleBağımlılık içeren paket - npm paketlere ihtiyaç duyan uzantılar için:
~/.pi/agent/extensions/
└── my-extension/
├── package.json # Declares dependencies and entry points
├── package-lock.json
├── node_modules/ # After npm install
└── src/
└── index.ts// package.json
{
"name": "my-extension",
"dependencies": {
"zod": "^3.0.0",
"chalk": "^5.0.0"
},
"pi": {
"extensions": ["./src/index.ts"]
}
}Uzantı dizininde npm install komutunu çalıştırın, ardından node_modules/'den içe aktarmalar otomatik olarak çalışır.
Olaylar
Yaşam Döngüsüne Genel Bakış
pi starts
│
├─► project_trust (user/global and CLI extensions only, before project resources load)
├─► session_start { reason: "startup" }
└─► resources_discover { reason: "startup" }
│
▼
user sends prompt ─────────────────────────────────────────┐
│ │
├─► (extension commands checked first, bypass if found) │
├─► input (can intercept, transform, or handle) │
├─► (skill/template expansion if not handled) │
├─► before_agent_start (can inject message, modify system prompt)
├─► agent_start │
├─► message_start / message_update / message_end │
│ │
│ ┌─── turn (repeats while LLM calls tools) ───┐ │
│ │ │ │
│ ├─► turn_start │ │
│ ├─► context (can modify messages) │ │
│ ├─► before_provider_headers (can mutate headers) |
│ ├─► before_provider_request (can inspect or replace payload)
│ ├─► after_provider_response (status + headers, before stream consume)
│ │ │ │
│ │ LLM responds, may call tools: │ │
│ │ ├─► tool_execution_start │ │
│ │ ├─► tool_call (can block) │ │
│ │ ├─► tool_execution_update │ │
│ │ ├─► tool_result (can modify) │ │
│ │ └─► tool_execution_end │ │
│ │ │ │
│ └─► turn_end │ │
│ │
├─► agent_end │
└─► agent_settled (no retry/compaction/follow-up left) │
│
user sends another prompt ◄────────────────────────────────┘
/new (new session) or /resume (switch session)
├─► session_before_switch (can cancel)
├─► session_shutdown
├─► session_start { reason: "new" | "resume", previousSessionFile? }
└─► resources_discover { reason: "startup" }
/fork or /clone
├─► session_before_fork (can cancel)
├─► session_shutdown
├─► session_start { reason: "fork", previousSessionFile }
└─► resources_discover { reason: "startup" }
/name or pi.setSessionName()
└─► session_info_changed
/compact or auto-compaction
├─► session_before_compact (can cancel or customize)
└─► session_compact
/tree navigation
├─► session_before_tree (can cancel or customize)
└─► session_tree
/model or Ctrl+P (model selection/cycling)
├─► thinking_level_select (if model change changes/clamps thinking level)
└─► model_select
thinking level changes (settings, keybinding, pi.setThinkingLevel())
└─► thinking_level_select
exit (Ctrl+C, Ctrl+D, SIGHUP, SIGTERM)
└─► session_shutdownBaşlangıç Etkinlikleri
proje_güveni
Pi, dinamik yapılandırmalara (.pi veya .agents/skills) sahip bir projeye güvenilip güvenilmeyeceğine karar vermeden önce tetiklenir. Başlangıç sırasında ve oturum değişimi (örneğin /resume) mevcut süreçte güveni çözülmemiş bir cwd'ye girdiğinde çalışır. Yalnızca kullanıcı/global uzantılar ve CLI -e uzantılar katılır; proje yerel uzantıları, güven çözümlenene kadar yüklenmez.
pi.on("project_trust", async (event, ctx) => {
// event.cwd - current working directory
// ctx has a limited trust context: cwd, mode, hasUI, and select/confirm/input/notify UI helpers
if (await ctx.ui.confirm("Trust project?", event.cwd)) {
return { trusted: "yes", remember: true };
}
return { trusted: "undecided" };
});Bir project_trust işleyicisi { trusted: "yes" | "no" | "undecided" } döndürmelidir. Kararın sahibi, "yes" veya "no" döndüren kullanıcı/global veya CLI uzantısıdır; ilk evet/hayır kararı kazanır ve yerleşik güven istemini bastırır. Evet/hayır kararına devam etmek için remember: true tuşlarını kullanın; aksi takdirde yalnızca mevcut süreç için geçerlidir. Daha sonraki işleyicilerin veya yerleşik güven akışının karar vermesine izin vermek için "undecided" değerini döndürün. İstemden önce ctx.hasUI seçeneğini işaretleyin. Hiçbir işleyici evet/hayır döndürmezse normal güven çözümlemesi devam eder: kaydedilen trust.json kararlar önce uygulanır, ardından defaultProjectTrust pi'nin varsayılan olarak sorup sormadığını, güvendiğini veya reddedip reddetmediğini kontrol eder.
Kaynak Etkinlikleri
kaynaklar_keşfet
Uzantıların ek beceri, bilgi istemi ve tema yollarına katkıda bulunabilmesi için session_start tarihinden sonra tetiklenir.
Başlangıç yolu reason: "startup" kullanır. Yeniden yükleme reason: "reload" kullanır.
pi.on("resources_discover", async (event, _ctx) => {
// event.cwd - current working directory
// event.reason - "startup" | "reload"
return {
skillPaths: ["/path/to/skills"],
promptPaths: ["/path/to/prompts"],
themePaths: ["/path/to/themes"],
};
});Oturum Etkinlikleri
Oturum depolama dahili bilgileri için Session Format ve SessionManager API'e bakın.
oturum_başlangıcı
Bir oturum başlatıldığında, yüklendiğinde veya yeniden yüklendiğinde tetiklenir.
pi.on("session_start", async (event, ctx) => {
// event.reason - "startup" | "reload" | "new" | "resume" | "fork"
// event.previousSessionFile - present for "new", "resume", and "fork"
ctx.ui.notify(`Session: ${ctx.sessionManager.getSessionFile() ?? "ephemeral"}`, "info");
});session_info_changed
Geçerli oturumun görünen adı /name, RPC veya pi.setSessionName() aracılığıyla ayarlandığında tetiklenir.
pi.on("session_info_changed", async (event, ctx) => {
// event.name - current normalized name, or undefined if cleared
ctx.ui.notify(`Session renamed: ${event.name ?? "(none)"}`, "info");
});session_before_switch
Yeni bir oturum başlatmadan (/new) veya oturum değiştirmeden (/resume) önce tetiklendi.
pi.on("session_before_switch", async (event, ctx) => {
// event.reason - "new" or "resume"
// event.targetSessionFile - session we're switching to (only for "resume")
if (event.reason === "new") {
const ok = await ctx.ui.confirm("Clear?", "Delete all messages?");
if (!ok) return { cancel: true };
}
});Başarılı bir geçiş veya yeni oturum eyleminden sonra pi, eski uzantı örneği için session_shutdown yayar, yeni oturum için uzantıları yeniden yükler ve yeniden bağlar, ardından reason: "new" | "resume" ve previousSessionFile ile session_start yayar.
session_shutdown'de temizleme çalışması yapın, ardından session_start'de herhangi bir bellek içi durumu yeniden kurun.
session_before_fork
/fork ile çatallanırken veya /clone ile klonlanırken ateşlenir.
pi.on("session_before_fork", async (event, ctx) => {
// event.entryId - ID of the selected entry
// event.position - "before" for /fork, "at" for /clone
return { cancel: true }; // Cancel fork/clone
// OR
return { skipConversationRestore: true }; // Reserved for future conversation restore control
});Başarılı bir çatallanma veya klonlamanın ardından pi, eski uzantı örneği için session_shutdown yayar, yeni oturum için uzantıları yeniden yükler ve yeniden bağlar, ardından reason: "fork" ve previousSessionFile ile session_start yayar.
session_shutdown'de temizleme çalışması yapın, ardından session_start'de herhangi bir bellek içi durumu yeniden kurun.
session_before_compact / session_compact
Sıkıştırma sırasında ateşlendi. Ayrıntılar için compaction.md'e bakın.
pi.on("session_before_compact", async (event, ctx) => {
const { preparation, branchEntries, customInstructions, reason, willRetry, signal } = event;
// reason - "manual" (/compact), "threshold", or "overflow"
// willRetry - whether the aborted turn is retried after compaction (overflow recovery)
// Cancel:
return { cancel: true };
// Custom summary:
return {
compaction: {
summary: "...",
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: preparation.tokensBefore,
// usage: summaryResponse.usage, // Optional; included in session totals
}
};
});
pi.on("session_compact", async (event, ctx) => {
// event.compactionEntry - the saved compaction
// event.fromExtension - whether extension provided it
// event.reason - "manual" (/compact), "threshold", or "overflow"
// event.willRetry - whether the aborted turn is retried after compaction (overflow recovery)
});oturum_önceki_ağaç / oturum_ağaç
/tree navigasyonda tetiklendi. Ağaç gezinme kavramları için Sessions'ye bakın.
pi.on("session_before_tree", async (event, ctx) => {
const { preparation, signal } = event;
return { cancel: true };
// OR provide custom summary:
return {
summary: {
summary: "...",
// usage: summaryResponse.usage, // Optional; included in session totals
details: {},
},
};
});
pi.on("session_tree", async (event, ctx) => {
// event.newLeafId, oldLeafId, summaryEntry, fromExtension
});session_shutdown
Başlatılmış bir oturum çalışma zamanı kesilmeden önce tetiklenir. session_start veya diğer oturum kapsamlı kancalardan açılan kaynakları temizlemek için bunu kullanın.
pi.on("session_shutdown", async (event, ctx) => {
// event.reason - "quit" | "reload" | "new" | "resume" | "fork"
// event.targetSessionFile - destination session for session replacement flows
// Cleanup, save state, etc.
});Temsilci Etkinlikleri
before_agent_start
Kullanıcı istemi gönderdikten sonra, aracı döngüsünden önce tetiklenir. Bir mesaj enjekte edebilir ve/veya sistem istemini değiştirebilir.
pi.on("before_agent_start", async (event, ctx) => {
// event.prompt - user's prompt text
// event.images - attached images (if any)
// event.systemPrompt - current chained system prompt for this handler
// (includes changes from earlier before_agent_start handlers)
// event.systemPromptOptions - structured options used to build the system prompt
// .customPrompt - any custom system prompt (from --system-prompt, SYSTEM.md, or custom templates)
// .selectedTools - tools currently active in the prompt
// .toolSnippets - one-line descriptions for each tool
// .promptGuidelines - custom guideline bullets
// .appendSystemPrompt - text from --append-system-prompt flags
// .cwd - working directory
// .contextFiles - AGENTS.md files and other loaded context files
// .skills - loaded skills
return {
// Inject a persistent message (stored in session, sent to LLM)
message: {
customType: "my-extension",
content: "Additional context for the LLM",
display: true,
},
// Replace the system prompt for this turn (chained across extensions)
systemPrompt: event.systemPrompt + "\n\nExtra instructions for this turn...",
};
});systemPromptOptions alanı, uzantılara, Pi'nin sistem istemini oluşturmak için kullandığı aynı yapılandırılmış verilere erişim sağlar. Bu, kaynakları yeniden keşfetmeden veya bayrakları yeniden ayrıştırmadan Pi'nin yüklediklerini (özel istemler, yönergeler, araç parçacıkları, context files, beceriler) incelemenizi sağlar. Uzantınızın, kullanıcı tarafından sağlanan yapılandırmayı korurken sistem isteminde derin ve bilinçli değişiklikler yapması gerektiğinde bunu kullanın.
İçerideki before_agent_start, event.systemPrompt ve ctx.getSystemPrompt()'nin her ikisi de mevcut işleyiciden itibaren zincirleme sistem istemini yansıtır. Daha sonra before_agent_start işleyiciler yine de onu yeniden değiştirebilir.
Agent_start / Agent_end / Agent_settled
agent_start düşük seviyeli bir ajan çalıştırması başladığında tetiklenir. agent_end bu çalıştırma sona erdiğinde tetiklenir, ancak Pi yine de otomatik olarak yeniden deneyebilir, otomatik olarak sıkıştırabilir ve yeniden deneyebilir veya sıradaki takip mesajlarıyla devam edebilir. Pi'nin otomatik olarak çalışmaya devam etmeyeceğini bilmesi gereken durum entegrasyonları için agent_settled kullanın.
pi.on("agent_start", async (_event, ctx) => {});
pi.on("agent_end", async (event, ctx) => {
// event.messages - messages from this low-level run
});
pi.on("agent_settled", async (_event, ctx) => {
// ctx.isIdle() is true here unless another extension started a new run.
});dönüş_başlangıç / dönüş_son
Her turda ateşlenir (bir LLM yanıtı + araç çağrıları).
pi.on("turn_start", async (event, ctx) => {
// event.turnIndex, event.timestamp
});
pi.on("turn_end", async (event, ctx) => {
// event.turnIndex, event.message, event.toolResults
});message_start / message_update / message_end
İleti yaşam döngüsü güncellemeleri nedeniyle tetiklendi.
message_startvemessage_endkullanıcı, asistan ve araçSonuç mesajları için tetiklenir.message_updateasistan akış güncellemeleri için etkinleşir.message_endişleyiciler, sonlandırılmış mesajı değiştirmek için{ message }değerini döndürebilir. Değiştirme aynıroletutmalıdır.
pi.on("message_start", async (event, ctx) => {
// event.message
});
pi.on("message_update", async (event, ctx) => {
// event.message
// event.assistantMessageEvent (token-by-token stream event)
});
pi.on("message_end", async (event, ctx) => {
if (event.message.role !== "assistant") return;
return {
message: {
...event.message,
usage: {
...event.message.usage,
cost: {
...event.message.usage.cost,
total: 0.123,
},
},
},
};
});tool_execution_start / tool_execution_update / tool_execution_end
Araç yürütme yaşam döngüsü güncellemeleri nedeniyle tetiklendi.
Paralel takım modunda:
tool_execution_startön kontrol aşamasında yardımcı kaynak sırasına göre yayılırtool_execution_updateolaylar araçlara karışabilirtool_execution_endher takım sonlandırıldıktan sonra takım tamamlama sırasına göre yayınlanır- son
toolResultmesaj etkinlikleri daha sonra yardımcı kaynak sırasına göre yayınlanmaya devam eder
pi.on("tool_execution_start", async (event, ctx) => {
// event.toolCallId, event.toolName, event.args
});
pi.on("tool_execution_update", async (event, ctx) => {
// event.toolCallId, event.toolName, event.args, event.partialResult
});
pi.on("tool_execution_end", async (event, ctx) => {
// event.toolCallId, event.toolName, event.result, event.isError
});bağlam
Her LLM çağrısından önce kovuldu. Mesajları tahribatsız olarak değiştirin. Mesaj türleri için Session Format'e bakın.
pi.on("context", async (event, ctx) => {
// event.messages - deep copy, safe to modify
const filtered = event.messages.filter(m => !shouldPrune(m));
return { messages: filtered };
});before_provider_headers
Giden HTTP üstbilgileri birleştirildikten sonra tetiklenir. İstek başlıklarını eklemek, geçersiz kılmak veya kaldırmak için bunu kullanın.
İşleyiciler yerinde event.headers mutasyona uğrar. Eklemek veya geçersiz kılmak için bir dizeye veya silmek için null'ye bir tuş ayarlayın.
pi.on("before_provider_headers", (event, ctx) => {
// Add or override — e.g. a session id for gateway tracing/attribution
event.headers["x-session-id"] = ctx.sessionManager.getSessionId();
// Drop a tracking header pi adds for this call
event.headers["X-OpenRouter-Title"] = null;
});Sağlayıcı isteği başına bir kez çalıştırılır; Kancayı yeniden ateşlemek yerine aynı başlıkları yeniden kullanmayı dener.
before_provider_request
Sağlayıcıya özel veri oluşturulduktan sonra, istek gönderilmeden hemen önce tetiklenir. İşleyiciler uzantı yükleme sırasına göre çalışır. undefined değerini döndürmek yükün değişmemesini sağlar. Başka herhangi bir değerin döndürülmesi, daha sonraki işleyicilerin ve gerçek isteğin yükünün yerini alır.
Bu kanca, sağlayıcı düzeyindeki sistem talimatlarını yeniden yazabilir veya bunları tamamen kaldırabilir. Bu yük düzeyi değişiklikleri, son serileştirilmiş sağlayıcı yükü yerine Pi'nin sistem istem dizesini bildiren ctx.getSystemPrompt() tarafından yansıtılmaz.
pi.on("before_provider_request", (event, ctx) => {
console.log(JSON.stringify(event.payload, null, 2));
// Optional: replace payload
// return { ...event.payload, temperature: 0 };
});Bu esas olarak sağlayıcı serileştirmesinde ve önbellek davranışında hata ayıklamak için kullanışlıdır.
after_provider_response
Bir HTTP yanıtı alındıktan sonra ve akış gövdesi tüketilmeden önce tetiklenir. İşleyiciler uzantı yükleme sırasına göre çalışır.
pi.on("after_provider_response", (event, ctx) => {
// event.status - HTTP status code
// event.headers - normalized response headers
if (event.status === 429) {
console.log("rate limited", event.headers["retry-after"]);
}
});Üstbilginin kullanılabilirliği sağlayıcıya ve aktarıma bağlıdır. Providers soyut HTTP yanıtlarının başlıkları açığa çıkarmaması.
Modeli Etkinlikleri
model_select
Model /model komutu, model döngüsü (Ctrl+P) veya oturum geri yükleme yoluyla değiştiğinde tetiklenir.
pi.on("model_select", async (event, ctx) => {
// event.model - newly selected model
// event.previousModel - previous model (undefined if first selection)
// event.source - "set" | "cycle" | "restore"
const prev = event.previousModel
? `${event.previousModel.provider}/${event.previousModel.id}`
: "none";
const next = `${event.model.provider}/${event.model.id}`;
ctx.ui.notify(`Model changed (${event.source}): ${prev} -> ${next}`, "info");
});Kullanıcı arayüzü öğelerini (durum çubukları, alt bilgiler) güncellemek veya etkin model değiştiğinde modele özel başlatma gerçekleştirmek için bunu kullanın.
think_level_select
Düşünme düzeyi değiştiğinde kovulur. Bu yalnızca bildirim amaçlıdır; işleyici dönüş değerleri göz ardı edilir.
pi.on("thinking_level_select", async (event, ctx) => {
// event.level - newly selected thinking level
// event.previousLevel - previous thinking level
ctx.ui.setStatus("thinking", `thinking: ${event.level}`);
});pi.setThinkingLevel(), model değişiklikleri veya yerleşik düşünme düzeyi kontrolleri aktif düşünme düzeyini değiştirdiğinde uzantı kullanıcı arayüzünü güncellemek için bunu kullanın.
Araç Olayları
tool_call
tool_execution_start sonrasında, araç çalıştırılmadan önce tetiklendi. Engelleyebilir. Yazılı girişleri daraltmak ve almak için isToolCallEventType tuşunu kullanın.
tool_call çalıştırılmadan önce pi, önceden yayılan Ajan olaylarının AgentSession boyunca boşaltılmasını bitirmesini bekler. Bu, ctx.sessionManager'nin mevcut yardımcı araç çağırma mesajı aracılığıyla güncel olduğu anlamına gelir.
Varsayılan paralel takım yürütme modunda, aynı asistan mesajından gelen kardeş takım çağrılarının sırasıyla ön kontrolü yapılır ve ardından eş zamanlı olarak yürütülür. tool_call, ctx.sessionManager'deki aynı asistan mesajından kardeş aracı sonuçlarını göreceğiniz garanti edilmez.
event.input değiştirilebilir. Yürütmeden önce araç bağımsız değişkenlerini yamamak için onu yerinde değiştirin.
Davranış garantileri:
event.input'deki mutasyonlar gerçek takım uygulamasını etkiler- Daha sonra
tool_callişleyiciler daha önceki işleyiciler tarafından yapılan mutasyonları görür - Mutasyonunuzdan sonra yeniden doğrulama yapılmaz
{ block: true, reason?: string, terminate?: boolean }aracılığıylatool_callkontrol engellemesinden değerleri döndürterminateyalnızca engellenen çağrı için geçerlidir; aracı yalnızca gruptaki her kesin sonuç sona erdiğinde erken durur
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
pi.on("tool_call", async (event, ctx) => {
// event.toolName - "bash", "read", "write", "edit", etc.
// event.toolCallId
// event.input - tool parameters (mutable)
// Built-in tools: no type params needed
if (isToolCallEventType("bash", event)) {
// event.input is { command: string; timeout?: number }
event.input.command = `source ~/.profile\n${event.input.command}`;
if (event.input.command.includes("rm -rf")) {
return { block: true, reason: "Dangerous command", terminate: true };
}
}
if (isToolCallEventType("read", event)) {
// event.input is { path: string; offset?: number; limit?: number }
console.log(`Reading: ${event.input.path}`);
}
});Özel araç girişi yazma
Özel araçlar giriş türlerini dışa aktarmalıdır:
// my-extension.ts
export type MyToolInput = Static<typeof myToolSchema>;Açık tür parametreleriyle isToolCallEventType kullanın:
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
import type { MyToolInput } from "my-extension";
pi.on("tool_call", (event) => {
if (isToolCallEventType<"my_tool", MyToolInput>("my_tool", event)) {
event.input.action; // typed
}
});tool_result
Aracın yürütülmesi tamamlandıktan sonra ve tool_execution_end artı son araç sonuç mesajı olayları yayınlanmadan önce tetiklenir. Sonucu değiştirebilir.
Paralel takım modunda, tool_result ve tool_execution_end takım tamamlama sırasına göre karışabilir, son toolResult mesaj olayları ise daha sonra yardımcı kaynak sırasına göre yayınlanmaya devam eder.
tool_result ara katman yazılımı gibi işleyiciler zinciri:
- İşleyiciler uzantı yükleme sırasına göre çalışır
- Her işleyici, önceki işleyici değişikliklerinden sonraki en son sonucu görür
- İşleyiciler kısmi yamaları döndürebilir (
content,details,isErrorveyausage); atlanan alanlar mevcut değerlerini korur
İşleyicinin içindeki iç içe eşzamansız çalışma için ctx.signal kullanın. Bu, Esc'nin model çağrılarını, fetch() ve uzantı tarafından başlatılan diğer iptal etmeye duyarlı işlemleri iptal etmesine olanak tanır.
import { isBashToolResult } from "@earendil-works/pi-coding-agent";
pi.on("tool_result", async (event, ctx) => {
// event.toolName, event.toolCallId, event.input
// event.content, event.details, event.isError, event.usage
if (isBashToolResult(event)) {
// event.details is typed as BashToolDetails
}
const response = await fetch("https://example.com/summarize", {
method: "POST",
body: JSON.stringify({ content: event.content }),
signal: ctx.signal,
});
// Modify result:
return { content: [...], details: {...}, isError: false, usage: nestedModelUsage };
});Kullanıcı Bash Etkinlikleri
kullanıcı_bash
Kullanıcı ! veya !! komutlarını çalıştırdığında tetiklenir. Araya girebilir.
import { createLocalBashOperations } from "@earendil-works/pi-coding-agent";
pi.on("user_bash", (event, ctx) => {
// event.command - the bash command
// event.excludeFromContext - true if !! prefix
// event.cwd - working directory
// Option 1: Provide custom operations (e.g., SSH)
return { operations: remoteBashOps };
// Option 2: Wrap pi's built-in local bash backend
const local = createLocalBashOperations();
return {
operations: {
exec(command, cwd, options) {
return local.exec(`source ~/.profile\n${command}`, cwd, options);
}
}
};
// Option 3: Full replacement - return result directly
return { result: { output: "...", exitCode: 0, cancelled: false, truncated: false } };
});Giriş Olayları
giriş
Kullanıcı girişi alındığında, uzantı komutları kontrol edildikten sonra ancak beceri ve şablon genişletmeden önce tetiklenir. Etkinlik ham giriş metnini gördüğünden /skill:foo ve /template henüz genişletilmedi.
İşleme sırası:
- Önce uzantı komutları (
/cmd) kontrol edilir - bulunursa işleyici çalışır ve giriş olayı atlanır inputolay tetiklenir - müdahale edebilir, dönüştürebilir veya işleyebilir- Eğer işlenmezse: beceri komutları (
/skill:name) beceri içeriğine genişletildi - İşlenmezse: prompt templates (
/template) şablon içeriğine genişletildi - Aracı işleme başlar (
before_agent_startvb.)
pi.on("input", async (event, ctx) => {
// event.text - raw input (before skill/template expansion)
// event.images - attached images, if any
// event.source - "interactive" (typed), "rpc" (API), or "extension" (via sendUserMessage)
// event.streamingBehavior - "steer" | "followUp" | undefined
// undefined when idle, "steer" for mid-stream interrupts,
// "followUp" for messages queued until the agent finishes
// Transform: rewrite input before expansion
if (event.text.startsWith("?quick "))
return { action: "transform", text: `Respond briefly: ${event.text.slice(7)}` };
// Handle: respond without LLM (extension shows its own feedback)
if (event.text === "ping") {
ctx.ui.notify("pong", "info");
return { action: "handled" };
}
// Route by source: skip processing for extension-injected messages
if (event.source === "extension") return { action: "continue" };
// Intercept skill commands before expansion
if (event.text.startsWith("/skill:")) {
// Could transform, block, or let pass through
}
return { action: "continue" }; // Default: pass through to expansion
});Sonuçlar:
continue- değişmeden geçiş (işleyici hiçbir şey döndürmezse varsayılan)transform- metni/resimleri değiştirin, ardından genişletmeye devam edinhandled- temsilciyi tamamen atla (bunu geri veren ilk işleyici kazanır)
Zinciri işleyiciler arasında dönüştürür. streamingBehavior bilinçli yönlendirme için input-transform.ts ve input-transform-streaming.ts'ye bakın.
Uzantı Bağlamı
Tüm işleyiciler ctx: ExtensionContext alır.
ctx.ui
Kullanıcı etkileşimi için kullanıcı arayüzü yöntemleri. Tüm ayrıntılar için Custom UI'e bakın.
ctx.mode
Geçerli çalışma modu: "tui", "rpc", "json" veya "print". custom(), bileşen fabrikaları, terminal girişi ve doğrudan TUI oluşturma gibi yalnızca terminal özelliklerini korumak için ctx.mode === "tui" kullanın.
ctx.hasUI
TUI ve RPC modlarında true. false baskı modunda (-p) ve JSON modunda. Hem TUI hem de çalışan diyalog yöntemlerini (select, confirm, input, editor) ve ateşle ve unut yöntemlerini (notify, setStatus, setWidget, setTitle, setEditorText) korumak için bunu kullanın. RPC modları. RPC modunda, TUI'ye özgü bazı yöntemler işlem gerektirmez veya varsayılanları döndürür (bkz. rpc.md).
ctx.cwd
Geçerli çalışma dizini.
Proje yerel yapılandırma yollarını oluştururken .pi sabit kodlama yerine CONFIG_DIR_NAME kullanın. Yeniden markalanan dağıtımlar farklı bir yapılandırma dizini adı kullanabilir.
import { CONFIG_DIR_NAME, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { join } from "node:path";
export default function (pi: ExtensionAPI) {
pi.on("session_start", (_event, ctx) => {
const projectConfigPath = join(ctx.cwd, CONFIG_DIR_NAME, "my-extension.json");
// ...
});
}ctx.isProjectTrusted()
Geçerli oturum bağlamı için proje yerel güveninin etkin olup olmadığını döndürür. Bu, yalnızca küresel güven deposunda kaydedilen kararları değil, geçici güven kararlarını ve CLI güven geçersiz kılmalarını da içerir.
Yalnızca güvenilir projeler için dikkate alınması gereken proje yerel uzantı yapılandırmasını okumadan önce bunu kullanın.
ctx.sessionManager'ı
Oturum durumuna salt okunur erişim. Tam SessionManager API ve giriş türleri için Session Format'ye bakın.
tool_call için bu durum, işleyiciler çalıştırılmadan önce mevcut asistan mesajı aracılığıyla senkronize edilir. Paralel takım yürütme modunda, aynı asistan mesajından kardeş takım sonuçlarının dahil edilmesi hala garanti edilmez.
ctx.sessionManager.getEntries() // All entries
ctx.sessionManager.getBranch() // Current branch
ctx.sessionManager.buildContextEntries() // Active branch entries with compaction applied
ctx.sessionManager.getLeafId() // Current leaf entry IDctx.modelRegistry / ctx.model / ctx.thinkingLevel / ctx.scopedModels
Modellere, sağlayıcılara ve çözümlenmiş kimlik doğrulamaya erişim. ctx.modelRegistry.getProvider(id) etkili pi-ai sağlayıcısını döndürürken getProviderAuth(id), yüklü bir model gerektirmeden mevcut API key, başlıklarını, temel URL'sini ve sağlayıcı kapsamlı ortamını çözer. ctx.model aktif model, ctx.thinkingLevel ise mevcut etkili düşünme düzeyidir.
ctx.scopedModels geçerli oturumun kapsamındaki modellerin salt okunur listesidir — /scoped-models komutunun gösterdiği kümenin aynısıdır. Oturum başlangıcında --models CLI bayrağı ve enabledModels ayarından çözümlenir (provider/modelId veya çıplak modelId'de mini eşleşme ile mevcut katalogla eşleştirilir). Kapsam belirleme yapılandırılmadığında boştur; bu, mevcut her modelin kullanılabileceği anlamına gelir. Her giriş { model, thinkingLevel? }'dir; burada thinkingLevel yalnızca bir desen sabitlendiğinde ayarlanır (örn. anthropic/*:high). Kataloğun tamamını ctx.modelRegistry.getAvailable() aracılığıyla numaralandırmak yerine yerleşik modeli yansıtan bir model seçiciyi doldurmak için bunu kullanın.
ctx.sinyali
Geçerli temsilci iptal sinyali veya hiçbir temsilci dönüşü etkin olmadığında undefined.
Uzantı işleyicileri tarafından başlatılan, iptal etme özelliğine sahip iç içe geçmiş işler için bunu kullanın, örneğin:
fetch(..., { signal: ctx.signal })- kabul edilen model çağrıları
signal AbortSignalkabul eden dosya veya işlem yardımcıları
ctx.signal genellikle tool_call, tool_result, message_update ve turn_end gibi aktif dönüş etkinlikleri sırasında tanımlanır.
Oturum olayları, uzantı komutları ve pi boştayken başlatılan kısayollar gibi boşta veya dönüşsüz bağlamlarda genellikle undefined olur.
pi.on("tool_result", async (event, ctx) => {
const response = await fetch("https://example.com/api", {
method: "POST",
body: JSON.stringify(event),
signal: ctx.signal,
});
const data = await response.json();
return { details: data };
});ctx.isIdle() / ctx.abort() / ctx.hasPendingMessages()
Akış yardımcılarını kontrol edin. Pi bir aracı çalıştırmayı, otomatik yeniden denemeyi, otomatik sıkıştırma yeniden denemesini veya sıraya alınmış devamı işlerken ctx.isIdle() yanlıştır.
ctx.shutdown()
Pi'nin zarif bir şekilde kapatılmasını talep edin.
- Etkileşimli mod: Temsilci boşta kalana kadar ertelenir (sıradaki tüm yönlendirme ve takip mesajlarını işledikten sonra).
- RPC modu: Bir sonraki boşta durumuna kadar ertelenir (mevcut komut yanıtını tamamladıktan sonra, sonraki komutu beklerken).
- Baskı modu: İşlem yok. Tüm istemler işlendiğinde işlemden otomatik olarak çıkılır.
Çıkmadan önce tüm uzantılara session_shutdown olayını yayar. Tüm bağlamlarda kullanılabilir (olay işleyicileri, araçlar, komutlar, kısayollar).
pi.on("tool_call", (event, ctx) => {
if (isFatal(event.input)) {
ctx.shutdown();
}
});ctx.getContextUsage()
Etkin model için geçerli bağlam kullanımını döndürür. Mümkün olduğunda son yardımcı kullanımını kullanır ve ardından takip eden mesajlar için belirteçleri tahmin eder.
const usage = ctx.getContextUsage();
if (usage && usage.tokens > 100_000) {
// ...
}ctx.compact()
Tamamlanmayı beklemeden sıkıştırmayı tetikleyin. Takip işlemleri için onComplete ve onError tuşlarını kullanın.
ctx.compact({
customInstructions: "Focus on recent changes",
onComplete: (result) => {
ctx.ui.notify("Compaction completed", "info");
},
onError: (error) => {
ctx.ui.notify(`Compaction failed: ${error.message}`, "error");
},
});ctx.getSystemPrompt()
Pi'nin geçerli sistem istem dizesini döndürür.
before_agent_startsırasında bu, mevcut tur için şu ana kadar yapılan zincirleme sistem istemi değişikliklerini yansıtır.- Daha sonraki
contextmesaj mutasyonlarını içermez. before_provider_requestyük yeniden yazma işlemlerini içermez.- Daha sonra yüklenen uzantılar sizinkinden sonra çalışırsa, sonuçta gönderilenleri yine de değiştirebilirler.
pi.on("before_agent_start", (event, ctx) => {
const prompt = ctx.getSystemPrompt();
console.log(`System prompt length: ${prompt.length}`);
});ExtensionCommandContext
Komut işleyicileri, oturum kontrol yöntemleriyle ExtensionContext'yi genişleten ExtensionCommandContext'yi alır. Bunlar yalnızca komutlarda mevcuttur çünkü olay işleyicilerinden çağrıldıklarında kilitlenebilirler.
ctx.getSystemPromptOptions()
Pi'nin şu anda sistem istemini oluşturmak için kullandığı temel girişleri döndürür.
const options = ctx.getSystemPromptOptions();
const contextPaths = options.contextFiles?.map((file) => file.path) ?? [];Bu, before_agent_start event.systemPromptOptions ile aynı şekle ve değiştirilebilirliğe sahiptir: özel bilgi istemi, aktif araçlar, araç parçacıkları, bilgi istemi yönergeleri, eklenen sistem bilgi istemi metni, cwd, yüklü context files ve yüklü beceriler. Tam bağlam dosyası içeriği içerebilir; bu nedenle, onu hassas uzantı yerel verileri olarak değerlendirin ve komut listeleri, günlükler veya otomatik tamamlama meta verileri aracılığıyla açığa çıkarmaktan kaçının.
Bu, geçerli temel bilgi istemi girişlerini rapor eder. Tur başına before_agent_start zincirleme sistem istemi değişikliklerini, daha sonra context olay mesajı mutasyonlarını veya before_provider_request yük yeniden yazma işlemlerini içermez.
ctx.waitForIdle()
Otomatik yeniden denemeler, otomatik sıkıştırma yeniden denemeleri ve sıraya alınmış devamlar da dahil olmak üzere aracının tamamen yerleşmesini bekleyin:
pi.registerCommand("my-cmd", {
handler: async (args, ctx) => {
await ctx.waitForIdle();
// Agent is now idle, safe to modify session
},
});ctx.newSession(seçenekler?)
Yeni bir oturum oluşturun:
const parentSession = ctx.sessionManager.getSessionFile();
const kickoff = "Continue in the replacement session";
const result = await ctx.newSession({
parentSession,
setup: async (sm) => {
sm.appendMessage({
role: "user",
content: [{ type: "text", text: "Context from previous session..." }],
timestamp: Date.now(),
});
},
withSession: async (ctx) => {
// Use only the replacement-session ctx here.
await ctx.sendUserMessage(kickoff);
},
});
if (result.cancelled) {
// An extension cancelled the new session
}Seçenekler:
parentSession: yeni oturum başlığına kaydedilecek ana oturum dosyasısetup:withSessionçalıştırılmadan önce yeni oturumunSessionManager'sini değiştirinwithSession: geçiş sonrası çalışmayı yeni bir değişim oturumu bağlamına göre çalıştırın. Yakalanan eskipi/ komutctx'yi kullanmayın; bkz. Session replacement lifecycle and footguns.
ctx.fork(giriş kimliği, seçenekler?)
Belirli bir girişten çatallanarak yeni bir oturum dosyası oluşturulur:
const result = await ctx.fork("entry-id-123", {
withSession: async (ctx) => {
// Use only the replacement-session ctx here.
ctx.ui.notify("Now in the forked session", "info");
},
});
if (result.cancelled) {
// An extension cancelled the fork
}
const cloneResult = await ctx.fork("entry-id-456", { position: "at" });
if (cloneResult.cancelled) {
// An extension cancelled the clone
}Seçenekler:
position:"before"(varsayılan) seçili kullanıcı mesajından önce çatallar, bu istemi düzenleyiciye geri yüklerposition:"at"düzenleyici metnini geri yüklemeden seçilen girişteki etkin yolu kopyalarwithSession: geçiş sonrası çalışmayı yeni bir değişim oturumu bağlamına göre çalıştırın. Yakalanan eskipi/ komutctx'yi kullanmayın; bkz. Session replacement lifecycle and footguns.
ctx.navigateTree(hedefId, seçenekler?)
session tree'de farklı bir noktaya gidin:
const result = await ctx.navigateTree("entry-id-456", {
summarize: true,
customInstructions: "Focus on error handling changes",
replaceInstructions: false, // true = replace default prompt entirely
label: "review-checkpoint",
});Seçenekler:
summarize: Terk edilen dalın özetinin oluşturulup oluşturulmayacağıcustomInstructions: Özetleyici için özel talimatlarreplaceInstructions: Doğruysa,customInstructions, eklenmek yerine varsayılan istemin yerine geçerlabel: Şube özet girişine eklenecek etiket (veya özetlemiyorsa hedef giriş)
ctx.switchSession(sessionPath, seçenekler?)
Farklı bir oturum dosyasına geçin:
const result = await ctx.switchSession("/path/to/session.jsonl", {
withSession: async (ctx) => {
await ctx.sendUserMessage("Resume work in the replacement session");
},
});
if (result.cancelled) {
// An extension cancelled the switch via session_before_switch
}Seçenekler:
withSession: geçiş sonrası çalışmayı yeni bir değişim oturumu bağlamına göre çalıştırın. Yakalanan eskipi/ komutctx'yi kullanmayın; bkz. Session replacement lifecycle and footguns.
Kullanılabilir oturumları keşfetmek için statik SessionManager.list() veya SessionManager.listAll() yöntemlerini kullanın:
import { SessionManager } from "@earendil-works/pi-coding-agent";
pi.registerCommand("switch", {
description: "Switch to another session",
handler: async (args, ctx) => {
const sessions = await SessionManager.list(ctx.cwd);
if (sessions.length === 0) return;
const choice = await ctx.ui.select(
"Pick session:",
sessions.map(s => s.file),
);
if (choice) {
await ctx.switchSession(choice, {
withSession: async (ctx) => {
ctx.ui.notify("Switched session", "info");
},
});
}
},
});Oturum değiştirme yaşam döngüsü ve temelleri
withSession yeni bir ReplacedSessionContext alır ve bu, değiştirme oturumuna bağlı async sendMessage() ve sendUserMessage() yardımcılarıyla ExtensionCommandContext'yi genişletir.
Yaşam döngüsü ve tüfekler:
withSessionyalnızca eski oturumsession_shutdownyayınlandıktan, eski çalışma zamanı bozulduktan, değiştirme oturumu geri döndükten ve yeni uzantı örneği zatensession_startaldıktan sonra çalışır.- Geri çağırma, yeni uzantı örneğinin içinde değil, orijinal kapanışta yürütülmeye devam eder. Bu, eski uzantı örneğinizin
withSessionbaşlamadan önce kapatma temizleme işlemini zaten çalıştırmış olabileceği anlamına gelir. - Yakalanan eski
pi/ eski komutctxoturuma bağlı nesneler değiştirildikten sonra eskidir ve kullanılırsa atılır. Oturuma bağlı çalışma için yalnızcawithSession'ye iletilenctx'yi kullanın. - Daha önce çıkarılan ham nesneler hâlâ sizin sorumluluğunuzdadır. Örneğin, değiştirmeden önce
const sm = ctx.sessionManageryakalarsanız,smhâlâ eskiSessionManagernesnesidir. Değiştirdikten sonra tekrar kullanmayın. withSession'deki kod,session_shutdownişleyiciniz tarafından geçersiz kılınan herhangi bir durumun zaten kaybolduğunu varsaymalıdır. Dizeler, kimlikler ve serileştirilmiş yapılandırma gibi yalnızca kapanmadan temiz bir şekilde kurtulabilen düz verileri yakalayın.
Güvenli desen:
pi.registerCommand("handoff", {
handler: async (_args, ctx) => {
const kickoff = "Continue from the replacement session";
await ctx.newSession({
withSession: async (ctx) => {
await ctx.sendUserMessage(kickoff);
},
});
},
});Güvenli olmayan model:
pi.registerCommand("handoff", {
handler: async (_args, ctx) => {
const oldSessionManager = ctx.sessionManager;
await ctx.newSession({
withSession: async (_ctx) => {
// stale old objects: do not do this
oldSessionManager.getSessionFile();
pi.sendUserMessage("wrong");
},
});
},
});ctx.reload()
/reload ile aynı yeniden yükleme akışını çalıştırın.
pi.registerCommand("reload-runtime", {
description: "Reload extensions, skills, prompts, themes, and context files",
handler: async (_args, ctx) => {
await ctx.reload();
return;
},
});Önemli davranış:
await ctx.reload()geçerli uzatma çalışma zamanı içinsession_shutdownyayar- Daha sonra kaynakları yeniden yükler ve
reason: "reload"ilesession_startve"reload"nedeni ileresources_discoveryayar - Şu anda çalışan komut işleyicisi hala eski çağrı çerçevesinde devam ediyor
await ctx.reload()'den sonraki kod, yeniden yükleme öncesi sürümden itibaren hala çalışıyorawait ctx.reload()sonrasındaki kod, eski bellek içi uzantı durumunun hala geçerli olduğunu varsaymamalıdır- İşleyici geri döndükten sonra gelecekteki komutlar/olaylar/araç çağrıları yeni uzantı sürümünü kullanır
Tahmin edilebilir davranış için, yeniden yüklemeyi söz konusu işleyicinin terminali olarak değerlendirin (await ctx.reload(); return;).
Araçlar ExtensionContext ile çalışır, dolayısıyla ctx.reload()'yi doğrudan çağıramazlar. Yeniden yükleme giriş noktası olarak bir komut kullanın, ardından bu komutu takip eden kullanıcı mesajı olarak sıraya koyan bir aracı kullanıma açın.
LLM'nin yeniden yüklemeyi tetiklemek için çağırabileceği örnek araç:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
export default function (pi: ExtensionAPI) {
pi.registerCommand("reload-runtime", {
description: "Reload extensions, skills, prompts, themes, and context files",
handler: async (_args, ctx) => {
await ctx.reload();
return;
},
});
pi.registerTool({
name: "reload_runtime",
label: "Reload Runtime",
description: "Reload extensions, skills, prompts, themes, and context files",
parameters: Type.Object({}),
async execute() {
pi.sendUserMessage("/reload-runtime", { deliverAs: "followUp" });
return {
content: [{ type: "text", text: "Queued /reload-runtime as a follow-up command." }],
};
},
});
}UzantıAPI Yöntemler
pi.on(olay, işleyici)
Etkinliklere abone olun. Etkinlik türleri ve dönüş değerleri için Events'ye bakın.
pi.registerTool(tanım)
Yüksek Lisans tarafından çağrılabilen özel bir aracı kaydedin. Tüm ayrıntılar için Custom Tools'e bakın.
pi.registerTool() hem uzatma yüklemesi sırasında hem de başlatma sonrasında çalışır. Bunu session_start, komut işleyicileri veya diğer olay işleyicileri içinden çağırabilirsiniz. Yeni araçlar aynı oturumda hemen yenilenir, böylece pi.getAllTools()'de görünürler ve LLM tarafından /reload olmadan çağrılabilirler.
Çalışma zamanında araçları (dinamik olarak eklenen araçlar dahil) etkinleştirmek veya devre dışı bırakmak için pi.setActiveTools() tuşunu kullanın.
Available tools'de özel bir aracı tek satırlık bir girişe dahil etmek için promptSnippet tuşunu, araç etkinken varsayılan Guidelines bölümüne araca özel madde işaretleri eklemek için promptGuidelines tuşunu kullanın.
Önemli: promptGuidelines madde işaretleri, araç adı öneki olmadan Guidelines bölümüne düz olarak eklenir. Her kılavuz, atıfta bulunduğu araca bir isim vermelidir; "Bu aracı şu durumlarda kullan..." kaçının çünkü Yüksek Lisans "bu"nun hangi araç anlamına geldiğini söyleyemez. Bunun yerine "My_tool'u şu durumlarda kullan..." yazın.
Tam bir örnek için dynamic-tools.ts'e bakın.
import { Type } from "typebox";
import { StringEnum } from "@earendil-works/pi-ai";
pi.registerTool({
name: "my_tool",
label: "My Tool",
description: "What this tool does",
promptSnippet: "Summarize or transform text according to action",
promptGuidelines: ["Use my_tool when the user asks to summarize previously generated text."],
parameters: Type.Object({
action: StringEnum(["list", "add"] as const),
text: Type.Optional(Type.String()),
}),
prepareArguments(args) {
// Optional compatibility shim. Runs before schema validation.
// Return the current schema shape, for example to fold legacy fields
// into the modern parameter object.
return args;
},
async execute(toolCallId, params, signal, onUpdate, ctx) {
// Stream progress
onUpdate?.({ content: [{ type: "text", text: "Working..." }] });
return {
content: [{ type: "text", text: "Done" }],
details: { result: "..." },
};
},
// Optional: Custom rendering
renderCall(args, theme, context) { ... },
renderResult(result, options, theme, context) { ... },
});pi.sendMessage(mesaj, seçenekler?)
Oturuma özel bir mesaj enjekte edin. Özel mesajlar LLM bağlamına katılır. LLM'ye gönderilmemesi gereken yalnızca TUI kalıcı içerik için, pi.registerEntryRenderer() ile pi.appendEntry() kullanın.
pi.sendMessage({
customType: "my-extension",
content: "Message text",
display: true,
details: { ... },
}, {
triggerTurn: true,
deliverAs: "steer",
});Seçenekler:
deliverAs- Teslimat modu:"steer"(varsayılan) - Akış sırasında mesajı sıraya koyar. Mevcut asistan sırası, bir sonraki LLM çağrısından önce, araç çağrılarını yürütmeyi tamamladıktan sonra teslim edilir."followUp"- Temsilcinin bitirmesini bekler. Yalnızca temsilcinin başka araç çağrısı kalmadığında teslim edilir."nextTurn"- Bir sonraki kullanıcı istemi için sıraya alındı. Hiçbir şeyi kesintiye uğratmaz veya tetiklemez.
triggerTurn: true- Temsilci boştaysa hemen bir LLM yanıtını tetikleyin. Yalnızca"steer"ve"followUp"modları için geçerlidir ("nextTurn"için dikkate alınmaz).
pi.sendUserMessage(içerik, seçenekler?)
Temsilciye bir kullanıcı mesajı gönderin. Özel mesajlar gönderen sendMessage()'den farklı olarak bu, sanki kullanıcı tarafından yazılmış gibi görünen gerçek bir kullanıcı mesajı gönderir. Her zaman bir dönüşü tetikler.
// Simple text message
pi.sendUserMessage("What is 2+2?");
// With content array (text + images)
pi.sendUserMessage([
{ type: "text", text: "Describe this image:" },
{ type: "image", source: { type: "base64", mediaType: "image/png", data: "..." } },
]);
// During streaming - must specify delivery mode
pi.sendUserMessage("Focus on error handling", { deliverAs: "steer" });
pi.sendUserMessage("And then summarize", { deliverAs: "followUp" });Seçenekler:
deliverAs- Aracı akış halindeyken gereklidir:"steer"- Geçerli asistan sırasının araç çağrılarını yürütmesi tamamlandıktan sonra mesajı teslim edilmek üzere sıraya koyar"followUp"- Temsilcinin tüm araçları bitirmesini bekler
Akış yapılmadığında mesaj hemen gönderilir ve yeni bir dönüşü tetikler. deliverAs olmadan yayın yaparken hata verir.
Tam bir örnek için send-user-message.ts'e bakın.
pi.appendEntry(customType, veri?)
Uzantı verilerini kalıcı hale getirin. Özel girişler LLM bağlamına katılmaz. Etkileşimli modda, pi.registerEntryRenderer() ile eşleştirildiğinde sohbet metninin içinde de görüntü oluşturabilirler.
pi.appendEntry("my-state", { count: 42 });
pi.appendEntry("status-card", { title: "Indexed files", count: 17 });
// Restore on reload
pi.on("session_start", async (_event, ctx) => {
for (const entry of ctx.sessionManager.getEntries()) {
if (entry.type === "custom" && entry.customType === "my-state") {
// Reconstruct from entry.data
}
}
});pi.setSessionName(ad)
Oturumun görünen adını ayarlayın (ilk mesaj yerine oturum seçicide gösterilir).
pi.setSessionName("Refactor auth module");pi.getSessionName()
Ayarlanmışsa geçerli oturum adını alın.
const name = pi.getSessionName();
if (name) {
console.log(`Session: ${name}`);
}pi.setLabel(girişKimliği, etiket)
Girişteki etiketi ayarlayın veya temizleyin. Etiketler, yer işareti koyma ve gezinme için kullanıcı tanımlı işaretçilerdir (/tree seçicide gösterilir).
// Set a label
pi.setLabel(entryId, "checkpoint-before-refactor");
// Clear a label
pi.setLabel(entryId, undefined);
// Read labels via sessionManager
const label = ctx.sessionManager.getLabel(entryId);Etiketler oturumda kalır ve yeniden başlatmalarda hayatta kalır. Konuşma ağacındaki önemli noktaları (dönüşler, kontrol noktaları) işaretlemek için bunları kullanın.
pi.registerCommand(ad, seçenekler)
Bir komutu kaydedin.
Birden fazla uzantı aynı komut adını kaydederse, pi bunların hepsini tutar ve yükleme sırasına göre sayısal çağırma soneklerini atar, örneğin /review:1 ve /review:2.
pi.registerCommand("stats", {
description: "Show session statistics",
handler: async (args, ctx) => {
const count = ctx.sessionManager.getEntries().length;
ctx.ui.notify(`${count} entries`, "info");
}
});İsteğe bağlı: /command... için bağımsız değişkenin otomatik tamamlanmasını ekleyin:
import type { AutocompleteItem } from "@earendil-works/pi-tui";
pi.registerCommand("deploy", {
description: "Deploy to an environment",
getArgumentCompletions: (prefix: string): AutocompleteItem[] | null => {
const envs = ["dev", "staging", "prod"];
const items = envs.map((e) => ({ value: e, label: e }));
const filtered = items.filter((i) => i.value.startsWith(prefix));
return filtered.length > 0 ? filtered : null;
},
handler: async (args, ctx) => {
ctx.ui.notify(`Deploying: ${args}`, "info");
},
});pi.getCommands()
Geçerli oturumda slash commands'yi prompt aracılığıyla çağırmaya uygun hale getirin. Uzatma komutlarını, prompt templates ve beceri komutlarını içerir.
Liste RPC get_commands sıralamasıyla eşleşiyor: önce uzantılar, sonra şablonlar, ardından beceriler.
const commands = pi.getCommands();
const bySource = commands.filter((command) => command.source === "extension");
const userScoped = commands.filter((command) => command.sourceInfo.scope === "user");Her giriş şu şekle sahiptir:
{
name: string; // Invokable command name without the leading slash. May be suffixed like "review:1"
description?: string;
source: "extension" | "prompt" | "skill";
sourceInfo: {
path: string;
source: string;
scope: "user" | "project" | "temporary";
origin: "package" | "top-level";
baseDir?: string;
};
}Kurallı kaynak alanı olarak sourceInfo kullanın. Komut adlarından veya özel yol ayrıştırmasından sahiplik sonucunu çıkarmayın.
Yerleşik etkileşimli komutlar (/model ve /settings gibi) buraya dahil edilmemiştir. Yalnızca etkileşimli olarak ele alınırlar
modundadır ve prompt aracılığıyla gönderilirse yürütülmez.
pi.registerMessageRenderer(customType, oluşturucu)
customType'nizle özel mesajlar için özel bir TUI oluşturucu kaydedin. Özel mesajlar pi.sendMessage() ile oluşturulur ve LLM bağlamına katılır. Bakınız Custom UI.
pi.registerMarkdownTransformer(transformer)
Normal kullanıcı metninde, yardımcı metinde ve düşünme bloklarında Markdown için bir dönüştürücü kaydedin. Transformatörler uzatma yükü sırasına göre çalışır ve her transformatör, önceki transformatörün döndürdüğü Markdown'yi alır. Zincir tamamlandıktan sonra Pi dönüştürülen içeriği yerleşik oluşturucuyla işler.
Transformatör Markdown dizesini ve aşağıdakileri içeren bir bağlamı alır:
messageType—"user","assistant"veya"assistant-thinking"isStreaming—truekısmi asistan güncellemeleri için;falsekullanıcı, sonlandırılan asistan ve geri yüklenen mesajlar içinavailableWidth— dönüştürülmüş Markdown içeriği için tam terminal sütunları mevcuttur
Dönüştürülen Markdown'yi döndür:
pi.registerMarkdownTransformer((markdown, { messageType, isStreaming }) => {
if (isStreaming || messageType === "assistant-thinking") return markdown;
return markdown.replaceAll("-->", "→");
});Bir transformatör atarsa Pi o ana kadar üretilen Markdown'yi korur ve bir sonraki transformatörle devam eder. Kanca yalnızca görüntülenir: orijinal mesaj, oturum ve model bağlamında değişmeden kalır. Yeni kullanıcı mesajları, asistan akış güncellemeleri, geri yüklenen oturum mesajları ve terminal genişliği değişiklikleri için çalışır, dolayısıyla transformatörlerin senkronize ve ucuz kalması gerekir.
pi.registerEntryRenderer(customType, oluşturucu)
customType'ınızla özel girişler için özel bir TUI oluşturucu kaydedin. Özel girişler pi.appendEntry() ile oluşturulur ve LLM bağlamına katılmaz.
import { Box, Text } from "@earendil-works/pi-tui";
pi.registerEntryRenderer("status-card", (entry, { expanded }, theme) => {
const data = entry.data as { title: string; count: number };
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
box.addChild(new Text(`${theme.bold(data.title)}: ${data.count}`));
if (expanded) {
box.addChild(new Text(theme.fg("dim", JSON.stringify(data, null, 2))));
}
return box;
});
pi.appendEntry("status-card", { title: "Indexed files", count: 17 });pi.registerShortcut(kısayol, seçenekler)
Bir klavye kısayolu kaydedin. Kısayol formatı ve yerleşik tuş atamaları için keybindings.md'ye bakın.
pi.registerShortcut("ctrl+shift+p", {
description: "Toggle plan mode",
handler: async (ctx) => {
ctx.ui.notify("Toggled!");
},
});pi.registerFlag(ad, seçenekler)
Bir CLI bayrağı kaydedin.
pi.registerFlag("plan", {
description: "Start in plan mode",
type: "boolean",
default: false,
});
// Check value
if (pi.getFlag("plan")) {
// Plan mode enabled
}pi.exec(komut, bağımsız değişkenler, seçenekler?)
Bir kabuk komutunu yürütün.
const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
// result.stdout, result.stderr, result.code, result.killedpi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(isimler)
Etkin araçları yönetin. Bu, hem yerleşik araçlar hem de dinamik olarak kayıtlı araçlar için işe yarar. pi.getActiveTools() etkin araç adlarını string[] olarak döndürür; pi.getAllTools() yapılandırılmış tüm araçlar için meta verileri döndürür.
const active = pi.getActiveTools(); // ["read", "bash", ...]
const all = pi.getAllTools();
// all = [{
// name: "read",
// description: "Read file contents...",
// parameters: ...,
// promptGuidelines: ["Use read to examine files instead of cat or sed."],
// sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
// }, ...]
const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
pi.setActiveTools([...new Set([...active, "my_custom_tool"])]); // Keep current tools and enable my_custom_tool
pi.setActiveTools(["read", "bash"]); // Switch to read-onlypi.getAllTools(), name, description, parameters, promptGuidelines ve sourceInfo'yi döndürür.
Tipik sourceInfo.source değerleri:
builtinyerleşik araçlar içinsdkcreateAgentSession({ customTools })üzerinden geçirilen takımlar için- Uzantılar tarafından kaydedilen araçlar için uzantı kaynağı meta verileri
pi.setModel(model)
Geçerli modeli ayarlayın. Model için API key mevcut değilse false değerini döndürür. Özel modelleri yapılandırmak için models.md'e bakın.
const model = ctx.modelRegistry.find("anthropic", "claude-sonnet-4-5");
if (model) {
const success = await pi.setModel(model);
if (!success) {
ctx.ui.notify("No API key for this model", "error");
}
}pi.getThinkingLevel() / pi.setThinkingLevel(seviye)
Düşünme seviyesini alın veya ayarlayın. Düzey, model yeteneklerine bağlıdır (akıl yürütmeyen modeller her zaman "kapalı"yı kullanır). Değişiklikler thinking_level_select yayar.
const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
pi.setThinkingLevel("high");pi.events
Uzantılar arasındaki iletişim için paylaşılan olay veri yolu:
pi.events.on("my:event", (data) => { ... });
pi.events.emit("my:event", { ... });pi.registerProvider(ad, yapılandırma)
Bir model sağlayıcıyı dinamik olarak kaydedin veya geçersiz kılın. Proxy'ler, özel uç noktalar veya ekip çapında model yapılandırmaları için kullanışlıdır.
Uzatma fabrikası işlevi sırasında yapılan çağrılar kuyruğa alınır ve çalıştırıcı başlatıldıktan sonra uygulanır. Bundan sonra yapılan çağrılar (örneğin, kullanıcı kurulum akışını izleyen bir komut işleyicisinden) /reload gerektirmeden hemen etkili olur.
Dinamik sağlayıcılar refreshModels uygulayabilir. Pi model yenileme sırasında bunu çağırır, döndürülen listeyi sağlayıcı aracılığıyla eşzamanlı olarak yayınlar ve kanonik kimlik bilgisi/depolanan katalog/ağ/sinyal bağlamını iletir. Uzantı, katalog meta verilerinin üretim kontrollü context.publish({ persist: entry }) aracılığıyla sürdürülüp sürdürülmeyeceğine karar verir; llama.cpp gibi canlı sunucular, modelleri ısrar etmeden döndürebilir.
context.signal her zaman somut bir sinyaldir ve sağlayıcı geri aramalarının bunu G/Ç engellemeye iletmesi gerekir. Genel ModelRuntime.refresh() ve ModelRegistry.refresh() çağrıları isteğe bağlı bir sinyali kabul eder ve atlandığında sınırsızdır; Uzatmalar ve başvurular kendi son tarihlerini seçer. İptal, sağlayıcı sinyali görmezden gelse bile arayanın beklemesini durdurur, ancak temeldeki işi durdurmak için yine de işbirliği gereklidir.
Yerel sağlayıcı kimlik doğrulaması, filtreleme, yenileme veya akış davranışına ihtiyaç duyan Extensions, @earendil-works/pi-ai'den tam bir Provider kaydedebilir. Sağlayıcı kompozisyon tabanı haline gelir ve models.json geçersiz kılmalar hâlâ bunun üzerinde geçerlidir.
import { createProvider, openAICompletionsApi } from "@earendil-works/pi-ai";
const provider = createProvider({
id: "local-server",
name: "Local Server",
baseUrl: "http://localhost:8080/v1",
auth: {
apiKey: {
name: "Local server setup",
async login(interaction) {
return {
type: "api_key",
key: await interaction.prompt({ type: "secret", message: "API key" }),
};
},
async resolve({ credential }) {
return credential?.key
? { auth: { apiKey: credential.key }, source: "stored API key" }
: undefined;
},
},
},
models: [],
api: openAICompletionsApi(),
});
pi.registerProvider(provider);
// Register a new provider with custom models
pi.registerProvider("my-proxy", {
name: "My Proxy",
baseUrl: "https://proxy.example.com",
apiKey: "$PROXY_API_KEY", // env var reference
api: "anthropic-messages",
models: [
{
id: "claude-sonnet-4-20250514",
name: "Claude 4 Sonnet (proxy)",
reasoning: false,
input: ["text", "image"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200000,
maxTokens: 16384
}
]
});
// Register a live llama.cpp catalog without persisting discovered models
pi.registerProvider("llama.cpp", {
baseUrl: "http://localhost:8080/v1",
apiKey: "local",
api: "openai-completions",
async refreshModels({ signal }) {
const response = await fetch("http://localhost:8080/v1/models", { signal });
const { data } = await response.json();
return data.map(({ id }) => ({
id,
name: id,
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 16384
}));
}
});
// Override baseUrl for an existing provider (keeps all models)
pi.registerProvider("anthropic", {
baseUrl: "https://proxy.example.com"
});
// Register provider with OAuth support for /login
pi.registerProvider("corporate-ai", {
baseUrl: "https://ai.corp.com",
api: "openai-responses",
models: [...],
oauth: {
name: "Corporate AI (SSO)",
async login(callbacks) {
// Custom OAuth flow
callbacks.onAuth({ url: "https://sso.corp.com/..." });
const code = await callbacks.onPrompt({ message: "Enter code:" });
return { refresh: code, access: code, expires: Date.now() + 3600000 };
},
async refreshToken(credentials, signal) {
signal.throwIfAborted();
// Refresh logic
return credentials;
},
getApiKey(credentials) {
return credentials.access;
}
}
});Nesne formu, yerel auth, getModels, refreshModels, filterModels, stream ve streamSimple davranışı da dahil olmak üzere tam bir pi-ai Provider'yi kabul eder.
Eski yapılandırma seçenekleri:
name- Sağlayıcının kullanıcı arayüzünde/logingibi görünen adı.baseUrl- API uç nokta URL'si. Modelleri tanımlarken gereklidir.apiKey- API key değişmez, ortam enterpolasyonu ($ENV_VARveya${ENV_VAR}) veya baştaki!command. Modelleri tanımlarken gereklidir (oauthsağlanmadığı sürece).$, ``apiKey- API key değişmez, ortam enterpolasyonu ($ENV_VARveya${ENV_VAR}) veya baştaki!command. Modelleri tanımlarken gereklidir (oauthsağlanmadığı sürece).$, 'den kaçar ve$!, komut yürütmeyi tetiklemeden değişmez bir!`'den kaçar.api- API yazın:"anthropic-messages","openai-completions","openai-responses", vb.headers- İsteklere eklenecek özel başlıklar.authHeader- Doğruysa, otomatik olarakAuthorization: Bearerbaşlığını ekler.models- Model tanımları dizisi. Sağlanırsa, bu sağlayıcı için mevcut tüm modellerin yerine geçer. Model tanımları, söz konusu model için sağlayıcı uç noktasını geçersiz kılmak üzerebaseUrlayarını yapabilir.refreshModels- Eşzamansız dinamik keşif geri araması. İade edilen modelleri, genişletme tarafından sağlanan modellerin yerini alır.context.storedkalıcı sağlayıcı anlık görüntüsünü içerir; nesil kontrollücontext.publish({ persist: entry })'yi yalnızca güncellenmiş katalog verilerinin devam etmesi gerektiğinde kullanın. Bu anlık görüntüyü silmek içinpersist: nulltuşlarını kullanın./logindesteği içinoauth- OAuth sağlayıcı yapılandırması. Sağlandığında, sağlayıcı oturum açma menüsünde görünür.streamSimple- Standart olmayan API'ler için özel akış uygulaması.
Gelişmiş konular için custom-provider.md'e bakın: özel akış API'ler, OAuth ayrıntıları, model tanımı referansı.
pi.unregisterProvider(isim)
Daha önce kayıtlı bir sağlayıcıyı ve modellerini kaldırın. Sağlayıcı tarafından geçersiz kılınan yerleşik modeller geri yüklenir. Sağlayıcı kayıtlı değilse hiçbir etkisi yoktur.
registerProvider gibi, bu da ilk yükleme aşamasından sonra çağrıldığında hemen etkili olur, dolayısıyla /reload gerekli değildir.
pi.registerCommand("my-setup-teardown", {
description: "Remove the custom proxy provider",
handler: async (_args, _ctx) => {
pi.unregisterProvider("my-proxy");
},
});Devlet Yönetimi
Extensions durumla birlikte, uygun dallanma desteği için bunu araç sonucu details'da saklamalıdır:
export default function (pi: ExtensionAPI) {
let items: string[] = [];
// Reconstruct state from session
pi.on("session_start", async (_event, ctx) => {
items = [];
for (const entry of ctx.sessionManager.getBranch()) {
if (entry.type === "message" && entry.message.role === "toolResult") {
if (entry.message.toolName === "my_tool") {
items = entry.message.details?.items ?? [];
}
}
}
});
pi.registerTool({
name: "my_tool",
// ...
async execute(toolCallId, params, signal, onUpdate, ctx) {
items.push("new item");
return {
content: [{ type: "text", text: "Added" }],
details: { items: [...items] }, // Store for reconstruction
};
},
});
}Özel Araçlar
LLM'nin pi.registerTool() aracılığıyla arayabileceği araçları kaydedin. Araçlar sistem isteminde görünür ve özel işleme sahip olabilir.
Varsayılan sistem istemindeki Available tools bölümünde kısa bir tek satırlık giriş için promptSnippet tuşunu kullanın. Atlanırsa özel araçlar bu bölümün dışında bırakılır.
Varsayılan sistem istemi Guidelines bölümüne araca özel madde işaretleri eklemek için promptGuidelines tuşunu kullanın. Bu madde işaretleri yalnızca araç etkinken dahil edilir (örneğin, pi.setActiveTools([...])'den sonra).
Önemli: promptGuidelines madde işaretleri, araç adı öneki veya gruplaması olmaksızın Guidelines bölümüne düz olarak eklenir. Her kılavuz, atıfta bulunduğu araca bir isim vermelidir; "Bu aracı şu durumlarda kullan..." kaçının çünkü Yüksek Lisans "bu"nun hangi araç anlamına geldiğini söyleyemez. Bunun yerine "My_tool'u şu durumlarda kullan..." yazın.
Not: Bazı modeller aptaldır ve takım yolu argümanlarında @ önekini içerir. Yerleşik araçlar, yolları çözümlemeden önce baştaki @ karakterini çıkarır. Özel aracınız bir yolu kabul ediyorsa baştaki @ karakterini de normalleştirin.
Özel aracınız dosyaları değiştiriyorsa, yerleşik edit ve write ile aynı dosya başına kuyruğa katılması için withFileMutationQueue() kullanın. Bu önemlidir çünkü araç çağrıları varsayılan olarak paralel olarak çalışır. Sıra olmadan, iki araç aynı eski dosya içeriğini okuyabilir, farklı güncellemeleri hesaplayabilir ve ardından hangisi en son yazılırsa diğerinin üzerine yazılabilir.
Örnek başarısızlık durumu: özel aracınız foo.ts'yi düzenlerken yerleşik edit aynı asistan turunda foo.ts'yi de değiştirir. Aracınız kuyruğa katılmıyorsa, her ikisi de orijinal foo.ts'yi okuyabilir, ayrı değişiklikler uygulayabilir ve bu değişikliklerden biri kaybolur.
Ham kullanıcı bağımsız değişkenini değil, gerçek hedef dosya yolunu withFileMutationQueue()'ye iletin. Bunu önce ctx.cwd'ye veya aracınızın çalışma dizinine göre mutlak bir yola çözümleyin. Mevcut dosyalar için yardımcı, realpath() aracılığıyla kanonikleştirir, böylece aynı dosya için sembolik bağlantı takma adları bir kuyruğu paylaşır. Yeni dosyalar için çözümlenen mutlak yola geri döner çünkü henüz realpath() için hiçbir şey yoktur.
Tüm mutasyon penceresini o hedef yol üzerinde sıraya alın. Bu, yalnızca son yazmayı değil, okuma-değiştirme-yazma mantığını da içerir.
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const absolutePath = resolve(ctx.cwd, params.path);
return withFileMutationQueue(absolutePath, async () => {
await mkdir(dirname(absolutePath), { recursive: true });
const current = await readFile(absolutePath, "utf8");
const next = current.replace(params.oldText, params.newText);
await writeFile(absolutePath, next, "utf8");
return {
content: [{ type: "text", text: `Updated ${params.path}` }],
details: {},
};
});
}Araç Tanımı
import { Type } from "typebox";
import { StringEnum } from "@earendil-works/pi-ai";
import { Text } from "@earendil-works/pi-tui";
pi.registerTool({
name: "my_tool",
label: "My Tool",
description: "What this tool does (shown to LLM)",
promptSnippet: "List or add items in the project todo list",
promptGuidelines: [
"Use my_tool for todo planning instead of direct file edits when the user asks for a task list."
],
parameters: Type.Object({
action: StringEnum(["list", "add"] as const), // Use StringEnum for Google compatibility
text: Type.Optional(Type.String()),
}),
prepareArguments(args) {
if (!args || typeof args !== "object") return args;
const input = args as { action?: string; oldAction?: string };
if (typeof input.oldAction === "string" && input.action === undefined) {
return { ...input, action: input.oldAction };
}
return args;
},
async execute(toolCallId, params, signal, onUpdate, ctx) {
// Check for cancellation
if (signal?.aborted) {
return { content: [{ type: "text", text: "Cancelled" }] };
}
// Stream progress updates
onUpdate?.({
content: [{ type: "text", text: "Working..." }],
details: { progress: 50 },
});
// Run commands via pi.exec (captured from extension closure)
const result = await pi.exec("some-command", [], { signal });
// Return result
return {
content: [{ type: "text", text: "Done" }], // Sent to LLM
details: { data: result }, // For rendering & state
// usage: nestedModelResponse.usage, // Optional nested LLM usage
// Optional: stop after this tool batch when every finalized tool result
// in the batch also returns terminate: true.
terminate: true,
};
},
// Optional: Custom rendering
renderCall(args, theme, context) { ... },
renderResult(result, options, theme, context) { ... },
});Kullanım hesaplaması: Bir araç iç içe geçmiş LLM çağrıları yapıyorsa, bunların birleştirilmiş Usage değerini usage olarak döndürün. Pi bunu araç sonucunda sürdürür ve altbilgiye, /session ve RPC oturum toplamlarına ekler. tool_result işleyiciler bu değeri inceleyebilir veya değiştirebilir.
Sinyalleme hataları: Bir aracın yürütülmesini başarısız olarak işaretlemek için (sonuçta isError: true ayarlar ve bunu LLM'ye bildirir), execute'den bir hata atın. Bir değerin döndürülmesi, dönüş nesnesine hangi özellikleri dahil ettiğinize bakılmaksızın hiçbir zaman hata bayrağını ayarlamaz.
Erken sonlandırma: Geçerli araç grubundan sonra otomatik takip LLM çağrısının atlanması gerektiğini belirtmek için execute()'den terminate: true'ye dönün. Bu yalnızca o gruptaki her sonlandırılmış araç sonucu sona erdiğinde etkili olur. Aracının son yapılandırılmış çıktı aracı çağrısıyla sona erdiği minimal bir örnek için examples/extensions/structured-output.ts'e bakın.
// Correct: throw to signal an error
async execute(toolCallId, params) {
if (!isValid(params.input)) {
throw new Error(`Invalid input: ${params.input}`);
}
return { content: [{ type: "text", text: "OK" }], details: {} };
}Önemli: Dize numaralandırmaları için @earendil-works/pi-ai'den StringEnum'ı kullanın. Type.Union/Type.Literal Google'ın API'si ile çalışmaz.
Argüman hazırlığı: prepareArguments(args) isteğe bağlıdır. Tanımlanmışsa şema doğrulamasından önce ve execute()'den önce çalışır. Pi, saklanan araç çağrısı argümanları artık geçerli şemayla eşleşmeyen eski bir oturumu sürdürdüğünde, kabul edilen eski bir giriş şeklini taklit etmek için bunu kullanın. parameters ile doğrulanmasını istediğiniz nesneyi döndürün. Genel şemayı sıkı tutun. Sırf eski sürdürülen oturumların çalışmaya devam etmesi için parameters'e kullanım dışı uyumluluk alanları eklemeyin.
Örnek: eski bir oturum, üst düzey oldText ve newText içeren bir edit araç çağrısı içerebilir, mevcut şema ise yalnızca edits: [{ oldText, newText }]'yi kabul eder.
pi.registerTool({
name: "edit",
label: "Edit",
description: "Edit a single file using exact text replacement",
parameters: Type.Object({
path: Type.String(),
edits: Type.Array(
Type.Object({
oldText: Type.String(),
newText: Type.String(),
}),
),
}),
prepareArguments(args) {
if (!args || typeof args !== "object") return args;
const input = args as {
path?: string;
edits?: Array<{ oldText: string; newText: string }>;
oldText?: unknown;
newText?: unknown;
};
if (typeof input.oldText !== "string" || typeof input.newText !== "string") {
return args;
}
return {
...input,
edits: [...(input.edits ?? []), { oldText: input.oldText, newText: input.newText }],
};
},
async execute(toolCallId, params, signal, onUpdate, ctx) {
// params now matches the current schema
return {
content: [{ type: "text", text: `Applying ${params.edits.length} edit block(s)` }],
details: {},
};
},
});Yerleşik Araçları Geçersiz Kılma
Extensions aynı ada sahip bir aracı kaydederek yerleşik araçları (read, bash, edit, write, grep, find, ls) geçersiz kılabilir. Etkileşimli mod bu durumda bir uyarı görüntüler.
# Extension's read tool replaces built-in read
pi -e ./tool-override.tsAlternatif olarak, uzatma araçlarını etkin durumda tutarken herhangi bir yerleşik araç olmadan başlamak için --no-builtin-tools tuşunu kullanın:
# No built-in tools, only extension tools
pi --no-builtin-tools -e ./my-extension.tsGünlüğe kaydetme ve erişim kontrolü ile read'yi geçersiz kılan tam bir örnek için examples/extensions/tool-override.ts'ye bakın.
Oluşturma: Yerleşik oluşturucunun devralınması yuva başına çözümlenir. Yürütme geçersiz kılma ve oluşturma geçersiz kılma bağımsızdır. Geçersiz kılma işleminizde renderCall atlanırsa yerleşik renderCall kullanılır. Geçersiz kılma işleminizde renderResult atlanırsa yerleşik renderResult kullanılır. Geçersiz kılma işleminizde her ikisi de atlanırsa yerleşik oluşturucu otomatik olarak kullanılır (sözdizimi vurgulama, farklar vb.). Bu, kullanıcı arayüzünü yeniden uygulamaya gerek kalmadan günlüğe kaydetme veya erişim kontrolü için yerleşik araçları sarmanıza olanak tanır.
Bilgi meta verileri: promptSnippet ve promptGuidelines yerleşik araçtan devralınmaz. Geçersiz kılma işleminizin bu istem talimatlarını tutması gerekiyorsa bunları geçersiz kılmada açıkça tanımlayın.
Uygulamanız, details türü de dahil olmak üzere sonuç şekliyle tam olarak eşleşmelidir. Kullanıcı arayüzü ve oturum mantığı, oluşturma ve durum takibi için bu şekillere bağlıdır.
Yerleşik araç uygulamaları:
- read.ts -
ReadToolDetails - bash.ts -
BashToolDetails - edit.ts
- write.ts
- grep.ts -
GrepToolDetails - find.ts -
FindToolDetails - ls.ts -
LsToolDetails
Uzaktan Yürütme
Yerleşik araçlar, uzak sistemlere (SSH, kapsayıcılar vb.) yetki vermek için takılabilir işlemleri destekler:
import { createReadTool, createBashTool, type ReadOperations } from "@earendil-works/pi-coding-agent";
// Create tool with custom operations
const remoteRead = createReadTool(cwd, {
operations: {
readFile: (path) => sshExec(remote, `cat ${path}`),
access: (path) => sshExec(remote, `test -r ${path}`).then(() => {}),
}
});
// Register, checking flag at execution time
pi.registerTool({
...remoteRead,
async execute(id, params, signal, onUpdate, _ctx) {
const ssh = getSshConfig();
if (ssh) {
const tool = createReadTool(cwd, { operations: createRemoteOps(ssh) });
return tool.execute(id, params, signal, onUpdate);
}
return localRead.execute(id, params, signal, onUpdate);
},
});İşlem arayüzleri: ReadOperations, WriteOperations, EditOperations, BashOperations, LsOperations, GrepOperations, FindOperations
user_bash için uzantılar, yerel süreç oluşturmayı, kabuk çözümlemesini ve süreç ağacı sonlandırmayı yeniden uygulamak yerine pi'nin yerel kabuk arka ucunu createLocalBashOperations() aracılığıyla yeniden kullanabilir.
bash aracı aynı zamanda yürütmeden önce komutu, cwd veya env'yi ayarlamak için bir ortaya çıkma kancasını da destekler:
import { createBashTool } from "@earendil-works/pi-coding-agent";
const bashTool = createBashTool(cwd, {
spawnHook: ({ command, cwd, env }) => ({
command: `source ~/.profile\n${command}`,
cwd: `/mnt/sandbox${cwd}`,
env: { ...env, CI: "1" },
}),
});createBashTool() mevcut oturumu PI_SESSION_ID, PI_SESSION_FILE, PI_PROVIDER, PI_MODEL ve PI_REASONING_LEVEL aracılığıyla komutlara maruz bırakır. Enjeksiyon spawnHook'dan önce gerçekleşir, dolayısıyla kancalar bu değerleri env'de alır ve yukarıdaki gibi mevcut ortama yayıldıklarında bunları korurlar. Bunları devre dışı bırakmak için exposeSessionEnvironment: false öğesini ayarlayın:
const bashTool = createBashTool(cwd, {
exposeSessionEnvironment: false,
});Değişken semantiği için Bash tool session environment'ye bakın. --ssh bayrağıyla tam bir SSH örneği için examples/extensions/ssh.ts'ye bakın.
Çıkış Kesilmesi
LLM bağlamının aşırı yüklenmesini önlemek için araçların çıktılarını kesmesi GEREKİR. Büyük çıktılar şunlara neden olabilir:
- Bağlam taşması hataları (istemin çok uzun olması)
- Sıkıştırma hataları
- Düşük model performansı
Dahili sınır 50 KB (~10 bin jeton) ve 2000 satır'dır (hangisi önce gerçekleşirse). Dışa aktarılan kesme yardımcı programlarını kullanın:
import {
truncateHead, // Keep first N lines/bytes (good for file reads, search results)
truncateTail, // Keep last N lines/bytes (good for logs, command output)
truncateLine, // Truncate a single line to maxBytes with ellipsis
formatSize, // Human-readable size (e.g., "50KB", "1.5MB")
DEFAULT_MAX_BYTES, // 50KB
DEFAULT_MAX_LINES, // 2000
} from "@earendil-works/pi-coding-agent";
async execute(toolCallId, params, signal, onUpdate, ctx) {
const output = await runCommand();
// Apply truncation
const truncation = truncateHead(output, {
maxLines: DEFAULT_MAX_LINES,
maxBytes: DEFAULT_MAX_BYTES,
});
let result = truncation.content;
if (truncation.truncated) {
// Write full output to temp file
const tempFile = writeTempFile(output);
// Inform the LLM where to find complete output
result += `\n\n[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines`;
result += ` (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}).`;
result += ` Full output saved to: ${tempFile}]`;
}
return { content: [{ type: "text", text: result }] };
}Önemli noktalar:
- Başlangıcın önemli olduğu içerik için
truncateHeadkullanın (arama sonuçları, dosya okumaları) - Sonunun önemli olduğu içerik için
truncateTailkullanın (günlükler, komut çıkışı) - Çıktı kesildiğinde ve tam sürümün nerede bulunacağı konusunda daima LLM'yi bilgilendirin
- Aracınızın açıklamasında kesme sınırlarını belgeleyin
rg (ripgrep) öğesinin uygun kesmeyle sarmalanmasının tam bir örneği için examples/extensions/truncated-tool.ts öğesine bakın.
Çoklu Araçlar
Bir uzantı birden fazla aracı paylaşılan duruma kaydedebilir:
export default function (pi: ExtensionAPI) {
let connection = null;
pi.registerTool({ name: "db_connect", ... });
pi.registerTool({ name: "db_query", ... });
pi.registerTool({ name: "db_close", ... });
pi.on("session_shutdown", async () => {
connection?.close();
});
}Özel İşleme
Araçlar, özel TUI ekranı için renderCall ve renderResult sağlayabilir. Bileşenin tamamı için tui.md'ye bakın API ve araç satırlarının nasıl oluşturulduğu için tool-execution.ts'ye bakın.
Varsayılan olarak araç çıktısı, dolguyu ve arka planı işleyen bir Box içine sarılır. Tanımlanmış bir renderCall veya renderResult, Component döndürmelidir. Bir yuva oluşturucu tanımlanmamışsa tool-execution.ts o yuva için geri dönüş oluşturmayı kullanır.
Varsayılan Box kullanmak yerine aracın ne zaman kendi kabuğunu oluşturması gerektiğini renderShell: "self" olarak ayarlayın. Bu, çerçeveleme veya arka plan davranışı üzerinde tam kontrole ihtiyaç duyan araçlar için kullanışlıdır; örneğin, araç yerleştikten sonra görsel olarak sabit kalması gereken büyük önizlemeler.
pi.registerTool({
name: "my_tool",
label: "My Tool",
description: "Custom shell example",
parameters: Type.Object({}),
renderShell: "self",
async execute() {
return { content: [{ type: "text", text: "ok" }], details: undefined };
},
renderCall(args, theme, context) {
return new Text(theme.fg("accent", "my custom shell"), 0, 0);
},
});renderCall ve renderResult'nin her biri aşağıdaki özelliklere sahip bir context nesnesi alır:
args- mevcut araç çağrısı argümanlarıstate-renderCallverenderResultgenelinde paylaşılan satır yerel durumulastComponent- varsa o yuva için önceden döndürülen bileşeninvalidate()- bu araç satırının yeniden oluşturulmasını talep edintoolCallId,cwd,executionStarted,argsComplete,isPartial,expanded,showImages,isError
Yuvalar arası paylaşım durumu için context.state kullanın. Aynı bileşeni işlemeler arasında yeniden kullanmak ve değiştirmek istediğinizde, döndürülen bileşen örneğinde yuva yerel önbelleklerini tutun.
renderÇağrı
Araç çağrısını veya başlığını işler:
import { Text } from "@earendil-works/pi-tui";
renderCall(args, theme, context) {
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
let content = theme.fg("toolTitle", theme.bold("my_tool "));
content += theme.fg("muted", args.action);
if (args.text) {
content += " " + theme.fg("dim", `"${args.text}"`);
}
text.setText(content);
return text;
}renderSonucu
Araç sonucunu veya çıktısını işler:
renderResult(result, { expanded, isPartial }, theme, context) {
if (isPartial) {
return new Text(theme.fg("warning", "Processing..."), 0, 0);
}
if (result.details?.error) {
return new Text(theme.fg("error", `Error: ${result.details.error}`), 0, 0);
}
let text = theme.fg("success", "✓ Done");
if (expanded && result.details?.items) {
for (const item of result.details.items) {
text += "\n " + theme.fg("dim", item);
}
}
return new Text(text, 0, 0);
}Bir yuvanın kasıtlı olarak görünür içeriği yoksa, boş bir Container gibi boş bir Component döndürün.
Tuş Bağlama İpuçları
Etkin tuş bağlama yapılandırmasına uygun tuş bağlama ipuçlarını görüntülemek için keyHint() tuşunu kullanın:
import { keyHint } from "@earendil-works/pi-coding-agent";
renderResult(result, { expanded }, theme, context) {
let text = theme.fg("success", "✓ Done");
if (!expanded) {
text += ` (${keyHint("app.tools.expand", "to expand")})`;
}
return new Text(text, 0, 0);
}Mevcut işlevler:
keyHint(keybinding, description)-"app.tools.expand"veya"tui.select.confirm"gibi yapılandırılmış bir tuş bağlama kimliğini biçimlendirirkeyText(keybinding)- Bir tuş bağlama kimliği için ham yapılandırılmış anahtar metnini döndürürrawKeyHint(key, description)- Ham anahtar dizesini biçimlendirin
Ad alanlı tuş bağlama kimliklerini kullanın:
- Kodlama aracısı kimlikleri
app.*ad alanını kullanır; örneğinapp.tools.expand,app.editor.external,app.session.rename - Paylaşılan TUI kimlikleri
tui.*ad alanını kullanır; örneğintui.select.confirm,tui.select.cancel,tui.input.tab
Tuş bağlama kimlikleri ve varsayılanlarının kapsamlı listesi için bkz. keybindings.md. keybindings.json aynı ad alanı kimliklerini kullanır.
Özel düzenleyiciler ve ctx.ui.custom() bileşenleri, eklenen argüman olarak keybindings: KeybindingsManager'yi alır. getKeybindings() veya setKeybindings()'ı çağırmak yerine, enjekte edilen yöneticiyi doğrudan kullanmaları gerekir.
En İyi Uygulamalar
(0, 0)dolgusu ileTextkullanın. Varsayılan Kutu dolguyu işler.- Çok satırlı içerik için
\nkullanın. - Akış ilerlemesi için
isPartialtutamacını kullanın. - Talep üzerine ayrıntılar için
expandeddesteği. - Varsayılan görünümü kompakt tutun.
- Bağımsız değişkenleri
context.state'ye kopyalamak yerinerenderResult'dekicontext.args'yi okuyun. context.stateişaretini yalnızca çağrı ve sonuç alanları arasında paylaşılması gereken veriler için kullanın.- Aynı bileşen örneği yerinde güncellenebildiğinde
context.lastComponent'yi yeniden kullanın. renderShell: "self"tuşunu yalnızca varsayılan kutulu kabuk engel teşkil ettiğinde kullanın. Kendi kendine kabuk modunda araç kendi çerçevelemesinden, dolgusundan ve arka planından sorumludur.
Geri çekilmek
Bir slot oluşturucu tanımlanmamışsa veya şunu atarsa:
renderCall: Araç adını gösterirrenderResult:content'den ham metni gösterir
Dinamik Takım Yükleme
Extensions yalnızca küçük bir başlangıç ayarını aktif tutarken birçok aracı kaydedebilir. Bir araç daha sonra yürütme sırasında pi.setActiveTools() ile daha fazla araç ekleyebilir. Pi tamamen eklemeli değişiklikleri algılar, yeni mevcut takım adlarını o takım sonucuna kaydeder ve güncellenmiş aktif seti bir sonraki model talebinden önce uygular.
Bu her modelde işe yarar. Models yerel ertelenmiş yükleme desteği ile kararlı bilgi istemi önekini koruyun ve yeni tanımları araç sonuç konumuna yükleyin. Diğer modeller aşağıda açıklanan geri dönüşü kullanır.
Yaşam döngüsü:
- Her aracı
pi.registerTool()ile kaydedin, böylecepi.getAllTools()'de görünecektir. search_toolsgibi yükleyici araçlarını etkin tutun ve aranabilir araçları devre dışı bırakın.- Yükleyicinin yürütülmesi sırasında
pi.setActiveTools([...currentTools,...matchingTools])'ı arayın. Değişiklik ek nitelikte olmalıdır: aynı çağrıda halihazırda etkin olan araçları kaldırmayın. - Pi yükleyicinin takım sonucuna hangi araçların eklendiğini kaydeder.
- Bir sonraki model yanıtından önce, Pi desteklendiğinde yerel ertelenmiş yüklemeyi veya aksi takdirde normal aktif araç listesini kullanarak eklenen tanımları ortaya çıkarır.
Sağlayıcıya özel araç referanslarını döndürmenize veya yükleyiciyi özel bir arama aracı olarak işaretlemenize gerek yoktur. Aktif takım değişimi sinyaldir. pi.setActiveTools()'ye aktarılan adların zaten kayıtlı olması gerekir; bilinmeyen isimler dikkate alınmaz.
Models yerel ertelenmiş yüklemeyle
- Antropik
- Models: Sonnet, Opus, Fable sürüm 4.5 veya daha yenisi (Haiku olmadan)
- Yerel gösterim: Ertelenmiş tanımlarda
defer_loadingkullanılır; yükleme noktasıtool_referenceiçeriğini kullanır.
- Açık AI
- Models:
gpt-5.4ve daha yeni aile - Yerel gösterim: Pi, yükleme noktasında tamamlanmış istemci
tool_search_callvetool_search_outputöğelerini ekler.
- Models:
Doğrulanmış bir özel model veya proxy için yerel işleme, anthropic-messages için compat.supportsToolReferences: true veya openai-responses ve openai-codex-responses için compat.supportsToolSearch: true ile etkinleştirilebilir. Uç nokta ve model ilgili yerel protokolü kabul etmediği sürece bunları devre dışı bırakın.
Geri çekilme davranışı
Diğer tüm modeller ve sağlayıcılar için dinamik aktivasyon hala çalışıyor: Pi bir sonraki istekte normal olarak mevcut aktif takım listesinin tamamını gönderir. Model, yeni etkinleştirilen araçları çağırabilir ancak bunların tanımlarını eklemek, sağlayıcının önbelleğe alınmış bilgi istemi önekini geçersiz kılabilir.
Pi aynı zamanda bu güvenli geri dönüşü, bir araç grubunu diğeriyle değiştirmek gibi, aktif küme tamamen eklemeli olmadığında da kullanır. Bu nedenle takım çıkarma işlemleri işe yarar, ancak ertelenmiş yüklemeyi kullanmazlar.
En iyi önbellek davranışı için, yükleyici aracını tüm oturum boyunca etkin tutun ve etkin kümeyi değiştirmek yerine araçlar ekleyin. Ayrıca bir aracı promptSnippet veya promptGuidelines ile etkinleştirmenin sistem istemini yeniden oluşturduğunu unutmayın; Bu sistem istemi değişikliği, sağlayıcı ertelenmiş şemaları desteklediğinde bile öneki geçersiz kılabilir. Geç yüklenen araçlar genellikle description araçlarına güvenmeli ve yalnızca etkin bilgi istemi meta verilerini çıkarmalıdır.
Arama aracı örneği
Aşağıdaki uzantı, aranabilir iki aracı kaydeder, bunları başlangıçtaki etkin kümeden kaldırır ve yükleyici olarak yalnızca search_tools'yi tutar. Örnek basit anahtar kelime eşlemeyi kullanıyor ancak arama uygulaması BM25'i, yerleştirmeleri, uzak kataloğu veya projeye özel yönlendirmeyi kullanabilir.
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
const SEARCHABLE_TOOL_NAMES = new Set(["lookup_weather", "search_issues"]);
export default function (pi: ExtensionAPI) {
pi.registerTool({
name: "lookup_weather",
label: "Lookup Weather",
description: "Look up the current weather for a city",
parameters: Type.Object({ city: Type.String() }),
async execute(_toolCallId, params) {
return {
content: [{ type: "text", text: `Weather for ${params.city}: sunny` }],
details: {},
};
},
});
pi.registerTool({
name: "search_issues",
label: "Search Issues",
description: "Search project issues by keyword",
parameters: Type.Object({ query: Type.String() }),
async execute(_toolCallId, params) {
return {
content: [{ type: "text", text: `No open issues matching ${params.query}` }],
details: {},
};
},
});
pi.registerTool({
name: "search_tools",
label: "Search Tools",
description: "Search for and enable tools relevant to a task",
promptSnippet: "Search for additional tools when the active tools cannot perform the task",
promptGuidelines: [
"Use search_tools when a task requires a capability that is not currently available.",
],
parameters: Type.Object({
query: Type.String({ description: "Capability or task to search for" }),
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })),
}),
async execute(_toolCallId, params) {
const terms = params.query.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
const matches = pi.getAllTools()
.filter((tool) => SEARCHABLE_TOOL_NAMES.has(tool.name))
.map((tool) => ({
tool,
score: terms.reduce(
(score, term) =>
score + (`${tool.name} ${tool.description}`.toLowerCase().includes(term) ? 1 : 0),
0,
),
}))
.filter((match) => match.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, params.limit ?? 3)
.map((match) => match.tool.name);
if (matches.length === 0) {
return {
content: [{ type: "text", text: `No tools found for: ${params.query}` }],
details: { matches: [] },
};
}
const active = pi.getActiveTools();
const added = matches.filter((name) => !active.includes(name));
pi.setActiveTools([...new Set([...active, ...added])]);
return {
content: [{
type: "text",
text: added.length > 0
? `Loaded tools: ${added.join(", ")}`
: `Matching tools already active: ${matches.join(", ")}`,
}],
details: { matches, added },
};
},
});
pi.on("session_start", () => {
// Keep searchable tools registered but initially inactive. Preserve built-ins
// and tools owned by other extensions, and keep the loader itself active.
const initialTools = pi.getActiveTools().filter(
(name) => !SEARCHABLE_TOOL_NAMES.has(name),
);
pi.setActiveTools([...new Set([...initialTools, "search_tools"])]);
});
}search_tools bir eşleşme eklediğinde model, hemen ardından gelen istek üzerine bu tanımı alır. Yerel özellikli bir modelde tanım, başlangıçtaki araç şeması öneki değiştirilmeden arama sonucundan sonra sabitlenir. Diğer modellerde aynı istek üzerine normal takım listesinde görünür.
Özel kullanıcı arayüzü
Extensions kullanıcılarla ctx.ui yöntemleri aracılığıyla etkileşim kurabilir ve mesajların/araçların nasıl oluşturulduğunu özelleştirebilir.
Özel bileşenler için, aşağıdakiler için kopyala-yapıştır kalıplarına sahip tui.md'e bakın:
- Seçim diyalogları (SelectList)
- İptal ile eşzamansız işlemler (BorderedLoader)
- Ayarlar arasında geçiş yapar (AyarlarList)
- Durum göstergeleri (setStatus)
- Akış sırasında çalışma mesajı, görünürlük ve gösterge (
setWorkingMessage,setWorkingVisible,setWorkingIndicator) - Düzenleyicinin üstünde/altında widget'lar (setWidget)
- Yerleşik eğik çizgi/yol tamamlamanın üstüne yerleştirilmiş otomatik tamamlama sağlayıcıları (addAutocompleteProvider)
- Özel altbilgiler (setFooter)
Diyaloglar
// Select from options
const choice = await ctx.ui.select("Pick one:", ["A", "B", "C"]);
// Confirm dialog
const ok = await ctx.ui.confirm("Delete?", "This cannot be undone");
// Text input
const name = await ctx.ui.input("Name:", "placeholder");
// Multi-line editor
const text = await ctx.ui.editor("Edit:", "prefilled text");
// Notification (non-blocking)
ctx.ui.notify("Done!", "info"); // "info" | "warning" | "error"Geri Sayımlı Zamanlanmış Diyaloglar
İletişim kutuları, canlı geri sayım ekranıyla otomatik olarak kapatılan timeout seçeneğini destekler:
// Dialog shows "Title (5s)" → "Title (4s)" → ... → auto-dismisses at 0
const confirmed = await ctx.ui.confirm(
"Timed Confirmation",
"This dialog will auto-cancel in 5 seconds. Confirm?",
{ timeout: 5000 }
);
if (confirmed) {
// User confirmed
} else {
// User cancelled or timed out
}Zaman aşımında döndürülen değerler:
select()undefineddeğerini döndürürconfirm()falsedeğerini döndürürinput()undefineddeğerini döndürür
AbortSignal ile Manuel İşten Çıkarma
Daha fazla kontrol için (örneğin, zaman aşımını kullanıcı iptalinden ayırt etmek için) AbortSignal kullanın:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const confirmed = await ctx.ui.confirm(
"Timed Confirmation",
"This dialog will auto-cancel in 5 seconds. Confirm?",
{ signal: controller.signal }
);
clearTimeout(timeoutId);
if (confirmed) {
// User confirmed
} else if (controller.signal.aborted) {
// Dialog timed out
} else {
// User cancelled (pressed Escape or selected "No")
}Tam örnekler için examples/extensions/timed-confirm.ts'e bakın.
Widget'lar, Durum ve Altbilgi
// Status in footer (persistent until cleared)
ctx.ui.setStatus("my-ext", "Processing...");
ctx.ui.setStatus("my-ext", undefined); // Clear
// Working loader (shown during streaming)
ctx.ui.setWorkingMessage("Thinking deeply...");
ctx.ui.setWorkingMessage(); // Restore default
ctx.ui.setWorkingVisible(false); // Hide the built-in working loader row entirely
ctx.ui.setWorkingVisible(true); // Show the built-in working loader row
// Working indicator (shown during streaming)
ctx.ui.setWorkingIndicator({ frames: [ctx.ui.theme.fg("accent", "●")] }); // Static dot
ctx.ui.setWorkingIndicator({
frames: [
ctx.ui.theme.fg("dim", "·"),
ctx.ui.theme.fg("muted", "•"),
ctx.ui.theme.fg("accent", "●"),
ctx.ui.theme.fg("muted", "•"),
],
intervalMs: 120,
});
ctx.ui.setWorkingIndicator({ frames: [] }); // Hide indicator
ctx.ui.setWorkingIndicator(); // Restore default spinner
// Widget above editor (default)
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"]);
// Widget below editor
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"], { placement: "belowEditor" });
ctx.ui.setWidget("my-widget", (tui, theme) => new Text(theme.fg("accent", "Custom"), 0, 0));
ctx.ui.setWidget("my-widget", undefined); // Clear
// Custom footer (replaces built-in footer entirely)
ctx.ui.setFooter((tui, theme) => ({
render(width) { return [theme.fg("dim", "Custom footer")]; },
invalidate() {},
}));
ctx.ui.setFooter(undefined); // Restore built-in footer
// Terminal title
ctx.ui.setTitle("pi - my-project");
// Editor text
ctx.ui.setEditorText("Prefill text");
const current = ctx.ui.getEditorText();
// Paste into editor (triggers paste handling, including collapse for large content)
ctx.ui.pasteToEditor("pasted content");
// Stack custom autocomplete behavior on top of the built-in provider
ctx.ui.addAutocompleteProvider((current) => ({
triggerCharacters: ["#"],
async getSuggestions(lines, line, col, options) {
const beforeCursor = (lines[line] ?? "").slice(0, col);
const match = beforeCursor.match(/(?:^|[ \t])#([^\s#]*)$/);
if (!match) {
return current.getSuggestions(lines, line, col, options);
}
return {
prefix: `#${match[1] ?? ""}`,
items: [{ value: "#2983", label: "#2983", description: "Extension API for autocomplete" }],
};
},
applyCompletion(lines, line, col, item, prefix) {
return current.applyCompletion(lines, line, col, item, prefix);
},
shouldTriggerFileCompletion(lines, line, col) {
return current.shouldTriggerFileCompletion?.(lines, line, col) ?? true;
},
}));
// Tool output expansion
const wasExpanded = ctx.ui.getToolsExpanded();
ctx.ui.setToolsExpanded(true);
ctx.ui.setToolsExpanded(wasExpanded);
// Custom editor (vim mode, emacs mode, etc.)
ctx.ui.setEditorComponent((tui, theme, keybindings) => new VimEditor(tui, theme, keybindings));
const currentEditor = ctx.ui.getEditorComponent();
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
new WrappedEditor(tui, theme, keybindings, currentEditor?.(tui, theme, keybindings))
);
ctx.ui.setEditorComponent(undefined); // Restore default editor
// Theme management (see themes.md for creating themes)
const themes = ctx.ui.getAllThemes(); // [{ name: "dark", path: "/..." | undefined }, ...]
const lightTheme = ctx.ui.getTheme("light"); // Load without switching
const result = ctx.ui.setTheme("light"); // Switch by name
if (!result.success) {
ctx.ui.notify(`Failed: ${result.error}`, "error");
}
ctx.ui.setTheme(lightTheme!); // Or switch by Theme object
ctx.ui.theme.fg("accent", "styled text"); // Access current themeÖzel çalışma göstergesi çerçeveleri kelimesi kelimesine işlenir. Renkleri istiyorsanız bunları çerçeve dizelerine kendiniz ekleyin, örneğin ctx.ui.theme.fg(...) ile.
Otomatik tamamlama Providers
Özel otomatik tamamlama mantığını yerleşik eğik çizgi komutu ve yol sağlayıcının üstüne yığmak için ctx.ui.addAutocompleteProvider() tuşunu kullanın. Özel otomatik tamamlama mantığını yerleşik eğik çizgi komutu ve yol sağlayıcının üstüne yığmak için ctx.ui.addAutocompleteProvider()tuşunu kullanın. gibi özel doğal tetikleyiciler içintriggerCharacters`'yi ayarlayın.
Tipik desen:
- imleçten önceki metni inceleyin
- Uzantıya özel söz diziminiz eşleştiğinde kendi önerilerinizi döndürün
- aksi takdirde
current.getSuggestions(...)'ye yetki verin - Özel ekleme davranışına ihtiyacınız olmadığı sürece
applyCompletion(...)delegesini verin
pi.on("session_start", (_event, ctx) => {
ctx.ui.addAutocompleteProvider((current) => ({
triggerCharacters: ["#"],
async getSuggestions(lines, cursorLine, cursorCol, options) {
const line = lines[cursorLine] ?? "";
const beforeCursor = line.slice(0, cursorCol);
const match = beforeCursor.match(/(?:^|[ \t])#([^\s#]*)$/);
if (!match) {
return current.getSuggestions(lines, cursorLine, cursorCol, options);
}
return {
prefix: `#${match[1] ?? ""}`,
items: [
{ value: "#2983", label: "#2983", description: "Extension API for registering custom @ autocomplete providers" },
{ value: "#2753", label: "#2753", description: "Reload stale resource settings" },
],
};
},
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
},
shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
},
}));
});gh issue list ile en son açık GitHub sayılarını önceden yükleyen ve hızlı #... tamamlama için bunları yerel olarak filtreleyen eksiksiz bir örnek için github-issue-autocomplete.ts'ye bakın. GitHub CLI (gh) ve GitHub depo kontrolü gerektirir.
Özel Bileşenler
Karmaşık kullanıcı arayüzü için ctx.ui.custom() kullanın. Bu, done() çağrılana kadar düzenleyiciyi geçici olarak bileşeninizle değiştirir:
import { Text, Component } from "@earendil-works/pi-tui";
const result = await ctx.ui.custom<boolean>((tui, theme, keybindings, done) => {
const text = new Text("Press Enter to confirm, Escape to cancel", 1, 1);
text.onKey = (key) => {
if (key === "return") done(true);
if (key === "escape") done(false);
return true;
};
return text;
});
if (result) {
// User pressed Enter
}Geri arama şunları alır:
tui- TUI örneği (ekran boyutları, odak yönetimi için)theme- Stil için güncel temakeybindings- Uygulama tuş bağlama yöneticisi (kısayolları kontrol etmek için)done(value)- Bileşeni kapatmak ve değeri döndürmek için çağrı
API bileşeninin tamamı için tui.md'ye bakın.
Yer Paylaşımı Modu (Deneysel)
Ekranı temizlemeden bileşeni mevcut içeriğin üzerinde kayan bir model olarak oluşturmak için { overlay: true } iletin:
const result = await ctx.ui.custom<string | null>(
(tui, theme, keybindings, done) => new MyOverlayComponent({ onClose: done }),
{ overlay: true }
);Gelişmiş konumlandırma için (sabitlemeler, kenar boşlukları, yüzdeler, duyarlı görünürlük), overlayOptions'yi geçin. Odağı veya görünürlüğü programlı olarak kontrol etmek için onHandle tuşunu kullanın:
const result = await ctx.ui.custom<string | null>(
(tui, theme, keybindings, done) => new MyOverlayComponent({ onClose: done }),
{
overlay: true,
overlayOptions: { anchor: "top-right", width: "50%", margin: 2 },
onHandle: (handle) => {
handle.focus(); // focus this overlay and bring it to the visual front
// handle.unfocus({ target: editorComponent }); // release input to a specific component
// handle.setHidden(true/false); // toggle visibility
// handle.hide(); // permanently remove
}
}
);Odaklanmış görünür bir yer paylaşımı, geçici olarak yer paylaşımsız özel kullanıcı arayüzü kapatıldıktan sonra girişi geri alabilir. Kaplama görünür kalırken kasıtlı olarak başka bir bileşenin girişi tutmasını istiyorsanız handle.unfocus({ target })'ı arayın. { target: null } geçişi, başka bir bileşene odaklanmadan kaplamayı serbest bırakır.
OverlayOptions'nin tamamı için tui.md ve örnekler için OverlayHandle API ve overlay-qa-tests.ts'ye bakın.
Özel Düzenleyici
Ana giriş düzenleyicisini özel bir uygulamayla (vim modu, emacs modu vb.) değiştirin:
import { CustomEditor, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { matchesKey } from "@earendil-works/pi-tui";
class VimEditor extends CustomEditor {
private mode: "normal" | "insert" = "insert";
handleInput(data: string): void {
if (matchesKey(data, "escape") && this.mode === "insert") {
this.mode = "normal";
return;
}
if (this.mode === "normal" && data === "i") {
this.mode = "insert";
return;
}
super.handleInput(data); // App keybindings + text editing
}
}
export default function (pi: ExtensionAPI) {
pi.on("session_start", (_event, ctx) => {
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
new VimEditor(tui, theme, keybindings)
);
});
}Önemli noktalar:
- Uygulama tuş atamalarını almak için
CustomEditor(temelEditordeğil) öğesini genişletin (iptal etmek için kaçış, ctrl+d, model değiştirme) - Kullanmadığınız anahtarlar için
super.handleInput(data)'ı arayın - Fabrika uygulamadan
tui,themevekeybindingsalır - Önceden yapılandırılmış özel düzenleyiciyi kaydırmak için
setEditorComponent()'den öncectx.ui.getEditorComponent()kullanın - Varsayılanı geri yüklemek için
undefinediletin:ctx.ui.setEditorComponent(undefined)
Düzenleyicinin yerini almış olan başka bir uzantıyla kompozisyon oluşturmak için, kendi fabrikanızı ayarlamadan önce önceki fabrikayı yakalayın:
const previous = ctx.ui.getEditorComponent();
ctx.ui.setEditorComponent((tui, theme, keybindings) =>
new MyEditor(tui, theme, keybindings, { base: previous?.(tui, theme, keybindings) })
);Mod göstergeli tam bir örnek için tui.md Desen 7'ye bakın.
Mesaj ve Giriş Oluşturma
customType'ınızla mesajlar için özel bir oluşturucu kaydedin. Yüksek Lisans bağlamına katılması gereken içerik için mesaj oluşturucuları kullanın:
import { Text } from "@earendil-works/pi-tui";
pi.registerMessageRenderer("my-extension", (message, options, theme) => {
const { expanded, outputPad } = options;
let text = theme.fg("accent", `[${message.customType}] `);
text += message.content;
if (expanded && message.details) {
text += "\n" + theme.fg("dim", JSON.stringify(message.details, null, 2));
}
return new Text(text, outputPad, 0);
});Mesajlar pi.sendMessage() aracılığıyla gönderilir:
pi.sendMessage({
customType: "my-extension", // Matches registerMessageRenderer
content: "Status update",
display: true, // Show in TUI
details: { ... }, // Available in renderer
});LLM'ye gönderilmemesi gereken yalnızca TUI içeriği için bunun yerine özel girişler oluşturun:
pi.registerEntryRenderer("my-card", (entry, options, theme) => {
return new Text(theme.fg("accent", JSON.stringify(entry.data)));
});
pi.appendEntry("my-card", { status: "done" });Tema Renkleri
Tüm oluşturma işlevleri bir theme nesnesi alır. Özel temalar ve tam renk paleti oluşturmak için themes.md'e bakın.
// Foreground colors
theme.fg("toolTitle", text) // Tool names
theme.fg("accent", text) // Highlights
theme.fg("success", text) // Success (green)
theme.fg("error", text) // Errors (red)
theme.fg("warning", text) // Warnings (yellow)
theme.fg("muted", text) // Secondary text
theme.fg("dim", text) // Tertiary text
// Text styles
theme.bold(text)
theme.italic(text)
theme.strikethrough(text)Özel araç oluşturucularda sözdizimi vurgulaması için:
import { highlightCode, getLanguageFromPath } from "@earendil-works/pi-coding-agent";
// Highlight code with explicit language
const highlighted = highlightCode("const x = 1;", "typescript", theme);
// Auto-detect language from file path
const lang = getLanguageFromPath("/path/to/file.rs"); // "rust"
const highlighted = highlightCode(code, lang, theme);Hata İşleme
- Uzantı hataları günlüğe kaydedilir, temsilci devam eder
tool_callhatalar aracı engeller (arızaya karşı korumalı)- Araç
executehataları atılarak bildirilmelidir; atılan hata yakalanır, LLM'yeisError: trueile bildirilir ve yürütme devam eder
Mod Davranışı
| Mod | ctx.mode |
ctx.hasUI |
Notlar |
|---|---|---|---|
| İnteraktif | "tui" |
true |
Terminal oluşturma ile tam TUI |
RPC (--mode rpc) |
"rpc" |
true |
JSON protokolü aracılığıyla diyaloglar ve bildirimler; custom(), undefined değerini döndürür. Bkz. rpc.md |
JSON (--mode json) |
"json" |
false |
stdout'e olay akışı; Kullanıcı arayüzü yöntemleri işlem gerektirmez |
Yazdır (-p) |
"print" |
false |
Extensions çalıştır ama istemde bulunamıyorum |
TUI'ye özgü özelliklerden (custom(), bileşen fabrikaları, terminal girişi) önce ctx.mode === "tui" kullanın. Hem TUI hem de RPC modlarında çalışan diyalog ve bildirim yöntemlerinden önce ctx.hasUI'yi kullanın.
Örnekler Referans
Tüm örnekler examples/extensions/'dedir.
| Örnek | Tanım | Tuş APIs |
|---|---|---|
| Aletler | ||
hello.ts |
Minimum takım kaydı | registerTool |
question.ts |
Kullanıcı etkileşimli araç | registerTool, ui.select |
questionnaire.ts |
Çok adımlı sihirbaz aracı | registerTool, ui.custom |
todo.ts |
Kalıcılığa sahip durum bilgisi olan araç | registerTool, appendEntry, renderResult, oturum etkinlikleri |
dynamic-tools.ts |
Araçları başlatma sonrasında ve komutlar sırasında kaydedin | registerTool, session_start, registerCommand |
structured-output.ts |
terminate: true ile son yapılandırılmış çıktı aracı |
registerTool, araç sonuçlarının sonlandırılması |
truncated-tool.ts |
Çıkış kesme örneği | registerTool, truncateHead |
tool-override.ts |
Yerleşik okuma aracını geçersiz kıl | registerTool (yerleşik ile aynı ad) |
| Komutlar | ||
pirate.ts |
Her turda sistem istemini değiştirin | registerCommand, before_agent_start |
summarize.ts |
Konuşma özeti komutu | registerCommand, ui.custom |
handoff.ts |
Sağlayıcılar arası model aktarımı | registerCommand, ui.editor, ui.custom |
qna.ts |
Özel kullanıcı arayüzü ile Soru-Cevap | registerCommand, ui.custom, setEditorText |
send-user-message.ts |
Kullanıcı mesajlarını enjekte etme | registerCommand, sendUserMessage |
reload-runtime.ts |
Yeniden yükleme komutu ve LLM aracı aktarımı | registerCommand, ctx.reload(), sendUserMessage |
shutdown-command.ts |
Zarif kapatma komutu | registerCommand, shutdown() |
| Etkinlikler ve Kapılar | ||
permission-gate.ts |
Tehlikeli komutları engelle | on("tool_call"), ui.confirm |
project-trust.ts |
Bir kullanıcı/global veya CLI uzantısından proje güvenine karar verin veya bu güveni erteleyin | on("project_trust"), güven kullanıcı arayüzü, gerekli güven sonucu |
protected-paths.ts |
Belirli yollara yazmayı engelle | on("tool_call") |
confirm-destructive.ts |
Oturum değişikliklerini onaylayın | on("session_before_switch"), on("session_before_fork") |
dirty-repo-guard.ts |
Kirli git deposu hakkında uyar | on("session_before_*"), exec |
input-transform.ts |
Kullanıcı girişini dönüştürün | on("input") |
input-transform-streaming.ts |
Akış uyumlu giriş dönüşümü | on("input"), streamingBehavior |
model-status.ts |
React model değişikliklerine | on("model_select"), setStatus |
provider-payload.ts |
Yükleri ve sağlayıcı yanıt başlıklarını inceleyin | on("before_provider_request"), on("after_provider_response") |
system-prompt-header.ts |
Sistem istemi bilgilerini görüntüle | on("agent_start"), getSystemPrompt |
claude-rules.ts |
Dosyalardan kuralları yükle | on("session_start"), on("before_agent_start") |
prompt-customizer.ts |
systemPromptOptions kullanarak bağlama duyarlı araç rehberliği ekleyin |
on("before_agent_start"), BuildSystemPromptOptions |
file-trigger.ts |
Dosya izleyici mesajları tetikler | sendMessage |
| Sıkıştırma ve Oturumlar | ||
custom-compaction.ts |
Özel sıkıştırma özeti | on("session_before_compact") |
trigger-compact.ts |
Sıkıştırmayı manuel olarak tetikleyin | compact() |
git-checkpoint.ts |
Git dönüşlerde saklanma | on("turn_start"), on("session_before_fork"), exec |
git-merge-and-resolve.ts |
Çakışmaları getirme, birleştirme ve çözme | on("agent_end"), exec, sendUserMessage |
auto-commit-on-exit.ts |
Kapatmayı taahhüt et | on("session_shutdown"), exec |
| Kullanıcı Arayüzü Bileşenleri | ||
status-line.ts |
Altbilgi durum göstergesi | setStatus, oturum etkinlikleri |
working-indicator.ts |
Akış çalışma göstergesini özelleştirin | setWorkingIndicator, registerCommand |
github-issue-autocomplete.ts |
gh issue list'den en son açık sayıları önceden yükleyerek yerleşik otomatik tamamlamanın üstüne #1234 sayı tamamlamaları ekleyin |
addAutocompleteProvider, on("session_start"), exec |
custom-footer.ts |
Alt bilgiyi tamamen değiştir | registerCommand, setFooter |
custom-header.ts |
Başlangıç başlığını değiştir | on("session_start"), setHeader |
modal-editor.ts |
Vim tarzı modal düzenleyici | setEditorComponent, CustomEditor |
rainbow-editor.ts |
Özel düzenleyici stili | setEditorComponent |
widget-placement.ts |
Widget düzenleyicinin üstünde/altında | setWidget |
overlay-test.ts |
Kaplama bileşenleri | ui.custom kaplama seçenekleriyle |
overlay-qa-tests.ts |
Kapsamlı kaplama testleri | ui.custom, tüm kaplama seçenekleri |
notify.ts |
Basit bildirimler | ui.notify |
timed-confirm.ts |
Zaman aşımı olan diyaloglar | ui.confirm zaman aşımı/sinyalli |
mac-system-theme.ts |
Temayı otomatik değiştir | setTheme, exec |
| Karmaşık Extensions | ||
plan-mode/ |
Tam plan modu uygulaması | Tüm etkinlik türleri, registerCommand, registerShortcut, registerFlag, setStatus, setWidget, sendMessage, setActiveTools |
preset.ts |
Kaydedilebilir ön ayarlar (model, araçlar, düşünme) | registerCommand, registerShortcut, registerFlag, setModel, setActiveTools, setThinkingLevel, appendEntry |
tools.ts |
Araçların kullanıcı arayüzünü açma/kapatma | registerCommand, setActiveTools, SettingsList, oturum etkinlikleri |
| Uzaktan Kumanda ve Korumalı Alan | ||
ssh.ts |
SSH uzaktan yürütme | registerFlag, on("user_bash"), on("before_agent_start"), araç işlemleri |
interactive-shell.ts |
Kalıcı kabuk oturumu | on("user_bash") |
sandbox/ |
Korumalı alanda araç yürütme | Takım işlemleri |
gondolin/ |
Yerleşik araçları ve ! komutlarını Gondolin mikro sanal makineye yönlendirin |
Araç işlemleri, yerleşik araç geçersiz kılmaları, on("user_bash") |
subagent/ |
Alt ajanları doğur | registerTool, exec |
| Oyunlar | ||
snake.ts |
Yılan oyunu | registerCommand, ui.custom, klavye kullanımı |
space-invaders.ts |
Uzay İstilacıları oyunu | registerCommand, ui.custom |
doom-overlay/ |
Yer paylaşımında kıyamet | ui.custom kaplamalı |
| Providers | ||
custom-provider-anthropic/ |
Özel Antropik proxy | registerProvider |
custom-provider-gitlab-duo/ |
GitLab Duo entegrasyonu | registerProvider ile OAuth |
| Mesajlar ve İletişim | ||
message-renderer.ts |
Özel mesaj oluşturma | registerMessageRenderer, sendMessage |
entry-renderer.ts |
TUI-yalnızca özel giriş oluşturma | registerEntryRenderer, appendEntry |
event-bus.ts |
Uzantılar arası olaylar | pi.events |
| Oturum Meta Verileri | ||
session-name.ts |
Seçici için oturumları adlandırın | setSessionName, getSessionName |
bookmark.ts |
/tree için yer imi girişleri | setLabel |
| Çeşitli | ||
inline-bash.ts |
Araç çağrılarında satır içi bash | on("tool_call") |
bash-spawn-hook.ts |
Yürütmeden önce bash komutunu, cwd'yi ve env'yi ayarlayın | createBashTool, spawnHook |
with-deps/ |
npm bağımlılıklara sahip uzantı | package.json ile paket yapısı |