diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx new file mode 100644 index 00000000000..ca463c17ff9 --- /dev/null +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CommitMessageTab.tsx @@ -0,0 +1,60 @@ +import { Component, Show, createSignal } from "solid-js" +import { Switch } from "@kilocode/kilo-ui/switch" +import { TextField } from "@kilocode/kilo-ui/text-field" +import { Card } from "@kilocode/kilo-ui/card" +import { useConfig } from "../../context/config" +import { useLanguage } from "../../context/language" +import SettingsRow from "./SettingsRow" + +const CommitMessageTab: Component = () => { + const { config, updateConfig } = useConfig() + const language = useLanguage() + + const [expanded, setExpanded] = createSignal(Boolean(config().commit_message?.prompt)) + + const toggle = (checked: boolean) => { + setExpanded(checked) + if (!checked) { + updateConfig({ commit_message: { prompt: "" } }) + } + } + + return ( +
+ + + + {language.t("settings.commitMessage.override.title")} + + + + +
+
+ {language.t("settings.commitMessage.prompt.title")} +
+
+ {language.t("settings.commitMessage.prompt.description")} +
+
+ { + updateConfig({ commit_message: { prompt: val } }) + }} + /> +
+
+
+
+
+ ) +} + +export default CommitMessageTab diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx index 7ce196444f4..93e51157546 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/Settings.tsx @@ -18,6 +18,7 @@ import AutocompleteTab from "./AutocompleteTab" import NotificationsTab from "./NotificationsTab" import ContextTab from "./ContextTab" +import CommitMessageTab from "./CommitMessageTab" import ExperimentalTab from "./ExperimentalTab" import LanguageTab from "./LanguageTab" import AboutKiloCodeTab from "./AboutKiloCodeTab" @@ -138,6 +139,10 @@ const Settings: Component = (props) => { {language.t("settings.context.title")} + + + {language.t("settings.commitMessage.title")} + {language.t("settings.experimental.title")} @@ -193,6 +198,10 @@ const Settings: Component = (props) => { + +

{language.t("settings.commitMessage.title")}

+ +

{language.t("settings.experimental.title")}

diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/settings-io.ts b/packages/kilo-vscode/webview-ui/src/components/settings/settings-io.ts index 42faae33d0b..56baf3aaec6 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/settings-io.ts +++ b/packages/kilo-vscode/webview-ui/src/components/settings/settings-io.ts @@ -29,6 +29,7 @@ export const KNOWN_KEYS: ReadonlyArray = [ "formatter", "lsp", "compaction", + "commit_message", "tools", "layout", "experimental", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 4ab78f89dc4..c7a7a5f2c2e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -1249,6 +1249,17 @@ export const dict = { "settings.context.prune.description": "إزالة مخرجات الأدوات القديمة أثناء الضغط", "settings.context.watcherPatterns": "أنماط تجاهل مراقب الملفات", "settings.context.watcherPatterns.description": "أنماط glob للملفات التي يجب على المراقب تجاهلها", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "استخدام prompt مخصص", + "settings.commitMessage.override.description": + "تجاوز prompt الـ commit message الافتراضي. عند التفعيل، سيستبدل الـ prompt المخصص الخاص بك الـ prompt المدمج لـ conventional commits بالكامل.", + "settings.commitMessage.prompt.title": "prompt مخصص", + "settings.commitMessage.prompt.description": + "الـ prompt النظامي المرسل إلى الذكاء الاصطناعي عند إنشاء commit messages. هذا يستبدل الـ prompt الافتراضي بالكامل.", + "settings.commitMessage.prompt.placeholder": + "على سبيل المثال: قم بإنشاء commit messages باللغة الإسبانية باتباع تنسيق conventional commits. أرجع الـ commit message فقط.", + "settings.display.username.title": "اسم المستخدم", "settings.display.username.description": "اسم مستخدم مخصص في المحادثات", "settings.display.layout.title": "التخطيط", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 24f58e33a5f..a129767be10 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -1278,6 +1278,17 @@ export const dict = { "settings.context.prune.description": "Remover saídas antigas de ferramentas durante a compactação", "settings.context.watcherPatterns": "Padrões de ignorar do observador", "settings.context.watcherPatterns.description": "Padrões glob para arquivos que o observador deve ignorar", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Usar prompt personalizado", + "settings.commitMessage.override.description": + "Substituir o prompt padrão de commit message. Quando ativado, o seu prompt personalizado substitui totalmente o prompt integrado de conventional commits.", + "settings.commitMessage.prompt.title": "prompt personalizado", + "settings.commitMessage.prompt.description": + "O prompt de sistema enviado à IA ao gerar commit messages. Isso substitui totalmente o prompt padrão.", + "settings.commitMessage.prompt.placeholder": + "ex: Gere commit messages em espanhol seguindo o formato conventional commits. Retorne APENAS o commit message.", + "settings.display.username.title": "Nome de usuário", "settings.display.username.description": "Nome de usuário personalizado nas conversas", "settings.display.layout.title": "Layout", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index 32c9f11fe1d..136b86a16e7 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -1274,6 +1274,17 @@ export const dict = { "settings.context.prune.description": "Ukloni stare izlaze alata tokom kompresije", "settings.context.watcherPatterns": "Uzorci ignoriranja za promatrač datoteka", "settings.context.watcherPatterns.description": "Glob uzorci za datoteke koje promatrač treba ignorirati", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Koristi prilagođeni prompt", + "settings.commitMessage.override.description": + "Nadjačaj podrazumijevani prompt za commit message. Kada je omogućeno, vaš prilagođeni prompt u potpunosti zamjenjuje ugrađeni prompt za conventional commits.", + "settings.commitMessage.prompt.title": "Prilagođeni prompt", + "settings.commitMessage.prompt.description": + "Sistemski prompt koji se šalje AI-u prilikom generisanja commit messages. Ovo u potpunosti zamjenjuje podrazumijevani prompt.", + "settings.commitMessage.prompt.placeholder": + "npr. Generiši commit messages na španskom jeziku prateći conventional commits format. Vrati SAMO commit message.", + "settings.display.username.title": "Korisničko ime", "settings.display.username.description": "Prilagođeno korisničko ime u razgovorima", "settings.display.layout.title": "Raspored", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index 79c9b67d166..0df476d47a5 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -1264,6 +1264,17 @@ export const dict = { "settings.context.prune.description": "Fjern gamle værktøjsoutput under komprimering", "settings.context.watcherPatterns": "Filvagt-ignormønstre", "settings.context.watcherPatterns.description": "Glob-mønstre for filer, som vagten skal ignorere", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Brug brugerdefineret prompt", + "settings.commitMessage.override.description": + "Tilsidesæt standard prompt for commit message. Når dette er aktiveret, erstatter din brugerdefinerede prompt fuldstændigt den indbyggede prompt for conventional commits.", + "settings.commitMessage.prompt.title": "Brugerdefineret prompt", + "settings.commitMessage.prompt.description": + "System prompt sendt til AI'en ved generering af commit messages. Dette erstatter fuldstændigt standard prompt.", + "settings.commitMessage.prompt.placeholder": + "f.eks. Generer commit messages på spansk efter conventional commits formatet. Returner KUN commit message.", + "settings.display.username.title": "Brugernavn", "settings.display.username.description": "Brugerdefineret brugernavn i samtaler", "settings.display.layout.title": "Layout", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index adaf05d244f..086c86aecf9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -1290,6 +1290,17 @@ export const dict = { "settings.context.prune.description": "Alte Werkzeugausgaben während der Komprimierung entfernen", "settings.context.watcherPatterns": "Datei-Watcher-Ignorierungsmuster", "settings.context.watcherPatterns.description": "Glob-Muster für Dateien, die der Watcher ignorieren soll", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Benutzerdefinierten prompt verwenden", + "settings.commitMessage.override.description": + "Den Standard-prompt für die commit message überschreiben. Wenn diese Option aktiviert ist, ersetzt Ihr benutzerdefinierter prompt den integrierten prompt für conventional commits vollständig.", + "settings.commitMessage.prompt.title": "Benutzerdefinierter prompt", + "settings.commitMessage.prompt.description": + "System-prompt, der beim Generieren von commit messages an die KI gesendet wird. Dies ersetzt den Standard-prompt vollständig.", + "settings.commitMessage.prompt.placeholder": + "z. B. Generiere commit messages auf Spanisch nach dem conventional commits Format. Gib NUR die commit message zurück.", + "settings.display.username.title": "Benutzername", "settings.display.username.description": "Benutzerdefinierter Benutzername in Gesprächen", "settings.display.layout.title": "Layout", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index 1db036f31dc..e20750d794f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -1266,6 +1266,16 @@ export const dict = { "settings.context.watcherPatterns": "File Watcher Ignore Patterns", "settings.context.watcherPatterns.description": "Glob patterns for files the watcher should ignore", + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Use Custom Prompt", + "settings.commitMessage.override.description": + "Override the default commit message prompt. When enabled, your custom prompt fully replaces the built-in conventional commits prompt.", + "settings.commitMessage.prompt.title": "Custom Prompt", + "settings.commitMessage.prompt.description": + "System prompt sent to the AI when generating commit messages. This replaces the default prompt entirely.", + "settings.commitMessage.prompt.placeholder": + "e.g. Generate commit messages in Spanish following conventional commits format. Return ONLY the commit message.", + "settings.display.username.title": "Username", "settings.display.username.description": "Custom username displayed in conversations", "settings.display.layout.title": "Layout", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index c40bb47ace3..03723eaee3d 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -1282,6 +1282,17 @@ export const dict = { "settings.context.prune.description": "Eliminar salidas de herramientas antiguas durante la compactación", "settings.context.watcherPatterns": "Patrones de ignorar del observador", "settings.context.watcherPatterns.description": "Patrones glob para archivos que el observador debe ignorar", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Usar prompt personalizado", + "settings.commitMessage.override.description": + "Sobrescribir el prompt por defecto para el commit message. Cuando está habilitado, tu prompt personalizado reemplaza completamente el prompt integrado para conventional commits.", + "settings.commitMessage.prompt.title": "prompt personalizado", + "settings.commitMessage.prompt.description": + "prompt del sistema enviado a la IA al generar commit messages. Esto reemplaza el prompt por defecto por completo.", + "settings.commitMessage.prompt.placeholder": + "ej. Genera commit messages en español siguiendo el formato conventional commits. Devuelve SOLO el commit message.", + "settings.display.username.title": "Nombre de usuario", "settings.display.username.description": "Nombre de usuario personalizado en conversaciones", "settings.display.layout.title": "Diseño", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index 997206e16e1..e0642521f9e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -1295,6 +1295,17 @@ export const dict = { "settings.context.prune.description": "Supprimer les anciennes sorties d'outils pendant la compaction", "settings.context.watcherPatterns": "Motifs d'ignorance de l'observateur", "settings.context.watcherPatterns.description": "Motifs glob pour les fichiers que l'observateur doit ignorer", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Utiliser un prompt personnalisé", + "settings.commitMessage.override.description": + "Remplacer le prompt par défaut pour le commit message. Lorsque cette option est activée, votre prompt personnalisé remplace entièrement le prompt intégré pour les conventional commits.", + "settings.commitMessage.prompt.title": "prompt personnalisé", + "settings.commitMessage.prompt.description": + "prompt système envoyé à l'IA lors de la génération des commit messages. Cela remplace entièrement le prompt par défaut.", + "settings.commitMessage.prompt.placeholder": + "par ex. Générer des commit messages en espagnol en suivant le format conventional commits. Retourner UNIQUEMENT le commit message.", + "settings.display.username.title": "Nom d'utilisateur", "settings.display.username.description": "Nom d'utilisateur personnalisé dans les conversations", "settings.display.layout.title": "Disposition", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index e39d38e7a35..33e9a1a90d7 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -1265,6 +1265,17 @@ export const dict = { "settings.context.prune.description": "圧縮時に古いツール出力を削除", "settings.context.watcherPatterns": "ファイルウォッチャー無視パターン", "settings.context.watcherPatterns.description": "ウォッチャーが無視すべきファイルのglobパターン", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "カスタム prompt を使用", + "settings.commitMessage.override.description": + "デフォルトの commit message の prompt を上書きします。有効にすると、カスタム prompt が組み込みの conventional commits の prompt を完全に置き換えます。", + "settings.commitMessage.prompt.title": "カスタム prompt", + "settings.commitMessage.prompt.description": + "commit messages を生成する際に AI に送信されるシステム prompt。これはデフォルトの prompt を完全に置き換えます。", + "settings.commitMessage.prompt.placeholder": + "例: スペイン語で conventional commits 形式に従って commit messages を生成して。commit message のみを返して。", + "settings.display.username.title": "ユーザー名", "settings.display.username.description": "会話に表示されるカスタムユーザー名", "settings.display.layout.title": "レイアウト", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index e31e3bef66c..2a759fa54b4 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -1253,6 +1253,17 @@ export const dict = { "settings.context.prune.description": "압축 중 이전 도구 출력 제거", "settings.context.watcherPatterns": "파일 감시자 무시 패턴", "settings.context.watcherPatterns.description": "감시자가 무시해야 할 파일의 글로브 패턴", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "사용자 지정 prompt 사용", + "settings.commitMessage.override.description": + "기본 commit message의 prompt를 재정의합니다. 활성화되면 사용자 지정 prompt가 기본 제공되는 conventional commits의 prompt를 완전히 대체합니다.", + "settings.commitMessage.prompt.title": "사용자 지정 prompt", + "settings.commitMessage.prompt.description": + "commit messages를 생성할 때 AI로 전송되는 시스템 prompt입니다. 이는 기본 prompt를 완전히 대체합니다.", + "settings.commitMessage.prompt.placeholder": + "예: 스페인어로 conventional commits 형식을 따라 commit messages를 생성해줘. 오직 commit message만 반환할 것.", + "settings.display.username.title": "사용자 이름", "settings.display.username.description": "대화에 표시되는 사용자 정의 사용자 이름", "settings.display.layout.title": "레이아웃", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts index 945a7ada25c..0f5b1297d46 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/nl.ts @@ -1248,6 +1248,16 @@ export const dict = { "settings.context.watcherPatterns": "File Watcher Negeer Patronen", "settings.context.watcherPatterns.description": "Glob-patronen voor bestanden die de watcher moet negeren", + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Aangepaste prompt gebruiken", + "settings.commitMessage.override.description": + "Overschrijf de standaard prompt voor de commit message. Indien ingeschakeld, vervangt uw aangepaste prompt de ingebouwde prompt voor conventional commits volledig.", + "settings.commitMessage.prompt.title": "Aangepaste prompt", + "settings.commitMessage.prompt.description": + "Systeem prompt die naar de AI wordt gestuurd bij het genereren van commit messages. Dit vervangt de standaard prompt volledig.", + "settings.commitMessage.prompt.placeholder": + "bijv. Genereer commit messages in het Spaans volgens het conventional commits formaat. Retourneer ALLEEN de commit message.", + "settings.display.username.title": "Gebruikersnaam", "settings.display.username.description": "Aangepaste gebruikersnaam weergegeven in gesprekken", "settings.display.layout.title": "Lay-out", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index 001cee887b3..b6218c83b77 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -1266,6 +1266,17 @@ export const dict = { "settings.context.prune.description": "Fjern gamle verktøyutdata under komprimering", "settings.context.watcherPatterns": "Filvakt-ignormønstre", "settings.context.watcherPatterns.description": "Glob-mønstre for filer som vakten skal ignorere", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Bruk egendefinert prompt", + "settings.commitMessage.override.description": + "Overstyr standard prompt for commit message. Når aktivert, erstatter din egendefinerte prompt fullstendig den innebygde prompten for conventional commits.", + "settings.commitMessage.prompt.title": "Egendefinert prompt", + "settings.commitMessage.prompt.description": + "System prompt sendt til AI-en ved generering av commit messages. Dette erstatter standard prompt fullstendig.", + "settings.commitMessage.prompt.placeholder": + "f.eks. Generer commit messages på spansk i henhold til conventional commits-formatet. Returner KUN commit message.", + "settings.display.username.title": "Brukernavn", "settings.display.username.description": "Egendefinert brukernavn i samtaler", "settings.display.layout.title": "Layout", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index a54bc73b839..600aada3c97 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -1274,6 +1274,17 @@ export const dict = { "settings.context.prune.description": "Usuń stare wyjścia narzędzi podczas kompakcji", "settings.context.watcherPatterns": "Wzorce ignorowania obserwatora plików", "settings.context.watcherPatterns.description": "Wzorce glob dla plików do ignorowania", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Użyj niestandardowego prompt", + "settings.commitMessage.override.description": + "Zastąp domyślny prompt dla commit message. Po włączeniu, Twój niestandardowy prompt całkowicie zastępuje wbudowany prompt dla conventional commits.", + "settings.commitMessage.prompt.title": "Niestandardowy prompt", + "settings.commitMessage.prompt.description": + "Systemowy prompt wysyłany do AI podczas generowania commit messages. Zastępuje to całkowicie domyślny prompt.", + "settings.commitMessage.prompt.placeholder": + "np. Generuj commit messages w języku hiszpańskim zgodnie z formatem conventional commits. Zwróć TYLKO commit message.", + "settings.display.username.title": "Nazwa użytkownika", "settings.display.username.description": "Niestandardowa nazwa użytkownika w rozmowach", "settings.display.layout.title": "Układ", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index 64eafd1c0c4..17aa9bf9058 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -1273,6 +1273,17 @@ export const dict = { "settings.context.prune.description": "Удалить старые выходные данные инструментов при сжатии", "settings.context.watcherPatterns": "Шаблоны игнорирования наблюдателя файлов", "settings.context.watcherPatterns.description": "Glob-шаблоны для файлов, которые наблюдатель должен игнорировать", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Использовать пользовательский prompt", + "settings.commitMessage.override.description": + "Переопределить prompt по умолчанию для commit message. Если включено, ваш пользовательский prompt полностью заменяет встроенный prompt для conventional commits.", + "settings.commitMessage.prompt.title": "Пользовательский prompt", + "settings.commitMessage.prompt.description": + "Системный prompt, отправляемый ИИ при генерации commit messages. Это полностью заменяет prompt по умолчанию.", + "settings.commitMessage.prompt.placeholder": + "напр., Сгенерируй commit messages на испанском языке в формате conventional commits. Верни ТОЛЬКО commit message.", + "settings.display.username.title": "Имя пользователя", "settings.display.username.description": "Пользовательское имя в разговорах", "settings.display.layout.title": "Макет", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index d66319761bf..4c7c1f12d41 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -1249,6 +1249,17 @@ export const dict = { "settings.context.prune.description": "ลบผลลัพธ์เครื่องมือเก่าระหว่างการบีบอัด", "settings.context.watcherPatterns": "รูปแบบการละเว้นตัวเฝ้าดูไฟล์", "settings.context.watcherPatterns.description": "รูปแบบ glob สำหรับไฟล์ที่ตัวเฝ้าดูควรละเว้น", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "ใช้ prompt แบบกำหนดเอง", + "settings.commitMessage.override.description": + "แทนที่ prompt เริ่มต้นของ commit message เมื่อเปิดใช้งาน prompt แบบกำหนดเองของคุณจะแทนที่ prompt เริ่มต้นของ conventional commits ทั้งหมด", + "settings.commitMessage.prompt.title": "prompt แบบกำหนดเอง", + "settings.commitMessage.prompt.description": + "System prompt ที่ส่งไปยัง AI เมื่อสร้าง commit messages สิ่งนี้จะแทนที่ prompt เริ่มต้นทั้งหมด", + "settings.commitMessage.prompt.placeholder": + "เช่น สร้าง commit messages เป็นภาษาสเปนตามรูปแบบ conventional commits คืนค่าเฉพาะ commit message เท่านั้น", + "settings.display.username.title": "ชื่อผู้ใช้", "settings.display.username.description": "ชื่อผู้ใช้กำหนดเองในบทสนทนา", "settings.display.layout.title": "เค้าโครง", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts index fa34a058ed2..42dc653a091 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/tr.ts @@ -1241,6 +1241,16 @@ export const dict = { "settings.context.watcherPatterns": "Dosya İzleyici Yok Sayma Kalıpları", "settings.context.watcherPatterns.description": "İzleyicinin yok sayması gereken dosyalar için glob kalıpları", + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Özel prompt Kullan", + "settings.commitMessage.override.description": + "Varsayılan commit message için olan prompt değerini geçersiz kıl. Etkinleştirildiğinde, özel prompt değeriniz yerleşik conventional commits için olan prompt değerini tamamen değiştirir.", + "settings.commitMessage.prompt.title": "Özel prompt", + "settings.commitMessage.prompt.description": + "commit messages oluşturulurken yapay zekaya gönderilen sistem prompt'u. Bu, varsayılan prompt'un tamamen yerini alır.", + "settings.commitMessage.prompt.placeholder": + "örn. conventional commits formatını izleyerek İspanyolca commit messages oluştur. SADECE commit message döndür.", + "settings.display.username.title": "Kullanıcı Adı", "settings.display.username.description": "Sohbetlerde görüntülenen özel kullanıcı adı", "settings.display.layout.title": "Düzen", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts index 821dfbe098b..798e5be2119 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/uk.ts @@ -1243,6 +1243,16 @@ export const dict = { "settings.context.watcherPatterns": "Шаблони ігнорування спостерігача файлів", "settings.context.watcherPatterns.description": "Glob-шаблони для файлів, які спостерігач має ігнорувати", + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "Використовувати власний prompt", + "settings.commitMessage.override.description": + "Перевизначити prompt за замовчуванням для commit message. Якщо ввімкнено, ваш власний prompt повністю замінює вбудований prompt для conventional commits.", + "settings.commitMessage.prompt.title": "Власний prompt", + "settings.commitMessage.prompt.description": + "Системний prompt, що надсилається ШІ під час генерації commit messages. Це повністю замінює prompt за замовчуванням.", + "settings.commitMessage.prompt.placeholder": + "напр., Згенеруй commit messages іспанською мовою у форматі conventional commits. Поверни ЛИШЕ commit message.", + "settings.display.username.title": "Ім'я користувача", "settings.display.username.description": "Власне ім'я користувача, що відображається в чатах", "settings.display.layout.title": "Макет", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index 8d2f6fa5614..041b12f8e70 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -1225,6 +1225,17 @@ export const dict = { "settings.context.prune.description": "压缩期间移除旧的工具输出", "settings.context.watcherPatterns": "文件监视器忽略模式", "settings.context.watcherPatterns.description": "监视器应忽略的文件的 glob 模式", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "使用自定义 prompt", + "settings.commitMessage.override.description": + "覆盖默认的 commit message 的 prompt。启用后,你的自定义 prompt 将完全替换内置的 conventional commits 的 prompt。", + "settings.commitMessage.prompt.title": "自定义 prompt", + "settings.commitMessage.prompt.description": + "在生成 commit messages 时发送给 AI 的系统 prompt。这将完全替换默认的 prompt。", + "settings.commitMessage.prompt.placeholder": + "例如:按照 conventional commits 格式用西班牙语生成 commit messages。只返回 commit message。", + "settings.display.username.title": "用户名", "settings.display.username.description": "对话中显示的自定义用户名", "settings.display.layout.title": "布局", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index f6819628b90..8bbfbefa123 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -1229,6 +1229,17 @@ export const dict = { "settings.context.prune.description": "壓縮期間移除舊的工具輸出", "settings.context.watcherPatterns": "檔案監視器忽略模式", "settings.context.watcherPatterns.description": "監視器應忽略的檔案的 glob 模式", + + "settings.commitMessage.title": "Commit Message", + "settings.commitMessage.override.title": "使用自訂 prompt", + "settings.commitMessage.override.description": + "覆寫預設的 commit message 的 prompt。啟用後,您的自訂 prompt 將完全取代內建的 conventional commits 的 prompt。", + "settings.commitMessage.prompt.title": "自訂 prompt", + "settings.commitMessage.prompt.description": + "在產生 commit messages 時發送給 AI 的系統 prompt。這將完全取代預設的 prompt。", + "settings.commitMessage.prompt.placeholder": + "例如:按照 conventional commits 格式用西班牙語產生 commit messages。只回傳 commit message。", + "settings.display.username.title": "使用者名稱", "settings.display.username.description": "對話中顯示的自訂使用者名稱", "settings.display.layout.title": "佈局", diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index 32071d057fc..51d142bc302 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -439,6 +439,10 @@ export interface ExperimentalConfig { mcp_timeout?: number } +export interface CommitMessageConfig { + prompt?: string +} + export interface Config { permission?: PermissionConfig model?: string | null @@ -460,6 +464,7 @@ export interface Config { formatter?: false | Record lsp?: false | Record compaction?: CompactionConfig + commit_message?: CommitMessageConfig tools?: Record layout?: "auto" | "stretch" experimental?: ExperimentalConfig diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 662a54d26b2..e46570bd4e7 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -107,15 +107,18 @@ "@hono/node-ws": "1.3.0", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", + "@kilocode/kilo-gateway": "workspace:*", + "@kilocode/kilo-telemetry": "workspace:*", + "@kilocode/plugin": "workspace:*", + "@kilocode/sdk": "workspace:*", "@lydell/node-pty": "catalog:", "@modelcontextprotocol/sdk": "1.29.0", + "@morphllm/morphsdk": "0.2.148", "@npmcli/arborist": "9.4.0", "@octokit/graphql": "9.0.2", "@octokit/rest": "catalog:", "@openauthjs/openauth": "catalog:", - "@kilocode/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", - "@kilocode/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "2.5.1", "@opentui/core": "0.1.97", @@ -155,8 +158,11 @@ "opentui-spinner": "0.0.6", "partial-json": "0.1.7", "remeda": "catalog:", + "rotating-file-stream": "3.2.9", "semver": "^7.6.3", + "simple-git": "3.35.2", "solid-js": "catalog:", + "stream-chat": "9.38.0", "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", "tree-sitter-powershell": "0.25.10", @@ -169,13 +175,7 @@ "xdg-basedir": "5.1.0", "yargs": "18.0.0", "zod": "catalog:", - "zod-to-json-schema": "3.24.5", - "@kilocode/kilo-gateway": "workspace:*", - "@kilocode/kilo-telemetry": "workspace:*", - "@morphllm/morphsdk": "0.2.148", - "rotating-file-stream": "3.2.9", - "simple-git": "3.35.2", - "stream-chat": "9.38.0" + "zod-to-json-schema": "3.24.5" }, "overrides": { "drizzle-orm": "catalog:" diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 40a7d1f800b..f99ec84989b 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1162,6 +1162,7 @@ export namespace Config { url: z.string().optional().describe("Enterprise URL"), }) .optional(), + commit_message: KilocodeConfig.CommitMessageSchema, // kilocode_change compaction: z .object({ auto: z.boolean().optional().describe("Enable automatic compaction when context is full (default: true)"), diff --git a/packages/opencode/src/commit-message/generate.ts b/packages/opencode/src/kilocode/commit-message/generate.ts similarity index 99% rename from packages/opencode/src/commit-message/generate.ts rename to packages/opencode/src/kilocode/commit-message/generate.ts index 9dbbb246756..889101c9976 100644 --- a/packages/opencode/src/commit-message/generate.ts +++ b/packages/opencode/src/kilocode/commit-message/generate.ts @@ -137,7 +137,7 @@ export async function generateCommitMessage(request: CommitMessageRequest): Prom hidden: true, options: {}, permission: [], - prompt: SYSTEM_PROMPT, + prompt: request.prompt || SYSTEM_PROMPT, temperature: 0.3, } diff --git a/packages/opencode/src/commit-message/git-context.ts b/packages/opencode/src/kilocode/commit-message/git-context.ts similarity index 100% rename from packages/opencode/src/commit-message/git-context.ts rename to packages/opencode/src/kilocode/commit-message/git-context.ts diff --git a/packages/opencode/src/commit-message/index.ts b/packages/opencode/src/kilocode/commit-message/index.ts similarity index 100% rename from packages/opencode/src/commit-message/index.ts rename to packages/opencode/src/kilocode/commit-message/index.ts diff --git a/packages/opencode/src/commit-message/types.ts b/packages/opencode/src/kilocode/commit-message/types.ts similarity index 87% rename from packages/opencode/src/commit-message/types.ts rename to packages/opencode/src/kilocode/commit-message/types.ts index 135fc13a3f9..580875f31b6 100644 --- a/packages/opencode/src/commit-message/types.ts +++ b/packages/opencode/src/kilocode/commit-message/types.ts @@ -5,6 +5,8 @@ export interface CommitMessageRequest { selectedFiles?: string[] /** Previously generated message — when set, the LLM is asked to produce a different one */ previousMessage?: string + /** Optional custom system prompt — overrides the default conventional commits prompt */ + prompt?: string } export interface CommitMessageResponse { diff --git a/packages/opencode/src/kilocode/config/config.ts b/packages/opencode/src/kilocode/config/config.ts index d36e5ec1fee..722a368950b 100644 --- a/packages/opencode/src/kilocode/config/config.ts +++ b/packages/opencode/src/kilocode/config/config.ts @@ -23,6 +23,21 @@ import { IgnoreMigrator } from "../ignore-migrator" export namespace KilocodeConfig { const log = Log.create({ service: "kilocode.config" }) + // ── Config schema extensions ───────────────────────────────────────── + + /** Schema for AI-generated commit message configuration. */ + export const CommitMessageSchema = z + .object({ + prompt: z + .string() + .optional() + .describe( + "Custom system prompt for AI commit message generation. When set, replaces the default conventional commits prompt entirely.", + ), + }) + .optional() + .describe("Configuration for AI-generated commit messages") + // ── Config file constants ──────────────────────────────────────────── /** Kilo-specific config file names (highest-to-lowest precedence within kilo). */ diff --git a/packages/opencode/src/kilocode/server/instance.ts b/packages/opencode/src/kilocode/server/instance.ts index b292b882e5c..0f7adc5f49c 100644 --- a/packages/opencode/src/kilocode/server/instance.ts +++ b/packages/opencode/src/kilocode/server/instance.ts @@ -6,7 +6,7 @@ import { Hono } from "hono" import { describeRoute, validator, resolver } from "hono-openapi" import z from "zod" import { TelemetryRoutes } from "../../server/routes/telemetry" -import { CommitMessageRoutes } from "../../server/routes/commit-message" +import { CommitMessageRoutes } from "./routes/commit-message" import { EnhancePromptRoutes } from "../../server/routes/enhance-prompt" import { KilocodeRoutes } from "../../server/routes/kilocode" import { PermissionKilocodeRoutes } from "../permission/routes" diff --git a/packages/opencode/src/server/routes/commit-message.ts b/packages/opencode/src/kilocode/server/routes/commit-message.ts similarity index 80% rename from packages/opencode/src/server/routes/commit-message.ts rename to packages/opencode/src/kilocode/server/routes/commit-message.ts index 8ebc8d8bd5f..05c0cad4dda 100644 --- a/packages/opencode/src/server/routes/commit-message.ts +++ b/packages/opencode/src/kilocode/server/routes/commit-message.ts @@ -2,8 +2,9 @@ import { Hono } from "hono" import { describeRoute, resolver, validator } from "hono-openapi" import z from "zod" import { generateCommitMessage } from "../../commit-message" -import { lazy } from "../../util/lazy" -import { errors } from "../error" +import { Config } from "../../../config/config" +import { lazy } from "../../../util/lazy" +import { errors } from "../../../server/error" export const CommitMessageRoutes = lazy(() => new Hono().post( @@ -37,7 +38,9 @@ export const CommitMessageRoutes = lazy(() => ), async (c) => { const body = c.req.valid("json") - const result = await generateCommitMessage(body) + const config = await Config.get() + const prompt = config.commit_message?.prompt || undefined + const result = await generateCommitMessage({ ...body, prompt }) return c.json({ message: result.message }) }, ), diff --git a/packages/opencode/src/kilocode/session/fork.ts b/packages/opencode/src/kilocode/session/fork.ts new file mode 100644 index 00000000000..bfbc0f24e7f --- /dev/null +++ b/packages/opencode/src/kilocode/session/fork.ts @@ -0,0 +1,66 @@ +import { Session } from "@/session" +import { MessageV2 } from "@/session/message-v2" +import { SessionID, PartID } from "@/session/schema" +import { Log } from "@/util/log" + +const log = Log.create({ service: "session.fork" }) + +/** + * Extracts the child session ID from a task tool part. + */ +function childID(part: MessageV2.Part): string | undefined { + if (part.type !== "tool" || part.tool !== "task") return undefined + return (part.state as { metadata?: { sessionId?: string } }).metadata?.sessionId +} + +/** + * Recursively fork all child (subagent) sessions referenced by task tool parts + * in the given session, then update the parts to point at the forked copies. + * + * This prevents subagent state from leaking between forked sessions in the + * same worktree: without remapping, two forked sessions would share the same + * child session references, causing SSE events and permission prompts to bleed + * across sessions. + */ +export async function remapChildren(sid: SessionID): Promise { + const msgs = await Session.messages({ sessionID: sid }) + const refs: { part: MessageV2.ToolPart; child: string }[] = [] + for (const msg of msgs) { + for (const part of msg.parts) { + const child = childID(part) + if (child) refs.push({ part: part as MessageV2.ToolPart, child }) + } + } + if (refs.length === 0) return + + const remapped = new Map() + for (const ref of refs) { + if (remapped.has(ref.child)) continue + const exists = await Session.get(SessionID.make(ref.child)).catch(() => undefined) + if (!exists) continue + // Session.fork() already calls remapChildren on the forked child, + // so nested subagents are handled recursively without an explicit call here. + const forked = await Session.fork({ sessionID: SessionID.make(ref.child) }) + remapped.set(ref.child, forked.id) + } + + if (remapped.size === 0) return + + for (const ref of refs) { + const replacement = remapped.get(ref.child) + if (!replacement) continue + const meta = (ref.part.state as { metadata?: Record }).metadata + if (!meta) continue + await Session.updatePart({ + ...ref.part, + id: PartID.make(ref.part.id), + sessionID: SessionID.make(ref.part.sessionID), + state: { + ...ref.part.state, + metadata: { ...meta, sessionId: replacement }, + }, + } as MessageV2.ToolPart) + } + + log.info("remapped child sessions", { session: sid, count: remapped.size }) +} diff --git a/packages/opencode/src/kilocode/session/index.ts b/packages/opencode/src/kilocode/session/index.ts index 2b76e3b5a1d..b723fc307ef 100644 --- a/packages/opencode/src/kilocode/session/index.ts +++ b/packages/opencode/src/kilocode/session/index.ts @@ -1,6 +1,11 @@ // kilocode_change - new file +import { remapChildren as _remapChildren } from "./fork" import z from "zod" import { BusEvent } from "@/bus/bus-event" +import { Session } from "@/session" +import { MessageID, SessionID } from "@/session/schema" +import { makeRuntime } from "@/effect/run-service" +import { fn } from "@/util/fn" import { Database, eq, and, gte, isNull, desc, like, inArray, lt, or } from "@/storage/db" import type { SQL } from "@/storage/db" import { ProjectTable } from "@/project/project.sql" @@ -282,4 +287,16 @@ export namespace KiloSession { yield { ...input.fromRow(row), project } as T & { project: ProjectInfo | null } } } + + export const remapChildren = _remapChildren } + +export const kiloSessionFork = fn( + z.object({ sessionID: SessionID.zod, messageID: MessageID.zod.optional() }), + async (input) => { + const { runPromise } = makeRuntime(Session.Service, Session.defaultLayer) + const session = await runPromise((svc) => svc.fork(input)) + await KiloSession.remapChildren(session.id) + return session + }, +) diff --git a/packages/opencode/src/kilocode/suggestion/index.ts b/packages/opencode/src/kilocode/suggestion/index.ts index 2f50b428eb0..5f03bb677f3 100644 --- a/packages/opencode/src/kilocode/suggestion/index.ts +++ b/packages/opencode/src/kilocode/suggestion/index.ts @@ -174,6 +174,20 @@ export namespace Suggestion { } } + export async function dismissAll(sessionID: string): Promise { + const s = await state() + for (const [id, entry] of Object.entries(s.pending)) { + if (entry.info.sessionID !== sessionID) continue + delete s.pending[id] + log.info("dismissed", { requestID: id }) + Bus.publish(Event.Dismissed, { + sessionID: entry.info.sessionID, + requestID: entry.info.id, + }) + entry.reject(new DismissedError()) + } + } + export async function list() { return state().then((state) => Object.values(state.pending).map((item) => item.info)) } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 644e52f2903..57fb2331b90 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -25,7 +25,7 @@ import { Snapshot } from "@/snapshot" import { ProjectID } from "../project/schema" import { WorkspaceID } from "../control-plane/schema" import { SessionID, MessageID, PartID } from "./schema" -import { KiloSession } from "@/kilocode/session" // kilocode_change +import { KiloSession, kiloSessionFork } from "@/kilocode/session" // kilocode_change import type { Provider } from "@/provider/provider" import { Permission } from "@/permission" @@ -745,9 +745,7 @@ export namespace Session { (input) => runPromise((svc) => svc.create(input)), ) - export const fork = fn(z.object({ sessionID: SessionID.zod, messageID: MessageID.zod.optional() }), (input) => - runPromise((svc) => svc.fork(input)), - ) + export const fork = kiloSessionFork // kilocode_change export const get = fn(SessionID.zod, (id) => runPromise((svc) => svc.get(id))) export const share = fn(SessionID.zod, (id) => runPromise((svc) => svc.share(id))) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 7faa83bd66b..19c7c816d96 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -3,6 +3,7 @@ import os from "os" import fs from "fs/promises" import { KiloSessionPrompt } from "@/kilocode/session/prompt" // kilocode_change import { KiloSession } from "@/kilocode/session" // kilocode_change +import { Suggestion } from "@/kilocode/suggestion" // kilocode_change import z from "zod" import { SessionID, MessageID, PartID } from "./schema" import { MessageV2 } from "./message-v2" @@ -1287,6 +1288,11 @@ NOTE: At any point in time through this workflow you should feel free to ask the } if (input.noReply === true) return message + // kilocode_change start — dismiss pending suggestions and cancel the session + // before starting a new loop to avoid the runner ignoring the new work + yield* Effect.promise(() => Suggestion.dismissAll(input.sessionID)) + yield* state.cancel(input.sessionID) + // kilocode_change end return yield* loop({ sessionID: input.sessionID }) }, ) diff --git a/packages/opencode/test/kilocode/commit-message-windows.test.ts b/packages/opencode/test/kilocode/commit-message-windows.test.ts index 4be42d980e9..609266b75d7 100644 --- a/packages/opencode/test/kilocode/commit-message-windows.test.ts +++ b/packages/opencode/test/kilocode/commit-message-windows.test.ts @@ -1,5 +1,5 @@ import { describe, expect, spyOn, test } from "bun:test" -import { getGitContext } from "../../src/commit-message/git-context" +import { getGitContext } from "../../src/kilocode/commit-message/git-context" describe("commit-message git context", () => { test("hides Windows console windows for git subprocesses", async () => { diff --git a/packages/opencode/src/commit-message/__tests__/generate.test.ts b/packages/opencode/test/kilocode/commit-message/generate.test.ts similarity index 71% rename from packages/opencode/src/commit-message/__tests__/generate.test.ts rename to packages/opencode/test/kilocode/commit-message/generate.test.ts index 0cbfb1c6476..87a93dd4d30 100644 --- a/packages/opencode/src/commit-message/__tests__/generate.test.ts +++ b/packages/opencode/test/kilocode/commit-message/generate.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test, mock, beforeEach } from "bun:test" -import type { GitContext } from "../types" +import type { GitContext } from "@/kilocode/commit-message/types" // Mock dependencies before importing the module under test. // IMPORTANT: Bun's mock.module() is process-wide and permanent. To avoid @@ -10,19 +10,33 @@ const realLog = await import("@/util/log") const realProvider = await import("@/provider/provider") const realLLM = await import("@/session/llm") const realAgent = await import("@/agent/agent") - -let mockGitContext: GitContext = { - branch: "main", - recentCommits: ["abc1234 initial commit"], - files: [{ status: "modified" as const, path: "src/index.ts", diff: "+console.log('hello')" }], -} - -mock.module("../git-context", () => ({ - getGitContext: async () => mockGitContext, -})) +const realGitContext = await import("@/kilocode/commit-message/git-context") let mockStreamText = "feat(src): add hello world logging" +const defaultGitContext: GitContext = { + branch: "main", + recentCommits: ["abc1234 initial commit"], + files: [ + { + status: "modified", + path: "src/index.ts", + diff: "+console.log('hello')", + }, + ], +} + +let mockGitContext: GitContext = { ...defaultGitContext } +let captured: { path: string; selected?: string[] } = { path: "" } + +mock.module("@/kilocode/commit-message/git-context", () => ({ + ...realGitContext, + getGitContext: async (repoPath: string, selectedFiles?: string[]) => { + captured = { path: repoPath, selected: selectedFiles } + return mockGitContext + }, +})) + mock.module("@/provider/provider", () => ({ ...realProvider, Provider: { @@ -36,7 +50,6 @@ mock.module("@/provider/provider", () => ({ }, })) -// kilocode_change start — upstream switched from stream.text to stream.textStream mock.module("@/session/llm", () => ({ ...realLLM, LLM: { @@ -49,7 +62,6 @@ mock.module("@/session/llm", () => ({ }), }, })) -// kilocode_change end mock.module("@/agent/agent", () => ({ ...realAgent, @@ -69,36 +81,32 @@ mock.module("@/util/log", () => ({ }, })) -import { generateCommitMessage } from "../generate" +import { generateCommitMessage } from "../../../src/kilocode/commit-message/generate" describe("commit-message.generate", () => { beforeEach(() => { - mockGitContext = { - branch: "main", - recentCommits: ["abc1234 initial commit"], - files: [{ status: "modified" as const, path: "src/index.ts", diff: "+console.log('hello')" }], - } mockStreamText = "feat(src): add hello world logging" + mockGitContext = { ...defaultGitContext } + captured = { path: "" } }) describe("prompt construction", () => { test("passes path to getGitContext", async () => { - const result = await generateCommitMessage({ path: "/my/repo" }) - // If getGitContext is called, it returns our mock context and generates a message + const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBeTruthy() + expect(captured.path).toBe("/repo") }) test("generates message from git context with multiple files", async () => { + mockStreamText = "feat(api): add api module" mockGitContext = { - branch: "feature/api", - recentCommits: ["abc feat: add api", "def fix: typo"], + branch: "main", + recentCommits: ["abc1234 initial commit"], files: [ - { status: "added" as const, path: "src/api.ts", diff: "+export function api() {}" }, - { status: "modified" as const, path: "src/index.ts", diff: "+import { api } from './api'" }, + { status: "added", path: "src/api.ts", diff: "+export function api() {}" }, + { status: "modified", path: "src/index.ts", diff: "+import { api } from './api'" }, ], } - mockStreamText = "feat(api): add api module" - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("feat(api): add api module") }) @@ -107,49 +115,42 @@ describe("commit-message.generate", () => { describe("response cleaning", () => { test("strips code block markers from response", async () => { mockStreamText = "```\nfeat: add feature\n```" - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("feat: add feature") }) test("strips code block markers with language tag", async () => { mockStreamText = "```text\nfix(auth): resolve token refresh\n```" - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("fix(auth): resolve token refresh") }) test("strips surrounding double quotes", async () => { mockStreamText = '"feat: add new feature"' - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("feat: add new feature") }) test("strips surrounding single quotes", async () => { mockStreamText = "'fix: resolve bug'" - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("fix: resolve bug") }) test("strips whitespace around the message", async () => { mockStreamText = " \n chore: update deps \n " - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("chore: update deps") }) test("strips code blocks AND quotes together", async () => { mockStreamText = '```\n"refactor: simplify logic"\n```' - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("refactor: simplify logic") }) test("returns clean message when no markers present", async () => { mockStreamText = "docs: update readme" - const result = await generateCommitMessage({ path: "/repo" }) expect(result.message).toBe("docs: update readme") }) @@ -157,12 +158,7 @@ describe("commit-message.generate", () => { describe("error on no changes", () => { test("throws when no git changes are found", async () => { - mockGitContext = { - branch: "main", - recentCommits: [], - files: [], - } - + mockGitContext = { branch: "main", recentCommits: [], files: [] } await expect(generateCommitMessage({ path: "/repo" })).rejects.toThrow( "No changes found to generate a commit message for", ) @@ -171,12 +167,25 @@ describe("commit-message.generate", () => { describe("selectedFiles pass-through", () => { test("passes selectedFiles to getGitContext", async () => { - // This verifies the function doesn't crash when selectedFiles is provided const result = await generateCommitMessage({ path: "/repo", selectedFiles: ["src/a.ts"], }) expect(result.message).toBeTruthy() + expect(captured.path).toBe("/repo") + expect(captured.selected).toEqual(["src/a.ts"]) + }) + }) + + describe("custom prompt", () => { + test("uses default prompt when no custom prompt provided", async () => { + const result = await generateCommitMessage({ path: "/repo" }) + expect(result.message).toBeTruthy() + }) + + test("uses custom prompt when provided", async () => { + const result = await generateCommitMessage({ path: "/repo", prompt: "Write a haiku commit message." }) + expect(result.message).toBeTruthy() }) }) }) diff --git a/packages/opencode/src/commit-message/__tests__/git-context.test.ts b/packages/opencode/test/kilocode/commit-message/git-context.test.ts similarity index 99% rename from packages/opencode/src/commit-message/__tests__/git-context.test.ts rename to packages/opencode/test/kilocode/commit-message/git-context.test.ts index 83754b71308..37d355b1977 100644 --- a/packages/opencode/src/commit-message/__tests__/git-context.test.ts +++ b/packages/opencode/test/kilocode/commit-message/git-context.test.ts @@ -16,7 +16,7 @@ function clearGitOutputs() { // Override the git-context module with a version that uses our mock spawnSync. // This avoids conflicts with generate.test.ts which also mocks this module. -mock.module("../git-context", () => { +mock.module("../../../src/kilocode/commit-message/git-context", () => { function git(args: string[], cwd: string): string { const key = args.join(" ") return spawnSyncResults[key] ?? "" @@ -180,7 +180,7 @@ mock.module("../git-context", () => { return { getGitContext } }) -import { getGitContext } from "../git-context" +import { getGitContext } from "../../../src/kilocode/commit-message/git-context" describe("commit-message.git-context", () => { beforeEach(() => { diff --git a/packages/opencode/test/kilocode/session-fork-remap.test.ts b/packages/opencode/test/kilocode/session-fork-remap.test.ts new file mode 100644 index 00000000000..eb148f9f711 --- /dev/null +++ b/packages/opencode/test/kilocode/session-fork-remap.test.ts @@ -0,0 +1,251 @@ +import { afterEach, describe, expect, test } from "bun:test" +import { Instance } from "../../src/project/instance" +import { Session } from "../../src/session" +import { MessageV2 } from "../../src/session/message-v2" +import { MessageID, PartID, SessionID } from "../../src/session/schema" +import { Log } from "../../src/util/log" +import { tmpdir } from "../fixture/fixture" + +Log.init({ print: false }) + +afterEach(async () => { + await Instance.disposeAll() +}) + +function taskPart(input: { messageID: string; sessionID: string; childSessionID: string }): MessageV2.ToolPart { + return { + id: PartID.ascending(), + messageID: MessageID.make(input.messageID), + sessionID: SessionID.make(input.sessionID), + type: "tool", + callID: "call_1", + tool: "task", + state: { + status: "completed", + input: { description: "test task", prompt: "do something" }, + output: `task_id: ${input.childSessionID}`, + title: "test task", + metadata: { + sessionId: input.childSessionID, + model: { modelID: "test", providerID: "test" }, + }, + time: { start: Date.now(), end: Date.now() }, + }, + } +} + +async function userMsg(sid: string) { + const id = MessageID.ascending() + await Session.updateMessage({ + id, + sessionID: SessionID.make(sid), + role: "user", + time: { created: Date.now() }, + agent: "test", + model: { providerID: "test", modelID: "test" }, + tools: {}, + } as MessageV2.User) + return id +} + +async function asstMsg(sid: string, parent: string) { + const id = MessageID.ascending() + await Session.updateMessage({ + id, + sessionID: SessionID.make(sid), + role: "assistant", + time: { created: Date.now() }, + parentID: MessageID.make(parent), + modelID: "test", + providerID: "test", + mode: "", + agent: "test", + path: { cwd: "/tmp", root: "/tmp" }, + cost: 0, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + } as MessageV2.Assistant) + return id +} + +describe("Session.fork child session remapping", () => { + test( + "forked session gets its own copy of child sessions", + async () => { + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const parent = await Session.create({ title: "parent" }) + const child = await Session.create({ parentID: parent.id, title: "child subagent" }) + + // Add a user message to the child so it has content + const childMsgId = await userMsg(child.id) + await Session.updatePart({ + id: PartID.ascending(), + messageID: childMsgId, + sessionID: child.id, + type: "text", + text: "child message content", + } as MessageV2.TextPart) + + // Add a user message then an assistant message with a task tool part referencing the child + const parentUserMsg = await userMsg(parent.id) + await Session.updatePart({ + id: PartID.ascending(), + messageID: parentUserMsg, + sessionID: parent.id, + type: "text", + text: "do something", + } as MessageV2.TextPart) + + const parentAsstMsg = await asstMsg(parent.id, parentUserMsg) + await Session.updatePart( + taskPart({ + messageID: parentAsstMsg, + sessionID: parent.id, + childSessionID: child.id, + }), + ) + + // Fork the parent session + const forked = await Session.fork({ sessionID: parent.id }) + expect(forked.id).not.toBe(parent.id) + + // Check that the forked session's task part references a DIFFERENT child session + const forkedMsgs = await Session.messages({ sessionID: forked.id }) + const parts = forkedMsgs.flatMap((m) => m.parts) + const tools = parts.filter((p) => p.type === "tool" && p.tool === "task") as MessageV2.ToolPart[] + + expect(tools).toHaveLength(1) + const meta = (tools[0].state as unknown as { metadata: { sessionId: string } }).metadata + expect(meta.sessionId).not.toBe(child.id) + + // Verify the forked child session actually exists and has content + const forkedChild = await Session.get(SessionID.make(meta.sessionId)) + expect(forkedChild).toBeDefined() + expect(forkedChild.id).not.toBe(child.id) + + const forkedChildMsgs = await Session.messages({ sessionID: forkedChild.id }) + expect(forkedChildMsgs).toHaveLength(1) + expect(forkedChildMsgs[0].parts[0].type).toBe("text") + }, + }) + }, + { timeout: 30000 }, + ) + + test( + "nested child sessions are also remapped", + async () => { + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + // grandchild -> child -> parent + const parent = await Session.create({ title: "parent" }) + const child = await Session.create({ parentID: parent.id, title: "child" }) + const grandchild = await Session.create({ parentID: child.id, title: "grandchild" }) + + // grandchild has a text message + const gcMsgId = await userMsg(grandchild.id) + await Session.updatePart({ + id: PartID.ascending(), + messageID: gcMsgId, + sessionID: grandchild.id, + type: "text", + text: "grandchild content", + } as MessageV2.TextPart) + + // child references grandchild via task part + const childUserMsg = await userMsg(child.id) + await Session.updatePart({ + id: PartID.ascending(), + messageID: childUserMsg, + sessionID: child.id, + type: "text", + text: "question", + } as MessageV2.TextPart) + const childAsstMsg = await asstMsg(child.id, childUserMsg) + await Session.updatePart( + taskPart({ + messageID: childAsstMsg, + sessionID: child.id, + childSessionID: grandchild.id, + }), + ) + + // parent references child via task part + const parentUserMsg = await userMsg(parent.id) + await Session.updatePart({ + id: PartID.ascending(), + messageID: parentUserMsg, + sessionID: parent.id, + type: "text", + text: "request", + } as MessageV2.TextPart) + const parentAsstMsg = await asstMsg(parent.id, parentUserMsg) + await Session.updatePart( + taskPart({ + messageID: parentAsstMsg, + sessionID: parent.id, + childSessionID: child.id, + }), + ) + + const forked = await Session.fork({ sessionID: parent.id }) + + // Verify parent-level remap + const forkedMsgs = await Session.messages({ sessionID: forked.id }) + const tools = forkedMsgs + .flatMap((m) => m.parts) + .filter((p) => p.type === "tool" && p.tool === "task") as MessageV2.ToolPart[] + const forkedChildID = (tools[0].state as unknown as { metadata: { sessionId: string } }).metadata.sessionId + expect(forkedChildID).not.toBe(child.id) + + // Verify child-level remap (grandchild) + const forkedChildMsgs = await Session.messages({ sessionID: SessionID.make(forkedChildID) }) + const childTools = forkedChildMsgs + .flatMap((m) => m.parts) + .filter((p) => p.type === "tool" && p.tool === "task") as MessageV2.ToolPart[] + expect(childTools).toHaveLength(1) + const forkedGrandchildID = (childTools[0].state as unknown as { metadata: { sessionId: string } }).metadata + .sessionId + expect(forkedGrandchildID).not.toBe(grandchild.id) + + // Verify grandchild content was copied + const gcMsgs = await Session.messages({ sessionID: SessionID.make(forkedGrandchildID) }) + expect(gcMsgs).toHaveLength(1) + }, + }) + }, + { timeout: 30000 }, + ) + + test( + "non-task tool parts are not affected", + async () => { + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const parent = await Session.create({ title: "parent" }) + const parentUserMsg = await userMsg(parent.id) + await Session.updatePart({ + id: PartID.ascending(), + messageID: parentUserMsg, + sessionID: parent.id, + type: "text", + text: "hello", + } as MessageV2.TextPart) + + const forked = await Session.fork({ sessionID: parent.id }) + const forkedMsgs = await Session.messages({ sessionID: forked.id }) + expect(forkedMsgs).toHaveLength(1) + expect(forkedMsgs[0].parts[0].type).toBe("text") + expect((forkedMsgs[0].parts[0] as MessageV2.TextPart).text).toBe("hello") + }, + }) + }, + { timeout: 30000 }, + ) +}) diff --git a/packages/opencode/test/memory/abort-leak.test.ts b/packages/opencode/test/memory/abort-leak.test.ts index b19f0d3f627..539bac0111b 100644 --- a/packages/opencode/test/memory/abort-leak.test.ts +++ b/packages/opencode/test/memory/abort-leak.test.ts @@ -26,7 +26,9 @@ const getHeapMB = () => { } describe("memory: abort controller leak", () => { - test("webfetch does not leak memory over many invocations", async () => { + // kilocode_change start - TODO(#8990): skip flaky test on Linux CI + test.skip("webfetch does not leak memory over many invocations", async () => { + // kilocode_change end await Instance.provide({ directory: projectRoot, fn: async () => { diff --git a/packages/opencode/test/session/prompt-effect.test.ts b/packages/opencode/test/session/prompt-effect.test.ts index c60651fa945..499669ebd60 100644 --- a/packages/opencode/test/session/prompt-effect.test.ts +++ b/packages/opencode/test/session/prompt-effect.test.ts @@ -190,6 +190,7 @@ function makeHttp() { const it = testEffect(makeHttp()) const unix = process.platform !== "win32" ? it.live : it.live.skip +const unixSkip = it.live.skip // kilocode_change - TODO(#8990): skip flaky cancel tests on Linux CI // Config that registers a custom "test" provider with a "test-model" model // so Provider.getModel("test", "test-model") succeeds inside the loop. @@ -1197,7 +1198,8 @@ it.live( 3_000, ) -unix( +// kilocode_change start - TODO(#8990): flaky on Linux CI +unixSkip( "cancel interrupts shell and resolves cleanly", () => withSh(() => @@ -1233,8 +1235,10 @@ unix( ), 30_000, ) +// kilocode_change end -unix( +// kilocode_change start - TODO(#8990): flaky on Linux CI +unixSkip( "cancel persists aborted shell result when shell ignores TERM", () => withSh(() => @@ -1265,6 +1269,7 @@ unix( ), 30_000, ) +// kilocode_change end unix( "cancel finalizes interrupted bash tool output through normal truncation", @@ -1317,7 +1322,8 @@ unix( 30_000, ) -unix( +// kilocode_change start - TODO(#8990): flaky on Linux CI +unixSkip( "cancel interrupts loop queued behind shell", () => provideTmpdirInstance( @@ -1344,6 +1350,7 @@ unix( ), 30_000, ) +// kilocode_change end unix( "shell rejects when another shell is already running", diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index edf335cdcc2..0da83ed7a12 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -3086,109 +3086,6 @@ export class Question extends HeyApiClient { } } -export class Suggestion extends HeyApiClient { - /** - * List pending suggestions - * - * Get all pending suggestion requests across all sessions. - */ - public list( - parameters?: { - directory?: string - workspace?: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - ], - }, - ], - ) - return (options?.client ?? this.client).get({ - url: "/suggestion", - ...options, - ...params, - }) - } - - /** - * Accept suggestion request - * - * Accept a suggestion request from the AI assistant. - */ - public accept( - parameters: { - requestID: string - directory?: string - workspace?: string - index?: number - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "path", key: "requestID" }, - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - { in: "body", key: "index" }, - ], - }, - ], - ) - return (options?.client ?? this.client).post({ - url: "/suggestion/{requestID}/accept", - ...options, - ...params, - headers: { - "Content-Type": "application/json", - ...options?.headers, - ...params.headers, - }, - }) - } - - /** - * Dismiss suggestion request - * - * Dismiss a suggestion request from the AI assistant. - */ - public dismiss( - parameters: { - requestID: string - directory?: string - workspace?: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "path", key: "requestID" }, - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - ], - }, - ], - ) - return (options?.client ?? this.client).post({ - url: "/suggestion/{requestID}/dismiss", - ...options, - ...params, - }) - } -} - export class Oauth extends HeyApiClient { /** * OAuth authorize @@ -4624,6 +4521,109 @@ export class Network extends HeyApiClient { } } +export class Suggestion extends HeyApiClient { + /** + * List pending suggestions + * + * Get all pending suggestion requests across all sessions. + */ + public list( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/suggestion", + ...options, + ...params, + }) + } + + /** + * Accept suggestion request + * + * Accept a suggestion request from the AI assistant. + */ + public accept( + parameters: { + requestID: string + directory?: string + workspace?: string + index?: number + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "requestID" }, + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "body", key: "index" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/suggestion/{requestID}/accept", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } + + /** + * Dismiss suggestion request + * + * Dismiss a suggestion request from the AI assistant. + */ + public dismiss( + parameters: { + requestID: string + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "requestID" }, + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/suggestion/{requestID}/dismiss", + ...options, + ...params, + }) + } +} + export class Telemetry extends HeyApiClient { /** * Capture telemetry event @@ -5764,11 +5764,6 @@ export class KiloClient extends HeyApiClient { return (this._question ??= new Question({ client: this.client })) } - private _suggestion?: Suggestion - get suggestion(): Suggestion { - return (this._suggestion ??= new Suggestion({ client: this.client })) - } - private _provider?: Provider get provider(): Provider { return (this._provider ??= new Provider({ client: this.client })) @@ -5834,6 +5829,11 @@ export class KiloClient extends HeyApiClient { return (this._network ??= new Network({ client: this.client })) } + private _suggestion?: Suggestion + get suggestion(): Suggestion { + return (this._suggestion ??= new Suggestion({ client: this.client })) + } + private _telemetry?: Telemetry get telemetry(): Telemetry { return (this._telemetry ??= new Telemetry({ client: this.client })) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 5a25220f80c..a055d14ab74 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -509,6 +509,65 @@ export type EventSessionIdle = { } } +export type SuggestionAction = { + /** + * Button or option label (1-5 words) + */ + label: string + /** + * Brief explanation of what this action does + */ + description?: string + /** + * Synthetic user prompt to inject when this action is accepted + */ + prompt: string +} + +export type SuggestionRequest = { + id: string + sessionID: string + /** + * Suggestion text shown to the user + */ + text: string + /** + * Available actions the user can take + */ + actions: Array + /** + * Whether this suggestion blocks prompt input (default: true) + */ + blocking?: boolean + tool?: { + messageID: string + callID: string + } +} + +export type EventSuggestionShown = { + type: "suggestion.shown" + properties: SuggestionRequest +} + +export type EventSuggestionAccepted = { + type: "suggestion.accepted" + properties: { + sessionID: string + requestID: string + index: number + action: SuggestionAction + } +} + +export type EventSuggestionDismissed = { + type: "suggestion.dismissed" + properties: { + sessionID: string + requestID: string + } +} + export type EventSessionCompacted = { type: "session.compacted" properties: { @@ -979,65 +1038,6 @@ export type EventMessagePartRemoved = { } } -export type SuggestionAction = { - /** - * Button or option label (1-5 words) - */ - label: string - /** - * Brief explanation of what this action does - */ - description?: string - /** - * Synthetic user prompt to inject when this action is accepted - */ - prompt: string -} - -export type SuggestionRequest = { - id: string - sessionID: string - /** - * Suggestion text shown to the user - */ - text: string - /** - * Available actions the user can take - */ - actions: Array - /** - * Whether this suggestion blocks prompt input (default: true) - */ - blocking?: boolean - tool?: { - messageID: string - callID: string - } -} - -export type EventSuggestionShown = { - type: "suggestion.shown" - properties: SuggestionRequest -} - -export type EventSuggestionAccepted = { - type: "suggestion.accepted" - properties: { - sessionID: string - requestID: string - index: number - action: SuggestionAction - } -} - -export type EventSuggestionDismissed = { - type: "suggestion.dismissed" - properties: { - sessionID: string - requestID: string - } -} - export type PermissionAction = "allow" | "deny" | "ask" export type PermissionRule = { @@ -1120,9 +1120,6 @@ export type Event = | EventGlobalConfigUpdated | EventLspClientDiagnostics | EventLspUpdated - | EventSuggestionShown - | EventSuggestionAccepted - | EventSuggestionDismissed | EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow @@ -1150,6 +1147,9 @@ export type Event = | EventTodoUpdated | EventSessionStatus | EventSessionIdle + | EventSuggestionShown + | EventSuggestionAccepted + | EventSuggestionDismissed | EventSessionCompacted | EventKiloSessionsRemoteStatusChanged | EventWorkspaceReady @@ -1756,6 +1756,15 @@ export type Config = { */ url?: string } + /** + * Configuration for AI-generated commit messages + */ + commit_message?: { + /** + * Custom system prompt for AI commit message generation. When set, replaces the default conventional commits prompt entirely. + */ + prompt?: string + } compaction?: { /** * Enable automatic compaction when context is full (default: true) @@ -4611,98 +4620,6 @@ export type QuestionRejectResponses = { export type QuestionRejectResponse = QuestionRejectResponses[keyof QuestionRejectResponses] -export type SuggestionListData = { - body?: never - path?: never - query?: { - directory?: string - workspace?: string - } - url: "/suggestion" -} - -export type SuggestionListResponses = { - /** - * List of pending suggestions - */ - 200: Array -} - -export type SuggestionListResponse = SuggestionListResponses[keyof SuggestionListResponses] - -export type SuggestionAcceptData = { - body?: { - /** - * Zero-based action index to accept - */ - index: number - } - path: { - requestID: string - } - query?: { - directory?: string - workspace?: string - } - url: "/suggestion/{requestID}/accept" -} - -export type SuggestionAcceptErrors = { - /** - * Bad request - */ - 400: BadRequestError - /** - * Not found - */ - 404: NotFoundError -} - -export type SuggestionAcceptError = SuggestionAcceptErrors[keyof SuggestionAcceptErrors] - -export type SuggestionAcceptResponses = { - /** - * Suggestion accepted successfully - */ - 200: boolean -} - -export type SuggestionAcceptResponse = SuggestionAcceptResponses[keyof SuggestionAcceptResponses] - -export type SuggestionDismissData = { - body?: never - path: { - requestID: string - } - query?: { - directory?: string - workspace?: string - } - url: "/suggestion/{requestID}/dismiss" -} - -export type SuggestionDismissErrors = { - /** - * Bad request - */ - 400: BadRequestError - /** - * Not found - */ - 404: NotFoundError -} - -export type SuggestionDismissError = SuggestionDismissErrors[keyof SuggestionDismissErrors] - -export type SuggestionDismissResponses = { - /** - * Suggestion dismissed successfully - */ - 200: boolean -} - -export type SuggestionDismissResponse = SuggestionDismissResponses[keyof SuggestionDismissResponses] - export type ProviderListData = { body?: never path?: never @@ -5840,6 +5757,98 @@ export type NetworkRejectResponses = { export type NetworkRejectResponse = NetworkRejectResponses[keyof NetworkRejectResponses] +export type SuggestionListData = { + body?: never + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/suggestion" +} + +export type SuggestionListResponses = { + /** + * List of pending suggestions + */ + 200: Array +} + +export type SuggestionListResponse = SuggestionListResponses[keyof SuggestionListResponses] + +export type SuggestionAcceptData = { + body?: { + /** + * Zero-based action index to accept + */ + index: number + } + path: { + requestID: string + } + query?: { + directory?: string + workspace?: string + } + url: "/suggestion/{requestID}/accept" +} + +export type SuggestionAcceptErrors = { + /** + * Bad request + */ + 400: BadRequestError + /** + * Not found + */ + 404: NotFoundError +} + +export type SuggestionAcceptError = SuggestionAcceptErrors[keyof SuggestionAcceptErrors] + +export type SuggestionAcceptResponses = { + /** + * Suggestion accepted successfully + */ + 200: boolean +} + +export type SuggestionAcceptResponse = SuggestionAcceptResponses[keyof SuggestionAcceptResponses] + +export type SuggestionDismissData = { + body?: never + path: { + requestID: string + } + query?: { + directory?: string + workspace?: string + } + url: "/suggestion/{requestID}/dismiss" +} + +export type SuggestionDismissErrors = { + /** + * Bad request + */ + 400: BadRequestError + /** + * Not found + */ + 404: NotFoundError +} + +export type SuggestionDismissError = SuggestionDismissErrors[keyof SuggestionDismissErrors] + +export type SuggestionDismissResponses = { + /** + * Suggestion dismissed successfully + */ + 200: boolean +} + +export type SuggestionDismissResponse = SuggestionDismissResponses[keyof SuggestionDismissResponses] + export type TelemetryCaptureData = { body?: { /** diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 87b7f4714f2..d94e7d72b2d 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -14887,6 +14887,16 @@ } } }, + "commit_message": { + "description": "Configuration for AI-generated commit messages", + "type": "object", + "properties": { + "prompt": { + "description": "Custom system prompt for AI commit message generation. When set, replaces the default conventional commits prompt entirely.", + "type": "string" + } + } + }, "compaction": { "type": "object", "properties": {