feat(walkthrough): guided AI walkthrough for diffs, branches, and PRs (#2572)
A diff is ordered by file path, which is almost never the order in which a change makes sense. This adds a Walkthrough surface that reorders it: the model groups related hunks into stops, explains what each group changes about behavior, and orders the stops so each builds on the last. It explains and orders; judging code stays with the existing Review action. Reviews uncommitted work (all, staged, unstaged), a branch against its base, or a pull request. Generation is always user-initiated — nothing runs on a timer, on a file change, or as a side effect of opening a panel. Invariants worth preserving: - Hunk identity is derived on the server and only there. Ids are content hashes, so an anchor that no longer resolves is proof the code it described changed, and staleness needs no heuristics. The client matches ids to ids and never recomputes them; two implementations would have to agree forever. - The digest is never truncated. A diff that does not fit the model's context is refused with an actionable reason, because a walkthrough written against half a diff reads as confident and is wrong. - Nothing disappears. Lockfiles and other generated output are excluded from the model's input by name — never by size — and everything no stop covers is listed at the end, so "have I seen all of it" stays answerable. - Cost is explicit. Results are content-addressed, so returning the working tree to an earlier state costs nothing; generation outlives its request, so a refresh detaches the client rather than discarding paid-for work, and only an explicit cancel stops it. Supporting changes to shared modules: - git: expose the existing getRangeDiff as GET /api/git listUntrackedPaths and getUntrackedDiffs. The latter resolve the repository once for a batch instead of per file, taking a panel ~340ms on an 80-file working tree. - small-model: structured output across four wire forma and abort signal, and an onOverflow policy so an oversized prompt fails loudly instead of being silently clipped. A provider remembered so the prompt-side fallback goes first next time. - models.dev metadata: surface structured_output as tri false blocks a model, a missing field does not, because the catalog omits it for roughly half of all models. Desktop and tablet only: VS Code serves Git through its these routes, and the mobile shell does not consume the surface registry. Docs: packages/docs walkthrough page in English and all eight locales.
This commit is contained in:
committed by
GitHub
parent
b1ec34162e
commit
34d0ff7383
@@ -146,6 +146,17 @@ export interface GetGitDiffOptions {
|
||||
contextLines?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff between two refs. Uses three-dot (`base...head`) semantics server-side, so changes
|
||||
* pulled into `head` by merging `base` are excluded — only the branch's own work is returned.
|
||||
*/
|
||||
export interface GetGitRangeDiffOptions {
|
||||
base: string;
|
||||
head: string;
|
||||
path?: string;
|
||||
contextLines?: number;
|
||||
}
|
||||
|
||||
export interface GitFileDiffResponse {
|
||||
original: string;
|
||||
modified: string;
|
||||
@@ -453,6 +464,7 @@ export interface GitAPI {
|
||||
getGitStatus(directory: string, options?: { mode?: 'light' }): Promise<GitStatus>;
|
||||
getGitDiff(directory: string, options: GetGitDiffOptions): Promise<GitDiffResponse>;
|
||||
getGitFileDiff(directory: string, options: GetGitFileDiffOptions): Promise<GitFileDiffResponse>;
|
||||
getGitRangeDiff?(directory: string, options: GetGitRangeDiffOptions): Promise<GitDiffResponse>;
|
||||
revertGitFile(directory: string, filePath: string, options?: { scope?: 'all' | 'working' }): Promise<void>;
|
||||
stageGitFile(directory: string, filePath: string): Promise<void>;
|
||||
stageGitFiles?(directory: string, filePaths: string[]): Promise<void>;
|
||||
|
||||
@@ -131,6 +131,10 @@ export type DesktopSettings = {
|
||||
sessionGoalDefaultBudgetEnabled?: boolean;
|
||||
sessionGoalDefaultBudget?: number;
|
||||
smallModelOverride?: string; // format: "provider/model"
|
||||
// The walkthrough needs structured output and a roomy context, which the
|
||||
// small model is often deliberately not chosen for. Unset means "use the
|
||||
// small model"; a value replaces it for this feature only.
|
||||
walkthroughModelOverride?: string; // format: "provider/model"
|
||||
defaultGitIdentityId?: string; // ''/undefined = unset, 'global' or profile id
|
||||
openInAppId?: string;
|
||||
autoCreateWorktree?: boolean;
|
||||
|
||||
@@ -135,6 +135,15 @@ export async function getGitFileDiff(
|
||||
return gitHttp.getGitFileDiff(directory, options);
|
||||
}
|
||||
|
||||
export async function getGitRangeDiff(
|
||||
directory: string,
|
||||
options: import('./api/types').GetGitRangeDiffOptions
|
||||
): Promise<import('./api/types').GitDiffResponse> {
|
||||
const runtime = getRuntimeGit();
|
||||
if (runtime?.getGitRangeDiff) return runtime.getGitRangeDiff(directory, options);
|
||||
return gitHttp.getGitRangeDiff(directory, options);
|
||||
}
|
||||
|
||||
export async function revertGitFile(
|
||||
directory: string,
|
||||
filePath: string,
|
||||
|
||||
@@ -2,6 +2,7 @@ import type {
|
||||
GitStatus,
|
||||
GitDiffResponse,
|
||||
GetGitDiffOptions,
|
||||
GetGitRangeDiffOptions,
|
||||
GitFileDiffResponse,
|
||||
GetGitFileDiffOptions,
|
||||
GitBranch,
|
||||
@@ -222,6 +223,31 @@ export async function getGitDiff(directory: string, options: GetGitDiffOptions):
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getGitRangeDiff(
|
||||
directory: string,
|
||||
options: GetGitRangeDiffOptions
|
||||
): Promise<GitDiffResponse> {
|
||||
const { base, head, path, contextLines } = options;
|
||||
if (!base || !head) {
|
||||
throw new Error('base and head are required to fetch git range diff');
|
||||
}
|
||||
|
||||
const response = await runtimeFetch(
|
||||
buildUrl(`${API_BASE}/range-diff`, directory, {
|
||||
base,
|
||||
head,
|
||||
path: path || undefined,
|
||||
context: contextLines,
|
||||
})
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to get git range diff: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getGitFileDiff(directory: string, options: GetGitFileDiffOptions): Promise<GitFileDiffResponse> {
|
||||
const { path, staged } = options;
|
||||
if (!path) {
|
||||
|
||||
@@ -1554,6 +1554,10 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.smallModel.useDefault': 'Use default small model',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': 'Use default small model',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': 'Override model',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': 'Changes Walkthrough Model',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': 'The AI review of your changes needs structured output and room for a whole diff, which a cheap small model often cannot give. Models the catalog reports as unable to produce structured output are hidden from this picker. Leave it unset and the small model is used.',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': 'Walkthrough model',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': 'Small model',
|
||||
'settings.openchamber.defaults.field.openFilesPreviewAria': 'Open previewable files in preview mode',
|
||||
'settings.openchamber.defaults.field.openFilesPreview': 'Open previewable files in preview mode',
|
||||
'settings.openchamber.defaults.option.default': 'Default',
|
||||
|
||||
@@ -1095,6 +1095,56 @@ export const dict = {
|
||||
'contextRail.surface.git.description': 'Commits, branches, and pull requests',
|
||||
'contextRail.surface.terminal.description': 'Built-in terminal',
|
||||
'contextRail.surface.diff.description': 'Review working changes',
|
||||
'contextPanel.mode.walkthrough': 'Walkthrough',
|
||||
'contextRail.surface.walkthrough.description': 'An AI-guided walkthrough of your changes',
|
||||
'walkthrough.scope.all': 'All uncommitted',
|
||||
'walkthrough.scope.group.workingTree': 'Working tree',
|
||||
'walkthrough.scope.group.committed': 'Committed',
|
||||
'walkthrough.scope.staged': 'Staged',
|
||||
'walkthrough.scope.working': 'Unstaged',
|
||||
'walkthrough.scope.branch': 'This branch',
|
||||
'walkthrough.scope.selectorAria': 'Select what to review',
|
||||
'walkthrough.scope.pullRequest': 'PR #{number}',
|
||||
'walkthrough.action.generate': 'Generate walkthrough',
|
||||
'walkthrough.action.regenerate': 'Regenerate',
|
||||
'walkthrough.action.cancel': 'Cancel',
|
||||
'walkthrough.action.next': 'Next step',
|
||||
'walkthrough.action.previous': 'Previous step',
|
||||
'walkthrough.action.refresh': 'Refresh',
|
||||
'walkthrough.action.open': 'AI walkthrough',
|
||||
'walkthrough.stage.collecting': 'Collecting the changes',
|
||||
'walkthrough.stage.asking': 'Waiting on the model',
|
||||
'walkthrough.stage.assembling': 'Assembling the walkthrough',
|
||||
'walkthrough.empty.title': 'No walkthrough yet',
|
||||
'walkthrough.empty.description': 'Build a guided reading path through these changes. This calls the small model and costs tokens, so it only ever runs when you ask.',
|
||||
'walkthrough.stale.banner': 'The code changed after this review. Outdated steps: {count}',
|
||||
'walkthrough.stop.staleAll': 'All of the code this step described has changed.',
|
||||
'walkthrough.stop.stalePartial': 'Part of the code this step described has changed. Missing pieces: {count}',
|
||||
'walkthrough.stop.staleShort': 'Outdated',
|
||||
'walkthrough.stop.noCode': 'None of the code for this step is in the current diff.',
|
||||
'walkthrough.uncovered.title': 'Changes the review left out: {count}',
|
||||
'walkthrough.uncovered.description': 'The review treated these as routine. Expand to check them yourself.',
|
||||
'walkthrough.toc.moreFiles': 'More files: {count}',
|
||||
'walkthrough.toc.uncovered': 'Not covered: {count}',
|
||||
'walkthrough.toc.resize': 'Resize the contents column',
|
||||
'walkthrough.importance.critical': 'Critical',
|
||||
'walkthrough.importance.context': 'Context',
|
||||
'walkthrough.blocked.noModel.title': 'No small model available',
|
||||
'walkthrough.blocked.noModel.description': 'Sign in to a model provider to generate a review.',
|
||||
'walkthrough.blocked.emptyDiff.title': 'Nothing to review',
|
||||
'walkthrough.blocked.emptyDiff.description': 'There are no changes in this scope yet.',
|
||||
'walkthrough.blocked.contextTooSmall.title': 'This diff is too large for the current model',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model} fits about {available}K characters and this diff needs about {required}K. Nothing gets truncated — pick a model with a larger context instead.',
|
||||
'walkthrough.blocked.structuredOutput.title': 'This model cannot produce structured output',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model} does not support the structured responses a walkthrough needs.',
|
||||
'walkthrough.blocked.chooseModel': 'Choose the small model',
|
||||
'walkthrough.blocked.outputExhausted.title': 'This model ran out of answer budget',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model} spent its whole output allowance on reasoning and returned nothing. Reasoning models often do this on large diffs — a model that thinks less, or reviewing a narrower scope, will get through.',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': 'The small model spent its whole output allowance on reasoning and returned nothing. Reasoning models often do this on large diffs — a model that thinks less, or reviewing a narrower scope, will get through.',
|
||||
'walkthrough.blocked.onlyGenerated.title': 'Only generated files changed',
|
||||
'walkthrough.blocked.onlyGenerated.description': 'Every change here is a lockfile or other tool-produced output, which the review deliberately skips.',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': 'The small model fits about {available}K characters and this diff needs about {required}K. Nothing gets truncated — pick a model with a larger context instead.',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'The small model does not support the structured responses a walkthrough needs.',
|
||||
'contextRail.surface.plan.description': 'View the current plan',
|
||||
'contextRail.surface.pr.description': 'Create, review, and merge the pull request for the current branch',
|
||||
'contextRail.surface.notes.description': 'Notes, todos, and plans for the project',
|
||||
|
||||
@@ -1520,6 +1520,10 @@ export const settingsDict = {
|
||||
"settings.openchamber.defaults.smallModel.useDefault": "Usar el modelo pequeño predeterminado",
|
||||
"settings.openchamber.defaults.smallModel.useDefaultAria": "Usar el modelo pequeño predeterminado",
|
||||
"settings.openchamber.defaults.smallModel.overrideModel": "Modelo de anulación",
|
||||
"settings.openchamber.defaults.walkthroughModel.title": "Modelo del recorrido de cambios",
|
||||
"settings.openchamber.defaults.walkthroughModel.description": "La revisión con IA de tus cambios necesita salida estructurada y espacio para un diff completo, algo que un modelo pequeño y barato a menudo no ofrece. Los modelos que el catálogo declara incapaces de producir salida estructurada se ocultan aquí. Si no eliges nada, se usa el modelo pequeño.",
|
||||
"settings.openchamber.defaults.walkthroughModel.overrideModel": "Modelo del recorrido",
|
||||
"settings.openchamber.defaults.walkthroughModel.usesSmallModel": "Modelo pequeño",
|
||||
"settings.openchamber.defaults.field.showDeletionDialog": "Mostrar diálogo de eliminación",
|
||||
"settings.openchamber.defaults.field.openFilesPreviewAria": "Abrir archivos previsualizables en modo vista previa",
|
||||
"settings.openchamber.defaults.field.openFilesPreview": "Abrir archivos previsualizables en modo vista previa",
|
||||
|
||||
@@ -1096,6 +1096,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextRail.surface.git.description": "Commits, ramas y pull requests",
|
||||
"contextRail.surface.terminal.description": "Terminal integrada",
|
||||
"contextRail.surface.diff.description": "Revisar cambios en curso",
|
||||
"contextPanel.mode.walkthrough": "Recorrido",
|
||||
"contextRail.surface.walkthrough.description": "Un recorrido por tus cambios guiado por IA",
|
||||
"walkthrough.scope.all": "Todo sin confirmar",
|
||||
"walkthrough.scope.group.workingTree": "Árbol de trabajo",
|
||||
"walkthrough.scope.group.committed": "Confirmado",
|
||||
"walkthrough.scope.staged": "Preparados",
|
||||
"walkthrough.scope.working": "Sin preparar",
|
||||
"walkthrough.scope.branch": "Esta rama",
|
||||
"walkthrough.scope.selectorAria": "Elegir qué revisar",
|
||||
"walkthrough.scope.pullRequest": "PR n.º {number}",
|
||||
"walkthrough.action.generate": "Generar recorrido",
|
||||
"walkthrough.action.regenerate": "Regenerar",
|
||||
"walkthrough.action.cancel": "Cancelar",
|
||||
"walkthrough.action.next": "Paso siguiente",
|
||||
"walkthrough.action.previous": "Paso anterior",
|
||||
"walkthrough.action.refresh": "Actualizar",
|
||||
"walkthrough.action.open": "Recorrido con IA",
|
||||
"walkthrough.stage.collecting": "Reuniendo los cambios",
|
||||
"walkthrough.stage.asking": "Esperando al modelo",
|
||||
"walkthrough.stage.assembling": "Montando el recorrido",
|
||||
"walkthrough.empty.title": "Todavía no hay recorrido",
|
||||
"walkthrough.empty.description": "Crea una ruta de lectura guiada por estos cambios. Esto llama al modelo pequeño y consume tokens, así que solo se ejecuta cuando lo pides.",
|
||||
"walkthrough.stale.banner": "El código cambió después de esta revisión. Pasos desactualizados: {count}",
|
||||
"walkthrough.stop.staleAll": "Todo el código que describía este paso ha cambiado.",
|
||||
"walkthrough.stop.stalePartial": "Parte del código que describía este paso ha cambiado. Fragmentos perdidos: {count}",
|
||||
"walkthrough.stop.staleShort": "Desactualizado",
|
||||
"walkthrough.stop.noCode": "El código de este paso no está en el diff actual.",
|
||||
"walkthrough.uncovered.title": "Cambios que la revisión omitió: {count}",
|
||||
"walkthrough.uncovered.description": "La revisión los consideró rutinarios. Despliega para revisarlos tú mismo.",
|
||||
"walkthrough.toc.moreFiles": "Más archivos: {count}",
|
||||
"walkthrough.toc.uncovered": "Sin cubrir: {count}",
|
||||
"walkthrough.toc.resize": "Cambiar el ancho de la columna de contenidos",
|
||||
"walkthrough.importance.critical": "Crítico",
|
||||
"walkthrough.importance.context": "Contexto",
|
||||
"walkthrough.blocked.noModel.title": "No hay ningún modelo pequeño disponible",
|
||||
"walkthrough.blocked.noModel.description": "Inicia sesión en un proveedor de modelos para generar una revisión.",
|
||||
"walkthrough.blocked.emptyDiff.title": "Nada que revisar",
|
||||
"walkthrough.blocked.emptyDiff.description": "Todavía no hay cambios en este ámbito.",
|
||||
"walkthrough.blocked.contextTooSmall.title": "Este diff es demasiado grande para el modelo actual",
|
||||
"walkthrough.blocked.contextTooSmall.description": "{model} admite unos {available} mil caracteres y este diff necesita unos {required} mil. No se recorta nada: elige un modelo con más contexto.",
|
||||
"walkthrough.blocked.structuredOutput.title": "Este modelo no puede generar salida estructurada",
|
||||
"walkthrough.blocked.structuredOutput.description": "{model} no admite las respuestas estructuradas que necesita un recorrido.",
|
||||
"walkthrough.blocked.chooseModel": "Elige el modelo pequeño",
|
||||
"walkthrough.blocked.outputExhausted.title": "El modelo se quedó sin presupuesto de respuesta",
|
||||
"walkthrough.blocked.outputExhausted.description": "{model} gastó todo su margen de salida razonando y no devolvió nada. Los modelos de razonamiento suelen hacerlo con diffs grandes: prueba con un modelo que razone menos o revisa un ámbito más reducido.",
|
||||
"walkthrough.blocked.outputExhausted.descriptionUnknownModel": "El modelo pequeño gastó todo su margen de salida razonando y no devolvió nada. Los modelos de razonamiento suelen hacerlo con diffs grandes: prueba con un modelo que razone menos o revisa un ámbito más reducido.",
|
||||
"walkthrough.blocked.onlyGenerated.title": "Solo cambiaron archivos generados",
|
||||
"walkthrough.blocked.onlyGenerated.description": "Todos los cambios son archivos de bloqueo u otra salida generada por herramientas, que la revisión omite a propósito.",
|
||||
"walkthrough.blocked.contextTooSmall.descriptionUnknownModel": "El modelo pequeño admite unos {available} mil caracteres y este diff necesita unos {required} mil. No se recorta nada: elige un modelo con más contexto.",
|
||||
"walkthrough.blocked.structuredOutput.descriptionUnknownModel": "El modelo pequeño no admite las respuestas estructuradas que necesita un recorrido.",
|
||||
"contextRail.surface.plan.description": "Ver el plan actual",
|
||||
"contextRail.surface.pr.description": "Crea, revisa y fusiona el pull request de la rama actual",
|
||||
"contextRail.surface.notes.description": "Notas, tareas y planes del proyecto",
|
||||
|
||||
@@ -1441,6 +1441,10 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.smallModel.useDefault': 'Utiliser le petit modèle par défaut',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': 'Utiliser le petit modèle par défaut',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': 'Modèle de remplacement',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': 'Modèle du parcours des modifications',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': 'La revue par IA de vos modifications exige une sortie structurée et de la place pour un diff entier, ce qu’un petit modèle économique ne fournit pas toujours. Les modèles que le catalogue déclare incapables de sortie structurée sont masqués ici. Sans choix explicite, le petit modèle est utilisé.',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': 'Modèle du parcours',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': 'Petit modèle',
|
||||
'settings.openchamber.defaults.field.showDeletionDialog': 'Afficher la boîte de dialogue de suppression',
|
||||
'settings.openchamber.defaults.field.openFilesPreviewAria': 'Ouvrir les fichiers prévisualisables en mode aperçu',
|
||||
'settings.openchamber.defaults.field.openFilesPreview': 'Ouvrir les fichiers prévisualisables en mode aperçu',
|
||||
|
||||
@@ -919,6 +919,56 @@ export const dict = {
|
||||
'contextRail.surface.git.description': 'Commits, branches et pull requests',
|
||||
'contextRail.surface.terminal.description': 'Terminal intégré',
|
||||
'contextRail.surface.diff.description': 'Passer en revue les modifications',
|
||||
'contextPanel.mode.walkthrough': 'Parcours',
|
||||
'contextRail.surface.walkthrough.description': 'Un parcours de vos modifications guidé par l’IA',
|
||||
'walkthrough.scope.all': 'Tout non validé',
|
||||
'walkthrough.scope.group.workingTree': 'Copie de travail',
|
||||
'walkthrough.scope.group.committed': 'Validé',
|
||||
'walkthrough.scope.staged': 'Indexées',
|
||||
'walkthrough.scope.working': 'Non indexées',
|
||||
'walkthrough.scope.branch': 'Cette branche',
|
||||
'walkthrough.scope.selectorAria': 'Choisir ce qui est examiné',
|
||||
'walkthrough.scope.pullRequest': 'PR n° {number}',
|
||||
'walkthrough.action.generate': 'Générer le parcours',
|
||||
'walkthrough.action.regenerate': 'Régénérer',
|
||||
'walkthrough.action.cancel': 'Annuler',
|
||||
'walkthrough.action.next': 'Étape suivante',
|
||||
'walkthrough.action.previous': 'Étape précédente',
|
||||
'walkthrough.action.refresh': 'Actualiser',
|
||||
'walkthrough.action.open': 'Parcours IA',
|
||||
'walkthrough.stage.collecting': 'Collecte des modifications',
|
||||
'walkthrough.stage.asking': 'En attente du modèle',
|
||||
'walkthrough.stage.assembling': 'Assemblage du parcours',
|
||||
'walkthrough.empty.title': 'Aucun parcours pour l’instant',
|
||||
'walkthrough.empty.description': 'Construisez un parcours de lecture guidé à travers ces modifications. Cela appelle le petit modèle et consomme des jetons, donc rien ne se lance sans votre demande.',
|
||||
'walkthrough.stale.banner': 'Le code a changé après cette revue. Étapes obsolètes : {count}',
|
||||
'walkthrough.stop.staleAll': 'Tout le code décrit par cette étape a changé.',
|
||||
'walkthrough.stop.stalePartial': 'Une partie du code décrit par cette étape a changé. Fragments manquants : {count}',
|
||||
'walkthrough.stop.staleShort': 'Obsolète',
|
||||
'walkthrough.stop.noCode': 'Le code de cette étape est absent du diff actuel.',
|
||||
'walkthrough.uncovered.title': 'Modifications non traitées par la revue : {count}',
|
||||
'walkthrough.uncovered.description': 'La revue les a jugées courantes. Dépliez pour les vérifier vous-même.',
|
||||
'walkthrough.toc.moreFiles': 'Autres fichiers : {count}',
|
||||
'walkthrough.toc.uncovered': 'Non traité : {count}',
|
||||
'walkthrough.toc.resize': 'Redimensionner la colonne du sommaire',
|
||||
'walkthrough.importance.critical': 'Critique',
|
||||
'walkthrough.importance.context': 'Contexte',
|
||||
'walkthrough.blocked.noModel.title': 'Aucun petit modèle disponible',
|
||||
'walkthrough.blocked.noModel.description': 'Connectez-vous à un fournisseur de modèles pour générer une revue.',
|
||||
'walkthrough.blocked.emptyDiff.title': 'Rien à examiner',
|
||||
'walkthrough.blocked.emptyDiff.description': 'Il n’y a encore aucune modification dans cette portée.',
|
||||
'walkthrough.blocked.contextTooSmall.title': 'Ce diff est trop volumineux pour le modèle actuel',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model} accepte environ {available} k caractères et ce diff en demande environ {required} k. Rien n’est tronqué : choisissez un modèle au contexte plus large.',
|
||||
'walkthrough.blocked.structuredOutput.title': 'Ce modèle ne produit pas de sortie structurée',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model} ne prend pas en charge les réponses structurées nécessaires à un parcours.',
|
||||
'walkthrough.blocked.chooseModel': 'Choisissez le petit modèle',
|
||||
'walkthrough.blocked.outputExhausted.title': 'Le modèle a épuisé son budget de réponse',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model} a dépensé toute sa marge de sortie en raisonnement et n’a rien renvoyé. Les modèles de raisonnement le font souvent sur de gros diffs : essayez un modèle qui réfléchit moins, ou une portée plus étroite.',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': 'Le petit modèle a dépensé toute sa marge de sortie en raisonnement et n’a rien renvoyé. Les modèles de raisonnement le font souvent sur de gros diffs : essayez un modèle qui réfléchit moins, ou une portée plus étroite.',
|
||||
'walkthrough.blocked.onlyGenerated.title': 'Seuls des fichiers générés ont changé',
|
||||
'walkthrough.blocked.onlyGenerated.description': 'Toutes les modifications concernent des fichiers de verrouillage ou d’autres sorties générées, que la revue ignore délibérément.',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': 'Le petit modèle accepte environ {available} k caractères et ce diff en demande environ {required} k. Rien n’est tronqué : choisissez un modèle au contexte plus large.',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'Le petit modèle ne prend pas en charge les réponses structurées nécessaires à un parcours.',
|
||||
'contextRail.surface.plan.description': 'Voir le plan actuel',
|
||||
'contextRail.surface.pr.description': 'Créer, relire et fusionner la pull request de la branche actuelle',
|
||||
'contextRail.surface.notes.description': 'Notes, tâches et plans du projet',
|
||||
|
||||
@@ -1553,6 +1553,10 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.smallModel.useDefault': 'デフォルトの小型モデルを使用',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': 'デフォルトの小型モデルを使用',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': '上書きモデル',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': '変更ウォークスルーのモデル',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': '変更の AI レビューには構造化出力と差分全体が収まる余裕が必要で、安価なスモールモデルでは足りないことがあります。カタログが構造化出力に非対応と示すモデルは、この一覧から除外されます。 未選択のままならスモールモデルが使われます。',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': 'ウォークスルーのモデル',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': 'スモールモデル',
|
||||
'settings.openchamber.defaults.field.showDeletionDialog': '削除ダイアログを表示',
|
||||
'settings.openchamber.defaults.field.openFilesPreviewAria': 'プレビュー可能なファイルをプレビューモードで開く',
|
||||
'settings.openchamber.defaults.field.openFilesPreview': 'プレビュー可能なファイルをプレビューモードで開く',
|
||||
|
||||
@@ -1092,6 +1092,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextRail.surface.git.description': 'コミット・ブランチ・プルリクエスト',
|
||||
'contextRail.surface.terminal.description': '内蔵ターミナル',
|
||||
'contextRail.surface.diff.description': '作業中の変更をレビュー',
|
||||
'contextPanel.mode.walkthrough': 'ウォークスルー',
|
||||
'contextRail.surface.walkthrough.description': 'AI による変更のガイド付きウォークスルー',
|
||||
'walkthrough.scope.all': '未コミットすべて',
|
||||
'walkthrough.scope.group.workingTree': '作業ツリー',
|
||||
'walkthrough.scope.group.committed': 'コミット済み',
|
||||
'walkthrough.scope.staged': 'ステージ済み',
|
||||
'walkthrough.scope.working': '未ステージ',
|
||||
'walkthrough.scope.branch': 'このブランチ',
|
||||
'walkthrough.scope.selectorAria': 'レビュー対象を選択',
|
||||
'walkthrough.scope.pullRequest': 'PR #{number}',
|
||||
'walkthrough.action.generate': 'ウォークスルーを生成',
|
||||
'walkthrough.action.regenerate': '再生成',
|
||||
'walkthrough.action.cancel': 'キャンセル',
|
||||
'walkthrough.action.next': '次のステップ',
|
||||
'walkthrough.action.previous': '前のステップ',
|
||||
'walkthrough.action.refresh': '更新',
|
||||
'walkthrough.action.open': 'AI ウォークスルー',
|
||||
'walkthrough.stage.collecting': '変更を集めています',
|
||||
'walkthrough.stage.asking': 'モデルの応答を待っています',
|
||||
'walkthrough.stage.assembling': 'ウォークスルーを組み立てています',
|
||||
'walkthrough.empty.title': 'ウォークスルーはまだありません',
|
||||
'walkthrough.empty.description': 'この変更を読み進めるためのガイドを作成します。スモールモデルを呼び出してトークンを消費するため、実行は明示的な操作時のみです。',
|
||||
'walkthrough.stale.banner': 'このレビュー後にコードが変更されました。古くなったステップ: {count}',
|
||||
'walkthrough.stop.staleAll': 'このステップが説明していたコードはすべて変更されました。',
|
||||
'walkthrough.stop.stalePartial': 'このステップが説明していたコードの一部が変更されました。失われた箇所: {count}',
|
||||
'walkthrough.stop.staleShort': '古い内容',
|
||||
'walkthrough.stop.noCode': 'このステップのコードは現在の差分にありません。',
|
||||
'walkthrough.uncovered.title': 'レビューが扱わなかった変更: {count}',
|
||||
'walkthrough.uncovered.description': 'レビューは定型的と判断しました。展開してご自身で確認できます。',
|
||||
'walkthrough.toc.moreFiles': 'その他のファイル: {count}',
|
||||
'walkthrough.toc.uncovered': '未対応: {count}',
|
||||
'walkthrough.toc.resize': '目次の列幅を変更',
|
||||
'walkthrough.importance.critical': '重要',
|
||||
'walkthrough.importance.context': '補足',
|
||||
'walkthrough.blocked.noModel.title': '利用できるスモールモデルがありません',
|
||||
'walkthrough.blocked.noModel.description': 'レビューを生成するにはモデルプロバイダーにサインインしてください。',
|
||||
'walkthrough.blocked.emptyDiff.title': 'レビュー対象がありません',
|
||||
'walkthrough.blocked.emptyDiff.description': 'この範囲にはまだ変更がありません。',
|
||||
'walkthrough.blocked.contextTooSmall.title': 'この差分は現在のモデルには大きすぎます',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model} が扱えるのは約 {available} 千文字ですが、この差分には約 {required} 千文字が必要です。切り詰めは行いません。コンテキストの大きいモデルを選んでください。',
|
||||
'walkthrough.blocked.structuredOutput.title': 'このモデルは構造化出力に対応していません',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model} はウォークスルーに必要な構造化応答をサポートしていません。',
|
||||
'walkthrough.blocked.chooseModel': 'スモールモデルを選択',
|
||||
'walkthrough.blocked.outputExhausted.title': 'モデルの出力枠が尽きました',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model} は出力枠をすべて推論に使い、回答を返しませんでした。推論モデルは大きな差分でよくこうなります。推論の少ないモデルを選ぶか、対象範囲を絞ってください。',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': 'スモールモデルは出力枠をすべて推論に使い、回答を返しませんでした。推論モデルは大きな差分でよくこうなります。推論の少ないモデルを選ぶか、対象範囲を絞ってください。',
|
||||
'walkthrough.blocked.onlyGenerated.title': '生成ファイルのみが変更されています',
|
||||
'walkthrough.blocked.onlyGenerated.description': 'ここでの変更はロックファイルなどツールが生成した出力だけで、レビューは意図的にこれらを対象外にしています。',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': 'スモールモデルが扱えるのは約 {available} 千文字ですが、この差分には約 {required} 千文字が必要です。切り詰めは行いません。コンテキストの大きいモデルを選んでください。',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'スモールモデルはウォークスルーに必要な構造化応答をサポートしていません。',
|
||||
'contextRail.surface.plan.description': '現在のプランを表示',
|
||||
'contextRail.surface.pr.description': '現在のブランチのプルリクエストを作成・確認・マージ',
|
||||
'contextRail.surface.notes.description': 'プロジェクトのノート・ToDo・プラン',
|
||||
|
||||
@@ -1520,6 +1520,10 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.smallModel.useDefault': '기본 소형 모델 사용',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': '기본 소형 모델 사용',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': '재정의 모델',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': '변경 워크스루 모델',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': '변경 사항의 AI 리뷰에는 구조화된 출력과 전체 diff를 담을 여유가 필요한데, 저렴한 스몰 모델로는 부족할 때가 많습니다. 카탈로그가 구조화된 출력을 지원하지 않는다고 알리는 모델은 이 목록에서 숨겨집니다. 선택하지 않으면 스몰 모델이 사용됩니다.',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': '워크스루 모델',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': '스몰 모델',
|
||||
'settings.openchamber.defaults.field.showDeletionDialog': '삭제 확인 대화상자 표시',
|
||||
'settings.openchamber.defaults.field.openFilesPreviewAria': '미리보기 가능한 파일을 미리보기 모드로 열기',
|
||||
'settings.openchamber.defaults.field.openFilesPreview': '미리보기 가능한 파일을 미리보기 모드로 열기',
|
||||
|
||||
@@ -1096,6 +1096,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextRail.surface.git.description': '커밋, 브랜치, 풀 리퀘스트',
|
||||
'contextRail.surface.terminal.description': '내장 터미널',
|
||||
'contextRail.surface.diff.description': '작업 중인 변경 사항 검토',
|
||||
'contextPanel.mode.walkthrough': '워크스루',
|
||||
'contextRail.surface.walkthrough.description': 'AI가 안내하는 변경 사항 워크스루',
|
||||
'walkthrough.scope.all': '커밋되지 않은 전체',
|
||||
'walkthrough.scope.group.workingTree': '작업 트리',
|
||||
'walkthrough.scope.group.committed': '커밋됨',
|
||||
'walkthrough.scope.staged': '스테이지됨',
|
||||
'walkthrough.scope.working': '스테이지 안 됨',
|
||||
'walkthrough.scope.branch': '이 브랜치',
|
||||
'walkthrough.scope.selectorAria': '리뷰 대상 선택',
|
||||
'walkthrough.scope.pullRequest': 'PR #{number}',
|
||||
'walkthrough.action.generate': '워크스루 생성',
|
||||
'walkthrough.action.regenerate': '다시 생성',
|
||||
'walkthrough.action.cancel': '취소',
|
||||
'walkthrough.action.next': '다음 단계',
|
||||
'walkthrough.action.previous': '이전 단계',
|
||||
'walkthrough.action.refresh': '새로 고침',
|
||||
'walkthrough.action.open': 'AI 워크스루',
|
||||
'walkthrough.stage.collecting': '변경 사항을 모으는 중',
|
||||
'walkthrough.stage.asking': '모델을 기다리는 중',
|
||||
'walkthrough.stage.assembling': '워크스루를 조립하는 중',
|
||||
'walkthrough.empty.title': '아직 워크스루가 없습니다',
|
||||
'walkthrough.empty.description': '이 변경 사항을 따라 읽을 수 있는 경로를 만듭니다. 스몰 모델을 호출해 토큰을 사용하므로 요청할 때만 실행됩니다.',
|
||||
'walkthrough.stale.banner': '이 리뷰 이후에 코드가 바뀌었습니다. 오래된 단계: {count}',
|
||||
'walkthrough.stop.staleAll': '이 단계가 설명하던 코드가 모두 바뀌었습니다.',
|
||||
'walkthrough.stop.stalePartial': '이 단계가 설명하던 코드 일부가 바뀌었습니다. 사라진 부분: {count}',
|
||||
'walkthrough.stop.staleShort': '오래됨',
|
||||
'walkthrough.stop.noCode': '이 단계의 코드는 현재 diff에 없습니다.',
|
||||
'walkthrough.uncovered.title': '리뷰가 다루지 않은 변경: {count}',
|
||||
'walkthrough.uncovered.description': '리뷰가 일상적인 변경으로 판단했습니다. 펼쳐서 직접 확인하세요.',
|
||||
'walkthrough.toc.moreFiles': '다른 파일: {count}',
|
||||
'walkthrough.toc.uncovered': '미포함: {count}',
|
||||
'walkthrough.toc.resize': '목차 열 너비 조절',
|
||||
'walkthrough.importance.critical': '중요',
|
||||
'walkthrough.importance.context': '참고',
|
||||
'walkthrough.blocked.noModel.title': '사용할 수 있는 스몰 모델이 없습니다',
|
||||
'walkthrough.blocked.noModel.description': '리뷰를 생성하려면 모델 제공자에 로그인하세요.',
|
||||
'walkthrough.blocked.emptyDiff.title': '리뷰할 내용이 없습니다',
|
||||
'walkthrough.blocked.emptyDiff.description': '이 범위에는 아직 변경 사항이 없습니다.',
|
||||
'walkthrough.blocked.contextTooSmall.title': '이 diff는 현재 모델에 너무 큽니다',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model}은 약 {available}천 자를 담을 수 있는데 이 diff에는 약 {required}천 자가 필요합니다. 잘라내지 않으니 컨텍스트가 더 큰 모델을 선택하세요.',
|
||||
'walkthrough.blocked.structuredOutput.title': '이 모델은 구조화된 출력을 지원하지 않습니다',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model}은 워크스루에 필요한 구조화된 응답을 지원하지 않습니다.',
|
||||
'walkthrough.blocked.chooseModel': '스몰 모델 선택',
|
||||
'walkthrough.blocked.outputExhausted.title': '모델의 응답 예산이 바닥났습니다',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model}이(가) 출력 예산을 모두 추론에 쓰고 아무것도 반환하지 않았습니다. 추론 모델은 큰 diff에서 흔히 이렇게 됩니다. 덜 추론하는 모델을 고르거나 범위를 좁혀 보세요.',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': '스몰 모델이 출력 예산을 모두 추론에 쓰고 아무것도 반환하지 않았습니다. 추론 모델은 큰 diff에서 흔히 이렇게 됩니다. 덜 추론하는 모델을 고르거나 범위를 좁혀 보세요.',
|
||||
'walkthrough.blocked.onlyGenerated.title': '생성된 파일만 변경되었습니다',
|
||||
'walkthrough.blocked.onlyGenerated.description': '여기의 변경은 모두 잠금 파일이거나 도구가 만든 산출물이며, 리뷰는 이런 파일을 의도적으로 건너뜁니다.',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': '스몰 모델은 약 {available}천 자를 담을 수 있는데 이 diff에는 약 {required}천 자가 필요합니다. 잘라내지 않으니 컨텍스트가 더 큰 모델을 선택하세요.',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': '스몰 모델은 워크스루에 필요한 구조화된 응답을 지원하지 않습니다.',
|
||||
'contextRail.surface.plan.description': '현재 계획 보기',
|
||||
'contextRail.surface.pr.description': '현재 브랜치의 풀 리퀘스트를 생성, 검토, 병합',
|
||||
'contextRail.surface.notes.description': '프로젝트의 노트, 할 일, 계획',
|
||||
|
||||
@@ -739,6 +739,10 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.smallModel.useDefault': 'Używaj domyślnego małego modelu',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': 'Używaj domyślnego małego modelu',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': 'Model zastępczy',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': 'Model przewodnika po zmianach',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': 'Przegląd zmian przez AI wymaga ustrukturyzowanej odpowiedzi i miejsca na całe różnice, czego tani mały model często nie zapewnia. Modele, które katalog zgłasza jako niezdolne do ustrukturyzowanej odpowiedzi, są tu ukryte. Bez wyboru używany jest mały model.',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': 'Model przewodnika',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': 'Mały model',
|
||||
'settings.openchamber.defaults.field.thinkingPlaceholder': 'Myślenie',
|
||||
'settings.openchamber.defaults.option.default': 'Domyślne',
|
||||
'settings.openchamber.defaults.option.defaultLowercase': 'domyślne',
|
||||
|
||||
@@ -1411,6 +1411,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextRail.surface.git.description': 'Commity, gałęzie i pull requesty',
|
||||
'contextRail.surface.terminal.description': 'Wbudowany terminal',
|
||||
'contextRail.surface.diff.description': 'Przeglądaj bieżące zmiany',
|
||||
'contextPanel.mode.walkthrough': 'Przewodnik',
|
||||
'contextRail.surface.walkthrough.description': 'Przewodnik po zmianach prowadzony przez AI',
|
||||
'walkthrough.scope.all': 'Wszystko niezatwierdzone',
|
||||
'walkthrough.scope.group.workingTree': 'Drzewo robocze',
|
||||
'walkthrough.scope.group.committed': 'Zatwierdzone',
|
||||
'walkthrough.scope.staged': 'W poczekalni',
|
||||
'walkthrough.scope.working': 'Poza poczekalnią',
|
||||
'walkthrough.scope.branch': 'Ta gałąź',
|
||||
'walkthrough.scope.selectorAria': 'Wybierz, co przejrzeć',
|
||||
'walkthrough.scope.pullRequest': 'PR #{number}',
|
||||
'walkthrough.action.generate': 'Wygeneruj przewodnik',
|
||||
'walkthrough.action.regenerate': 'Wygeneruj ponownie',
|
||||
'walkthrough.action.cancel': 'Anuluj',
|
||||
'walkthrough.action.next': 'Następny krok',
|
||||
'walkthrough.action.previous': 'Poprzedni krok',
|
||||
'walkthrough.action.refresh': 'Odśwież',
|
||||
'walkthrough.action.open': 'Przewodnik AI',
|
||||
'walkthrough.stage.collecting': 'Zbieram zmiany',
|
||||
'walkthrough.stage.asking': 'Czekam na model',
|
||||
'walkthrough.stage.assembling': 'Składam przewodnik',
|
||||
'walkthrough.empty.title': 'Nie ma jeszcze przewodnika',
|
||||
'walkthrough.empty.description': 'Zbuduj prowadzoną ścieżkę czytania tych zmian. Wywołuje to mały model i zużywa tokeny, więc uruchamia się tylko na Twoje żądanie.',
|
||||
'walkthrough.stale.banner': 'Kod zmienił się po tym przeglądzie. Nieaktualne kroki: {count}',
|
||||
'walkthrough.stop.staleAll': 'Cały kod opisywany przez ten krok uległ zmianie.',
|
||||
'walkthrough.stop.stalePartial': 'Część kodu opisywanego przez ten krok uległa zmianie. Brakujące fragmenty: {count}',
|
||||
'walkthrough.stop.staleShort': 'Nieaktualne',
|
||||
'walkthrough.stop.noCode': 'Kodu z tego kroku nie ma w bieżących różnicach.',
|
||||
'walkthrough.uncovered.title': 'Zmiany pominięte w przeglądzie: {count}',
|
||||
'walkthrough.uncovered.description': 'Przegląd uznał je za rutynowe. Rozwiń, aby sprawdzić je samodzielnie.',
|
||||
'walkthrough.toc.moreFiles': 'Więcej plików: {count}',
|
||||
'walkthrough.toc.uncovered': 'Nieuwzględnione: {count}',
|
||||
'walkthrough.toc.resize': 'Zmień szerokość kolumny spisu treści',
|
||||
'walkthrough.importance.critical': 'Krytyczne',
|
||||
'walkthrough.importance.context': 'Kontekst',
|
||||
'walkthrough.blocked.noModel.title': 'Brak dostępnego małego modelu',
|
||||
'walkthrough.blocked.noModel.description': 'Zaloguj się u dostawcy modeli, aby wygenerować przegląd.',
|
||||
'walkthrough.blocked.emptyDiff.title': 'Nie ma czego przeglądać',
|
||||
'walkthrough.blocked.emptyDiff.description': 'W tym zakresie nie ma jeszcze zmian.',
|
||||
'walkthrough.blocked.contextTooSmall.title': 'Te różnice są za duże dla bieżącego modelu',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model} mieści około {available} tys. znaków, a te różnice potrzebują około {required} tys. Nic nie jest obcinane — wybierz model z większym kontekstem.',
|
||||
'walkthrough.blocked.structuredOutput.title': 'Ten model nie tworzy ustrukturyzowanych odpowiedzi',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model} nie obsługuje ustrukturyzowanych odpowiedzi wymaganych przez przewodnik.',
|
||||
'walkthrough.blocked.chooseModel': 'Wybierz mały model',
|
||||
'walkthrough.blocked.outputExhausted.title': 'Modelowi zabrakło budżetu na odpowiedź',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model} zużył cały limit wyjścia na rozumowanie i nic nie zwrócił. Modele rozumujące często tak robią przy dużych różnicach — pomoże model mniej „myślący” albo węższy zakres przeglądu.',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': 'Mały model zużył cały limit wyjścia na rozumowanie i nic nie zwrócił. Modele rozumujące często tak robią przy dużych różnicach — pomoże model mniej „myślący” albo węższy zakres przeglądu.',
|
||||
'walkthrough.blocked.onlyGenerated.title': 'Zmieniły się tylko pliki generowane',
|
||||
'walkthrough.blocked.onlyGenerated.description': 'Wszystkie zmiany to pliki blokad lub inne wyniki pracy narzędzi, które przegląd celowo pomija.',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': 'Mały model mieści około {available} tys. znaków, a te różnice potrzebują około {required} tys. Nic nie jest obcinane — wybierz model z większym kontekstem.',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'Mały model nie obsługuje ustrukturyzowanych odpowiedzi wymaganych przez przewodnik.',
|
||||
'contextRail.surface.plan.description': 'Zobacz bieżący plan',
|
||||
'contextRail.surface.pr.description': 'Twórz, przeglądaj i scalaj pull request bieżącej gałęzi',
|
||||
'contextRail.surface.notes.description': 'Notatki, zadania i plany projektu',
|
||||
|
||||
@@ -1520,6 +1520,10 @@ export const settingsDict = {
|
||||
"settings.openchamber.defaults.smallModel.useDefault": "Usar o modelo pequeno padrão",
|
||||
"settings.openchamber.defaults.smallModel.useDefaultAria": "Usar o modelo pequeno padrão",
|
||||
"settings.openchamber.defaults.smallModel.overrideModel": "Modelo de substituição",
|
||||
"settings.openchamber.defaults.walkthroughModel.title": "Modelo do percurso de mudanças",
|
||||
"settings.openchamber.defaults.walkthroughModel.description": "A revisão das suas mudanças por IA precisa de saída estruturada e espaço para um diff inteiro, o que um modelo pequeno e barato muitas vezes não oferece. Modelos que o catálogo indica como incapazes de saída estruturada ficam ocultos aqui. Sem escolha, o modelo pequeno é usado.",
|
||||
"settings.openchamber.defaults.walkthroughModel.overrideModel": "Modelo do percurso",
|
||||
"settings.openchamber.defaults.walkthroughModel.usesSmallModel": "Modelo pequeno",
|
||||
"settings.openchamber.defaults.field.showDeletionDialog": "Mostrar diálogo de eliminación",
|
||||
"settings.openchamber.defaults.field.openFilesPreviewAria": "Abrir arquivos com visualização em modo de prévia",
|
||||
"settings.openchamber.defaults.field.openFilesPreview": "Abrir arquivos com visualização em modo de prévia",
|
||||
|
||||
@@ -1096,6 +1096,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextRail.surface.git.description": "Commits, branches e pull requests",
|
||||
"contextRail.surface.terminal.description": "Terminal integrado",
|
||||
"contextRail.surface.diff.description": "Revisar alterações em andamento",
|
||||
"contextPanel.mode.walkthrough": "Percurso",
|
||||
"contextRail.surface.walkthrough.description": "Um percurso pelas suas mudanças guiado por IA",
|
||||
"walkthrough.scope.all": "Tudo sem commit",
|
||||
"walkthrough.scope.group.workingTree": "Árvore de trabalho",
|
||||
"walkthrough.scope.group.committed": "Com commit",
|
||||
"walkthrough.scope.staged": "No stage",
|
||||
"walkthrough.scope.working": "Fora do stage",
|
||||
"walkthrough.scope.branch": "Este branch",
|
||||
"walkthrough.scope.selectorAria": "Escolher o que revisar",
|
||||
"walkthrough.scope.pullRequest": "PR nº {number}",
|
||||
"walkthrough.action.generate": "Gerar percurso",
|
||||
"walkthrough.action.regenerate": "Gerar novamente",
|
||||
"walkthrough.action.cancel": "Cancelar",
|
||||
"walkthrough.action.next": "Próxima etapa",
|
||||
"walkthrough.action.previous": "Etapa anterior",
|
||||
"walkthrough.action.refresh": "Atualizar",
|
||||
"walkthrough.action.open": "Percurso com IA",
|
||||
"walkthrough.stage.collecting": "Reunindo as mudanças",
|
||||
"walkthrough.stage.asking": "Aguardando o modelo",
|
||||
"walkthrough.stage.assembling": "Montando o percurso",
|
||||
"walkthrough.empty.title": "Ainda não há percurso",
|
||||
"walkthrough.empty.description": "Monte um percurso de leitura por estas mudanças. Isso chama o modelo pequeno e consome tokens, então só roda quando você pede.",
|
||||
"walkthrough.stale.banner": "O código mudou depois desta revisão. Etapas desatualizadas: {count}",
|
||||
"walkthrough.stop.staleAll": "Todo o código que esta etapa descrevia mudou.",
|
||||
"walkthrough.stop.stalePartial": "Parte do código que esta etapa descrevia mudou. Trechos perdidos: {count}",
|
||||
"walkthrough.stop.staleShort": "Desatualizado",
|
||||
"walkthrough.stop.noCode": "O código desta etapa não está no diff atual.",
|
||||
"walkthrough.uncovered.title": "Mudanças que a revisão deixou de fora: {count}",
|
||||
"walkthrough.uncovered.description": "A revisão considerou essas mudanças rotineiras. Expanda para conferir você mesmo.",
|
||||
"walkthrough.toc.moreFiles": "Mais arquivos: {count}",
|
||||
"walkthrough.toc.uncovered": "Sem cobertura: {count}",
|
||||
"walkthrough.toc.resize": "Redimensionar a coluna de conteúdo",
|
||||
"walkthrough.importance.critical": "Crítico",
|
||||
"walkthrough.importance.context": "Contexto",
|
||||
"walkthrough.blocked.noModel.title": "Nenhum modelo pequeno disponível",
|
||||
"walkthrough.blocked.noModel.description": "Entre em um provedor de modelos para gerar uma revisão.",
|
||||
"walkthrough.blocked.emptyDiff.title": "Nada para revisar",
|
||||
"walkthrough.blocked.emptyDiff.description": "Ainda não há mudanças neste escopo.",
|
||||
"walkthrough.blocked.contextTooSmall.title": "Este diff é grande demais para o modelo atual",
|
||||
"walkthrough.blocked.contextTooSmall.description": "{model} comporta cerca de {available} mil caracteres e este diff precisa de cerca de {required} mil. Nada é cortado — escolha um modelo com contexto maior.",
|
||||
"walkthrough.blocked.structuredOutput.title": "Este modelo não produz saída estruturada",
|
||||
"walkthrough.blocked.structuredOutput.description": "{model} não suporta as respostas estruturadas que um percurso exige.",
|
||||
"walkthrough.blocked.chooseModel": "Escolha o modelo pequeno",
|
||||
"walkthrough.blocked.outputExhausted.title": "O modelo ficou sem orçamento de resposta",
|
||||
"walkthrough.blocked.outputExhausted.description": "{model} gastou toda a margem de saída raciocinando e não devolveu nada. Modelos de raciocínio costumam fazer isso em diffs grandes — escolha um modelo que raciocine menos ou revise um escopo menor.",
|
||||
"walkthrough.blocked.outputExhausted.descriptionUnknownModel": "O modelo pequeno gastou toda a margem de saída raciocinando e não devolveu nada. Modelos de raciocínio costumam fazer isso em diffs grandes — escolha um modelo que raciocine menos ou revise um escopo menor.",
|
||||
"walkthrough.blocked.onlyGenerated.title": "Só mudaram arquivos gerados",
|
||||
"walkthrough.blocked.onlyGenerated.description": "Todas as mudanças são arquivos de lock ou outra saída gerada por ferramentas, que a revisão ignora de propósito.",
|
||||
"walkthrough.blocked.contextTooSmall.descriptionUnknownModel": "O modelo pequeno comporta cerca de {available} mil caracteres e este diff precisa de cerca de {required} mil. Nada é cortado — escolha um modelo com contexto maior.",
|
||||
"walkthrough.blocked.structuredOutput.descriptionUnknownModel": "O modelo pequeno não suporta as respostas estruturadas que um percurso exige.",
|
||||
"contextRail.surface.plan.description": "Ver o plano atual",
|
||||
"contextRail.surface.pr.description": "Crie, revise e faça merge do pull request do branch atual",
|
||||
"contextRail.surface.notes.description": "Notas, tarefas e planos do projeto",
|
||||
|
||||
@@ -1520,6 +1520,10 @@ export const settingsDict = {
|
||||
"settings.openchamber.defaults.smallModel.useDefault": "Використовувати типову малу модель",
|
||||
"settings.openchamber.defaults.smallModel.useDefaultAria": "Використовувати типову малу модель",
|
||||
"settings.openchamber.defaults.smallModel.overrideModel": "Модель заміни",
|
||||
"settings.openchamber.defaults.walkthroughModel.title": "Модель для розбору змін",
|
||||
"settings.openchamber.defaults.walkthroughModel.description": "AI-розбір ваших змін потребує structured output і місця під увесь diff, чого дешева small model часто дати не може. Моделі, які за каталогом не вміють structured output, у цьому списку приховані. Якщо нічого не обрано, використовується small model.",
|
||||
"settings.openchamber.defaults.walkthroughModel.overrideModel": "Модель для розбору",
|
||||
"settings.openchamber.defaults.walkthroughModel.usesSmallModel": "Small model",
|
||||
"settings.openchamber.defaults.field.showDeletionDialog": "Показати діалогове вікно видалення",
|
||||
"settings.openchamber.defaults.field.openFilesPreviewAria": "Відкривати файли з підтримкою перегляду в режимі попереднього перегляду",
|
||||
"settings.openchamber.defaults.field.openFilesPreview": "Відкривати файли з підтримкою перегляду в режимі попереднього перегляду",
|
||||
|
||||
@@ -1096,6 +1096,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextRail.surface.git.description": "Коміти, гілки та pull request-и",
|
||||
"contextRail.surface.terminal.description": "Вбудований термінал",
|
||||
"contextRail.surface.diff.description": "Перегляд поточних змін",
|
||||
"contextPanel.mode.walkthrough": "Розбір",
|
||||
"contextRail.surface.walkthrough.description": "Покроковий розбір ваших змін за допомогою AI",
|
||||
"walkthrough.scope.all": "Усе незакомічене",
|
||||
"walkthrough.scope.group.workingTree": "Робоче дерево",
|
||||
"walkthrough.scope.group.committed": "Закомічене",
|
||||
"walkthrough.scope.staged": "В індексі",
|
||||
"walkthrough.scope.working": "Поза індексом",
|
||||
"walkthrough.scope.branch": "Ця гілка",
|
||||
"walkthrough.scope.selectorAria": "Обрати, що розбирати",
|
||||
"walkthrough.scope.pullRequest": "PR #{number}",
|
||||
"walkthrough.action.generate": "Створити розбір",
|
||||
"walkthrough.action.regenerate": "Створити заново",
|
||||
"walkthrough.action.cancel": "Скасувати",
|
||||
"walkthrough.action.next": "Наступний крок",
|
||||
"walkthrough.action.previous": "Попередній крок",
|
||||
"walkthrough.action.refresh": "Оновити",
|
||||
"walkthrough.action.open": "AI-розбір",
|
||||
"walkthrough.stage.collecting": "Збираю зміни",
|
||||
"walkthrough.stage.asking": "Чекаю на модель",
|
||||
"walkthrough.stage.assembling": "Складаю розбір",
|
||||
"walkthrough.empty.title": "Розбору ще немає",
|
||||
"walkthrough.empty.description": "Побудуйте маршрут читання цих змін. Це звертається до small model і витрачає токени, тому запускається лише на ваш запит.",
|
||||
"walkthrough.stale.banner": "Код змінився після цього розбору. Застарілих кроків: {count}",
|
||||
"walkthrough.stop.staleAll": "Увесь код, який описував цей крок, змінився.",
|
||||
"walkthrough.stop.stalePartial": "Частина коду, який описував цей крок, змінилася. Втрачених фрагментів: {count}",
|
||||
"walkthrough.stop.staleShort": "Застаріле",
|
||||
"walkthrough.stop.noCode": "Коду для цього кроку немає в поточному diff.",
|
||||
"walkthrough.uncovered.title": "Зміни, які розбір не описав: {count}",
|
||||
"walkthrough.uncovered.description": "Розбір визнав їх рутинними. Розгорніть, щоб переглянути самостійно.",
|
||||
"walkthrough.toc.moreFiles": "Ще файлів: {count}",
|
||||
"walkthrough.toc.uncovered": "Не описано: {count}",
|
||||
"walkthrough.toc.resize": "Змінити ширину колонки змісту",
|
||||
"walkthrough.importance.critical": "Критично",
|
||||
"walkthrough.importance.context": "Контекст",
|
||||
"walkthrough.blocked.noModel.title": "Немає доступної small model",
|
||||
"walkthrough.blocked.noModel.description": "Увійдіть до провайдера моделей, щоб створити розбір.",
|
||||
"walkthrough.blocked.emptyDiff.title": "Немає що оглядати",
|
||||
"walkthrough.blocked.emptyDiff.description": "У цій області поки що немає змін.",
|
||||
"walkthrough.blocked.contextTooSmall.title": "Цей diff завеликий для поточної моделі",
|
||||
"walkthrough.blocked.contextTooSmall.description": "{model} вміщає близько {available} тис. символів, а цьому diff потрібно близько {required} тис. Нічого не обрізається — оберіть модель із більшим контекстом.",
|
||||
"walkthrough.blocked.structuredOutput.title": "Ця модель не вміє structured output",
|
||||
"walkthrough.blocked.structuredOutput.description": "{model} не підтримує структуровані відповіді, потрібні для розбору.",
|
||||
"walkthrough.blocked.chooseModel": "Оберіть small model",
|
||||
"walkthrough.blocked.outputExhausted.title": "Моделі забракло бюджету на відповідь",
|
||||
"walkthrough.blocked.outputExhausted.description": "{model} витратила весь бюджет виводу на роздуми й нічого не повернула. Reasoning-моделі часто так поводяться на великих diff — допоможе модель, яка менше «думає», або вужча область огляду.",
|
||||
"walkthrough.blocked.outputExhausted.descriptionUnknownModel": "Small model витратила весь бюджет виводу на роздуми й нічого не повернула. Reasoning-моделі часто так поводяться на великих diff — допоможе модель, яка менше «думає», або вужча область огляду.",
|
||||
"walkthrough.blocked.onlyGenerated.title": "Змінились лише згенеровані файли",
|
||||
"walkthrough.blocked.onlyGenerated.description": "Усі зміни тут — це lock-файли чи інший результат роботи інструментів, які розбір свідомо пропускає.",
|
||||
"walkthrough.blocked.contextTooSmall.descriptionUnknownModel": "Small model вміщає близько {available} тис. символів, а цьому diff потрібно близько {required} тис. Нічого не обрізається — оберіть модель із більшим контекстом.",
|
||||
"walkthrough.blocked.structuredOutput.descriptionUnknownModel": "Small model не підтримує структуровані відповіді, потрібні для розбору.",
|
||||
"contextRail.surface.plan.description": "Перегляд поточного плану",
|
||||
"contextRail.surface.pr.description": "Створюйте, переглядайте та зливайте pull request поточної гілки",
|
||||
"contextRail.surface.notes.description": "Нотатки, задачі та плани проєкту",
|
||||
|
||||
@@ -1520,6 +1520,10 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.smallModel.useDefault': '使用默认小模型',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': '使用默认小模型',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': '覆盖模型',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': '改动导读模型',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': 'AI 评审改动需要结构化输出和容纳整份差异的空间,廉价的小模型往往做不到。目录中标注为不支持结构化输出的模型不会出现在此列表里。 未选择时将使用小模型。',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': '导读模型',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': '小模型',
|
||||
'settings.openchamber.defaults.field.showDeletionDialog': '显示删除对话框',
|
||||
'settings.openchamber.defaults.field.openFilesPreviewAria': '以预览模式打开可预览文件',
|
||||
'settings.openchamber.defaults.field.openFilesPreview': '以预览模式打开可预览文件',
|
||||
|
||||
@@ -1096,6 +1096,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextRail.surface.git.description': '提交、分支和拉取请求',
|
||||
'contextRail.surface.terminal.description': '内置终端',
|
||||
'contextRail.surface.diff.description': '查看工作区更改',
|
||||
'contextPanel.mode.walkthrough': '导读',
|
||||
'contextRail.surface.walkthrough.description': '由 AI 引导的改动导读',
|
||||
'walkthrough.scope.all': '全部未提交',
|
||||
'walkthrough.scope.group.workingTree': '工作区',
|
||||
'walkthrough.scope.group.committed': '已提交',
|
||||
'walkthrough.scope.staged': '已暂存',
|
||||
'walkthrough.scope.working': '未暂存',
|
||||
'walkthrough.scope.branch': '当前分支',
|
||||
'walkthrough.scope.selectorAria': '选择评审范围',
|
||||
'walkthrough.scope.pullRequest': 'PR #{number}',
|
||||
'walkthrough.action.generate': '生成导读',
|
||||
'walkthrough.action.regenerate': '重新生成',
|
||||
'walkthrough.action.cancel': '取消',
|
||||
'walkthrough.action.next': '下一步',
|
||||
'walkthrough.action.previous': '上一步',
|
||||
'walkthrough.action.refresh': '刷新',
|
||||
'walkthrough.action.open': 'AI 导读',
|
||||
'walkthrough.stage.collecting': '正在收集改动',
|
||||
'walkthrough.stage.asking': '正在等待模型',
|
||||
'walkthrough.stage.assembling': '正在组装导读',
|
||||
'walkthrough.empty.title': '还没有导读',
|
||||
'walkthrough.empty.description': '为这些改动生成一条引导式阅读路线。该操作会调用小模型并消耗 token,因此只在你主动请求时运行。',
|
||||
'walkthrough.stale.banner': '本次评审之后代码有变动。过时的步骤:{count}',
|
||||
'walkthrough.stop.staleAll': '该步骤描述的代码已全部变动。',
|
||||
'walkthrough.stop.stalePartial': '该步骤描述的部分代码已变动。缺失片段:{count}',
|
||||
'walkthrough.stop.staleShort': '已过时',
|
||||
'walkthrough.stop.noCode': '当前差异中没有该步骤的代码。',
|
||||
'walkthrough.uncovered.title': '评审未涵盖的改动:{count}',
|
||||
'walkthrough.uncovered.description': '评审认为这些改动较常规。展开可自行查看。',
|
||||
'walkthrough.toc.moreFiles': '其他文件:{count}',
|
||||
'walkthrough.toc.uncovered': '未涵盖:{count}',
|
||||
'walkthrough.toc.resize': '调整目录栏宽度',
|
||||
'walkthrough.importance.critical': '关键',
|
||||
'walkthrough.importance.context': '背景',
|
||||
'walkthrough.blocked.noModel.title': '没有可用的小模型',
|
||||
'walkthrough.blocked.noModel.description': '请登录模型提供方后再生成评审。',
|
||||
'walkthrough.blocked.emptyDiff.title': '没有可评审的内容',
|
||||
'walkthrough.blocked.emptyDiff.description': '该范围内暂无改动。',
|
||||
'walkthrough.blocked.contextTooSmall.title': '当前模型无法容纳这份差异',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model} 大约可容纳 {available} 千字符,而这份差异约需 {required} 千字符。我们不会截断内容,请改选上下文更大的模型。',
|
||||
'walkthrough.blocked.structuredOutput.title': '该模型不支持结构化输出',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model} 不支持导读所需的结构化响应。',
|
||||
'walkthrough.blocked.chooseModel': '选择小模型',
|
||||
'walkthrough.blocked.outputExhausted.title': '模型的回答额度已用尽',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model} 把全部输出额度用在了推理上,没有返回结果。推理模型在大差异上经常如此——可以换一个少推理的模型,或缩小评审范围。',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': '小模型把全部输出额度用在了推理上,没有返回结果。推理模型在大差异上经常如此——可以换一个少推理的模型,或缩小评审范围。',
|
||||
'walkthrough.blocked.onlyGenerated.title': '只有生成文件发生了改动',
|
||||
'walkthrough.blocked.onlyGenerated.description': '这里的改动全部是锁文件或其他工具生成的产物,评审会有意跳过它们。',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': '小模型大约可容纳 {available} 千字符,而这份差异约需 {required} 千字符。我们不会截断内容,请改选上下文更大的模型。',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': '小模型不支持导读所需的结构化响应。',
|
||||
'contextRail.surface.plan.description': '查看当前计划',
|
||||
'contextRail.surface.pr.description': '创建、审查并合并当前分支的拉取请求',
|
||||
'contextRail.surface.notes.description': '项目的笔记、待办和计划',
|
||||
|
||||
@@ -1426,6 +1426,10 @@
|
||||
'settings.openchamber.defaults.smallModel.useDefault': '使用預設小模型',
|
||||
'settings.openchamber.defaults.smallModel.useDefaultAria': '使用預設小模型',
|
||||
'settings.openchamber.defaults.smallModel.overrideModel': '覆寫模型',
|
||||
'settings.openchamber.defaults.walkthroughModel.title': '變更導讀模型',
|
||||
'settings.openchamber.defaults.walkthroughModel.description': 'AI 審閱變更需要結構化輸出與容納整份差異的空間,便宜的小模型常常做不到。目錄標示為不支援結構化輸出的模型不會出現在這份清單中。 未選擇時將使用小模型。',
|
||||
'settings.openchamber.defaults.walkthroughModel.overrideModel': '導讀模型',
|
||||
'settings.openchamber.defaults.walkthroughModel.usesSmallModel': '小模型',
|
||||
'settings.openchamber.defaults.field.showDeletionDialog': '顯示刪除對話方塊',
|
||||
'settings.openchamber.defaults.field.openFilesPreviewAria': '以預覽模式開啟可預覽檔案',
|
||||
'settings.openchamber.defaults.field.openFilesPreview': '以預覽模式開啟可預覽檔案',
|
||||
|
||||
@@ -1108,6 +1108,56 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextRail.surface.git.description': '提交、分支與拉取請求',
|
||||
'contextRail.surface.terminal.description': '內建終端機',
|
||||
'contextRail.surface.diff.description': '檢視工作區變更',
|
||||
'contextPanel.mode.walkthrough': '導讀',
|
||||
'contextRail.surface.walkthrough.description': '由 AI 引導的變更導讀',
|
||||
'walkthrough.scope.all': '全部未提交',
|
||||
'walkthrough.scope.group.workingTree': '工作區',
|
||||
'walkthrough.scope.group.committed': '已提交',
|
||||
'walkthrough.scope.staged': '已暫存',
|
||||
'walkthrough.scope.working': '未暫存',
|
||||
'walkthrough.scope.branch': '目前分支',
|
||||
'walkthrough.scope.selectorAria': '選擇審閱範圍',
|
||||
'walkthrough.scope.pullRequest': 'PR #{number}',
|
||||
'walkthrough.action.generate': '產生導讀',
|
||||
'walkthrough.action.regenerate': '重新產生',
|
||||
'walkthrough.action.cancel': '取消',
|
||||
'walkthrough.action.next': '下一步',
|
||||
'walkthrough.action.previous': '上一步',
|
||||
'walkthrough.action.refresh': '重新整理',
|
||||
'walkthrough.action.open': 'AI 導讀',
|
||||
'walkthrough.stage.collecting': '正在收集變更',
|
||||
'walkthrough.stage.asking': '正在等待模型',
|
||||
'walkthrough.stage.assembling': '正在組裝導讀',
|
||||
'walkthrough.empty.title': '尚未有導讀',
|
||||
'walkthrough.empty.description': '為這些變更建立一條引導式閱讀路線。此操作會呼叫小模型並消耗 token,因此只在你主動要求時執行。',
|
||||
'walkthrough.stale.banner': '本次審閱之後程式碼有變動。過時的步驟:{count}',
|
||||
'walkthrough.stop.staleAll': '此步驟描述的程式碼已全部變動。',
|
||||
'walkthrough.stop.stalePartial': '此步驟描述的部分程式碼已變動。缺少的片段:{count}',
|
||||
'walkthrough.stop.staleShort': '已過時',
|
||||
'walkthrough.stop.noCode': '目前差異中沒有此步驟的程式碼。',
|
||||
'walkthrough.uncovered.title': '審閱未涵蓋的變更:{count}',
|
||||
'walkthrough.uncovered.description': '審閱認為這些變更較為例行。展開即可自行檢視。',
|
||||
'walkthrough.toc.moreFiles': '其他檔案:{count}',
|
||||
'walkthrough.toc.uncovered': '未涵蓋:{count}',
|
||||
'walkthrough.toc.resize': '調整目錄欄寬度',
|
||||
'walkthrough.importance.critical': '關鍵',
|
||||
'walkthrough.importance.context': '背景',
|
||||
'walkthrough.blocked.noModel.title': '沒有可用的小模型',
|
||||
'walkthrough.blocked.noModel.description': '請先登入模型供應商再產生審閱。',
|
||||
'walkthrough.blocked.emptyDiff.title': '沒有可審閱的內容',
|
||||
'walkthrough.blocked.emptyDiff.description': '此範圍目前沒有變更。',
|
||||
'walkthrough.blocked.contextTooSmall.title': '目前模型無法容納這份差異',
|
||||
'walkthrough.blocked.contextTooSmall.description': '{model} 大約可容納 {available} 千字元,而這份差異約需 {required} 千字元。我們不會截斷內容,請改選上下文更大的模型。',
|
||||
'walkthrough.blocked.structuredOutput.title': '此模型不支援結構化輸出',
|
||||
'walkthrough.blocked.structuredOutput.description': '{model} 不支援導讀所需的結構化回應。',
|
||||
'walkthrough.blocked.chooseModel': '選擇小模型',
|
||||
'walkthrough.blocked.outputExhausted.title': '模型的回答額度已用完',
|
||||
'walkthrough.blocked.outputExhausted.description': '{model} 把全部輸出額度用在推理上,沒有回傳結果。推理模型在大型差異上經常如此——可以改用較少推理的模型,或縮小審閱範圍。',
|
||||
'walkthrough.blocked.outputExhausted.descriptionUnknownModel': '小模型把全部輸出額度用在推理上,沒有回傳結果。推理模型在大型差異上經常如此——可以改用較少推理的模型,或縮小審閱範圍。',
|
||||
'walkthrough.blocked.onlyGenerated.title': '只有產生的檔案有變動',
|
||||
'walkthrough.blocked.onlyGenerated.description': '這裡的變更全部是鎖定檔或其他工具產生的輸出,審閱會刻意略過它們。',
|
||||
'walkthrough.blocked.contextTooSmall.descriptionUnknownModel': '小模型大約可容納 {available} 千字元,而這份差異約需 {required} 千字元。我們不會截斷內容,請改選上下文更大的模型。',
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': '小模型不支援導讀所需的結構化回應。',
|
||||
'contextRail.surface.plan.description': '檢視目前計畫',
|
||||
'contextRail.surface.pr.description': '建立、審查並合併目前分支的提取請求',
|
||||
'contextRail.surface.notes.description': '專案的筆記、待辦與計畫',
|
||||
|
||||
@@ -1169,6 +1169,9 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
|
||||
if (typeof candidate.smallModelOverride === 'string' && candidate.smallModelOverride.length > 0) {
|
||||
result.smallModelOverride = candidate.smallModelOverride;
|
||||
}
|
||||
if (typeof candidate.walkthroughModelOverride === 'string' && candidate.walkthroughModelOverride.length > 0) {
|
||||
result.walkthroughModelOverride = candidate.walkthroughModelOverride;
|
||||
}
|
||||
if (typeof candidate.autoCreateWorktree === 'boolean') {
|
||||
result.autoCreateWorktree = candidate.autoCreateWorktree;
|
||||
}
|
||||
|
||||
@@ -378,6 +378,13 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
|
||||
descriptionKey: 'settings.openchamber.defaults.smallModel.description',
|
||||
keywords: ['small model', 'utility', 'summary', 'recap', 'cheap', 'override'],
|
||||
},
|
||||
{
|
||||
id: 'sessions.walkthrough-model',
|
||||
page: 'sessions',
|
||||
titleKey: 'settings.openchamber.defaults.walkthroughModel.title',
|
||||
descriptionKey: 'settings.openchamber.defaults.walkthroughModel.description',
|
||||
keywords: ['walkthrough', 'diff', 'review', 'changes', 'structured output', 'model', 'override'],
|
||||
},
|
||||
{
|
||||
id: 'sessions.auto-cleanup',
|
||||
page: 'sessions',
|
||||
|
||||
@@ -7,6 +7,7 @@ export type ContextSurfaceId =
|
||||
| 'git'
|
||||
| 'pr'
|
||||
| 'diff'
|
||||
| 'walkthrough'
|
||||
| 'terminal'
|
||||
| 'plan'
|
||||
| 'notes'
|
||||
@@ -74,6 +75,15 @@ export const CONTEXT_SURFACES: readonly ContextSurfaceDescriptor[] = [
|
||||
labelKey: 'contextPanel.mode.diff',
|
||||
availability: 'always',
|
||||
},
|
||||
{
|
||||
id: 'walkthrough',
|
||||
descriptionKey: 'contextRail.surface.walkthrough.description',
|
||||
defaultWidthFraction: 3 / 5,
|
||||
mode: 'walkthrough',
|
||||
icon: 'route',
|
||||
labelKey: 'contextPanel.mode.walkthrough',
|
||||
availability: 'always',
|
||||
},
|
||||
{
|
||||
id: 'editor',
|
||||
descriptionKey: 'contextRail.surface.editor.description',
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { runtimeFetch } from '@/lib/runtime-fetch';
|
||||
import {
|
||||
WalkthroughError,
|
||||
type WalkthroughResult,
|
||||
type WalkthroughSource,
|
||||
type WalkthroughStage,
|
||||
} from './types';
|
||||
|
||||
const BASE = '/api/walkthrough';
|
||||
|
||||
interface ErrorPayload {
|
||||
error?: unknown;
|
||||
code?: unknown;
|
||||
model?: unknown;
|
||||
requiredChars?: unknown;
|
||||
availableChars?: unknown;
|
||||
}
|
||||
|
||||
// An authoritative read that fails must never look like "there is nothing
|
||||
// here" — the caller would clear a perfectly good walkthrough off the screen.
|
||||
const throwFromResponse = async (response: Response, fallback: string): Promise<never> => {
|
||||
const payload = (await response.json().catch(() => null)) as ErrorPayload | null;
|
||||
throw new WalkthroughError(typeof payload?.error === 'string' ? payload.error : fallback, {
|
||||
code: typeof payload?.code === 'string' ? (payload.code as WalkthroughError['code']) : undefined,
|
||||
model: (payload?.model as WalkthroughResult['model']) ?? undefined,
|
||||
requiredChars: typeof payload?.requiredChars === 'number' ? payload.requiredChars : undefined,
|
||||
availableChars: typeof payload?.availableChars === 'number' ? payload.availableChars : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
export async function fetchWalkthrough(
|
||||
directory: string,
|
||||
source: WalkthroughSource,
|
||||
options: { model?: string; signal?: AbortSignal } = {}
|
||||
): Promise<WalkthroughResult> {
|
||||
const response = await runtimeFetch(BASE, {
|
||||
query: {
|
||||
directory,
|
||||
source: JSON.stringify(source),
|
||||
...(options.model ? { model: options.model } : {}),
|
||||
},
|
||||
signal: options.signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
return throwFromResponse(response, 'Failed to load walkthrough');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function generateWalkthrough(
|
||||
directory: string,
|
||||
source: WalkthroughSource,
|
||||
options: { force?: boolean; model?: string; signal?: AbortSignal } = {}
|
||||
): Promise<WalkthroughResult> {
|
||||
const response = await runtimeFetch(`${BASE}/generate`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
directory,
|
||||
source,
|
||||
force: options.force === true,
|
||||
...(options.model ? { model: options.model } : {}),
|
||||
}),
|
||||
signal: options.signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
return throwFromResponse(response, 'Failed to generate walkthrough');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a running generation. Explicit, because merely leaving the page must not
|
||||
* throw away work the user is paying for.
|
||||
*/
|
||||
export async function cancelWalkthroughGeneration(
|
||||
directory: string,
|
||||
source: WalkthroughSource
|
||||
): Promise<void> {
|
||||
const response = await runtimeFetch(`${BASE}/cancel`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ directory, source }),
|
||||
});
|
||||
if (!response.ok) {
|
||||
await throwFromResponse(response, 'Failed to cancel walkthrough generation');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Current stage of a running generation. Reads server memory only, so this is
|
||||
* safe to poll — unlike the full read, which re-runs the whole git pipeline.
|
||||
*/
|
||||
export async function fetchWalkthroughStage(
|
||||
directory: string,
|
||||
source: WalkthroughSource,
|
||||
signal?: AbortSignal
|
||||
): Promise<WalkthroughStage | null> {
|
||||
const response = await runtimeFetch(`${BASE}/progress`, {
|
||||
query: { directory, source: JSON.stringify(source) },
|
||||
signal,
|
||||
});
|
||||
if (!response.ok) return null;
|
||||
const payload = (await response.json().catch(() => null)) as { stage?: unknown } | null;
|
||||
return typeof payload?.stage === 'string' ? (payload.stage as WalkthroughStage) : null;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { describe, expect, test as it } from 'bun:test';
|
||||
import { buildWalkthroughView, groupHunksByFile, mergeRunPatch, summarizeHunkFiles } from './model';
|
||||
import type { WalkthroughHunk, WalkthroughResult } from './types';
|
||||
|
||||
const hunk = (id: string, path: string, overrides: Partial<WalkthroughHunk> = {}): WalkthroughHunk => ({
|
||||
id,
|
||||
path,
|
||||
oldPath: null,
|
||||
status: 'modified',
|
||||
scope: 'working',
|
||||
header: '@@ -1,2 +1,3 @@',
|
||||
newStart: 1,
|
||||
added: 1,
|
||||
deleted: 0,
|
||||
patch: `diff --git a/${path} b/${path}\n--- a/${path}\n+++ b/${path}\n@@ -1,2 +1,3 @@\n+line\n`,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const result = (overrides: Partial<WalkthroughResult> = {}): WalkthroughResult => ({
|
||||
source: { kind: 'working-tree', scope: 'all' },
|
||||
walkthrough: {
|
||||
title: 'Change',
|
||||
focus: 'why',
|
||||
chapters: [
|
||||
{
|
||||
id: 'chapter-1',
|
||||
title: 'Data',
|
||||
icon: 'doc',
|
||||
blurb: '',
|
||||
stops: [
|
||||
{ id: 'stop-1-1', title: 'First', hunkIds: ['a', 'b'], importance: 'critical', prose: 'p1' },
|
||||
{ id: 'stop-1-2', title: 'Second', hunkIds: ['c'], importance: 'normal', prose: 'p2' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
hunks: [hunk('a', 'src/a.ts'), hunk('b', 'src/a.ts'), hunk('c', 'src/b.ts')],
|
||||
hunkCount: 3,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('buildWalkthroughView', () => {
|
||||
it('resolves stops and numbers them globally', () => {
|
||||
const view = buildWalkthroughView(result())!;
|
||||
|
||||
expect(view.stops).toHaveLength(2);
|
||||
expect(view.stops.map((stop) => stop.position)).toEqual([1, 2]);
|
||||
expect(view.stops[0].hunks.map((h) => h.id)).toEqual(['a', 'b']);
|
||||
expect(view.isStale).toBe(false);
|
||||
expect(view.uncoveredHunks).toEqual([]);
|
||||
});
|
||||
|
||||
it('marks only the stops whose code changed', () => {
|
||||
const view = buildWalkthroughView(result({
|
||||
hunks: [hunk('a', 'src/a.ts'), hunk('c', 'src/b.ts')],
|
||||
}))!;
|
||||
|
||||
expect(view.stops[0].isStale).toBe(true);
|
||||
expect(view.stops[0].missingHunkIds).toEqual(['b']);
|
||||
expect(view.stops[0].hunks.map((h) => h.id)).toEqual(['a']);
|
||||
expect(view.stops[1].isStale).toBe(false);
|
||||
expect(view.staleStopCount).toBe(1);
|
||||
expect(view.isStale).toBe(true);
|
||||
});
|
||||
|
||||
it('surfaces hunks no stop covers instead of dropping them', () => {
|
||||
const view = buildWalkthroughView(result({
|
||||
hunks: [hunk('a', 'src/a.ts'), hunk('b', 'src/a.ts'), hunk('c', 'src/b.ts'), hunk('d', 'src/c.ts')],
|
||||
}))!;
|
||||
|
||||
expect(view.uncoveredHunks.map((h) => h.id)).toEqual(['d']);
|
||||
});
|
||||
|
||||
it('returns null without a walkthrough', () => {
|
||||
expect(buildWalkthroughView(null)).toBeNull();
|
||||
expect(buildWalkthroughView(result({ walkthrough: null }))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('groupHunksByFile', () => {
|
||||
it('coalesces consecutive hunks from the same file only', () => {
|
||||
const runs = groupHunksByFile([
|
||||
hunk('a', 'src/a.ts'),
|
||||
hunk('b', 'src/a.ts'),
|
||||
hunk('c', 'src/b.ts'),
|
||||
hunk('d', 'src/a.ts'),
|
||||
]);
|
||||
|
||||
expect(runs.map((run) => [run.path, run.hunks.length])).toEqual([
|
||||
['src/a.ts', 2],
|
||||
['src/b.ts', 1],
|
||||
['src/a.ts', 1],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeRunPatch', () => {
|
||||
it('keeps a single patch untouched', () => {
|
||||
const single = hunk('a', 'src/a.ts');
|
||||
expect(mergeRunPatch([single])).toBe(single.patch);
|
||||
});
|
||||
|
||||
it('joins hunks under one file header', () => {
|
||||
const first = hunk('a', 'src/a.ts');
|
||||
const second = hunk('b', 'src/a.ts', {
|
||||
patch: 'diff --git a/src/a.ts b/src/a.ts\n--- a/src/a.ts\n+++ b/src/a.ts\n@@ -10,2 +11,3 @@\n+second\n',
|
||||
});
|
||||
|
||||
const merged = mergeRunPatch([first, second]);
|
||||
|
||||
expect(merged.match(/^diff --git/gm)).toHaveLength(1);
|
||||
expect(merged.match(/^@@/gm)).toHaveLength(2);
|
||||
expect(merged).toContain('+line');
|
||||
expect(merged).toContain('+second');
|
||||
});
|
||||
|
||||
it('returns nothing for an empty run', () => {
|
||||
expect(mergeRunPatch([])).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('summarizeHunkFiles', () => {
|
||||
it('totals per file in first-appearance order', () => {
|
||||
const files = summarizeHunkFiles([
|
||||
hunk('a', 'src/b.ts', { added: 2, deleted: 1 }),
|
||||
hunk('b', 'src/a.ts', { added: 1, deleted: 0 }),
|
||||
hunk('c', 'src/b.ts', { added: 3, deleted: 4 }),
|
||||
]);
|
||||
|
||||
expect(files).toEqual([
|
||||
{ path: 'src/b.ts', added: 5, deleted: 5 },
|
||||
{ path: 'src/a.ts', added: 1, deleted: 0 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,151 @@
|
||||
import type {
|
||||
Walkthrough,
|
||||
WalkthroughChapter,
|
||||
WalkthroughHunk,
|
||||
WalkthroughResult,
|
||||
WalkthroughStop,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Flattens a walkthrough plus the current hunk index into the ordered stream
|
||||
* the view renders: every stop with its resolved hunks, followed by everything
|
||||
* the walkthrough did not cover.
|
||||
*
|
||||
* Resolution is pure id matching — the server owns hunk identity, so a stop
|
||||
* whose hunks are missing is not a bug to paper over, it is a stop whose code
|
||||
* has changed.
|
||||
*/
|
||||
|
||||
export interface WalkthroughStopView {
|
||||
stop: WalkthroughStop;
|
||||
chapter: WalkthroughChapter;
|
||||
chapterIndex: number;
|
||||
/** Global 1-based position, used for the stepper and keyboard navigation. */
|
||||
position: number;
|
||||
hunks: WalkthroughHunk[];
|
||||
/** Anchors that no longer resolve: their code changed or was removed. */
|
||||
missingHunkIds: string[];
|
||||
isStale: boolean;
|
||||
}
|
||||
|
||||
export interface WalkthroughView {
|
||||
walkthrough: Walkthrough;
|
||||
stops: WalkthroughStopView[];
|
||||
chapters: Array<{ chapter: WalkthroughChapter; stops: WalkthroughStopView[] }>;
|
||||
/** Current hunks no stop covers. Never dropped — rendered as a collapsed tail. */
|
||||
uncoveredHunks: WalkthroughHunk[];
|
||||
staleStopCount: number;
|
||||
isStale: boolean;
|
||||
}
|
||||
|
||||
export const buildWalkthroughView = (result: WalkthroughResult | null): WalkthroughView | null => {
|
||||
if (!result?.walkthrough) return null;
|
||||
|
||||
const index = new Map(result.hunks.map((hunk) => [hunk.id, hunk]));
|
||||
const covered = new Set<string>();
|
||||
const stops: WalkthroughStopView[] = [];
|
||||
const chapters: WalkthroughView['chapters'] = [];
|
||||
|
||||
for (const [chapterIndex, chapter] of result.walkthrough.chapters.entries()) {
|
||||
const chapterStops: WalkthroughStopView[] = [];
|
||||
|
||||
for (const stop of chapter.stops) {
|
||||
const hunks: WalkthroughHunk[] = [];
|
||||
const missingHunkIds: string[] = [];
|
||||
|
||||
for (const id of stop.hunkIds) {
|
||||
const hunk = index.get(id);
|
||||
if (hunk) {
|
||||
hunks.push(hunk);
|
||||
covered.add(id);
|
||||
} else {
|
||||
missingHunkIds.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
const view: WalkthroughStopView = {
|
||||
stop,
|
||||
chapter,
|
||||
chapterIndex,
|
||||
position: stops.length + 1,
|
||||
hunks,
|
||||
missingHunkIds,
|
||||
isStale: missingHunkIds.length > 0,
|
||||
};
|
||||
stops.push(view);
|
||||
chapterStops.push(view);
|
||||
}
|
||||
|
||||
chapters.push({ chapter, stops: chapterStops });
|
||||
}
|
||||
|
||||
return {
|
||||
walkthrough: result.walkthrough,
|
||||
stops,
|
||||
chapters,
|
||||
uncoveredHunks: result.hunks.filter((hunk) => !covered.has(hunk.id)),
|
||||
staleStopCount: stops.filter((stop) => stop.isStale).length,
|
||||
isStale: stops.some((stop) => stop.isStale),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Files touched by a set of hunks, in first-appearance order, with their
|
||||
* per-file totals. Used for the table of contents rows.
|
||||
*/
|
||||
export const summarizeHunkFiles = (
|
||||
hunks: WalkthroughHunk[]
|
||||
): Array<{ path: string; added: number; deleted: number }> => {
|
||||
const byPath = new Map<string, { path: string; added: number; deleted: number }>();
|
||||
for (const hunk of hunks) {
|
||||
const existing = byPath.get(hunk.path);
|
||||
if (existing) {
|
||||
existing.added += hunk.added;
|
||||
existing.deleted += hunk.deleted;
|
||||
continue;
|
||||
}
|
||||
byPath.set(hunk.path, { path: hunk.path, added: hunk.added, deleted: hunk.deleted });
|
||||
}
|
||||
return [...byPath.values()];
|
||||
};
|
||||
|
||||
/**
|
||||
* Consecutive hunks from the same file are rendered as one diff block so the
|
||||
* reader sees continuous code instead of a stack of one-hunk cards.
|
||||
*/
|
||||
export const groupHunksByFile = (
|
||||
hunks: WalkthroughHunk[]
|
||||
): Array<{ path: string; hunks: WalkthroughHunk[] }> => {
|
||||
const runs: Array<{ path: string; hunks: WalkthroughHunk[] }> = [];
|
||||
for (const hunk of hunks) {
|
||||
const last = runs.at(-1);
|
||||
if (last && last.path === hunk.path) {
|
||||
last.hunks.push(hunk);
|
||||
continue;
|
||||
}
|
||||
runs.push({ path: hunk.path, hunks: [hunk] });
|
||||
}
|
||||
return runs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge a file's hunk patches back into one patch so a run renders as a single
|
||||
* diff. All hunks in a run share a file header, so only the first one's header
|
||||
* is kept.
|
||||
*/
|
||||
export const mergeRunPatch = (hunks: WalkthroughHunk[]): string => {
|
||||
if (hunks.length === 0) return '';
|
||||
if (hunks.length === 1) return hunks[0].patch;
|
||||
|
||||
const first = hunks[0].patch;
|
||||
const headerEnd = first.indexOf('\n@@');
|
||||
if (headerEnd === -1) return first;
|
||||
|
||||
const header = first.slice(0, headerEnd);
|
||||
const bodies = hunks.map((hunk) => {
|
||||
const start = hunk.patch.indexOf('\n@@');
|
||||
return start === -1 ? '' : hunk.patch.slice(start + 1);
|
||||
});
|
||||
|
||||
return `${header}\n${bodies.join('')}`;
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Contract for the AI diff walkthrough, mirrored from
|
||||
* `packages/web/server/lib/walkthrough`.
|
||||
*
|
||||
* Hunk ids are opaque here on purpose: the server owns how they are derived,
|
||||
* and the client only ever matches them against the index it is handed.
|
||||
*/
|
||||
|
||||
export type WalkthroughWorkingTreeScope = 'all' | 'staged' | 'working';
|
||||
|
||||
export type WalkthroughSource =
|
||||
| { kind: 'working-tree'; scope: WalkthroughWorkingTreeScope }
|
||||
| { kind: 'branch'; baseRef: string; headRef: string }
|
||||
| { kind: 'pr'; number: number };
|
||||
|
||||
export type WalkthroughChapterIcon = 'bug' | 'wrench' | 'path' | 'flask' | 'doc' | 'gear';
|
||||
export type WalkthroughStopImportance = 'critical' | 'normal' | 'context';
|
||||
|
||||
export interface WalkthroughStop {
|
||||
id: string;
|
||||
title: string;
|
||||
hunkIds: string[];
|
||||
importance: WalkthroughStopImportance;
|
||||
prose: string;
|
||||
}
|
||||
|
||||
export interface WalkthroughChapter {
|
||||
id: string;
|
||||
title: string;
|
||||
icon: WalkthroughChapterIcon;
|
||||
blurb: string;
|
||||
stops: WalkthroughStop[];
|
||||
}
|
||||
|
||||
export interface Walkthrough {
|
||||
title: string;
|
||||
focus: string;
|
||||
chapters: WalkthroughChapter[];
|
||||
}
|
||||
|
||||
export interface WalkthroughHunk {
|
||||
id: string;
|
||||
path: string;
|
||||
oldPath: string | null;
|
||||
status: 'added' | 'deleted' | 'modified' | 'renamed';
|
||||
scope: string;
|
||||
header: string;
|
||||
newStart: number;
|
||||
added: number;
|
||||
deleted: number;
|
||||
/** Standalone patch for this hunk, including the file header. */
|
||||
patch: string;
|
||||
}
|
||||
|
||||
export interface WalkthroughModel {
|
||||
providerID: string;
|
||||
modelID: string;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export interface WalkthroughResult {
|
||||
source: WalkthroughSource;
|
||||
walkthrough: Walkthrough | null;
|
||||
model?: WalkthroughModel;
|
||||
generatedAt?: string;
|
||||
fromCache?: boolean;
|
||||
hunks: WalkthroughHunk[];
|
||||
hunkCount: number;
|
||||
/** True when at least one stop points at code that has since changed. */
|
||||
isStale?: boolean;
|
||||
missingHunkIds?: string[];
|
||||
staleStopIds?: string[];
|
||||
/** Hunks in the current diff that no stop covers. Rendered as a tail. */
|
||||
uncoveredHunkIds?: string[];
|
||||
/** Whether generating is possible at all, computed from the same diff. */
|
||||
readiness?: WalkthroughReadiness;
|
||||
/** A generation is already running on the server for this source. */
|
||||
generating?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only phases a person can wait on. Building the digest and reading the cache
|
||||
* take milliseconds; naming them would imply progress that is not happening.
|
||||
*/
|
||||
export type WalkthroughStage = 'collecting' | 'asking' | 'retrying' | 'assembling';
|
||||
|
||||
export type WalkthroughBlockedReason =
|
||||
| 'no-model'
|
||||
| 'empty-diff'
|
||||
| 'only-generated'
|
||||
| 'context-too-small'
|
||||
| 'structured-output-unsupported'
|
||||
| 'output-exhausted';
|
||||
|
||||
export interface WalkthroughReadiness {
|
||||
ready: boolean;
|
||||
reason?: WalkthroughBlockedReason;
|
||||
model?: WalkthroughModel & {
|
||||
inputCharBudget?: number;
|
||||
contextTokens?: number;
|
||||
structuredOutput?: boolean | null;
|
||||
};
|
||||
requiredChars?: number;
|
||||
availableChars?: number;
|
||||
hunkCount?: number;
|
||||
fileCount?: number;
|
||||
generatedFileCount?: number;
|
||||
}
|
||||
|
||||
export class WalkthroughError extends Error {
|
||||
readonly code?: WalkthroughBlockedReason | 'invalid-walkthrough' | 'github-not-connected' | 'no-github-remote';
|
||||
readonly model?: WalkthroughModel;
|
||||
readonly requiredChars?: number;
|
||||
readonly availableChars?: number;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
details: {
|
||||
code?: WalkthroughError['code'];
|
||||
model?: WalkthroughModel;
|
||||
requiredChars?: number;
|
||||
availableChars?: number;
|
||||
} = {}
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'WalkthroughError';
|
||||
this.code = details.code;
|
||||
this.model = details.model;
|
||||
this.requiredChars = details.requiredChars;
|
||||
this.availableChars = details.availableChars;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user