// ─── Kronn i18n — lightweight translation system ────────────────────────────
// UI language (stored in localStorage) is separate from agent output language (stored in backend config).

export type UILocale = 'fr' | 'en' | 'es';

export const UI_LOCALES: { code: UILocale; label: string; flag: string }[] = [
  { code: 'fr', label: 'Français', flag: '🇫🇷' },
  { code: 'en', label: 'English', flag: '🇬🇧' },
  { code: 'es', label: 'Español', flag: '🇪🇸' },
];

const STORAGE_KEY = 'kronn:ui-locale';

export function getUILocale(): UILocale {
  const stored = localStorage.getItem(STORAGE_KEY);
  if (stored && ['fr', 'en', 'es'].includes(stored)) return stored as UILocale;
  return 'fr';
}

export function setUILocale(locale: UILocale) {
  localStorage.setItem(STORAGE_KEY, locale);
}

// ─── Translation dictionary ─────────────────────────────────────────────────

type TranslationDict = Record<string, string>;

const fr: TranslationDict = {
  // ── Nav & Tabs ──
  'nav.projects': 'Projets',
  'nav.discussions': 'Discussions',
  'nav.mcps': 'Plugins',
  'nav.workflows': 'Automatisation',
  'nav.config': 'Config',
  'nav.scan': 'Scanner',

  // ── Projects page ──
  'projects.title': 'Projets',
  'projects.aiReady': 'AI-ready',
  'projects.hidden': 'cache',
  'projects.hiddenPlural': 'caches',
  'projects.hideHidden': 'Masquer les caches',
  'projects.showHidden': 'Voir les caches',
  'projects.search': 'Rechercher un projet...',
  'projects.group.local': 'Projets locaux',
  'projects.group.other': 'Autres',
  'projects.showMore': 'Voir plus ({0} projet{1} restant{2})',
  'projects.collapse': 'Réduire',
  'projects.empty': 'Aucun projet. Cliquez sur "Scanner" pour détecter vos dépôts.',
  'projects.emptyHint': 'Aucun projet pour le moment. Utilisez le bouton + en haut pour ajouter un dossier, cloner un dépôt, ou créer un nouveau projet.',
  'projects.emptySearch': 'Aucun projet correspondant.',
  'projects.manageMcps': 'Gérer dans Plugins',
  'projects.noMcp': 'Aucun plugin — configurer',
  'projects.mcpHint.beforeAudit': 'Ajoute quelques plugins MCP avant de lancer briefing ou audit — ça enrichit beaucoup les résultats.',
  'projects.mcpHint.cta': 'Ajouter un plugin',
  'projects.delete': 'Supprimer',
  'projects.deleteConfirm': 'Supprimer ce projet de Kronn ?',
  'projects.deleteHard': 'Supprimer définitivement',
  'projects.deleteHardWarn': 'Cette action est irréversible. Le dossier du projet sera supprimé du disque.',
  'projects.deleteHardConfirmLabel': 'Tapez le nom du projet pour confirmer',
  'projects.deleteSoft': 'Retirer de Kronn',
  'projects.skills': 'Skills',
  'projects.noSkills': 'Aucun skill disponible — créer dans Config',
  // 0.8.3 — Linked repos / companion projects
  'linkedRepos.title': 'Dépôts liés',
  'linkedRepos.hint': 'Dépôts compagnons (API, IaC, design…) que les agents doivent connaître pour le contexte cross-repo. Inclus dans le prompt système de chaque audit / discussion / workflow.',
  'linkedRepos.empty': 'Aucun dépôt lié — ajoute-en un pour donner du contexte cross-repo aux agents.',
  'linkedRepos.addLink': 'Ajouter un dépôt lié',
  'linkedRepos.addBtn': 'Ajouter',
  'linkedRepos.remove': 'Retirer',
  'linkedRepos.confirmRemove': 'Retirer ce dépôt lié ? Les agents ne le verront plus dans leur contexte.',
  'linkedRepos.namePlaceholder': 'Nom (ex. backend-api)',
  'linkedRepos.locationPlaceholder': 'Chemin ou URL (ex. /home/user/Repos/backend-api ou https://github.com/org/backend-api)',
  'linkedRepos.descriptionPlaceholder': 'À quoi sert ce dépôt ? (optionnel — ex. "GraphQL schema du frontend")',
  'linkedRepos.requiredField': 'Le nom et le chemin/URL sont obligatoires.',
  'linkedRepos.maxReached': 'Limite atteinte (max 20 dépôts liés).',
  'linkedRepos.saveError': 'Échec de la sauvegarde des dépôts liés.',
  // 0.8.6 (#27) — autocomplete picker
  'linkedRepos.pickerLabel': 'Tes projets Kronn — clique pour auto-remplir',
  'linkedRepos.pickerMoreHint': '{0} autres',
  'linkedRepos.pickerShowLess': 'Voir moins',
  'projects.workflows': 'Workflows',
  'projects.noWorkflows': 'Aucun workflow — créer',
  'projects.loading': 'Chargement des projets…',
  'projects.scanning': 'Scan en cours…',
  'projects.scanFound': '{0} nouveau(x) projet(s) détecté(s)',
  'projects.scanNone': 'Aucun nouveau projet détecté',
  'projects.scanError': 'Erreur lors du scan',
  'projects.bootstrap': 'Ajouter un projet',
  'projects.bootstrap.name': 'Nom du projet',
  'projects.bootstrap.desc': 'Description du projet',
  'projects.bootstrap.descPlaceholder': 'Décrivez votre projet : objectif, fonctionnalités souhaitées, contraintes...',
  'projects.bootstrap.docs': 'Documents (optionnel)',
  'projects.bootstrap.docsHint': 'Glissez ou cliquez — architecture, specs, PRD...',
  'projects.bootstrap.options': 'Options avancées',
  'projects.bootstrap.noCreateRepo': 'Ne pas créer de dépôt Git',
  'projects.bootstrap.createRepoVia': 'Créer le dépôt via',
  'projects.bootstrap.noGeneratePlan': 'Ne pas générer de plan/issues',
  'projects.bootstrap.generatePlanVia': 'Plan & issues via',
  'projects.bootstrap.creating': 'Création en cours...',
  'projects.bootstrap.start': 'Créer et lancer le bootstrap',
  'projects.bootstrap.mcps': 'Plugins à activer',
  'projects.addFolder': 'Dossier local',
  'projects.folder.hint': 'Ajoutez un dossier comme projet — pas besoin de dépôt git. Si un .git est présent, Kronn le détecte automatiquement.',
  'projects.folder.path': 'Chemin du dossier',
  'projects.folder.name': 'Nom (optionnel, déduit du dossier)',
  'projects.folder.namePlaceholder': 'Déduit automatiquement si vide',
  'projects.folder.add': 'Ajouter',
  'projects.folder.success': 'Projet ajouté',

  'projects.clone': 'Cloner un dépôt',
  'projects.clone.url': 'URL du dépôt',
  'projects.clone.urlPlaceholder': 'https://github.com/... ou git@github.com:...',
  'projects.clone.name': 'Nom (optionnel)',
  'projects.clone.start': 'Cloner',
  'projects.clone.cloning': 'Clonage en cours...',
  'projects.clone.success': 'Dépôt cloné avec succès',
  'projects.clone.discover': 'Détecter mes dépôts',
  'projects.clone.discovering': 'Détection...',
  'projects.clone.discovered': '{0} dépôt(s) détecté(s) depuis {1}',
  'projects.clone.noToken': 'Aucun token GitHub/GitLab trouvé. Configurez le plugin GitHub ou GitLab dans l\'onglet Plugins.',
  'projects.clone.alreadyCloned': 'Déjà dans Kronn',
  'projects.clone.stars': 'étoiles',
  'projects.clone.searchRepos': 'Filtrer les dépôts...',
  'projects.clone.selectSources': 'Sources à interroger',
  'projects.docAi': 'Documentation projet',
  'projects.techDebtBadge': '{0} dette(s) technique(s) à traiter — voir docs/tech-debt/',
  'projects.docAi.selectFile': 'Sélectionnez un fichier pour le consulter',
  'projects.docAi.loading': 'Chargement...',
  'projects.docAi.empty': 'Aucune documentation projet disponible',
  'projects.docAi.search': 'Rechercher...',
  'projects.docAi.noResults': 'Aucun résultat',
  'projects.docAi.file': 'fichier',
  'projects.docAi.files': 'fichiers',
  'projects.docAi.discuss': 'Discuter de ce fichier',
  'projects.docAi.fixThis': 'Régler ce problème',
  'projects.docAi.fixThisPrompt': "Voici une dette technique documentée dans `{0}` — aide-moi à la résoudre. Lis le fichier, propose un plan court (1-3 étapes), puis exécute les modifications nécessaires dans le code. Une fois résolue, mets à jour le fichier TD-* (statut → résolu, ou supprime-le) et la ligne correspondante dans `docs/inconsistencies-tech-debt.md`.\n\nDébut :",
  'projects.docAi.banner.runAudit': "Lance un audit IA pour (re)documenter intégralement le projet et découvrir sa dette technique et ses incohérences.",
  'projects.docAi.banner.auditRunning': "Audit en cours — la documentation se construit progressivement, étape par étape. Les fichiers ci-dessous se remplissent au fur et à mesure ; les placeholders restants seront remplacés avant la fin.",
  'projects.docAi.banner.bootstrapDone': "Bootstrap terminé. Lance l'audit complet pour générer la documentation détaillée du projet.",
  'projects.docAi.banner.validate': "Valide l'audit pour avoir une documentation à jour et plus pertinente (l'IA pose des questions sur les zones d'ambiguïté).",
  // 0.8.4 (#298) — post-audit step recap panel
  'projects.docAi.auditRecap.title': 'Détails du dernier audit',
  'projects.docAi.auditRecap.stepsLabel': 'étapes',
  'projects.docAi.auditRecap.runsLabel': 'audits',
  'projects.docAi.auditRecap.filterAll': 'Tous',
  'projects.docAi.auditRecap.showMoreChips': '+ {0} plus anciens',
  'projects.docAi.auditRecap.showLessChips': '– Réduire',
  'projects.docAi.auditRecap.openDrawer': 'Voir les audits précédents ({0})',
  'projects.docAi.auditRecap.drawerTitle': 'Historique des audits',
  // Tooltips sur les chips de méta-info de chaque audit run.
  // Affichés via l'attribut `title` HTML standard pour rester
  // accessibles (lecteurs d'écran) + visibles au survol souris.
  'projects.docAi.auditRecap.metaDurationTooltip': 'Durée totale de l\'audit (du lancement à la fin du Step 10).',
  'projects.docAi.auditRecap.metaTdTooltip': 'Nombre de Tech Debts (TDs) détectées par l\'audit. Cliquez sur la carte pour voir le détail par étape.',
  'projects.docAi.auditRecap.metaHealthTooltip': 'Score de santé du projet (0-100). Calculé depuis la sévérité des TDs : 100 = aucune dette critique, 0 = saturation critique. Pondération : Critical = -12, High = -5, Medium = -2, Low = -0.3.',
  'projects.docAi.auditRecap.colStepTooltip': 'Numéro d\'étape dans le pipeline d\'audit (1-10 pour un audit global, 1-1 pour un audit ciblé). Cliquez pour trier.',
  'projects.docAi.auditRecap.colFileTooltip': 'Fichier `docs/*.md` produit ou raffiné par cette étape. Le marqueur 🔧 indique une réparation depuis le template (placeholder leak détecté).',
  'projects.docAi.auditRecap.colDurationTooltip': 'Durée de cette étape uniquement (Δ entre step_start et step_done). Cliquez pour trier — utile pour identifier l\'étape la plus lente.',
  'projects.docAi.auditRecap.colTokensTooltip': 'Tokens consommés par cette étape (max de input + output, pas cumulatif). Cliquez pour trier — utile pour identifier l\'étape la plus coûteuse.',
  'projects.docAi.auditRecap.totalsPrefix': 'Total :',
  'projects.docAi.auditRecap.colFile': 'Fichier',
  'projects.docAi.auditRecap.colDuration': 'Durée',
  'projects.docAi.auditRecap.colTokens': 'Tokens',
  'projects.docAi.auditRecap.empty': "Aucune métrique par étape pour cet audit (run pré-0.8.4).",
  'projects.docAi.auditRecap.repairedHint': "Cette étape a écrit un fichier vide ou un placeholder — réparé à partir du template.",
  // 0.8.4 (#287) — audit kind dropdown next to the agent select
  'audit.kindSelector.tooltip': "Type d'audit à lancer. Les audits ciblés (Sécurité, RGAA, Database…) deviennent dispos une fois l'audit global terminé.",
  'audit.kindSelector.reAuditHint': "Relancer un audit thématique (sécurité, RGAA, base de données…) pour aller plus loin sur un point précis — ou relancer un audit global pour rafraîchir toute la doc.",
  // 0.8.4 (#321 / F1) — pre-fix the template was "Lancer l'audit {0}"
  // which produced "Lancer l'audit Audit global (10 étapes)" — double
  // "audit" reads as a typo. The colon form puts the kind on equal
  // footing with the action and reads cleanly for every kind label.
  'audit.kindSelector.launchLabel': "Lancer : {0}",
  'audit.kind.Full': "Audit global (10 étapes)",
  'audit.kind.Security': "Sécurité",
  'audit.kind.Docker': "Docker",
  'audit.kind.Performance': "Performance",
  'audit.kind.Accessibility': "Accessibilité (WCAG 2.1)",
  'audit.kind.Rgaa': "RGAA 4.1 (France)",
  'audit.kind.Database': "Base de données",
  'audit.kind.ApiDesign': "Design d'API",
  'audit.kind.afterFull': "(après audit global)",
  // 0.8.4 (#287) — sub-audit modal (legacy keys; kept for backwards-compat
  // until the modal is removed in 0.8.5+)
  'audit.subAudit.modalLabel': "Choix du type d'audit",
  'audit.subAudit.title': "Quel audit lancer ?",
  'audit.subAudit.intro': "Choisis un audit global (les 10 étapes, ~20 min) ou un audit ciblé sur une dimension précise (1 étape, plus rapide, mieux pour creuser un sujet).",
  'audit.subAudit.fullTitle': "Audit global (10 étapes)",
  'audit.subAudit.fullDesc': "Pipeline complet : doc projet, repo-map, architecture, schémas Mermaid, tech-debt, recommandations cluster.",
  'audit.subAudit.targetedSectionTitle': "Audits ciblés (1 étape)",
  'audit.subAudit.cta': "Audit ciblé",
  'audit.subAudit.kind.Security.title': "Sécurité",
  'audit.subAudit.kind.Security.desc': "Auth, secrets, validations input, headers, dependances vulnérables.",
  'audit.subAudit.kind.Docker.title': "Docker",
  'audit.subAudit.kind.Docker.desc': "Dockerfile, multi-stage, layer caching, image size, healthcheck, secrets.",
  'audit.subAudit.kind.Performance.title': "Performance",
  'audit.subAudit.kind.Performance.desc': "Hot paths, bundle size, lazy-load, requêtes N+1, indexes manquants.",
  'audit.subAudit.kind.Accessibility.title': "Accessibilité (WCAG 2.1)",
  'audit.subAudit.kind.Accessibility.desc': "Semantic HTML, ARIA, focus, contrast, alternatives. Standard international.",
  'audit.subAudit.kind.Rgaa.title': "RGAA 4.1 (France)",
  'audit.subAudit.kind.Rgaa.desc': "Norme française obligatoire (décret 2019-768). Plus strict que WCAG. Inclut un volet audit manuel + formation.",
  'audit.subAudit.kind.Database.title': "Base de données",
  'audit.subAudit.kind.Database.desc': "Schema, migrations, indexes, ORM N+1, intégrité référentielle.",
  'audit.subAudit.kind.ApiDesign.title': "Design d'API",
  'audit.subAudit.kind.ApiDesign.desc': "Contract, versioning, pagination, authn/authz, rate limiting, doc drift.",
  'projects.docAi.discussPrompt': 'Je voudrais discuter du contenu du fichier `{0}` de la documentation projet. Voici mes remarques :\n\n',
  'projects.status.valid': 'valide',
  'projects.status.validating': 'validation...',
  'projects.status.auditOk': 'audit OK',
  'projects.status.template': 'template',
  'projects.status.none': 'aucun',
  'projects.status.bootstrapped': 'MVP défini',
  'projects.status.bootstrapping': 'bootstrap...',

  // ── AI Audit ──
  'audit.noTemplate': 'Aucun template installé. Installez le template pour structurer la documentation projet.',
  'audit.installTemplate': 'Installer le template AI',
  'audit.description': "L'audit AI analyse l'intégralité du projet en 10 étapes et remplit la doc docs/.",
  'audit.warning': 'Durée estimée : ~20 minutes. Consommation élevée de tokens.',
  'audit.launch': "Lancer l'audit AI",
  'audit.startFullAudit': "Lancer l'audit AI",
  'audit.resumeFromStep': "Reprendre à l'étape {0}/10",
  'audit.resumeFromTooltip': "Audit précédent interrompu après l'étape {0}. Les étapes déjà terminées seront sautées pour économiser tokens + temps.",
  'audit.fullAuditDesc': 'Installe le template, lance un audit 10 étapes, puis crée la discussion de validation.',
  'audit.briefingLabel': 'Briefing projet (optionnel)',
  'audit.briefingPlaceholder': 'Décrivez votre projet en quelques mots : stack, conventions, points importants...',
  'audit.fullAuditDone': 'Audit terminé ! Discussion de validation créée.',
  'audit.templateStep': 'Installation du template...',
  'audit.step': 'Étape {0}/{1} — {2}',
  'audit.elapsed': '⏱ {0}',
  'audit.elapsedTooltip': 'Temps écoulé depuis le démarrage de l\'audit (compteur côté client, recalé sur le started_at du serveur).',
  'audit.lastStepTokens': '💬 {0} tk',
  'audit.lastStepTokensTooltip': 'Tokens consommés par le dernier step terminé (max(input+output) — l\'agent reporte un cumul par appel, on prend donc le maximum, pas une somme).',
  'audit.totalTokens': 'Σ {0} tk',
  'audit.totalTokensTooltip': 'Tokens cumulés sur tous les steps terminés depuis le début de l\'audit. Pour identifier le step le plus coûteux : regarde le delta entre deux step_done successifs.',
  'audit.currentTool': '🔧 {0}',
  'audit.currentToolTooltip': 'Outil que l\'agent vient d\'appeler (Read, Glob, Bash, mcp__...). Last-write-wins — le chip se met à jour à chaque nouvel outil et disparait à la fin du step.',
  'audit.validationInProgress': 'Validation en cours — {0} messages échangés.',
  'audit.activityInProgress': 'Activité en cours sur ce projet',
  'audit.validationHint': "La validation se termine depuis la discussion, quand l'IA confirme que tout est résolu.",
  'audit.resumeValidation': 'Reprendre la validation',
  'audit.readyToValidate': "Documentation projet générée. Validez l'audit pour résoudre les zones d'ambiguïté avec l'IA.",
  'audit.validate': "Valider l'audit",
  'audit.done': 'Audit validé. Documentation projet prête dans docs/.',
  'audit.markValid': "Marquer l'audit comme valide",
  'audit.validationComplete': "L'IA confirme que la validation est terminée.",
  'audit.autopilotCtaText': "Audit validé. Branche AutoPilot pour traiter les tickets de ce projet automatiquement.",
  'audit.autopilotCtaBtn': "Configurer AutoPilot",
  'audit.trackerHint': "Audit plus complet avec un tracker branché (GitHub / GitLab / Jira / Linear) — Phase 3 propose la création de tickets et AutoPilot tourne sur de vraies issues.",
  'audit.trackerHintConfigure': "Configurer un tracker",
  'audit.trackerHintDismiss': "Ne plus afficher pour ce projet",
  'audit.validationTitle': 'Validation audit AI',
  'audit.validationPrompt': "Voici la documentation du projet (dossier docs/). Suis le protocole strict de validation en 4 phases. NE PAS émettre KRONN:VALIDATION_COMPLETE tant que les 4 phases ne sont pas terminées.\n\n## Phase 1 — Auto-correction (autonome)\nLis le code source et corrige tout ce que tu peux : marqueurs TODO orphelins, fichiers squelettes, infos obsolètes. Mets à jour les fichiers docs/ directement.\n\n## Phase 2 — Questions d'ambiguïté (interactif)\nPose tes questions une par une. À chaque réponse, mets à jour les fichiers docs/ avant la question suivante. Si une réponse révèle de nouvelles zones d'ombre, pose des questions de suivi.\n\n## Phase 3 — Revue dette technique (interactif)\nPasse en revue CHAQUE entrée de docs/inconsistencies-tech-debt.md et docs/tech-debt/. Pour chaque item : est-ce correct ? Quelle priorité ? Faut-il créer un ticket ?\n\n## Phase 4 — Challenge documentation (interactif)\nPose-moi 2-3 questions pratiques qu'un dev rejoignant le projet poserait. Vérifie que la doc y répond. Si insuffisant, mets à jour et repose une question.\n\nUniquement quand les 4 phases sont terminées, termine par : \"KRONN:VALIDATION_COMPLETE\".",
  'audit.validationFilesFilled': 'Fichiers de doc remplis :',
  'audit.validationFilesIncomplete': 'Fichiers de doc incomplets :',
  'audit.validationFilesNone': '(aucun)',
  'audit.validationTodos': 'Marqueurs TODO restants ({0}):',
  'audit.cancelAudit': 'Annuler',
  'audit.cancelled': 'Audit annulé. Tous les fichiers ont été supprimés.',
  'audit.streamError': "L'audit s'est arrêté sur une erreur : {0}",
  'audit.stepWarning': "⚠ L'étape `{0}` n'a rien produit d'utile — {1}. {2}",
  'audit.stepWarningRepaired': 'Le template a été restauré ; relance l\'audit pour récupérer le contenu.',
  'audit.stepWarningNotRepaired': 'Aucun template à restaurer ; vérifie manuellement le fichier.',
  'audit.viewTechDebts': 'Voir les {0} TDs →',
  'audit.viewTechDebtsAfterValidation': 'Voir tous les Tech Debts du projet',
  'audit.partialStreamError': "L'audit partiel s'est arrêté sur une erreur : {0}",
  'audit.bootstrapInProgress': 'Bootstrap en cours...',
  'audit.resumeBootstrap': 'Reprendre le bootstrap',
  'audit.bootstrapComplete': 'Le bootstrap est terminé. Le MVP du projet est défini.',
  'audit.markBootstrapped': 'Marquer comme défini',
  'audit.bootstrapDone': 'MVP défini. Lancez l\'audit pour générer la documentation projet complète.',
  'audit.staleSections': '{0} section(s) obsolète(s)',
  'audit.updateStale': 'Mettre à jour ({0} sections)',
  'audit.noStale': 'Audit à jour',
  'audit.auditDate': 'Audité le {0}',
  'audit.startBriefing': 'Briefing (IA)',
  'audit.briefingDesc': 'Discussion rapide (5 min) pour donner du contexte à l\'audit.',
  // 0.8.4 (#285) — Désagentified briefing form
  'briefing.formBtn': 'Définir le briefing',
  'briefing.formBtnTooltip': 'Remplis les 6 questions du formulaire. À la sauvegarde, un agent IA relit tes réponses et te demande des clarifications uniquement sur les points ambigus, puis enregistre le briefing final.',
  'briefing.formTitle': 'Briefing projet',
  'briefing.formIntro': 'Réponds aux 6 questions ci-dessous. À l\'enregistrement, un agent IA relit tes réponses, te demande des clarifications uniquement si besoin, puis écrit `docs/briefing.md`. Tu n\'as pas à le faire en 2 étapes.',
  'briefing.saveAndReviewBtn': 'Enregistrer + relire avec l\'IA',
  'briefing.savedButNoDiscToast': 'Briefing enregistré, mais la relecture IA n\'a pas pu démarrer. L\'audit utilisera quand même tes réponses.',
  'briefing.q1Purpose': '1. Que fait ce projet ?',
  'briefing.q1Placeholder': 'Une phrase. Ce qu\'il fait pour ses utilisateurs.',
  'briefing.q2Team': '2. Qui travaille dessus ?',
  'briefing.q2Placeholder': 'solo / petite équipe / grande équipe',
  'briefing.q3Maturity': '3. À quel stade en est le projet ?',
  'briefing.q3Placeholder': 'prototype, MVP, production, legacy, rewrite…',
  'briefing.q4Dependencies': '4. Dépendances externes clés ?',
  'briefing.q4Placeholder': 'Exemples : "PostgreSQL sur AWS RDS", "user-service API sur gitlab.company.com/org/repo" — ou "aucune".',
  'briefing.q5Traps': '5. Ce qu\'un nouveau contributeur ferait mal le premier jour ?',
  'briefing.q5Placeholder': 'pièges, règles implicites, zones fragiles',
  'briefing.q6Additional': '6. Autre chose à savoir pour l\'audit ?',
  'briefing.q6Placeholder': 'Optionnel — court.',
  'briefing.saveBtn': 'Enregistrer le briefing',
  'briefing.savedToast': 'Briefing enregistré (docs/briefing.md).',
  'briefing.missingFields': 'Champs obligatoires manquants : {0}',
  'audit.briefingDone': 'Briefing terminé !',
  'audit.resumeBriefing': 'Reprendre le briefing',
  'audit.goToProject': 'Voir le projet',
  'audit.runningMcpFilterBanner': 'Audit IA en cours sur ce projet — certains MCPs sont temporairement désactivés pour améliorer les performances de l\'audit. Les MCPs habituels reviendront automatiquement à la fin de l\'audit. Si tu as besoin du set complet maintenant, attends quelques minutes ou ouvre cette discussion sur un autre projet.',
  'audit.activeAuditsTitle': 'Audits en cours',
  'audit.activeAuditsEmpty': 'Aucun audit en cours',
  'audit.activeAuditsStep': 'Étape {0}/{1}',
  'audit.runningTooltip': '{0} audit{1} en cours',
  'audit.viewAllProjects': 'Voir tous les projets',
  'mermaid.parseError': 'Impossible de rendre ce diagramme Mermaid — vérifie la syntaxe ci-dessous.',
  'mermaid.errorDetails': 'Détails de l\'erreur',
  'mermaid.showSource': 'Voir le code source',
  'mermaid.hideSource': 'Masquer le code source',
  'mermaid.fullscreen': 'Plein écran',
  'mermaid.closeFullscreen': 'Fermer le plein écran',
  'mermaid.print': 'Imprimer',
  'audit.unauditedWarning': 'Ce projet n\'a pas encore d\'audit IA validé. Commencez par un briefing pour donner du contexte business — l\'audit produit ensuite une source de vérité partagée par TOUS les agents (Claude Code, Cursor, Vibe, Ollama…) qui survit aux changements d\'agent, de session et d\'équipe.',
  'audit.unauditedWarningWithBriefing': 'Briefing effectué, mais l\'audit IA n\'a pas encore été lancé. Lancez-le pour générer la documentation Kronn (`docs/AGENTS.md` + structure) qui sert de source de vérité **inter-agent** : Claude Code, Cursor, Vibe, Ollama et autres lisent le même contexte. Ça évite les divergences entre agents et préserve la connaissance projet au-delà d\'une session.',
  'audit.unauditedCtaBriefing': 'Faire le briefing du projet',
  'audit.unauditedCtaLaunch': 'Lancer l\'audit IA',
  'audit.auditInProgress': 'Audit en cours...',
  'audit.auditDoneResume': 'Audit terminé ! La validation est prête.',

  // ── Discussions ──
  'disc.new': 'Nouvelle',
  'disc.batchFromWorkflowRun': 'run #{0} · {1}',
  'disc.batchFromWorkflow': '{0}',
  'disc.batchParentClickHint': 'Ouvrir le workflow parent de ce batch',
  'disc.batchDeleteHint': 'Supprimer ce batch ({0} discussions)',
  'disc.batchRetryHint': 'Relancer ce batch ({0} discussions, copie identique)',
  'disc.batchRetryConfirm': 'Relancer ce batch ?\n\n« {1} » va re-fan-out {0} nouvelles discussions avec les mêmes prompts. L\'ancien batch reste en place — supprime-le manuellement si besoin.',
  'disc.batchRetryToast': 'Batch relancé ({0} discussions créées)',
  'disc.batchRetryError': 'Échec du retry : {0}',
  'disc.batchRetryEmpty': 'Aucun item à relancer (les discussions enfant ont peut-être été supprimées).',
  'disc.batchDeleteConfirm': 'Supprimer ce batch et ses {0} discussions ?\n\n« {1} »\n\nCette action est irréversible.',
  'disc.batchDeletedToast': 'Batch supprimé ({0} discussions effacées)',
  'disc.batchDeleteError': 'Échec de la suppression : {0}',
  'disc.stopAgent': 'Arrêter l\'agent',
  'disc.stopAgentToast': 'Agent arrêté',
  'disc.stopAgentNothing': 'Rien à arrêter — l\'agent a déjà terminé',
  'disc.stopAgentError': 'Échec de l\'arrêt : {0}',
  'disc.partialRecoveredToast': '{0} discussion(s) restaurée(s) après redémarrage — réponses d\'agents interrompues récupérées',
  'disc.partialPendingPrompt': 'Une réponse d\'agent précédente est en cours de récupération.\n\nForcer la récupération maintenant et envoyer ton message ?',
  'disc.partialDismissed': 'Récupération forcée — tu peux renvoyer ton message',
  'disc.searchPlaceholder': 'Rechercher...',
  'disc.searchClear': 'Effacer la recherche',
  // 0.8.4 (#294) — cross-agent source binding (badge + filter)
  'disc.source.importedHint': 'Discussion importée depuis {0}.',
  'disc.source.divergedHint': '{0} : a divergé (édité dans Kronn après import — un re-push écraserait ces changements).',
  'disc.source.filterTooltip': "Filtrer par CLI source (Claude Code, Cursor, etc.)",
  'disc.source.filterAll': 'Toutes les sources',
  'disc.source.filterFrom': 'Depuis {0}',
  'disc.markAllRead': 'Tout marquer comme lu',
  'disc.markAllReadTooltip': 'Marquer toutes les discussions ({0} messages non lus) comme lues. Inclut les discussions archivées et les batchs.',
  'disc.newTitle': 'Nouvelle discussion',
  'disc.project': 'Projet (optionnel)',
  'disc.noProject': 'Aucun projet',
  'disc.agent': 'Agent',
  'disc.noAgent': 'Aucun agent installé',
  'disc.title': 'Titre (optionnel)',
  'disc.titlePlaceholder': 'Auto-généré depuis le prompt...',
  'disc.prompt': 'Prompt initial',
  'disc.promptPlaceholder': 'Décrivez ce que vous souhaitez...',
  'disc.attachFiles': 'Joindre des fichiers contexte',
  'disc.start': 'Démarrer la discussion',
  // 0.8.6 phase 2 — disc-first refactor
  'disc.launchAgentNow': 'Lancer un agent tout de suite',
  'disc.launchAgentNowHint': "Décoche pour préparer ton brief sans agent, ou pour ouvrir un salon multi-agent (Claude + Codex...) que tu invites ensuite depuis le header.",
  'disc.discFirstHint': "Aucun agent ne sera lancé. Tu pourras inviter Claude, Codex, Gemini etc. depuis le header de la discussion.",
  'disc.discFirstDefaultTitle': 'Discussion sans agent',
  'disc.createEmpty': 'Créer la discussion',
  'disc.discFirstCreatedToast': "Discussion créée. Clique sur « + Inviter » dans le header pour ajouter un agent.",
  'disc.participantsEmpty': "Aucun agent attaché. Clique « + Inviter » pour ajouter un participant.",
  'disc.invitePeer': '+ Inviter',
  'disc.invitePeerTooltip': "Génère un token à coller dans un autre CLI (Claude, Codex, Gemini…) qui rejoindra cette discussion.",
  'disc.inviteFailed': "Échec de l'invitation : {0}",
  'disc.inviteCopied': 'Instruction copiée dans le presse-papier.',
  'disc.inviteCopyFailed': 'Impossible de copier — copie la commande à la main.',
  'disc.inviteModalTitle': 'Inviter un agent dans cette discussion',
  'disc.inviteModalIntro': "Colle cette instruction dans chaque CLI compatible MCP que tu veux inviter. Le même token est valable pour N agents pendant {0} min :",
  'disc.inviteModalClose': 'Fermer',
  'disc.inviteCopyBtn': "Copier l'instruction",
  'disc.inviteExpiresHint': 'Expire à : {0}',
  'disc.auditWarn': "L'IA va analyser l'ensemble de la documentation projet et du code. Cette première analyse peut prendre quelques minutes et consommer des tokens.",
  'disc.auditHint': "Il peut y avoir plusieurs questions. La conversation peut être reprise à tout moment depuis l'onglet Discussions.",
  'disc.advancedOptions': 'Skills, Profils & Directives',
  'disc.mcps': 'Plugins disponibles',
  'disc.mcpSearch': 'Filtrer les plugins…',
  'disc.noMcps': 'Aucun plugin configuré',
  'disc.editConfig': 'Modifier profils, skills & directives',
  'git.title': 'Fichiers modifiés',
  'git.refresh': 'Rafraîchir',
  'git.noChanges': 'Aucune modification',
  'git.filesChanged': 'fichier(s) modifié(s)',
  'git.committedOnBranch': '{0} fichier(s) déjà commit(s) sur cette branche (vs {1})',
  'git.onDefaultBranch': 'Vous êtes sur la branche principale',
  'git.createBranch': 'Créer une branche',
  'git.branchName': 'Nom de la branche...',
  'git.commit': 'Commit',
  'git.commitMessage': 'Message du commit...',
  'git.amend': 'Amend',
  'git.sign': 'Signer (-S)',
  'git.commitSelected': 'Commiter {0} fichier(s)',
  'git.push': 'Push',
  'git.pushSuccess': 'Push réussi',
  'git.createPr': 'Créer PR',
  'git.createMr': 'Créer MR',
  'git.prTitle': 'Titre de la PR',
  'git.prEdit': 'Éditer',
  'git.prPreview': 'Aperçu',
  'git.prBodyPlaceholder': 'Description de la PR (Markdown)...',
  'git.prTemplateProject': 'template projet',
  'git.prTemplateKronn': 'template Kronn',
  'git.submitPr': 'Créer la Pull Request',
  'git.submitMr': 'Créer la Merge Request',
  'git.selectAll': 'Tout sélectionner',
  'git.deselectAll': 'Tout désélectionner',
  'git.filesBtn': 'Fichiers',
  'git.pendingFilesTooltip': '{0} fichier(s) en attente de commit',
  // ── Test mode (worktree swap) ──
  'testMode.cta': 'Tester cette version',
  'testMode.ctaTooltip': 'Charge le code de la discussion dans ton repo principal — checkout la branche + met l\'agent en pause pendant que tu testes.',
  'testMode.bannerHeadline': 'Tu testes la version de cette discussion dans ton IDE — l\'agent est en pause.',
  'testMode.bannerRestore': 'retour prévu sur `{0}`',
  'testMode.exit': 'Arrêter le test',
  'testMode.exitTooltip': 'Revenir à ton état précédent et recréer le worktree de la discussion.',
  'testMode.exiting': 'Sortie…',
  'testMode.enterSuccess': 'Mode test activé — ton repo est sur {0}',
  'testMode.enterError': 'Impossible d\'entrer en mode test : {0}',
  'testMode.exitSuccess': 'Mode test désactivé — retour sur {0}',
  'testMode.exitError': 'Erreur pendant la sortie du mode test : {0}',
  'testMode.modal.worktreeDirtyTitle': 'Modifications non commitées côté agent',
  'testMode.modal.worktreeDirtyExplain': 'L\'agent a laissé des modifications non commitées dans son worktree. Si tu testes maintenant, elles seront perdues. Commit-les d\'abord — ou demande à l\'agent de le faire.',
  'testMode.modal.mainDirtyTitle': 'Modifications en cours sur `{0}`',
  'testMode.modal.mainDirtyExplain': 'Ton repo principal a des modifications non commitées. Que faire avant de passer sur la version de l\'agent ?',
  'testMode.modal.detachedTitle': 'HEAD détaché',
  'testMode.modal.detachedExplain': 'Ton repo principal n\'est pas sur une branche nommée. Après le test, on te ramènera sur la branche d\'origine si tu en avais une.',
  'testMode.modal.fallbackTitle': 'Impossible d\'activer le mode test',
  'testMode.modal.openGitPanel': 'Ouvrir le panneau git',
  'testMode.modal.stashAndProceed': 'Stasher et continuer',
  'testMode.modal.stashing': 'Stashage…',
  'testMode.modal.commitFirst': 'Commit d\'abord',
  'testMode.modal.proceedAnyway': 'Continuer quand même',
  'testMode.modal.cancel': 'Annuler',
  'testMode.modal.filesMore': '… et {0} de plus',
  'git.terminal': 'Terminal',
  'git.terminalPlaceholder': 'git log --oneline -5',
  'git.terminalRestricted': 'Terminal désactivé — activez full_access sur un agent dans Config',
  'disc.empty': 'Aucune discussion.\nCliquez "Nouvelle" pour commencer.',
  'disc.selectOrCreate': 'Sélectionnez ou créez une discussion',
  'disc.mentionHint': '{0} pour cibler un agent...',
  'disc.messagePlaceholder': 'Votre message...',
  'disc.attachFile': 'Ajouter un fichier contexte',
  'disc.general': 'Général',
  'disc.local': 'Local',
  'disc.system': 'Système',
  'disc.summaryCached': 'Synthèse',
  'disc.viewSummary': 'Voir',
  'disc.hideSummary': 'Masquer',
  'disc.thinking': '{0} réfléchit...',
  'disc.running': "Agent en cours d'exécution...",
  'disc.debateLaunching': 'Débat en cours de démarrage… (les CLI agents peuvent mettre 5-30s à booter, surtout Codex au cold-start)',
  'disc.debateAgentEmpty': '{0} a terminé sans produire de contenu — CLI possiblement crashé ou rate-limited. Re-lance le débat pour ré-essayer.',
  'disc.newContent': 'Nouveau contenu',
  'disc.badgeInfo.showDetails': "Afficher les détails",
  'disc.badgeInfo.profileKind': 'Profil IA',
  'disc.badgeInfo.skillKind': 'Skill',
  'disc.badgeInfo.directiveKind': 'Directive',
  'disc.badgeInfo.tokens': 'tokens',
  'disc.badgeInfo.builtin': 'intégré',
  'disc.badgeInfo.notFound': 'Élément introuvable (peut-être supprimé depuis).',
  'disc.synthesis': 'Synthèse',
  'disc.cancel': 'Annuler',
  'disc.resend': 'Renvoyer',
  'disc.editResend': 'Éditer et relancer',
  'disc.retryResponse': 'Relancer la réponse',
  'disc.copyMessage': 'Copier le message',
  'disc.copy': 'Copier',
  'disc.copied': 'Copié !',
  'disc.confirmDelete': 'Supprimer cette discussion ? Cette action est irréversible.',
  'disc.overrideKey': 'Activer ou remplacer la clé API',
  'disc.orCheckAgent': 'ou vérifiez que votre agent local est connecté',
  'disc.stopThinking': 'Arrêter la réflexion',
  'disc.agentDisabled': 'L\'agent {0} est actuellement désactivé ou désinstallé.',
  'disc.agentDisabledLink': 'Gérer les agents',
  'disc.archive': 'Archiver',
  'disc.favorites': 'Favoris',
  'disc.pin': 'Ajouter aux favoris',
  'disc.unpin': 'Retirer des favoris',
  'disc.pinError': 'Erreur : {0}',
  'disc.chainQP': 'Enchaîner un Quick Prompt après la réponse',
  'disc.cancelQueuedQP': 'Annuler le Quick Prompt en attente (clic)',
  'disc.delete': 'Supprimer',
  'disc.archived': 'Archives',
  'disc.showMore': 'discussions plus anciennes',
  'disc.unseenMessagesTooltip': '{0} message{1} non lu{1}',
  'disc.scrollToTop': 'Aller au début',
  'disc.scrollToBottom': 'Aller à la fin',
  'disc.introspectionPillTooltip': '{0} appel{1} aux outils d\'historique (kronn-internal MCP)',
  // 0.8.5 — disc-id pill in ChatHeader + sidebar tooltip + search by id prefix.
  'disc.idPillTooltip': 'ID complet : {0} · clic pour copier',
  'disc.idCopied': 'ID copié dans le presse-papiers',
  'disc.idCopyFailed': 'Copie impossible (accès au presse-papiers refusé)',
  'disc.titleHoverTooltip': '{0}\nID : {1}',
  'disc.summaryStrategyLabel': 'Synthèse auto',
  'disc.summaryStrategy.Auto.label': '⚡ Auto',
  'disc.summaryStrategy.Auto.hint': 'Synthèse automatique tous les N messages selon le budget contexte de l\'agent. Idéal pour les debate ou les petits modèles.',
  'disc.summaryStrategy.OnDemand.label': '🤖 À la demande',
  'disc.summaryStrategy.OnDemand.hint': 'Pas de synthèse auto. L\'agent peut en demander une explicitement (tool surface à venir). Adapté aux gros contextes (Claude Opus, GPT-5).',
  'disc.summaryStrategy.Off.label': '🚫 Désactivée',
  'disc.summaryStrategy.Off.hint': 'Aucune synthèse. Conversation brute jusqu\'à saturation du contexte. Évite tout coût de synthèse — risque tronquer si la discussion s\'allonge beaucoup.',
  'disc.introspectionUnsupportedNote': '{0} ne lit pas le MCP — les outils d\'historique kronn-internal (disc_meta / disc_get_message / disc_summarize) ne seront pas appelés. L\'agent retombe sur la transcription brute dans sa fenêtre de contexte. Préfère `Auto` pour conserver une synthèse de sécurité sur les longues discussions.',
  'workflows.runningTooltip': '{0} workflow{1} en cours d\'exécution',
  'disc.unarchive': 'Désarchiver',
  'disc.editTitle': 'Renommer',
  'disc.workspaceLabel': 'Espace de travail',
  'disc.workspaceDirect': 'Sur le projet',
  'disc.workspaceIsolated': 'Isolé (worktree)',
  'disc.workspaceDirectDesc': 'Travaille directement sur le répertoire du projet',
  'disc.workspaceIsolatedDesc': 'Crée une branche dédiée dans un worktree git',
  'disc.workspaceIsolatedNeedsRepo': 'Nécessite un projet lié à un repo git',
  'disc.branchName': 'Nom de la branche',
  'disc.baseBranch': 'Branche de base',
  'disc.isolatedBadge': 'isolé',
  'disc.worktreeUnlock': 'Libérer la branche',
  'disc.worktreeLock': 'Verrouiller la branche',
  'disc.worktreeUnlocked': 'déverrouillé',
  'disc.switchAgent': "Changer d'agent",
  'disc.modelTier': 'Mode IA',
  'settings.identity': 'Identité',
  'settings.identityHint': 'Votre pseudo et avatar, affichés dans les conversations.',
  'settings.pseudo': 'Pseudo',
  'settings.avatarEmail': 'Email (Gravatar)',
  'settings.avatarHint': 'Utilisé pour afficher votre avatar Gravatar. Optionnel.',
  'settings.bio': 'Bio',
  'settings.bioPlaceholder': 'Ex: Product Manager chez Acme Corp, spécialisé en analytics et growth...',
  'settings.bioHint': 'Partagé avec les agents au début de chaque nouvelle discussion. Aide les agents à adapter leurs réponses à votre profil.',
  'settings.globalContext': 'Contexte global',
  'settings.globalContextHint': 'Ce texte est partagé avec tous vos agents au début de chaque discussion. Idéal pour y mettre votre glossaire métier, vos conventions internes, ou des infos que vos agents doivent toujours connaître.',
  'settings.globalContextPlaceholder': '## Glossaire\n- CMS : notre CMS custom basé sur...\n- DAM : Digital Asset Manager\n\n## Stack\n- Frontend : Next.js 14\n- Backend : Node.js + Express\n\n## Conventions\n- Tests obligatoires pour tout changement\n- PR review par au moins 1 personne',
  'settings.globalContextSaved': 'Contexte global sauvegardé',
  'settings.gcModeAlways': 'Toujours injecter',
  'settings.gcModeNoProject': 'Seulement sans projet',
  'settings.gcModeNever': 'Désactivé',

  // ── Guided tour v2 (0.3.6 — 17 steps) ──
  'tour.welcome.title': 'Bienvenue sur Kronn !',
  'tour.welcome.desc': 'En 2 minutes, vous saurez piloter vos agents IA. Échap pour quitter, ? pour relancer.',
  'tour.conceptProject.title': 'Vos projets',
  'tour.conceptProject.desc': 'Un projet = un dossier ou dépôt qui donne du contexte à vos agents. Code, docs, configs — tout part de là.',
  'tour.scan.title': 'Scanner automatique',
  'tour.scan.desc': 'Détecte les dépôts git déjà présents sur votre machine. Un clic et c\'est fait.',
  'tour.newProject.title': 'Ajoutez un projet',
  'tour.newProject.desc': 'Cliquez sur ce bouton pour voir les options de création.',
  'tour.modalOverview.title': '3 façons de commencer',
  'tour.modalOverview.desc': 'Bootstrap — créez un projet de zéro avec l\'IA\n\nClone — importez un dépôt Git existant\n\nDossier — ajoutez un répertoire local sans git',
  'tour.modalClose.title': 'Bien noté !',
  'tour.modalClose.desc': 'On continue la visite — la modale se ferme automatiquement.',
  'tour.navPlugins.title': 'Plugins',
  'tour.navPlugins.desc': 'Connectez vos outils : GitHub, Jira, Slack... 56 plugins disponibles pour enrichir vos agents.',
  'tour.addPlugin.title': 'Ajouter un plugin',
  'tour.addPlugin.desc': 'Ce bouton ouvre le catalogue des 56 plugins disponibles. Cherchez par nom (ex: "github", "jira") et activez-les sur vos projets. Chaque plugin activé sera accessible par vos agents.',
  'tour.navDiscussions.title': 'Discussions',
  'tour.navDiscussions.desc': 'C\'est ici que vous parlez à vos agents IA, dans le contexte de vos projets.',
  'tour.sidebar.title': 'Vos conversations',
  'tour.sidebar.desc': 'Organisées par projet. Les discussions de workflows sont regroupées en dossiers.',
  'tour.newDisc.title': 'Lancez une discussion',
  'tour.newDisc.desc': 'Cliquez pour ouvrir le formulaire. Vous choisirez un agent, un projet, et des skills.',
  'tour.discForm.title': 'Le formulaire',
  'tour.discForm.desc': 'Agent, projet, skills, fichiers joints — tout se configure ici avant de lancer la conversation.',
  'tour.discProfiles.title': 'Cadrer l\'agent avec un profil',
  'tour.discProfiles.desc': 'Chaque discussion peut être encadrée par un profil (Architect, Tech-Lead, Mentor, QA…). Ils définissent le ton, la méthode et le rôle que l\'agent va adopter — même question, réponses très différentes selon le profil. Clique ici pour déplier la liste.',
  'tour.discProfileChip.title': 'Choisis un profil',
  'tour.discProfileChip.desc': 'Survole un profil pour voir sa description complète. Clique sur un profil pour l\'activer et continuer. Tu peux créer/modifier tes propres profils dans Settings > Skills, Profils & Directives.',
  'tour.navAutomation.title': 'Automatisation',
  'tour.navAutomation.desc': 'Deux outils : Quick Prompts (templates réutilisables) et Workflows (automatisations multi-étapes, cron).',
  'tour.navConfig.title': 'Configuration',
  'tour.navConfig.desc': 'Vos clés API, profils, skills personnalisés et le contexte global partagé avec tous les agents.',
  'tour.usage.title': 'Suivi de consommation',
  'tour.usage.desc': 'Visualisez vos tokens consommés par agent et par modèle. Indispensable pour maîtriser les coûts.',
  'tour.done.title': 'Vous êtes prêt !',
  'tour.done.desc': 'Commencez par lancer une discussion avec un agent. Le bouton ? en haut à droite relance ce tour à tout moment.\n\n🎁 Petit cadeau pour être arrivé·e jusqu\'ici : un easter egg se cache dans Settings — saisis le Konami Code `↑↑↓↓←→←→BA` au bon endroit, puis tape `kronnMatrix` dans le champ qui apparaît. Chuut. 🤫',
  'tour.next': 'Suivant',
  'tour.prev': 'Précédent',
  'tour.skip': 'Passer le tour',
  'tour.finish': 'Terminer',
  'tour.clickHint': 'Cliquez sur l\'élément en surbrillance pour continuer',
  'tour.replayHint': 'Relancer le tour guidé',
  'tour.replay': 'Relancer le tour guidé',

  // ── Ollama (0.4.0) ──
  'ollama.online': 'En ligne',
  'ollama.offline': 'Serveur arrêté',
  'ollama.unreachable': 'Injoignable',
  'ollama.notInstalled': 'Non installé',
  'ollama.models': 'modèle(s)',
  'ollama.refresh': 'Actualiser',
  'ollama.installTitle': 'Installer Ollama',
  'ollama.installDesc': 'Ollama permet de faire tourner des modèles IA localement sur votre machine, gratuitement et sans connexion internet.',
  'ollama.launchTitle': 'Lancer Ollama',
  'ollama.launchDesc': 'Le serveur Ollama n\'est pas lancé. Exécutez : ollama serve',
  'ollama.pullTitle': 'Télécharger un modèle',
  'ollama.pullDesc': 'Ollama est en ligne mais aucun modèle n\'est installé. Exécutez une des commandes suivantes dans votre terminal :',
  'ollama.installedModels': 'Modèles installés',
  'ollama.defaultTag': 'défaut',
  'ollama.pickDefaultHint': 'Clique sur un modèle pour le définir par défaut.',
  'ollama.pickedDefaultHint': 'Défaut actuel : {0}',
  'ollama.pullMoreHint': 'Pour ajouter d\'autres modèles : ollama pull <nom> — voir ollama.com/library',
  'ollama.canirunHint': 'canirun.ai — vérifie si ton hardware peut faire tourner un modèle',
  'contacts.title': 'Contacts',
  'contacts.pending': 'en attente',
  'contacts.inviteCode': "Code d'invitation",
  'contacts.inviteHint': "Partagez ce code pour que d'autres utilisateurs Kronn puissent vous ajouter.",
  'contacts.add': 'Ajouter un contact',
  'contacts.addPlaceholder': 'kronn:pseudo@host:port',
  'contacts.wsConnected': 'Connecté en temps réel',
  'contacts.wsDisconnected': 'Déconnecté',
  'contacts.added': 'Contact ajouté',
  'contacts.addError': "Impossible d'ajouter ce contact",
  'contacts.warnGeneric': 'Contact ajouté mais injoignable — vérifiez que les deux machines sont sur le même réseau.',
  'contacts.warn.TAILSCALE_MISSING': "Contact ajouté mais injoignable — ce contact utilise Tailscale, mais Tailscale n'est pas actif ici. Installez ou connectez Tailscale.",
  'contacts.warn.TAILSCALE_UNREACHABLE': "Contact ajouté mais injoignable — Tailscale est actif mais le contact ne répond pas. Vérifiez qu'il est en ligne.",
  'contacts.warn.LAN_UNREACHABLE': 'Contact ajouté mais injoignable — ce contact est sur un réseau local. Vérifiez que vous êtes sur le même Wi-Fi/LAN.',
  'contacts.warn.NETWORK_UNREACHABLE': "Contact ajouté mais injoignable — vérifiez l'adresse et que le contact est en ligne.",
  'contacts.delete': 'Supprimer',
  'contacts.deleteConfirm': 'Supprimer ce contact ? Vous ne pourrez plus lui envoyer de message ni partager de discussion.',
  'contacts.deleted': 'Contact supprimé',
  'contacts.sharedByPeer': '{0} a partagé "{1}"',
  'contacts.deleteError': 'Impossible de supprimer ce contact',
  'contacts.tailscaleHint': 'Tailscale détecté — ce code fonctionne partout, même si vous changez de réseau.',
  'contacts.detectedIps': 'Adresses IP détectées',
  'contacts.usedInCode': "utilisé dans le code",
  'contacts.clickToUse': "Cliquez pour utiliser cette IP dans le code d'invitation",
  'contacts.ipSelected': 'IP sélectionnée pour le code d\'invitation',
  'contacts.guideTitle': 'Comment connecter deux Kronn',
  'contacts.guideStep1': "Les deux machines doivent être sur le même réseau (LAN, VPN d'entreprise, ou Tailscale).",
  'contacts.guideStep2': 'Copiez votre code d\'invitation ci-dessus.',
  'contacts.guideStep3': "Envoyez-le à votre contact (par email, chat, etc.).",
  'contacts.guideStep4': 'Votre contact le colle dans Discussions > Contacts > bouton "+".',
  'contacts.guideNetworkTitle': 'Quel réseau utiliser ?',
  'contacts.guideNetwork': "VPN d'entreprise (AWS VPN, OpenVPN...) : utilisez votre IP VPN dans le champ Domaine ci-dessous. Tailscale (gratuit) : installez-le sur les deux machines, l'IP est détectée automatiquement. Même WiFi : ça marche aussi, mais l'IP change si vous changez de réseau.",
  'disc.tier.economy': 'Éco',
  'disc.tier.default': 'Standard',
  'disc.tier.reasoning': 'Avancé',

  // ── Debate ──
  'debate.title': 'Débat multi-agents',
  'debate.header': 'Débat inter-agents',
  'debate.instructions': 'Sélectionnez au moins 2 agents. Ils débattront sur {0} round{1} puis l\'agent principal fera la synthèse.',
  'debate.main': 'principal',
  'debate.launch': 'Lancer le débat ({0} agents)',
  'debate.rounds': 'Rounds',

  // ── Config ──
  'config.appearance': 'Apparence',
  'config.appearanceHint': 'Choisissez le thème visuel de l\'interface.',
  'config.themeSystem': 'Système',
  'config.themeLight': 'Clair',
  'config.themeDark': 'Sombre',
  'config.themeMatrix': 'Matrix',
  'config.themeSakura': 'Sakura',
  'config.themeGotham': 'Gotham',
  'config.secretLabel': 'Code secret :',
  'config.secretPlaceholder': '••••••••',
  'config.secretSubmit': 'Débloquer',
  'config.secretUnlocked': 'Thème « {0} » débloqué.',
  'config.secretInvalid': 'Code invalide.',
  'config.konamiReveal': '🎮 Mode secret activé',
  'config.batmanRecruited': '🦇 Batman rejoint la Batcave',
  'skills.autoActivated': '⚡ Skill « {0} » activé automatiquement',
  'skills.autoTriggerOn': 'Auto-activation ON',
  'skills.autoTriggerOff': 'Auto-activation OFF',
  'skills.autoTriggerHint': 'Si ce skill est automatiquement activé quand tu tapes certains mots-clés (ex : « PDF », « rapport »). Clique pour désactiver — le skill reste disponible manuellement.',
  'skills.autoToggleFailed': 'Impossible de changer l\'auto-activation du skill.',
  'config.subtitle': 'Paramètres généraux de Kronn',
  'config.uiLanguage': "Langue de l'interface",
  'config.uiLanguageHint': "Langue d'affichage de l'interface Kronn.",
  'config.outputLanguage': 'Langue de sortie',
  'config.outputLanguageHint': 'Langue dans laquelle les agents répondront par défaut.',
  'config.scanPaths': 'Dossiers à scanner',
  'config.scanPathsHint': 'Dossiers racines dans lesquels Kronn cherche vos dépôts git.',
  'config.scanPathPlaceholder': '/home/user/projects',
  'config.path': 'dossier',
  'config.pathsPlural': 'dossiers',
  'config.scanIgnore': 'Patterns à ignorer',
  'config.scanIgnoreHint': 'Noms de dossiers à ignorer lors du scan (node_modules, .git, etc.).',
  'config.scanIgnorePlaceholder': 'ex: vendor',
  'config.pattern': 'pattern',
  'config.patternsPlural': 'patterns',
  'config.scanDepth': 'Profondeur de scan',
  'config.scanDepthHint': 'Nombre de niveaux de sous-dossiers à explorer pour trouver les dépôts git (2-10).',
  'config.agents': 'Agents',
  'config.installed': 'installé',
  'config.installedPlural': 'installés',
  'config.refresh': 'Rafraîchir',
  'config.fullAccess': 'Autorise l\'accès web, la modification de fichiers et l\'exécution de commandes.',
  'config.autoApply': 'Applique automatiquement les modifications sans confirmation.',
  'config.restrictedAgent': 'L\'agent {0} n\'a pas le mode "full access" activé — il ne pourra pas modifier de fichiers.',
  'config.restrictedAgentLink': 'Activer dans Config > Agents',
  'config.restrictedDebate': 'Certains agents n\'ont pas le mode "full access" — ils ne pourront pas modifier de fichiers.',
  'config.restrictedStep': 'Cet agent n\'a pas le mode "full access" — il ne pourra pas modifier de fichiers.',
  'config.fullAccessBadge': 'Full access',
  'config.uninstallConfirm': 'Désinstaller {0} ?',
  'config.uninstall': 'Désinstaller',
  'config.hostManaged': 'Installé sur l\'hôte',
  'config.uninstallFailed': 'Échec désinstallation — désinstallez manuellement sur l\'hôte',
  'config.installFailed': "Installation de {0} échouée : {1}",
  'config.getKey': 'Obtenir une clé API',
  'config.wslWarning': 'WSL détecté — préférez une installation directement sur WSL, Kronn détectera automatiquement l\'agent. Une installation dans Docker sera perdue lors d\'un rebuild.',
  // ── RTK (compression hook for shell outputs) ──
  'config.rtk.title': 'Mode économique',
  'config.rtk.explainer': 'Les agents lancent beaucoup de commandes (git, cargo, ls, tests) qui produisent du texte volumineux. Kronn peut compresser ces sorties avant qu\'elles n\'arrivent à l\'IA, pour économiser jusqu\'à 90 % de tokens, sans perte de qualité.',
  'config.rtk.poweredBy': 'Propulsé par',
  'config.rtk.openSource': 'open source',
  'config.rtk.stateAll': 'Actif sur tous les agents compatibles',
  'config.rtk.statePartial': 'Actif sur {0}/{1} agents compatibles',
  'config.rtk.stateNone': 'Pas encore activé',
  'config.rtk.notInstalled': 'RTK n\'est pas installé sur cette machine',
  'config.rtk.savings': '~{0} tokens économisés ce mois',
  'config.rtk.activateAll': 'Activer sur tous les agents compatibles',
  'config.rtk.activateRemaining': 'Activer sur les {0} manquants',
  'config.rtk.activating': 'Activation…',
  'config.rtk.installCta': 'Installer RTK puis activer',
  'config.rtk.installHelp': 'RTK doit être installé sur votre machine. Ouvrez un terminal et collez la commande suivante, ou demandez à votre collègue technique de l\'installer pour vous.',
  'config.rtk.installCommand': 'Commande d\'installation',
  'config.rtk.viewOnGithub': 'Voir RTK sur GitHub',
  'config.rtk.viewDocs': 'Documentation',
  'config.rtk.badgeActive': 'RTK actif',
  'config.rtk.badgeMissing': 'RTK — hook non configuré',
  'config.rtk.badgeNotInstalled': 'RTK non installé',
  'config.rtk.badgeNotApplicable': 'Non pris en charge par RTK',
  'config.rtk.aboutLink': 'À propos de RTK',
  'agentRuntimeWarning.vibe.sdk_fallback': 'SDK Vibe incompatible — Kronn bascule sur l\'API Mistral directe (réponses ~4s plus rapides mais sans outils locaux : bash, fichiers, MCP). Mets à jour `mistral-vibe` ou ignore.',
  'app.updateAvailable': 'Kronn {0} → {1} disponible',
  'app.updateOpenRelease': 'Voir la release',
  'app.updateDismiss': 'Masquer cette notification jusqu\'à la prochaine version',
  'app.backendOffline': 'Backend injoignable',
  'app.backendOfflineTitle': 'Le backend Kronn ne répond pas — les actions échoueront tant qu\'il ne sera pas redémarré.',
  'disc.kronnToolResult': 'Voir la réponse',
  'config.rtk.activateError': 'Échec de l\'activation RTK. Voir console pour les détails.',
  'config.rtk.activateSuccess': 'RTK activé.',
  'config.rtk.activatePartial': 'RTK activé partiellement — {0} sur {1} agents ont échoué (voir détails).',
  'config.rtk.activateNoTarget': 'Aucun agent à activer — tout est déjà configuré.',
  'config.rtk.deactivate': 'Désactiver RTK ({0})',
  'config.rtk.deactivateHint': 'Retire le hook RTK des configs des agents — réversible via "Activer".',
  'config.rtk.deactivateError': 'Échec de la désactivation RTK. Voir console.',
  'config.rtk.deactivateSuccess': 'RTK désactivé.',
  'config.rtk.detailsToggle': 'Détails',
  'config.rtk.statTokens': 'Tokens économisés',
  'config.rtk.statRatio': 'Ratio moyen',
  'config.rtk.statSamples': 'Commandes compressées',
  'config.rtk.sobrietyTitle': 'À propos du "mode économique"',
  'config.rtk.sobrietyBody': 'L\'usage le plus sobre reste de ne pas utiliser d\'IA. Si vous en utilisez, RTK compresse les sorties shell avant qu\'elles n\'arrivent au modèle — moins de tokens, moins d\'énergie, même qualité.',
  'config.rtk.updateAvailable': 'Maj {0} dispo',
  'config.rtk.updateAvailableTitle': 'RTK {0} → {1}',
  'config.rtk.updateAvailableAria': 'Mise à jour RTK {0} vers {1} disponible',
  'config.rtk.updateModalTitle': 'Mettre à jour RTK',
  'config.rtk.updateModalBody': 'Installé : {0} — Dernière version connue : {1}. Lance la commande ci-dessous (le script est idempotent et met à jour sur place).',
  'config.agentUpdateAvailableAria': 'Mise à jour {0} vers {1} disponible',
  'config.agentUpdateAvailableTitle': 'Installé {0} → dernière {1}',
  'config.agentUpdateModalTitle': 'Mettre à jour {0}',
  'config.agentUpdateModalBody': 'Installé : {0} — Dernière version connue : {1}. Réexécute la commande d\'installation pour mettre à jour (npm/curl/uv installent par-dessus).',
  'config.enabled': 'Actif',
  'config.disabled': 'En pause',
  'config.toggleEnable': 'Réactiver',
  'config.toggleDisable': 'Mettre en pause',
  'config.apiKeys': 'Clés API (optionnel)',
  'config.apiKeysHint1': 'Vos agents locaux utilisent leur propre authentification (abonnements).',
  'config.apiKeysHint2': 'Si vous souhaitez utiliser une clé API spécifique à la place, vous pouvez la renseigner ici en override.',
  'config.localAuth': 'auth locale',
  'config.tokenUsage': 'Consommation tokens',
  'config.tokenCostHint': 'Coût estimé en tokens quand injecté dans le prompt agent',
  'config.viewModels': 'Voir les modèles',
  'config.account': 'Compte',
  'config.viewAccount': 'Voir mon compte/conso {0}',
  'config.defaultModel': 'Par défaut',
  'config.saveError': 'Erreur de sauvegarde',
  'config.estimateTokenUsage': 'Estimation tokens',
  'config.enableOverride': 'Activer l\'override API key',
  'config.disableOverride': 'Désactiver l\'override (utiliser auth locale)',
  'config.syncTokensConfirm': 'Mettre à jour la clé API dans la config de tous les agents installés ?',
  'config.syncTokensDone': 'Clé synchronisée : {0}',
  'config.syncTokensNone': 'Aucun agent à synchroniser',
  'config.bestPractices': 'Bonnes pratiques par provider',
  'config.discoverKeys': 'Auto-détecter',
  'config.discoverKeysFound': '{0} clé(s) trouvée(s), {1} importée(s)',
  'config.discoverKeysNone': 'Aucune clé détectée',
  'config.addKey': 'Ajouter une clé',
  'config.keyName': 'Nom de la clé',
  'config.defaultKeyName': 'Clé API personnelle',
  'config.deleteKey': 'Supprimer cette clé',
  'config.deleteKeyConfirm': 'Supprimer la clé "{0}" ?',
  'config.activateKey': 'Activer cette clé',
  'config.overrideActive': 'override actif',
  'config.overrideDisabled': 'override désactivé',
  'config.save': 'Sauvegarder',
  'config.saved': 'Sauvegardé !',
  'config.server': 'Serveur & sécurité',
  'config.authToken': 'Token API',
  'config.authEnabled': 'actif',
  'config.authDisabled': 'désactivé',
  'config.authActivate': 'Activer l\'authentification',
  'config.authActivated': 'Authentification activée ! Token copié dans le presse-papier.',
  'config.authDisabledHint': 'L\'API est ouverte sans authentification. Activez un token pour sécuriser l\'accès.',
  'config.authHint': 'Utilisez ce token dans le header Authorization: Bearer <token> pour authentifier les appels API.',
  'config.authCopied': 'Token copié',
  'config.authRegenConfirm': 'Régénérer le token ? Les clients existants devront se reconnecter.',
  'config.authRegenerated': 'Token régénéré',
  'config.authRegenError': 'Erreur lors de la régénération',
  'config.regenerate': 'Régénérer',
  'config.domain': 'Domaine personnalisé',
  'config.domainHint': 'Ex: kronn.local — utilisé pour le CORS et le TLS. Nécessite un redémarrage.',
  'config.domainSave': 'Sauvegarder le domaine',
  'config.domainSaved': 'Domaine sauvegardé (redémarrage nécessaire)',
  'config.domainError': 'Erreur de sauvegarde',
  'config.maxAgents': 'Agents simultanés max',
  'config.maxAgentsHint': 'Nombre maximum de processus agents en parallèle. Redémarrage nécessaire pour appliquer.',
  'config.usage': 'Usage',
  'config.usageEmpty': 'Aucune donnée de consommation disponible.',
  'config.usageTotalTokens': 'Tokens totaux',
  'config.usageTotalCost': 'Coût estimé',
  'config.usageDiscussions': 'Discussions',
  'config.usageWorkflows': 'Workflows',
  'config.usageByProvider': 'Par fournisseur',
  'config.usageByProject': 'Par projet',
  'config.usageTopDiscussions': 'Top discussions',
  'config.usageTopWorkflows': 'Top workflows',
  'config.usageDaily': 'Historique (30 jours)',
  'config.usageFilterAll': 'Tout',
  'config.usageFilterDisc': 'Disc.',
  'config.usageFilterWf': 'Wf.',
  'config.database': 'Base de données',
  'config.dbProjects': 'Projets',
  'config.dbDiscussions': 'Discussions',
  'config.dbMessages': 'Messages',
  'config.dbMcps': 'Plugins',
  'config.dbWorkflows': 'Workflows',
  'config.dbSkills': 'Skills custom',
  'config.dbProfiles': 'Profils custom',
  'config.dbDirectives': 'Directives custom',
  'config.export': 'Exporter',
  'config.import': 'Importer',
  'config.dbBackup': 'Snapshot DB',
  'config.dbBackupSuccess': 'Snapshot écrit : {0}',
  'config.dbBackupError': 'Échec du snapshot — voir console',
  'config.importInvalid': 'Fichier invalide',
  'config.importConfirm': 'Cela remplacera toutes les données actuelles. Continuer ?',
  'config.importError': "Erreur lors de l'import",
  'config.importSuccess': 'Import réussi',
  'config.importPathWarning': '{0} projet(s) avec un chemin invalide — remappez-les dans la page Projets',
  'config.exportApiWarning': "Les tokens d'authentification ne sont pas inclus dans l'export. Notez-les avant de migrer.",
  'config.exportApiWarningLink': 'Voir mes tokens →',
  'config.resetHint': "Remettre à zéro la configuration et relancer l'assistant de setup.",
  'config.resetConfirm': "ATTENTION : cette action va supprimer toute la configuration (clés des fournisseurs IA, préférences, agents).\n\nPensez à :\n- Exporter vos données (Config → Base de données → Exporter)\n- Noter vos clés API et votre token d'authentification\n\nCette action est irréversible. Continuer ?",
  'config.reset': 'Reset configuration',
  'config.configFile': 'Fichier de config',

  // ── Plugins ──
  'mcp.title': 'Plugins',
  'mcp.subtitle': '(MCP / API)',
  'mcp.add': 'Ajouter',
  'mcp.detect': 'Synchroniser',
  'mcp.syncing': 'Synchronisation…',
  'mcp.incomplete.title': '{0} plugin(s) non opérationnel(s)',
  'mcp.incomplete.hint': 'Leur config est incomplète. Kronn ne les charge PAS dans les projets pour ne pas casser les agents au boot. Clique sur une ligne pour corriger.',
  'mcp.search': 'Rechercher un plugin ou un projet...',
  'mcp.searchRegistry': 'Rechercher un plugin...',
  'mcp.sortAz': 'Trier A → Z',
  'mcp.sortZa': 'Trier Z → A',
  'mcp.configure': 'Configurer {0}',
  'mcp.addTitle': 'Ajouter un plugin',
  'mcp.addAnother': 'Ajouter une autre config {0}',
  'mcp.label': 'Nom / Label',
  'mcp.envVars': "Variables d'environnement",
  'mcp.custom.tileTitle': 'Custom API',
  'mcp.custom.tileCat': 'API personnalisée',
  'mcp.custom.tileDesc': "Définis ta propre API REST : nom, URL de base, description libre, lien doc optionnel et les champs dont l'agent a besoin (tokens, IDs, headers).",
  'mcp.custom.tileBadge': 'Définie par toi',
  'mcp.custom.name': "Nom de l'API",
  'mcp.custom.namePlaceholder': 'ex : Salesforce Sales API',
  'mcp.custom.baseUrl': 'URL de base',
  'mcp.custom.baseUrlPlaceholder': 'https://my-org.salesforce.com/services/data/v59.0',
  'mcp.custom.description': "Décris l'API",
  'mcp.custom.descriptionPlaceholder': "À quoi sert cette API ? Auth ? Notes que l'agent doit lire. L'agent ira aussi chercher dans le lien doc ci-dessous.",
  'mcp.custom.docsUrl': 'Lien vers la doc (optionnel)',
  'mcp.custom.fields': 'Champs',
  'mcp.custom.fieldsHint': "Une paire {Label, Valeur} par champ. Kronn dérive automatiquement la clé d'env (ex : « Bearer Token » → BEARER_TOKEN).",
  'mcp.custom.fieldLabel': 'Label (ex : Bearer Token)',
  'mcp.custom.fieldValue': 'Valeur',
  'mcp.custom.fieldAdd': 'Ajouter un champ',
  'mcp.custom.fieldRemove': 'Retirer ce champ',
  // 0.8.6 — edit mode: secrets-preserved reassurance
  'mcp.custom.fieldValueStored': '•••• stocké en chiffré',
  'mcp.custom.fieldValueStoredTooltip': 'Valeur conservée. Modifie-la via « Éditer les secrets » sur le drawer du plugin.',
  'mcp.custom.fieldValueEditElsewhere': '→ Édite la valeur via « Éditer les secrets »',
  'mcp.custom.fieldsHintEditMode': "Tu peux modifier ici la STRUCTURE (nom des champs, endpoints, auth) ET les VALEURS des credentials. Les valeurs existantes sont pré-remplies (masquées) — laisse-les telles quelles pour les conserver, ou tape par-dessus pour remplacer.",
  'mcp.custom.envViewOnlyHint': "Vue lecture seule. Pour modifier les valeurs, clique « Modifier le plugin ».",
  'mcp.custom.save': 'Enregistrer',
  // 0.8.6 — Edit existing Custom plugin (separate save label so the
  // UX is clearly "you're editing, not creating").
  'mcp.custom.saveEdit': 'Enregistrer les modifications',
  'mcp.custom.editSpec': 'Modifier le plugin',
  // 0.8.6 (#29) — banner for legacy Custom plugins with no endpoints.
  'mcp.custom.autodiscoveryTitle': 'Aucun endpoint déclaré',
  'mcp.custom.autodiscoveryHint': "Sans endpoints, les agents doivent lire la doc à chaque appel. Demande à l'IA d'en proposer une liste à partir de la `docs_url`.",
  'mcp.custom.autodiscoveryCta': "Demander à l'IA d'aider",
  'mcp.custom.updated': 'API « {0} » mise à jour',
  'mcp.custom.specSavedEnvFailed': 'Spec sauvée, mais les valeurs des credentials ont échoué : {0}',
  'mcp.custom.errorName': "Le nom de l'API est obligatoire",
  'mcp.custom.errorBaseUrl': "L'URL de base est obligatoire",
  'mcp.custom.created': 'API « {0} » ajoutée',
  'mcp.custom.error': 'Échec : {0}',
  // 0.8.6 (#33) — Custom plugin import / export (clipboard JSON).
  'mcp.custom.copyAsJson': 'Copier comme JSON',
  'mcp.custom.copied': 'JSON du plugin copié dans le presse-papiers (sans credentials)',
  'mcp.custom.copyManual': 'Copie ce JSON manuellement :',
  'mcp.custom.exportTitle': 'Exporter « {0} »',
  'mcp.custom.exportHint': 'Voici le JSON du plugin (sans credentials). Il est copié dans le presse-papiers ; sinon utilise Ctrl+C / Cmd+C sur le texte ci-dessous.',
  'mcp.custom.copyManualInstruction': 'Le presse-papiers a refusé l\'écriture. Sélectionne le texte et copie-le manuellement.',
  'mcp.custom.exportError': "Ce plugin n'a pas de spec exportable",
  'mcp.custom.importTileTitle': 'Importer depuis JSON',
  'mcp.custom.importTileDesc': "Colle l'export d'un autre Kronn (spec uniquement, jamais de credentials).",
  'mcp.custom.importPasteLabel': 'Colle le JSON du plugin',
  'mcp.custom.importPlaceholder': '{ "name": "...", "base_url": "https://...", "endpoints": [ ... ] }',
  'mcp.custom.importPasteFromClipboard': 'Coller depuis le presse-papiers',
  'mcp.custom.importPasteUnavailable': "Accès au presse-papiers refusé — colle manuellement",
  'mcp.custom.importSubmit': 'Importer le plugin',
  'mcp.custom.importSecretsHint': "Les credentials ne sont jamais importés. Renseigne-les ensuite via « Éditer les secrets ».",
  'mcp.custom.importErrorParse': "JSON invalide — vérifie qu'il n'y a pas de virgule ou guillemet manquant",
  'mcp.custom.importErrorShape': 'Le JSON doit être un objet, pas un tableau ou une valeur primitive',
  'mcp.custom.importErrorName': 'Champ « name » manquant ou vide',
  'mcp.custom.importErrorBaseUrl': 'Champ « base_url » manquant ou vide',
  'mcp.custom.imported': 'Plugin « {0} » importé — renseigne ses credentials',
  // ── 0.8.6 — Endpoints declared at create time ──
  'mcp.custom.endpoints.header': 'Endpoints',
  'mcp.custom.endpoints.emptyHint': "Aucun endpoint déclaré : tes agents devront lire la doc à chaque appel. Renseigne `docs_url` ci-dessus puis demande à l'IA de remplir cette liste (« Construire avec l'IA »).",
  'mcp.custom.endpoints.populatedHint': '{0} endpoint(s) déclarés. Les agents pourront les appeler directement via le MCP `api_call` (sans credentials côté prompt).',
  'mcp.custom.endpoints.methodLabel': 'Méthode HTTP',
  'mcp.custom.endpoints.descPlaceholder': "Courte description (ex : 'Liste les widgets')",
  'mcp.custom.endpoints.add': 'Ajouter un endpoint',
  'mcp.custom.endpoints.remove': 'Retirer cet endpoint',
  // 0.8.6 — Auth picker
  'mcp.custom.auth.header': 'Authentification',
  'mcp.custom.auth.hint': "Comment Kronn injecte les credentials dans chaque call. Choisis le pattern de l'API. L'IA helper peut remplir cette section depuis la doc.",
  'mcp.custom.auth.kind.none': 'Aucune (endpoints publics)',
  'mcp.custom.auth.kind.bearer': 'Bearer (Authorization: Bearer <token>)',
  'mcp.custom.auth.kind.tokenExchange': 'Token exchange (POST creds → access_token → Bearer)',
  'mcp.custom.auth.kind.other': 'Autre (édition JSON requise)',
  'mcp.custom.auth.kind.otherWarning': "⚠ Ce plugin utilise un schéma d'auth non encore exposé dans le picker (ApiKeyQuery / Header / Basic / OAuth2). Conservé tel quel — édition manuelle requise pour le changer.",
  'mcp.custom.auth.bearer.envKey': 'Champ contenant le token',
  'mcp.custom.auth.bearer.envKeyPlaceholder': '— choisir un champ —',
  'mcp.custom.auth.tokenExchange.hint': "L'API exige un échange 2-étapes : POST creds → réponse contient un access_token → utilisé en Bearer sur les calls suivants. Kronn cache le token et le rafraîchit avant expiration.",
  'mcp.custom.auth.tokenExchange.endpoint': "Endpoint d'échange (relatif au base_url)",
  'mcp.custom.auth.tokenExchange.method': 'Méthode HTTP',
  'mcp.custom.auth.tokenExchange.bodyFormat': 'Format du body',
  'mcp.custom.auth.tokenExchange.bodyTemplate': 'Template du body (JSON)',
  'mcp.custom.auth.tokenExchange.bodyTemplateHint': 'Utilise ${ENV.NOM_DU_CHAMP} pour référencer un credential stocké. Exemple Didomi : {"type": "api-key", "key": "${ENV.API_KEY}", "secret": "${ENV.API_SECRET}"}',
  'mcp.custom.auth.tokenExchange.tokenJsonpath': 'JSONPath du token dans la réponse',
  'mcp.custom.auth.tokenExchange.ttl': 'TTL du token (secondes — 3600 = 1h, 0 = pas de cache)',
  'mcp.custom.auth.tokenExchange.inject': 'Injection du token dans les calls suivants',
  // 0.8.6 — orphan env warning
  'mcp.envOrphanHint': "Des valeurs stockées sous d'anciens noms de champs ({0}) sont devenues inaccessibles après ton rename. Elles seront supprimées au prochain Save. Repaste tes credentials sous les nouveaux noms.",
  // ── Custom API AI helper (chat bubble) ──
  'mcp.custom.helper.trigger': "Construire avec l'IA",
  'mcp.custom.helper.triggerHint': "Demande à un agent local de pré-remplir le formulaire à partir d'un curl, d'un lien doc ou d'une description libre",
  'mcp.custom.helper.discTitle': 'Aide config API custom (éphémère)',
  'mcp.custom.helper.bubbleTitle': 'Aide config API',
  'mcp.custom.helper.ephemeral': 'éphémère',
  'mcp.custom.helper.minimize': 'Réduire',
  'mcp.custom.helper.restore': "Rouvrir l'aide",
  'mcp.custom.helper.close': 'Fermer (supprime la conversation)',
  'mcp.custom.helper.switchAgent': "Changer d'agent (réinitialise la conversation)",
  'mcp.custom.helper.thinking': "L'agent réfléchit…",
  'mcp.custom.helper.inputPlaceholder': "Colle un curl, un lien doc, ou décris ton API en libre…",
  'mcp.custom.helper.send': 'Envoyer',
  'mcp.custom.helper.stop': 'Stop',
  'mcp.custom.helper.noAgents': "Aucun agent installé. Configure-en au moins un dans Settings → Agents.",
  'mcp.custom.helper.contextHint': "L'agent voit ce que tu as déjà rempli",
  'mcp.custom.helper.welcome': "Salut ! Comment veux-tu décrire ton API ? Choisis un raccourci ou tape librement.",
  'mcp.custom.helper.starter.curl': 'Coller un curl',
  'mcp.custom.helper.starter.curlPrompt': "Voici un exemple de curl qui appelle l'API à configurer. Extrais le nom, base URL, les champs nécessaires (tokens, IDs, headers) et propose-moi une config Kronn :\n\n```\n",
  'mcp.custom.helper.starter.docs': 'Lien vers la doc',
  'mcp.custom.helper.starter.docsPrompt': "Voici l'URL de la doc de l'API. Lis-la et propose-moi une config Kronn (name, base_url, description, fields nécessaires) :\n",
  'mcp.custom.helper.starter.describe': 'Décrire en libre',
  'mcp.custom.helper.starter.describePrompt': "Je veux configurer une API pour ",
  'mcp.custom.helper.suggestion': 'Proposition',
  'mcp.custom.helper.apply': 'Appliquer',
  'mcp.custom.helper.applied': '✓ Appliqué',
  'mcp.custom.helper.ctx.header': '=== ÉTAT ACTUEL DU FORMULAIRE ===',
  'mcp.custom.helper.ctx.empty': '(vide)',
  'mcp.custom.helper.ctx.unnamed': 'Custom API',
  'mcp.custom.helper.ctx.noFields': '  (aucun champ déclaré)',
  'mcp.custom.helper.ctx.fieldsCount': '{0} champ(s)',
  'mcp.custom.helper.ctx.noEndpoints': '  (aucun endpoint déclaré — fetch docs_url puis émets-en)',
  'mcp.custom.helper.sys.role': "Tu es un assistant Kronn spécialisé dans la création de configs d'API REST custom. Tu aides l'utilisateur à remplir un formulaire qui décrit une API tierce que Kronn pilotera.",
  'mcp.custom.helper.sys.boundaries': "Le formulaire a 4 champs principaux : name (court, ex \"Salesforce Sales API\"), base_url (URL racine), description (libre, ce que fait l'API + auth notes), docs_url (lien doc, optionnel), et une liste de fields (paires {label, value} pour tokens/IDs/headers que l'utilisateur fournira).",
  'mcp.custom.helper.sys.action': "Quand tu as assez d'infos pour proposer une config (même partielle), émets un bloc KRONN:APPLY au format ci-dessous. Tu peux émettre plusieurs blocs au fil de la conversation.",
  'mcp.custom.helper.sys.format': "Format exact pour les propositions (le bloc DOIT commencer par 'KRONN:APPLY' suivi d'une fence ```json) :",
  'mcp.custom.helper.sys.partial': "Tu peux émettre une proposition partielle (ex juste name + base_url) si tu as les infos pour ça mais qu'il manque encore les fields. Ne devine JAMAIS de vraies valeurs de secrets ; laisse value=\"\" pour les fields, l'utilisateur tapera ses credentials lui-même.",
  'mcp.custom.helper.sys.endpoints': "ENDPOINTS — quand `docs_url` est renseigné dans le formSnapshot, utilise ton outil WebFetch (si tu l'as) pour aller fetch la doc, puis émets un champ `endpoints` dans ton KRONN:APPLY avec 5-15 endpoints les plus utiles (chacun : `{path, method, description}`). Préfère les endpoints de lecture (GET) sauf si l'utilisateur a explicitement demandé une opération d'écriture. Description courte (≤120 chars). Ne ré-émets PAS les endpoints déjà visibles dans le formSnapshot. Si tu n'as pas WebFetch, dis-le à l'utilisateur et demande-lui de coller les paths importants.",
  'mcp.custom.helper.sys.style': "Reste concis. Pose une question à la fois si tu as besoin de clarifier. Précise toujours ce qui manque pour finaliser la config.",
  'mcp.custom.helper.sys.starter': "Salut ! Décris-moi l'API que tu veux configurer : colle un curl, un lien vers la doc, ou explique en langage naturel.",
  'mcp.custom.helper.sys.userQuestion': '=== QUESTION UTILISATEUR ===',
  'mcp.setup': 'Configuration',
  'mcp.getToken': 'Obtenir un token',
  'mcp.value': 'valeur...',
  'mcp.globalAll': 'Global (tous les projets)',
  'mcp.addBtn': 'Ajouter',
  'mcp.back': 'Retour',
  'mcp.alreadyAdded': 'déjà présent',
  'mcp.keys': 'clé',
  'mcp.keysPlural': 'clés',
  'mcp.clickToRename': 'Cliquer pour renommer',
  'mcp.editKeys': 'Modifier les clés',
  'mcp.close': 'Fermer',
  'mcp.show': 'Afficher',
  'mcp.hide': 'Masquer',
  'mcp.saving': 'Sauvegarde...',
  'mcp.save': 'Sauvegarder',
  'mcp.cancel': 'Annuler',
  'mcp.revealWarning': 'Les valeurs actuelles n\u2019ont pas pu être déchiffrées. Saisissez les nouvelles valeurs ci-dessous.',
  'mcp.secretsBroken': 'Les secrets de ce plugin sont corrompus — cliquez pour re-saisir les valeurs',
  'mcp.secretsBrokenShort': 'Secrets',
  'mcp.disableGlobal': 'Désactiver global — garder uniquement les projets cochés',
  'mcp.enableGlobal': 'Activer sur tous les projets',
  'mcp.general': 'Général',
  'mcp.disableGeneral': 'Désactiver pour les discussions sans projet',
  'mcp.enableGeneral': 'Activer pour les discussions sans projet',
  'mcp.editContext': 'Éditer le contexte de {0} pour {1}',
  'mcp.customized': '(personnalisé)',
  'mcp.default': '(par défaut)',
  'mcp.moreProjects': '+{0} projets',
  'mcp.lessProjects': 'Réduire',
  'mcp.deleteConfig': 'Supprimer cette config',
  'mcp.deleteConfigConfirm': 'Supprimer la config "{0}" ? Cette action est irréversible — clés, projets liés et contextes personnalisés seront perdus.',
  'mcp.deleteConfigSuccess': 'Config "{0}" supprimée',
  'mcp.deleteConfigError': 'Suppression échouée : {0}',
  'mcp.empty': 'Aucun plugin configuré. Cliquez "Ajouter" pour en installer un ou "Synchroniser" pour scanner vos projets.',
  'mcp.contextTitle': 'Contexte plugin — {0}',
  'mcp.contextInfo': 'Projet: {0} · Fichier: docs/operations/mcp-servers/{1}.md',
  'mcp.contextPlaceholder': 'Instructions pour les agents utilisant ce plugin dans ce projet...',
  'mcp.config': 'config',
  'mcp.configPlural': 'configs',
  'mcp.server': 'plugin',
  'mcp.serverPlural': 'plugins',
  'mcp.global': 'globale',
  'mcp.globalPlural': 'globales',
  'mcp.project': 'projet',
  'mcp.projectPlural': 'projets',
  // Categories
  'mcp.cat.all': 'Tous',
  'mcp.cat.gitCode': 'Git & Code',
  'mcp.cat.databases': 'Bases de données',
  'mcp.cat.cloud': 'Cloud & Infra',
  'mcp.cat.search': 'Recherche & Web',
  'mcp.cat.monitoring': 'Monitoring',
  'mcp.cat.communication': 'Communication',
  'mcp.cat.projectMgmt': 'Gestion de projet',
  'mcp.cat.design': 'Design',
  'mcp.cat.utilities': 'Utilitaires',
  'mcp.cat.other': 'Autres',
  'mcp.setupRequired': 'Configuration requise',
  'mcp.official': 'Officiel',
  'mcp.community': 'Communautaire',
  // Plugin kind badges + filter
  'mcp.kind.mcp': 'MCP',
  'mcp.kind.api': 'API',
  'mcp.kind.hybrid': 'MCP + API',
  'mcp.kind.all': 'Tous',
  'mcp.kind.mcpTooltip': 'Plugin MCP — l\'agent utilise des tools dédiés exposés par le serveur MCP.',
  'mcp.kind.apiTooltip': 'Plugin API — l\'agent appelle une API REST via curl (pas de tools MCP).',
  'mcp.kind.hybridTooltip': 'Plugin hybride — expose à la fois un MCP et une API REST. L\'agent choisit selon le besoin.',
  'mcp.kind.allTooltip': 'Tous les plugins (MCP + API + hybrides).',
  'mcp.by': 'par',
  'mcp.mcpLoad': 'Charge MCP',
  'mcp.mcpLoadOk': 'Charge MCP OK',
  'mcp.mcpLoadWarn': 'Beaucoup de MCPs — possible ralentissement',
  'mcp.mcpLoadDanger': 'Trop de MCPs — risque de timeout agent',
  'mcp.scope': 'Portée',

  // ── MCP setup help (i18n overrides for token_help) ──
  'mcp.help.mcp-fastly': "Nécessite le Fastly CLI et le binaire fastly-mcp.\n\n1. Installer le CLI : https://developer.fastly.com/reference/cli/\n2. Installer le serveur MCP : https://github.com/fastly/mcp/releases\n3. Créer un profil d'authentification :\n   fastly profile create mon-profil\n   (le nom est libre, au choix — ex : \"prod\", \"company\"…)\n4. Coller votre API token quand demandé.\n\nAucune variable d'environnement requise — l'auth est lue depuis les profils CLI.",
  'mcp.help.mcp-gitlab': "Nécessite le CLI GitLab (glab).\n\n1. Installer le CLI : https://gitlab.com/gitlab-org/cli/-/releases\n   brew install glab (macOS/Linux) ou winget install glab (Windows)\n2. Créer un Personal Access Token (scope api) sur votre GitLab\n3. Renseigner les champs ci-dessous :\n   GITLAB_TOKEN : votre PAT\n   GITLAB_HOST : le hostname de votre GitLab (ex : gitlab.company.com)\n   Laisser GITLAB_HOST vide pour gitlab.com.\n\nKronn transmet automatiquement ces valeurs à glab — pas besoin de lancer glab auth login.",
  'mcp.help.mcp-microsoft-365': "Connecte Outlook (mail, calendrier), Teams, OneDrive et OneNote via Microsoft Graph.\nServeur communautaire : @softeria/ms-365-mcp-server (--org-mode pour compte professionnel).\nPas de serveur officiel Microsoft pour le moment.\n\nOption 1 — App Softeria par défaut (rapide) :\nLaisser les champs vides. Au premier lancement, l'agent demandera d'aller sur https://microsoft.com/devicelogin et d'entrer un code affiché dans le terminal. Votre admin Microsoft peut bloquer les apps tierces.\n\nOption 2 — Votre propre app Azure (recommandé pour les organisations) :\n1. portal.azure.com → Entra ID → Inscriptions d'applications → Nouvelle inscription\n2. Nom : \"Kronn MCP\", type : Comptes dans cet annuaire uniquement\n3. Copier l'ID d'application (client) → MS365_MCP_CLIENT_ID\n4. Copier l'ID de l'annuaire (locataire) → MS365_MCP_TENANT_ID\n5. Authentification → Cocher \"Autoriser les flux de clients publics\" (obligatoire !)\n6. Permissions API → Microsoft Graph → Permissions déléguées :\n   Mail.Read, Mail.Send, Calendars.ReadWrite, Chat.Read, Files.ReadWrite, User.Read\n7. Accorder le consentement administrateur si requis\n\nAuthentification :\nAu premier lancement, le serveur affiche un code et une URL.\nOuvrir https://microsoft.com/devicelogin dans un navigateur, entrer le code, se connecter.\nLes tokens sont cachés localement — pas besoin de se reconnecter à chaque fois.\n\nEn cas de \"Failed to reconnect\" :\nnpx @softeria/ms-365-mcp-server --org-mode --logout\nnpx @softeria/ms-365-mcp-server --org-mode --login\n\nFonctionne avec tous les agents Kronn (cross-agent).",

  // ── Workflows ──
  'wf.title': 'Automatisation',
  'wf.subtitle': 'Workflows, quick prompts',
  'wf.new': 'Nouveau workflow',
  'wf.newHint': 'Créer un workflow manuellement, étape par étape. Tu contrôles chaque détail.',
  'wf.tabWorkflows': 'Workflows',
  'wf.tabQuickPrompts': 'Quick Prompts',
  'wf.tabQuickApis': 'Quick APIs',
  'qa.new': 'Nouveau Quick API',
  'qa.empty': 'Aucun Quick API',
  'qa.emptyHint': 'Crée des appels API réutilisables (POST Jira issue, get GitHub PR…) avec des variables {{title}}, {{key}}…',
  'qa.name': 'Nom',
  'qa.namePlaceholder': 'Créer un ticket Jira',
  'qa.descriptionLabel': 'Description (recommandée)',
  'qa.descriptionPlaceholder': 'Crée un ticket dans le projet EW avec un titre + description fournis en variables.',
  'qa.variables': 'Variables détectées',
  'qa.varLabel': 'Label affiché',
  'qa.varPlaceholder': 'Placeholder',
  'qa.varRequired': 'Obligatoire',
  'qa.varDescriptionPlaceholder': 'Description (optionnelle)',
  'qa.varsHint': 'Tape `{{nom}}` dans n\'importe quel champ ci-dessus (endpoint, body, headers, query) — les variables apparaîtront ici.',
  'qa.save': 'Enregistrer',
  'qa.edit': 'Éditer',
  'qa.delete': 'Supprimer',
  'qa.deleteConfirm': 'Supprimer le Quick API « {name} » ?',
  'qa.export': 'Exporter (JSON)',
  'qa.exportDone': '« {name} » exporté ✓',
  'qa.vars': '{0} variable(s)',
  'qa.noPluginsWarning': 'Aucun plugin API n\'est configuré sur cet hôte. Crée d\'abord un plugin (Atlassian, GitHub, SpeedCurve, …) dans l\'onglet MCPs / Plugins, puis reviens ici.',
  'qa.noPluginsCta': 'Configurer un plugin →',
  'qa.saveBlockedHint': 'Champs obligatoires manquants',
  'qa.fieldName': 'nom',
  'qa.fieldPlugin': 'plugin API',
  'qa.fieldConfig': 'config plugin',
  'qa.fieldEndpoint': 'endpoint',
  'qa.launch': 'Lancer (en direct)',
  'qa.runTitle': 'Lancer « {name} »',
  'qa.runNoVars': 'Ce Quick API n\'a pas de variables à fournir.',
  'qa.runGo': 'Exécuter',
  'qa.runFailed': 'Échec de l\'exécution',
  'qa.batch.launch': 'Batch (lancer N appels en parallèle)',
  'qa.batch.cancel': 'Annuler',
  'qa.batch.go': 'Lancer le batch',
  'qa.batch.inputLabel': 'Une valeur de `{0}` par ligne (ou séparées par , ou ;)',
  'qa.batch.inputPlaceholder': 'www.euronews.com\nde.euronews.com\nfr.euronews.com',
  'qa.batch.jsonInputLabel': 'JSON array d\'objets — un objet par appel',
  'qa.batch.jsonInputHint': 'Variables attendues : {0}. Exemple : `[{"host":"de.example.com","limit":"5"}, ...]`',
  'qa.batch.jsonInputPlaceholder': '[\n  {"host":"www.example.com","limit":"5"},\n  {"host":"de.example.com","limit":"5"}\n]',
  'qa.batch.countHint': '{0} appel(s) seront lancés',
  'qa.batch.jsonCountHint': '{0} appel(s) parsé(s) du JSON',
  'qa.batch.resultSummary': '{0}/{1} OK · {2} échec(s) ·',
  'qa.batch.colInput': 'Input',
  'qa.batch.colStatus': 'Statut',
  'qa.batch.colResult': 'Résultat',
  'qa.batch.expandRow': 'voir tout',
  'qa.batch.collapseRow': 'masquer',
  'qa.batch.errorEmpty': 'Aucun item à lancer.',
  'qa.batch.errorJsonArray': 'Le JSON doit être un array (`[...]`).',
  'qa.batch.errorJsonParse': 'JSON invalide : {0}',
  'qp.new': 'Nouveau prompt',
  'qp.empty': 'Aucun quick prompt',
  'qp.emptyHint': 'Créez des prompts réutilisables avec des variables {{ticket}}, {{url}}...',
  'qp.name': 'Nom',
  'qp.namePlaceholder': 'Analyse ticket Jira',
  'qp.prompt': 'Prompt',
  'qp.promptPlaceholder': 'Analyse le ticket {{ticket}} sur Jira et identifie...',
  'qp.addVariable': '+ Variable',
  'qp.varName': 'Nom',
  'qp.varLabel': 'Label affiché',
  'qp.varPlaceholder': 'Placeholder',
  'qp.save': 'Enregistrer',
  'qp.saveBlockedHint': 'Champs obligatoires manquants',
  'qp.bindingsLabel': 'Liaisons (skills · profils · directives)',
  'qp.improveWithAi': 'Améliorer ce QP avec l\'IA',
  'qp.improveInitialPrompt': 'Audite et améliore le Quick Prompt « {0} » ci-dessous. Suis le protocole : audit en table, recommandations, puis QP refactoré complet en bloc JSON suivi du signal KRONN:QP_IMPROVED.',
  'qp.improveVisibleIntro': '✨ Audit et amélioration du Quick Prompt « {0} » en cours…',
  'qp.history.openLabel': '🕒 {0} version(s)',
  'qp.history.openTitle': 'Historique des versions de « {0} »',
  'qp.history.drawerTitle': 'Historique — {0}',
  'qp.history.empty': 'Aucune version archivée. Les versions sont créées automatiquement à chaque modification du QP.',
  'qp.history.current': 'actuelle',
  'qp.history.launchesTooltip': 'Nombre de discussions lancées avec cette version du QP.',
  'qp.history.avgTokensTooltip': 'Tokens moyens du premier message agent (avant les éventuels échanges suivants).',
  'qp.history.avgDurationTooltip': 'Durée moyenne (wallclock) du premier message agent.',
  'qp.history.avgCostTooltip': 'Coût moyen en USD du premier message agent.',
  'qp.history.showDiff': 'Voir le diff v{0} → v{1}',
  'qp.history.hideDiff': 'Masquer le diff',
  'qp.history.diffPrev': 'v{0} (précédente)',
  'qp.history.diffNext': 'v{0} (cette version)',
  'qp.history.btnTitle': 'Voir l\'historique et les stats',
  'qp.history.deleteAria': 'Supprimer la version {0}',
  'qp.history.deleteConfirm': 'Supprimer la version v{0} ? Les statistiques des lancements liés à cette version ne seront plus attribuées au QP (les discussions elles-mêmes restent intactes).',
  'qp.metrics.chipTooltip': 'Statistiques sur la version actuelle (v{0}) : moyenne du premier message agent sur {1} lancement(s).',
  'qp.launch.missingRequired': 'Champs obligatoires manquants : {0}',
  'qp.improvedReady': '✨ QP amélioré prêt — un clic pour le déployer.',
  'qp.deployImproved': 'Déployer le QP amélioré',
  'qp.deployInProgress': 'Déploiement en cours…',
  'qp.deployFailed': 'Échec du déploiement : {0}',
  'qp.deploySuccess': 'QP déployé en v{0}',
  'qp.deployedAtVersion': '✅ QP déployé en v{0}',
  'qp.improveCatalogHeader': 'Catalogue disponible (à exploiter dans le refactor)',
  'qp.improveCatalogIntro': 'Voici la liste des skills, profils et directives installés dans cette instance Kronn. Tu peux les ajouter aux `skill_ids` / `profile_ids` / `directive_ids` du QP refactoré quand c\'est pertinent — préserve toujours les bindings déjà présents sauf s\'ils sont clairement à côté de la plaque.',
  'qp.improveCatalogSkills': 'Skills',
  'qp.improveCatalogProfiles': 'Profils',
  'qp.improveCatalogDirectives': 'Directives',
  'qp.improveCatalogEmpty': '_Aucun élément disponible dans cette catégorie._',
  'qp.fieldName': 'nom',
  'qp.fieldPrompt': 'prompt',
  'qp.launch': 'Lancer',
  'qp.agentBadgeTooltip': 'QP lié à l\'agent {0}',
  'qp.compareAgents.button': 'Comparer sur {0} agents installés',
  'qp.compareAgents.tooltip': 'Lance ce QP en parallèle sur les {0} agents installés — un disc par agent, regroupés en batch.',
  'qp.compareAgents.cta': 'Comparer ({0})',
  'qp.compareAgents.selectorLabel': 'Comparer sur :',
  'qp.compareAgents.selectAll': 'Tout',
  'qp.compareAgents.selectNone': 'Aucun',
  'qp.vars': '{0} variable(s)',
  'qp.delete': 'Supprimer',
  'qp.deleteConfirm': 'Supprimer ce quick prompt ?',
  'qp.syntaxHint': 'Variables : {{nom}}. Sections conditionnelles : {{#nom}}texte si rempli{{/nom}}',
  'qp.descriptionLabel': "Description (optionnelle — aide les workflows batch à comprendre à quoi sert ce prompt)",
  'qp.descriptionPlaceholder': "Ex : Analyse technique d'un ticket Jira pour le cadrage (vision tech, effort, ouvertures)",
  'qp.variables': 'Variables',
  'qp.varRequired': 'Obligatoire',
  'qp.varDescriptionPlaceholder': "À quoi sert cette variable ? (ex : Identifiant du ticket Jira, ex EW-7182)",
  'qp.batch.launch': 'Lancer en batch',
  'qp.batch.worktree': 'Isoler chaque discussion dans un worktree git',
  'qp.batch.worktreeHint': 'Chaque discussion enfant tourne dans son propre worktree git avec une branche dédiée. Indispensable si les agents écrivent du code en parallèle.',
  'qp.batch.worktreeNoProject': 'Ce Quick Prompt n\'est lié à aucun projet (repo git). Lie-le à un projet pour activer l\'option.',
  'qp.batch.cancel': 'Annuler',
  'qp.batch.inputLabel': 'Liste de valeurs pour « {0} » (une par ligne OU séparées par des virgules / points-virgules)',
  'qp.batch.inputPlaceholder': 'EW-7100, EW-7101, EW-7102\nOU une valeur par ligne',
  'qp.batch.countHint': '{0} discussion(s) seront créées en parallèle (max 5 en simultané)',
  'qp.batch.confirm': "Lancer {0} discussions en parallèle avec « {1} » ?\n\nChaque discussion consomme des tokens. Cette opération peut coûter de l'argent.",
  'qp.batch.emptyInput': "Colle au moins une valeur (une par ligne).",
  'qp.batch.tooManyItems': "Trop de lignes ({0}). Maximum 50 par batch.",
  'qp.batch.needsVariable': "Ce Quick Prompt n'a pas de variable — impossible de lancer en batch. Ajoute une variable {{key}} dans le template.",
  'qp.batch.failed': "Échec du lancement batch : {0}",
  'qp.batch.toast.ok': '{0} — terminé ({1} OK)',
  'qp.batch.toast.partial': '{0} — {1} OK, {2} en erreur',
  'bootstrap.repoReady': 'Dépôt Git créé — prêt pour validation',
  'bootstrap.repoValidated': 'Dépôt validé. Analyse maintenant l\'architecture du projet à partir des documents fournis.',
  'bootstrap.analyzeArchitecture': 'Valider → Analyser l\'architecture',
  'bootstrap.architectureReady': 'Architecture analysée — prête pour validation',
  'bootstrap.architectureValidated': 'Architecture validée. Génère le plan projet avec les épics, stories et estimations.',
  'bootstrap.generatePlan': 'Valider → Générer le plan',
  'bootstrap.planReady': 'Plan projet généré — prêt pour validation',
  'bootstrap.planValidated': 'Plan validé. Crée les issues sur le tracker du projet.',
  'bootstrap.createIssues': 'Valider → Créer les issues',
  'bootstrap.issuesCreated': 'Issues créées sur le tracker',
  'bootstrap.viewProject': 'Voir le projet',
  'bootstrap.startDev': 'Démarrer le dev sur l\'issue #1',
  'bootstrap.devKickoffTitle': 'Dev kickoff — issue #1',
  'bootstrap.devKickoffPrompt': 'Ouvre l\'issue #1 dans le tracker du projet et commence à travailler dessus. Lis la description, pose des questions de clarification si besoin, puis implémente la solution étape par étape. Utilise le MCP tracker pour mettre à jour le statut au fur et à mesure, et commit des changements logiques au fil de l\'avancée.',
  'wf.createWithAI': 'Créer avec l\'IA',
  'wf.createWithAIHint': 'Décris ton besoin en langage naturel, l\'IA te génère le workflow complet. Plus rapide pour les workflows complexes, mais tu dois vérifier le résultat.',
  'wf.aiArchitectPrompt': 'Je veux créer un nouveau workflow automatisé. Aide-moi à le concevoir étape par étape.',
  'wf.aiWorkflowReady': 'Workflow conçu par l\'IA — prêt à être déployé',
  'wf.createThisWorkflow': 'Créer ce workflow',
  // 0.8.3 — bundle CTA. {0}=QPs, {1}=QAs, {2}=Custom APIs.
  'wf.aiBundleReady': 'Bundle conçu par l\'IA — prêt à déployer ({0} Quick Prompts · {1} Quick APIs · {2} Custom APIs)',
  // 0.8.3 — bundle button. {0}=total extras (QPs+QAs+CustomAPIs).
  'wf.createBundleBtn': 'Créer tout (1 workflow + {0} artefacts)',
  'wf.workflowCreated': 'Workflow créé avec succès',
  'wf.workflowCreateError': 'Erreur lors de la création du workflow',
  'wf.empty': 'Aucun workflow configuré',
  'wf.emptyHint': 'Créez un workflow pour automatiser vos tâches',
  'wf.active': 'Actif',
  'wf.inactive': 'Inactif',
  'wf.trigger': 'Lancer',
  'wf.delete': 'Suppr.',
  'wf.selectOne': 'Sélectionnez un workflow',
  'wf.unknown': 'Inconnu',
  'wf.edit': 'Éditer',
  'wf.back': 'Retour',
  'wf.refresh': 'Rafraîchir',
  'wf.launch': 'Lancer',
  'wf.running': 'Exécution en cours',
  'wf.runDone': 'Run terminé — {0}',
  'wf.runWaiting': 'En attente de ta validation',
  'wf.stepsExecuted': '{0} steps exécutés',
  'wf.deleteAll': 'Tout supprimer',
  'wf.noRuns': 'Aucun run',
  'wf.skipNext': 'Skip step suivant',
  'wf.manual': 'Manuel',
  'wf.batchStepQpMissing': 'Quick Prompt introuvable (id: {0}). A-t-il été supprimé ?',
  'wf.batchStepItemsLabel': 'Items depuis :',
  'wf.batchStepWaits': '⏳ Attend la fin de toutes les discussions',
  'wf.batchStepFireForget': '⚡ Fire-and-forget (ne pas attendre)',
  'wf.batchStepMaxItems': 'Max {0} items',
  'wf.batchStepWorktree': 'Worktree isolé par discussion',
  'wf.batchStepChainLabel': 'Chaîne :',
  'wf.runBatchChip': '📋 Voir les {0} conversations',
  'wf.runBatchChipHint': 'Aller à la sidebar Discussions et ouvrir le batch créé par ce run',
  'wf.cancelRun': 'Arrêter',
  'wf.cancelRunConfirm': 'Arrêter ce run et toutes ses discussions enfants en cours ?',
  'wf.cancelRunError': 'Échec de l\'arrêt. Voir la console pour le détail.',
  'wf.activeRunsTitle': 'Runs en cours',
  'wf.activeRunsEmpty': 'Aucun run en cours',
  'wf.cancelling': 'Arrêt…',
  'wf.viewAllWorkflows': 'Voir tous les workflows',
  'wf.elapsed': 'il y a {0}',
  // ─── ApiCall step (désagentification — 0.5.2) ──────────────────────
  'wf.apicall.title': 'Récupérer des données',
  'wf.apicall.subtitle': 'Appel direct à une API — zéro token consommé',
  'wf.apicall.pluginPicker': 'Plugin API',
  'wf.apicall.pluginPickerHint': 'Choisis une API configurée sur ce projet',
  'wf.apicall.qaPicker': 'Depuis un Quick API existant',
  'wf.apicall.qaPickerInline': '— Aucun (config inline ci-dessous) —',
  'wf.apicall.qaInheritedFrom': 'Hérité de « {0} »',
  'wf.apicall.qaInheritedHint': 'L\'appel hérite de la config du Quick API. Clique sur ✏️ ci-dessous pour overrider un ou plusieurs fields uniquement pour ce step.',
  'wf.apicall.qaDetach': 'Détacher du Quick API (mode inline)',
  'wf.apicall.qaOverrideToggle': 'Personnaliser cet appel pour ce step (override)',
  'wf.apicall.qaSummaryEndpoint': 'Appel :',
  'wf.apicall.qaSummaryExtract': 'Extract :',
  'wf.apicall.endpointPicker': 'Endpoint',
  'wf.apicall.endpointPlaceholder': 'ex : /repos/owner/repo/issues — clique pour piocher',
  'wf.apicall.pathParamsTitle': 'Paramètres du chemin',
  'wf.apicall.pathParamsHint': 'Détectés automatiquement dans l\'endpoint. Remplis chaque jeton — ils sont injectés dans l\'URL au moment de l\'appel.',
  'wf.apicall.pathParamsPlaceholder': 'Valeur pour {0}',
  'wf.apicall.pathParamsPreview': 'URL résolue',
  'wf.apicall.methodLabel': 'Méthode',
  'wf.apicall.queryParams': 'Paramètres (query)',
  'wf.apicall.headers': 'Headers',
  'wf.apicall.body': 'Body JSON',
  'wf.apicall.testBtn': 'Tester l\'appel',
  'wf.apicall.testing': 'Test en cours…',
  'wf.apicall.testHint': 'Fait une vraie requête avec les credentials configurés',
  'wf.apicall.testVarsTitle': 'Variables à fournir pour le test',
  'wf.apicall.testVarsHint': 'Ta config contient des variables `{{var}}` non résolues. Donne-leur une valeur de test pour cette requête uniquement (la config sauvegardée n\'est pas modifiée).',
  'wf.apicall.testVarsPlaceholder': 'valeur de test',
  'wf.apicall.testVarsGo': 'Tester avec ces valeurs',
  'wf.apicall.responseTitle': 'Réponse JSON',
  'wf.apicall.responseEmpty': 'Lance un test pour voir la réponse ici',
  'wf.apicall.clickToPickHint': '💡 Clique sur une clé dans un tableau → tous les éléments. Sur une valeur → cet élément précis. Sur [N] → itérer.',
  'wf.apicall.suggest.title': 'Suggestions sur cette réponse',
  'wf.apicall.suggest.iterate': 'Itérer sur les {0} éléments',
  'wf.apicall.suggest.allField': 'Tous les "{0}" ({1} valeurs)',
  'wf.apicall.suggest.firstItem': 'Le 1er élément',
  'wf.apicall.suggest.counter': 'Compteur "{0}" = {1}',
  'wf.apicall.extractSection': 'Ce qui sort d\'ici',
  'wf.apicall.extractSectionHint': 'Le step suivant recevra la valeur extraite via {{steps.nom.data}}',
  'wf.apicall.extractData': 'Donnée',
  'wf.apicall.extractStatus': 'Statut',
  'wf.apicall.extractSummary': 'Résumé',
  'wf.apicall.pathLabel': 'Chemin (JSONPath)',
  'wf.apicall.pathPlaceholder': 'ex : $.issues[*].key',
  'wf.apicall.preview': 'Aperçu',
  'wf.apicall.previewEmpty': 'Aucun résultat pour ce chemin',
  'wf.apicall.previewType': 'Type',
  'wf.apicall.errorInvalidPath': 'Chemin JSONPath invalide',
  'wf.apicall.examples': 'Exemples',
  'wf.apicall.advancedToggle': 'Options avancées (body, méthode, timeout, retries, output var)',
  'wf.apicall.exampleAllIds': 'Tous les IDs',
  'wf.apicall.exampleFirst': 'Premier élément',
  'wf.apicall.exampleCount': 'Nombre total',
  'wf.apicall.failOnEmpty': 'Marquer comme NO_RESULTS si vide',
  'wf.apicall.failOnEmptyHint': 'Déclenche les conditions Skip/Stop/Goto de l\'étape suivante',
  'wf.apicall.fallbackLabel': 'Valeur par défaut (si vide)',
  'wf.apicall.nextStepBatchOk': 'L\'étape suivante attend un tableau — Data est un tableau ✓',
  'wf.apicall.nextStepBatchMismatch': 'L\'étape suivante (batch) attend un tableau, mais Data est {0}',
  'wf.apicall.nextStepBatchWrap': 'Entourer dans un tableau',
  'wf.apicall.tokensSaved': '0 token consommé par cette étape',
  'wf.apicall.timeoutLabel': 'Timeout (ms)',
  'wf.apicall.retriesLabel': 'Retries (5xx / 429)',
  'wf.apicall.outputVarLabel': 'Nom de la variable',
  'wf.apicall.outputVarHint': 'Disponible dans les prochaines étapes via {{steps.<nom>.data}}',
  'wf.apicall.testSuccess': 'Appel réussi en {0} ms',
  'wf.apicall.testFailed': 'Échec',
  'wf.apicall.notSupported': 'Aucun plugin API configuré sur ce projet',
  'wf.apicall.authManagedTitle': 'Auth — gérée par Kronn',
  'wf.apicall.authManagedHint': 'Ces champs sont injectés au runtime à partir de ta config dans Settings → APIs. Tu n\'as rien à remettre ici.',
  'wf.apicall.authManagedReveal': 'Révéler le nom de la variable d\'env',
  'wf.apicall.testNeedsProjectLink': 'Cette config plugin n\'est liée à aucun projet — va dans Settings → APIs et coche au moins un projet pour pouvoir tester.',
  // ── AI helper bubble (0.5.2) ──
  'wf.apicall.helper.trigger': 'Aide config IA',
  'wf.apicall.helper.triggerHint': 'Demande à un agent local de t\'aider à choisir l\'endpoint et l\'extraction',
  'wf.apicall.helper.triggerDisabled': 'Choisis d\'abord une API ci-dessus — sans plugin sélectionné l\'agent n\'a pas de spec à lire',
  'wf.apicall.helper.pickAgent': 'Quel agent t\'aide ?',
  'wf.apicall.helper.switchAgent': 'Changer d\'agent (réinitialise la conversation)',
  'wf.apicall.helper.welcome': 'Salut ! Que veux-tu faire ? Choisis une piste ou tape ta question.',
  'wf.apicall.helper.starter.build': 'Aide-moi à construire l\'appel',
  'wf.apicall.helper.starter.buildPrompt': 'Aide-moi à construire cet appel API. Je veux ',
  'wf.apicall.helper.starter.endpoint': 'Suggère un endpoint',
  'wf.apicall.helper.starter.endpointPrompt': 'Suggère-moi un endpoint pour ',
  'wf.apicall.helper.starter.body': 'Aide-moi avec le body',
  'wf.apicall.helper.starter.bodyPrompt': 'Aide-moi à construire le body JSON pour ',
  'wf.apicall.helper.cancel': 'Annuler',
  'wf.apicall.helper.discTitle': 'Aide config (éphémère)',
  'wf.apicall.helper.bubbleTitle': 'Aide config',
  'wf.apicall.helper.ephemeral': 'éphémère',
  'wf.apicall.helper.minimize': 'Réduire',
  'wf.apicall.helper.restore': 'Rouvrir l\'aide',
  'wf.apicall.helper.close': 'Fermer (supprime la conversation)',
  'wf.apicall.helper.thinking': 'L\'agent réfléchit…',
  'wf.apicall.helper.inputPlaceholder': 'Décris ce que tu veux récupérer…',
  'wf.apicall.helper.send': 'Envoyer',
  'wf.apicall.helper.stop': 'Stop',
  'wf.apicall.helper.suggestion': 'Suggestion',
  'wf.apicall.helper.apply': 'Appliquer',
  'wf.apicall.helper.applied': 'Appliqué ✓',
  'wf.apicall.helper.noAgents': 'Aucun agent installé sur ce poste.',
  'wf.apicall.helper.contextHint': 'Le contexte (API, endpoint, query, dernier test) est attaché à chaque message envoyé à l\'agent.',
  'wf.apicall.helper.contextNoApi': 'Aucune API choisie',
  'wf.apicall.helper.contextLastErr': 'dernier test : ❌',
  'wf.apicall.helper.contextLastOk': 'dernier test : ✓',
  // System prompt strings — drive the agent's reply language. Changing the UI locale
  // changes which language the agent answers in for the API helper conversation.
  'wf.apicall.helper.sys.unknown': '(inconnu)',
  'wf.apicall.helper.sys.noPlugin': '(aucun plugin sélectionné)',
  'wf.apicall.helper.sys.none': '(aucun)',
  'wf.apicall.helper.sys.default': '(par défaut)',
  'wf.apicall.helper.sys.empty': '(vide)',
  'wf.apicall.helper.sys.ctxHeader': '### CONTEXTE COURANT (snapshot au moment de cet envoi)',
  'wf.apicall.helper.sys.ctxLastFail': '### DERNIER TEST → ÉCHEC',
  'wf.apicall.helper.sys.ctxLastOk': '### DERNIER TEST → SUCCÈS (extrait de réponse)',
  'wf.apicall.helper.sys.userQuestion': '### QUESTION DU USER',
  'wf.apicall.helper.sys.role': `# Rôle
Tu es un assistant de configuration pour une étape "Récupérer des données" (\`StepType::ApiCall\`)
dans un workflow Kronn. Cette étape fait un appel HTTP direct (sans LLM consommé) et extrait
une valeur via JSONPath, qui pipe vers le step suivant.`,
  'wf.apicall.helper.sys.boundaries': `# Ce que tu peux et ne peux PAS faire
- ✅ Lire la spec API ci-dessous, l'état courant du step, le résultat du dernier test.
- ✅ Proposer des modifications de config (endpoint, method, query, headers, body, extract).
- ❌ Tu n'as **AUCUN** outil pour appeler l'API toi-même. Pas de Bash, pas de MCP, pas de fetch.
     C'est Kronn (backend Rust) qui fait l'appel quand l'utilisateur clique "Test the call".
- ❌ Ne propose JAMAIS d'aller "vérifier en ligne" via un MCP — ce n'est pas dispo dans cette
     conversation. Si la cause d'un échec t'échappe, oriente vers la doc officielle ou le
     dashboard du fournisseur.`,
  'wf.apicall.helper.sys.action': `# Ta seule action utile : proposer un \`KRONN:APPLY\`
Quand tu suggères une config, écris EXACTEMENT ce format (sinon l'UI ne te lit pas) :

KRONN:APPLY
\`\`\`json
{ "endpoint": "/path/v4", "query": { "k": "v" }, "extract": "$.data[*]" }
\`\`\`

Champs autorisés : \`endpoint\`, \`method\`, \`query\`, \`headers\`, \`body\`, \`extract\`.
Tu peux ne mettre QU'UN champ — le reste du step est préservé.
Pas plus d'un bloc \`KRONN:APPLY\` par message (sinon l'utilisateur ne sait plus lequel choisir).`,
  'wf.apicall.helper.sys.debug': `# Méthode de debug (à appliquer dès qu'un test échoue)
1. Lis le bloc \`### DERNIER TEST → ÉCHEC\` — il contient \`HTTP <status> on <method> <url> — <body excerpt>\`.
2. Note le **status** (4xx vs 5xx → 4xx = ta config est en cause, 5xx = côté serveur).
3. Note les **query params** dans l'URL composée (l'apikey est masquée — c'est normal et ATTENDU).
4. Croise avec les TIPS du plugin ci-dessous + les endpoints autorisés.
5. Propose **UNE** modification ciblée (un seul KRONN:APPLY). Pas de salve de 3 hypothèses simultanées.
6. Si l'échec persiste après 2 tentatives, recommande à l'utilisateur de vérifier dans le dashboard
   du fournisseur (URL de doc plus bas) — ne tourne pas en rond.`,
  'wf.apicall.helper.sys.endpointsHeader': '# Endpoints AUTORISÉS (n\'invente jamais hors de cette liste)',
  'wf.apicall.helper.sys.endpointsEmpty': '(aucun — l\'utilisateur n\'a pas encore choisi d\'API)',
  'wf.apicall.helper.sys.tipsHeader': '# TIPS PLUGIN — {0}',
  'wf.apicall.helper.sys.docsHeader': '# Doc officielle',
  'wf.apicall.helper.sys.style': `# Style
- Français, ≤ 3 lignes par message.
- Pas de blabla, pas de "je vais analyser", pas de "voilà ce que je suggère :" — direct au but.
- L'apikey/token affichés en \`••••••••\` ou \`***\` dans les logs sont MASQUÉS UNIQUEMENT À L'AFFICHAGE.
  La vraie valeur configurée par l'utilisateur EST envoyée par Kronn. Si on te demande "tu es sûr
  que la clé est bien envoyée ?" — réponds OUI sans réserve, et redirige le diag ailleurs (host,
  endpoint, scope du token).`,
  'wf.apicall.helper.sys.starter': 'Pour démarrer, demande au user ce qu\'il veut récupérer.',
  'wf.apicall.helper.sys.authHeader': `### AUTH — gérée automatiquement par Kronn (NE PAS suggérer)
Le backend injecte ces champs au moment de la requête, avec la clé que le
user a entrée dans Settings → APIs. ⚠️ Ne JAMAIS les remettre dans une
suggestion KRONN:APPLY ; cela écraserait la vraie valeur par un placeholder
("VOTRE_API_KEY", etc.) et casserait l'auth.`,
  'wf.apicall.helper.sys.authQueryItem': '- query param `{0}` (valeur tirée de l\'env `{1}`, déjà configurée dans Kronn)',
  'wf.apicall.helper.sys.authHeaderItem': '- header `{0}` (valeur tirée de l\'env `{1}`, déjà configurée dans Kronn)',
  'wiz.testBatchStep': 'Tester (dry-run)',
  'wiz.testBatchInfo': 'Prévisualise ce que ferait ce step batch sans rien créer : extraction de la liste, prompt rendu sur le 1er item, QP/agent utilisé, mode worktree.',
  'wiz.testBatchAlwaysDryRun': 'Toujours en dry-run — aucune discussion ne sera créée.',
  'wiz.testBatchOk': 'Configuration valide',
  'wiz.testBatchErrors': '{0} erreur(s) bloquante(s) :',
  'wiz.testBatchItemsCount': 'item(s) seraient lancés',
  'wiz.testBatchCappedAt': 'aperçu limité aux {0} premiers',
  'wiz.testBatchFillsVar': 'remplit {{{0}}}',
  'wiz.testBatchWorkspace': 'Workspace',
  'wiz.testBatchWaits': 'attend la fin',
  'wiz.testBatchFireForget': 'fire-and-forget',
  'wiz.testBatchItemsSample': 'Items extraits :',
  'wiz.testBatchSamplePromptFor': 'Prompt rendu pour le 1er item ({0}) :',
  'wiz.testMockPrefilled': 'rempli automatiquement depuis le test de « {0} »',
  'wiz.testMockPrefilledExtract': 'extrait depuis le test de « {0} » (dernière ligne)',
  'wiz.testMockPrefilledFull': 'résultat complet de « {0} »',
  'wiz.testMockShowFull': '↻ Voir tout le résultat',
  'wiz.testMockShowExtract': '↻ Garder juste la dernière ligne',
  'wiz.testMockUsePrev': 'Utiliser le résultat de « {0} »',
  'wiz.testBatchWarnings': '{0} avertissement(s) :',
  'wiz.testBatchItemToggle': 'Voir le prompt rendu pour cet item',
  'wiz.testBatchItemPrompt': 'Prompt envoyé à l\'agent :',
  'wiz.testBatchItemDryRunBtn': 'Lancer dry-run',
  'wiz.testBatchItemDryRunLoading': 'Dry-run en cours…',
  'wiz.testBatchItemDryRunHint': 'Exécute l\'agent en mode lecture-seule sur cet item — décrit ce qu\'il ferait sans modifier quoi que ce soit',
  'wiz.testBatchItemDryRunResult': 'Ce que l\'agent ferait :',

  // ── Structured agent questions (0.3.5) ──
  'agentQuestions.hint': 'L\'agent vous pose des questions. Remplissez chaque champ et cliquez sur Répondre — c\'est plus rapide que de tout taper dans le chat.',
  'agentQuestions.submit': 'Répondre',
  'agentQuestions.fillAll': 'Remplissez tous les champs pour envoyer',

  'wf.inProgress': 'en cours...',
  'wf.pending': 'en attente',
  'wf.live.stepNoOutput': '(pas de contenu produit par ce step)',
  'wf.live.stepStreamingWaiting': "L'agent démarre… (aucun chunk reçu pour l'instant)",
  'wf.live.totalElapsedHint': 'Durée totale écoulée depuis le déclenchement du run.',
  'wf.liveStep.agent': 'L\'agent réfléchit',
  'wf.liveStep.api': 'Appel HTTP en cours',
  'wf.liveStep.notify': 'Envoi du webhook',
  'wf.liveStep.batch': 'Fan-out en cours',
  'wf.liveStep.gate': 'En attente d\'une décision humaine',
  'wf.liveStep.exec': 'Exécution shell en cours',
  'wf.runStatusToReview': 'À VALIDER',
  'wf.pausedJustNow': 'à l\'instant',
  'wf.pausedMinutes': 'en pause depuis {0} min',
  'wf.pausedHours': 'en pause depuis {0}h',
  'wf.pausedHoursMinutes': 'en pause depuis {0}h{1}',
  'wf.pausedDays': 'en pause depuis {0}j',
  'wf.pausedZeroTokens': '0 token consommé pendant la pause',
  'wf.pausedZeroTokensHint': 'Aucun appel d\'IA pendant la pause d\'un Gate — la facture s\'arrête tant que l\'opérateur n\'a pas décidé.',
  'wf.tokensTotal': 'tokens (total run)',
  'wf.tokensTotalHint': 'Somme des tokens consommés par tous les steps de ce run depuis son démarrage. Inclus les agents passés, les gates restent à 0.',
  'wf.stepTokensSuffix': 'tk',
  'wf.stepTokensHint': 'Tokens consommés par ce step. Les gates / Exec / ApiCall / Notify ne brûlent pas de tokens — ils restent à 0. Les agents (Agent / BatchQuickPrompt) en consomment proportionnellement à la longueur du prompt + réponse.',
  'wf.gate.title': 'Décision humaine requise',
  'wf.gate.defaultMessage': 'Validation requise pour continuer.',
  'wf.gate.commentPlaceholder': 'Commentaire (requis pour « Demander des changements »)…',
  'wf.gate.commentRequired': 'Un commentaire est requis pour demander des changements.',
  'wf.gate.approve': 'Approuver',
  'wf.gate.requestChanges': 'Demander des changements',
  'wf.gate.reject': 'Rejeter',
  'wf.gate.triage.clear': 'Évidents',
  'wf.gate.triage.decided': 'Décidés (vérifier)',
  'wf.gate.triage.mocked': 'Mockés (à remplacer)',
  'wf.gate.triage.blocked': 'Bloqués (en attente)',
  'wf.gate.triage.chosen': 'Choisi :',
  'wf.gate.triage.why': 'Raison :',
  'wf.gate.triage.placeholder': 'Placeholder :',
  'wf.gate.triage.strategy': 'Stratégie :',
  'wf.gate.triage.revisitWhen': 'À revoir quand :',
  'wf.gate.triage.neededFrom': 'Attendu de :',
  'wf.gate.triage.workaround': 'Contournement :',
  'wf.gate.triage.optionsConsidered': '{0} alternative(s) écartée(s)',
  'wf.gate.triage.filesTouched': '{0} fichier(s) prévu(s)',
  'wf.deleteRun': 'Supprimer ce run',
  'wf.deleteWorkflowConfirm': 'Supprimer ce workflow ? Cette action est irréversible — tous les runs et leurs discussions enfants seront perdus.',
  'wf.deleteRunConfirm': 'Supprimer ce run ? Le worktree et les artefacts seront retirés.',
  'wf.deleteAllRunsConfirm': 'Supprimer TOUS les runs de ce workflow ? Action irréversible — {0} run(s) + worktrees seront purgés.',
  'wf.runWorktreePath': 'Worktree :',
  'wf.runWorktreeCopy': 'Copier le chemin',
  'wf.produced.title': '{0} branche(s) préservée(s)',
  'wf.produced.aheadCount': '{0} commit(s) en avance',
  'wf.produced.pushed': 'pushé',
  'wf.produced.localOnly': 'local seulement',
  'wf.produced.copyCheckout': 'Copier git checkout',
  'wf.produced.copyDiff': 'Copier git diff',
  'wf.produced.copyCd': 'Copier cd',
  'wf.produced.test': 'Tester en worktree',
  'wf.produced.testHint': 'Crée un worktree isolé sur le commit pour tester sans toucher à main',
  'wf.produced.creating': 'Création…',
  'wf.produced.cleanup': 'Nettoyer',
  'wf.export': 'Exporter',
  'wf.exportHint': 'Télécharge ce workflow en JSON (avec ses Quick Prompts référencés). Partageable entre instances Kronn.',
  'wf.exportDone': 'Workflow « {name} » exporté',
  'wf.import': 'Importer',
  'wf.importHint': 'Importe un workflow exporté depuis Kronn (.json). Drag-and-drop ou file picker.',
  'qp.export': 'Exporter ce Quick Prompt',
  'qp.exportDone': 'Quick Prompt « {name} » exporté',
  'qp.import': 'Importer',
  'qp.importHint': 'Importe un Quick Prompt exporté depuis Kronn (.json).',
  'imp.workflowTitle': 'Importer un workflow',
  'imp.qpTitle': 'Importer un Quick Prompt',
  'imp.dropOrPick': 'Dépose un fichier ici, ou choisis-en un',
  'imp.fileHint': 'Format attendu : .kronn-workflow.json ou .kronn-qp.json',
  'imp.pickFile': 'Choisir un fichier',
  'imp.cancel': 'Annuler',
  'imp.confirm': 'Importer',
  'imp.pickAnother': '← Choisir un autre fichier',
  'imp.previewName': 'Nom',
  'imp.previewSteps': 'Steps',
  'imp.previewVars': 'Variables',
  'imp.previewBundledQps': 'Quick Prompts inclus',
  'imp.targetProject': 'Attacher au projet (optionnel)',
  'imp.noProject': 'Aucun (workflow non-rattaché)',
  'imp.errorNotJson': 'Ce fichier n\'a pas l\'extension .json — vérifie le format.',
  'imp.errorReadFailed': 'Impossible de lire le fichier.',
  'imp.errorJsonParse': 'JSON invalide — le fichier est corrompu ou incomplet.',
  'imp.errorWrongKind': 'Mauvais type d\'export : attendu `{expected}`, reçu `{got}`. Tu as peut-être inversé un workflow et un Quick Prompt ?',
  'imp.workflowDone': 'Workflow « {name} » importé',
  'imp.qpDone': 'Quick Prompt « {name} » importé',
  'wf.noOutput': '(aucune sortie)',
  'wf.status': 'Statut',
  'wf.duration': 'Durée',
  'wf.noProject': 'Sans projet',

  // ── Workflow wizard ──
  'wiz.modeSimple': 'Simple',
  'wiz.modeAdvanced': 'Avancé',
  'wiz.helpModeSimple': 'Une seule étape : un agent, une tâche. Parfait pour 80% des cas.',
  'wiz.helpModeAdvanced': 'Plusieurs étapes en chaîne, conditions, déclencheurs programmés, batch.',
  'wiz.helpSimpleVsAdvanced': 'Simple = 1 étape, 1 agent, 1 tâche (ex: "génère un rapport hebdo"). Avancé = pipeline multi-étapes avec conditions (ex: "récupère les tickets → lance un batch → notifie"). Tu peux toujours passer en Avancé plus tard.',
  'wiz.task': 'Tâche',
  'wiz.agentLabel': 'Agent IA',
  'wiz.helpAgent': 'Le modèle IA qui va exécuter la tâche. Claude Code est généralement le plus capable ; Codex et Gemini sont des alternatives. Chaque agent doit être installé séparément sur ta machine.',
  'wiz.promptLabel': 'Que doit faire l\'agent ?',
  'wiz.helpPromptSimple': 'Décris la tâche en langage naturel, comme si tu demandais à un collègue. Tu peux utiliser des variables comme {{issue.title}} si le workflow est déclenché par une issue tracker.',
  'wiz.promptSimplePlaceholder': 'Décrivez la tâche en langage naturel...',
  'wiz.starterLabel': 'Ou démarrer depuis un exemple :',
  'wiz.helpStarters': 'Clique sur un exemple pour pré-remplir le nom et le prompt. Tu peux ensuite les modifier librement avant de valider.',
  'wiz.starter.codeReview': 'Revue de code',
  'wiz.starter.codeReviewPrompt': 'Analyse les derniers commits sur la branche principale. Pour chaque fichier modifié, identifie :\n- Les problèmes de qualité (code dupliqué, complexité excessive)\n- Les failles de sécurité potentielles\n- Les violations de conventions du projet\n\nGénère un rapport Markdown avec les problèmes classés par sévérité (critique / attention / suggestion).',
  'wiz.starter.changelog': 'Changelog automatique',
  'wiz.starter.changelogPrompt': 'Analyse tous les commits depuis le dernier tag de release. Pour chaque commit, identifie s\'il s\'agit d\'une nouvelle fonctionnalité, d\'un correctif, d\'une amélioration ou d\'un changement technique.\n\nGénère un changelog Markdown structuré avec les sections : Nouveautés, Correctifs, Améliorations, Technique.',
  'wiz.starter.techDebt': 'Détection dette technique',
  'wiz.starter.techDebtPrompt': 'Scanne le code source du projet et identifie les éléments de dette technique :\n- TODO/FIXME/HACK restants dans le code\n- Dépendances obsolètes ou avec vulnérabilités connues\n- Fichiers de plus de 500 lignes sans tests\n- Code mort ou imports inutilisés\n\nGénère un rapport priorisé avec l\'effort estimé (S/M/L) pour chaque élément.',
  'wiz.starter.testCoverage': 'Audit couverture tests',
  'wiz.starter.testCoveragePrompt': 'Analyse la structure du projet et identifie les fichiers et fonctions critiques qui n\'ont pas de tests :\n- Endpoints API sans test d\'intégration\n- Fonctions utilitaires sans test unitaire\n- Composants UI avec interactions sans test\n\nGénère une liste priorisée des tests manquants les plus importants à écrire, avec un squelette de test pour chacun.',
  'wiz.starter.docUpdate': 'Mise à jour documentation',
  'wiz.starter.docUpdatePrompt': 'Compare la documentation existante (README, docs/, commentaires) avec le code actuel. Identifie :\n- Les sections obsolètes ou incorrectes\n- Les nouvelles fonctionnalités non documentées\n- Les instructions d\'installation ou de configuration à mettre à jour\n\nPropose les corrections à apporter dans chaque fichier.',
  'wiz.starter.securityScan': 'Scan sécurité',
  'wiz.starter.securityScanPrompt': 'Effectue un audit de sécurité du projet :\n- Vérifie les dépendances pour les CVE connues\n- Cherche les secrets ou tokens hardcodés dans le code\n- Identifie les injections potentielles (SQL, XSS, command injection)\n- Vérifie les permissions et la configuration d\'authentification\n\nGénère un rapport avec les vulnérabilités classées par criticité (CRITICAL / HIGH / MEDIUM / LOW).',
  'wiz.triggerLabel': 'Déclenchement',
  'wiz.triggerWhenLabel': 'Quand ça se lance ?',
  'wiz.triggerManual': 'Manuellement',
  'wiz.triggerScheduled': 'Sur un planning',
  'wiz.triggerTracker': 'Quand une issue arrive',
  'wiz.helpTriggerSimple': 'Manuellement : tu cliques sur "Lancer" quand tu en as besoin. Sur un planning : le workflow tourne tout seul à heures fixes (ex: tous les lundis à 9h).',
  'wiz.helpTriggerAdvanced': 'Trois modes de déclenchement : Manuellement (clic), Sur un planning (heures/jours/semaines fixes), ou Quand une issue arrive (GitHub/Jira, filtré par labels).',
  'wiz.helpTriggerManual': 'Tu lances le workflow toi-même depuis cette page en cliquant sur "Lancer".',
  'wiz.helpTriggerCron': 'Le workflow tourne automatiquement selon un planning (ex: tous les lundis à 9h, ou toutes les 10 minutes).',
  'wiz.helpTriggerTracker': 'Le workflow se déclenche quand une nouvelle issue apparaît sur GitHub (ou autre tracker) avec certains labels. Idéal pour du triage ou de l\'auto-fix.',
  'wiz.suggestionsCount': '{0} suggestion(s) disponible(s)',
  'wiz.suggestionsTitle': 'Suggestions pour ce projet',
  // 0.8.5 — unified QuickStart picker (replaces 3 separate UI sections).
  'wiz.quickstart.toggle': '✨ {0} modèles disponibles pour démarrer',
  'wiz.quickstart.title': 'Démarrer rapidement',
  'wiz.quickstart.hint': 'Sélectionne un modèle pour pré-remplir le workflow.',
  'wiz.quickstart.collapse': 'Réduire',
  'wiz.quickstart.searchPlaceholder': 'Rechercher un modèle…',
  'wiz.quickstart.filterComplexity': 'Complexité :',
  'wiz.quickstart.filterSource': 'Source :',
  'wiz.quickstart.complexity.simple': 'Simple',
  'wiz.quickstart.complexity.intermediate': 'Intermédiaire',
  'wiz.quickstart.complexity.advanced': 'Avancé',
  'wiz.quickstart.source.starter': 'Démo plugin',
  'wiz.quickstart.source.project-suggestion': 'Suggestion projet',
  'wiz.quickstart.source.preset': 'Préset',
  'wiz.quickstart.complexityTooltip': 'Niveau de configuration estimé',
  'wiz.quickstart.sourceTooltip': 'Origine du modèle',
  'wiz.quickstart.stepsCount': '{0} étape(s)',
  'wiz.quickstart.empty': 'Aucun modèle ne correspond. Essaie de relâcher les filtres ou démarre de zéro en saisissant juste un nom.',
  'wiz.quickstart.loading': 'Chargement des suggestions…',
  'wiz.quickstart.apply': 'Utiliser ce modèle',
  'wiz.quickstart.notApplicable': 'Non disponible : {0}',
  'wiz.quickstart.disabledNoName': 'Saisissez un nom de workflow avant de sélectionner un modèle pré-construit.',
  'wiz.activate': 'Activer',
  'wiz.importDraft': 'Importer comme brouillon',
  'wiz.outputFormat': 'Format de sortie',
  'wiz.outputFree': 'Libre',
  'wiz.outputStructured': 'Structuré',
  'wiz.outputStructuredHint': 'La sortie sera encapsulée en JSON (data + status + summary). Le step suivant pourra accéder à {{previous_step.data}} au lieu du texte brut.',
  'wiz.outputFreeHint': 'Texte brut. Le step suivant ne pourra lire que {{previous_step.output}} — aucun champ .data / .summary / .status.',
  'wiz.infos': 'Infos',
  'wiz.trigger': 'Trigger',
  'wiz.steps': 'Steps',
  'wiz.config': 'Config',
  'wiz.summary': 'Résumé',
  'wiz.name': 'Nom du workflow',
  'wiz.namePlaceholder': 'ex: Auto-fix 5xx errors',
  'wiz.nameRequired': 'Donne un nom à ton workflow pour passer à l\'étape suivante.',
  'wiz.project': 'Projet',
  'wiz.projectOptional': 'Projet (optionnel)',
  'wiz.noProject': 'Aucun projet',
  'wiz.triggerType': 'Type de trigger',
  'wiz.frequency': 'Fréquence',
  'wiz.every': 'Tous les',
  'wiz.minutes': 'minutes',
  'wiz.hours': 'heures',
  'wiz.days': 'jours',
  'wiz.weeks': 'semaines',
  'wiz.months': 'mois',
  'wiz.at': 'à',
  'wiz.cronWeekdaysLabel': 'Seulement certains jours ?',
  'wiz.helpCronWeekdays': 'Coche les jours où le workflow doit tourner. Laisse tout décoché pour qu\'il tourne TOUS les jours. Ex: cocher L et V → le workflow tournera uniquement les lundis et vendredis.',
  'wiz.cronWeekdaysAll': 'tous les jours',
  'wiz.cronWeekdaysEveryday': 'Tourne tous les jours',
  'wiz.cronWeekdaysPreview': 'Tourne uniquement : {0}',
  'wiz.cronEveryDisabledHint': 'Désactivé parce que des jours spécifiques sont sélectionnés. Décoche tous les jours pour réactiver.',
  'wiz.weekday.0': 'Dimanche',
  'wiz.weekday.1': 'Lundi',
  'wiz.weekday.2': 'Mardi',
  'wiz.weekday.3': 'Mercredi',
  'wiz.weekday.4': 'Jeudi',
  'wiz.weekday.5': 'Vendredi',
  'wiz.weekday.6': 'Samedi',
  'wiz.weekdayShort.0': 'Dim',
  'wiz.weekdayShort.1': 'Lun',
  'wiz.weekdayShort.2': 'Mar',
  'wiz.weekdayShort.3': 'Mer',
  'wiz.weekdayShort.4': 'Jeu',
  'wiz.weekdayShort.5': 'Ven',
  'wiz.weekdayShort.6': 'Sam',
  'wiz.labels': 'Labels (virgule)',
  'wiz.labelsPlaceholder': 'bug-5xx, auto-fix',
  'wiz.stepType': 'Type de step',
  'wiz.stepSwapConfirm': 'Convertir cette étape « {0} » en « {1} » ? Les champs spécifiques au type actuel (prompt, profil, skills, plugin/endpoint, payload JSON…) seront effacés.',
  'wiz.stepTypeAgent': 'Agent',
  'wiz.stepTypeAgentHint': 'Un agent IA qui raisonne, lit du code, écrit des fichiers, appelle des MCPs. Le step le plus polyvalent — mais consomme des tokens.',
  'wiz.stepTypeApiCall': 'API',
  'wiz.stepTypeApiCallHint': 'Appel API direct (Chartbeat, Jira, Cloudflare…) — zéro token. Récupère des données et les pipe au step suivant.',
  'wiz.stepTypeBatchQP': 'Batch QP',
  'wiz.stepTypeBatchQPHint': 'Fan-out d\'un Quick Prompt sur une liste d\'items issue d\'un step précédent.',
  'wiz.stepTypeNotify': 'Notify',
  'wiz.stepTypeNotifyHint': 'Appel HTTP direct (webhook) — zéro token. Idéal pour notifier Slack, Teams ou déclencher un pipeline.',
  'wiz.stepTypeGate': 'Gate',
  'wiz.stepTypeGateHint': 'Pause le run pour validation humaine — zéro token. L\'opérateur approuve, demande des changements, ou rejette depuis le RunDetail.',
  'wiz.stepTypeExec': 'Exec',
  'wiz.stepTypeExecHint': 'Exécute un binaire autorisé directement (cargo test, npm build…) — zéro token, jamais via shell, args passés en argv littéral.',
  'wiz.stepTypeBatchApi': 'Batch API',
  'wiz.stepTypeBatchApiHint': 'Fan-out d\'un appel API sur une liste d\'items, en parallèle. Zéro token, idéal pour créer N tickets / poster N commentaires / mettre à jour N status d\'un coup.',
  'wiz.stepTypeJsonData': 'JSON',
  'wiz.stepTypeJsonDataHint': 'Source de données déterministe — émet un payload JSON littéral. Zéro token, zéro réseau. Idéal pour alimenter un Batch (10 hosts en dur) sans monter d\'API, ou comme fixture de dev.',
  'wiz.jsonDataTitle': 'JSON Data',
  'wiz.jsonDataHint': 'Le payload est émis tel quel à l\'étape suivante via `{{steps.<name>.data}}`. Pratique pour un BatchQuickPrompt sur une liste fixe (10 hosts, 5 régions, etc.) sans avoir à monter une API.',
  'wiz.jsonDataPayload': 'Payload JSON',
  'wiz.jsonDataPlaceholder': '[\n  { "host": "fr.example.com" },\n  { "host": "de.example.com" },\n  { "host": "en.example.com" }\n]',
  'wiz.jsonDataParseError': 'JSON invalide :',
  'wiz.jsonDataNoTemplating': 'Pas de templating runtime : la valeur est retournée littéralement. Pour un payload dynamique, utilise un step Agent ou ApiCall.',
  'wiz.jsonDataSummaryArray': '{0} item(s) — sera consommé par un Batch en aval',
  'wiz.jsonDataSummaryObject': '1 objet, {0} champ(s)',
  'wiz.jsonDataSummaryScalar': '1 valeur scalaire',
  'wiz.jsonDataMissingPayload': 'aucun payload',
  'wiz.errorJsonDataNoPayload': 'Le step JSON « {0} » n\'a pas de payload — colle un JSON valide dans le textarea.',
  'wiz.batchApiTitle': 'Batch API call',
  'wiz.batchApiHint': 'Réutilise la config API ci-dessous pour chaque item de la liste. Chaque item rend `{{batch.item.<champ>}}` dans le body, headers, query, etc. Items en parallèle, capés par le concurrent_limit.',
  'wiz.batchApiItemsFrom': 'Source des items',
  'wiz.batchApiItemsFromPlaceholder': '{{steps.plan.data.sub_tasks}} ou {{steps.plan.data}} ou un JSON array brut',
  'wiz.batchApiConcurrentLimit': 'Concurrence',
  'wiz.batchApiConcurrentLimitHint': 'Max appels parallèles (défaut 5, max 20)',
  'wiz.batchApiMaxItems': 'Max items',
  'wiz.batchApiMaxItemsHint': 'Plafond de sécurité (défaut 50)',
  'wiz.batchApiMissingConfig': 'config incomplète',
  'wiz.batchApiQaPicker': 'Source de la config API',
  'wiz.batchApiQaPickerInline': '— Inline (config définie ici) —',
  'wiz.batchApiQaPickerHint': 'Tu peux référencer un Quick API sauvegardé : sa config sera utilisée à l\'exécution. Les champs ci-dessous restent en override per-field.',
  'wiz.execTitle': 'Exécution shell directe (Exec)',
  'wiz.execHint': 'Lance un binaire de l\'allowlist du workflow dans le workspace. Args templatés mais passés littéralement (pas d\'interprétation shell). Timeout obligatoire.',
  'wiz.execWorktreeHint': 'Cette commande voit les modifications des steps précédents.',
  'wiz.execWorktreeHintFirst': 'Cette commande tourne dans un worktree git fraîchement créé pour ce run (copie isolée du dépôt sous .kronn/worktrees/).',
  'wiz.execNoProjectWarn': 'Aucun projet lié : la commande tourne dans le dossier courant de Kronn, sans isolation worktree. Lie un projet pour activer le worktree par run.',
  'wiz.worktreeHint': 'Chaque run s\'exécute dans une copie git isolée. Les steps partagent les mêmes fichiers.',
  'wiz.worktreeIconTooltip': 'Worktree git : copie isolée du dépôt créée pour la durée du run, sous .kronn/worktrees/.',
  'wiz.execCommand': 'Binaire',
  'wiz.execCommandSelect': 'choisir un binaire',
  'wiz.execArgs': 'Arguments (un par ligne)',
  'wiz.execArgsPlaceholder': 'test\n--\n{{steps.fetch.summary}}',
  'wiz.execTimeoutSecs': 'Timeout (secondes, défaut 300, max 1800)',
  'wiz.execSetupToggle': 'Ce workflow tourne dans un worktree git — installer les dépendances avant',
  'wiz.execSetupHint': 'Le worktree contient les fichiers tracked (composer.json, package.json, …) mais PAS les dossiers générés (vendor/, node_modules/, target/). Coche pour lancer un install AVANT ta commande principale.',
  'wiz.execSetupPreset': 'Pattern courant (auto-remplit les champs ci-dessous)',
  'wiz.execSetupPresetPick': 'Choisir un préset',
  'wiz.execSetupCommand': 'Setup command',
  'wiz.execSetupArgs': 'Setup args (un par ligne)',
  'wiz.execAllowlistEmpty': 'Le type Exec est désactivé : aucune commande autorisée pour ce workflow.',
  'wiz.execAllowlistConfigureNow': 'Configurer l\'allowlist →',
  'wiz.execAllowlistTitle': 'Allowlist Exec',
  'wiz.execAllowlistHint': 'Liste des binaires que les steps Exec peuvent invoquer pour ce workflow. Vide = Exec désactivé. Noms simples uniquement (pas de chemin, pas de méta-caractères shell). Sépare par une virgule.',
  'wiz.execAllowlistPlaceholder': 'npm, cargo, make, pytest',
  'wiz.gateTitle': 'Pause humaine (Gate)',
  'wiz.gateHint': 'Le run s\'arrête à ce step en attendant une décision (approuver, demander des changements, rejeter). Templates {{steps.X.summary}} supportés dans le message.',
  'wiz.gateMessage': 'Message affiché à l\'opérateur',
  'wiz.gateMessagePlaceholder': 'Valider l\'audit `{{steps.audit.summary}}` ?',
  'wiz.gateRequestChangesTarget': 'Cible « Demander des changements »',
  'wiz.gateRequestChangesDefault': '(par défaut : step précédent)',
  'wiz.gateNotifyUrl': 'Notifier sur cette URL quand le Gate fire (optionnel)',
  'wiz.gateNotifyUrlPlaceholder': 'https://hooks.slack.com/services/... ou {{state.slack_url}}',
  'wiz.gateNotifyUrlHint': 'POST best-effort avec {run_id, workflow_name, step_name, message}. Templates supportés. Échecs loggés, jamais bloquants.',
  'wiz.rollbackTitle': 'Rollback / compensation (en cas d\'échec)',
  'wiz.rollbackHint': 'Étapes exécutées si le run termine en Failed. Tu peux référencer {{failed_step.name}} et {{failed_step.output}}. Notify, Agent et ApiCall sont supportés (Gate exclu — il bloquerait un run déjà Failed).',
  'wiz.rollbackAgentPromptPlaceholder': 'Analyse l\'échec ci-dessus : {{failed_step.output}}. Produis un post-mortem en 3 lignes.',
  'wiz.presetsTitle': 'Démarrer depuis un pattern',
  'wiz.presetsHint': '— ou pars d\'un workflow vide en bas',
  'wiz.presetsBlankHint': 'Tu peux aussi descendre directement et construire ton workflow étape par étape. Les présets sont entièrement éditables après application.',
  'wiz.preset.autoDev.title': 'Auto-Dev avec tests',
  'wiz.preset.autoDev.desc': 'Implémente, teste, review, boucle jusqu\'à OK ou max 5 itérations. Le feedback de la review est passé à l\'implémentation suivante via state.',
  'wiz.preset.prGate.title': 'Pipeline PR avec Gate humain',
  'wiz.preset.prGate.desc': 'Implémente, lance les tests, attend ta validation humaine (avec notification webhook), puis merge si approuvé.',
  'wiz.preset.deployRollback.title': 'Déploiement avec rollback',
  'wiz.preset.deployRollback.desc': 'Build, smoke tests, deploy. Si une étape plante : alerte ops + post-mortem agent automatique.',
  'wiz.preset.autoDev.issueKeyLabel': 'Clé du ticket / issue (optionnel)',
  'wiz.preset.autoDev.issueKeyPlaceholder': 'EW-1234  ·  org/repo#42  ·  KRN-7',
  'wiz.preset.autoDev.issueKeyDesc': 'Optionnel — utilisé uniquement si tu transformes l\'étape `fetch_issue` en ApiCall (référence `{{issue_key}}` dans `api_path_params` ou `api_query` du plugin tracker).',
  'wiz.preset.autoDev.fetchIssueDesc': 'Fixture par défaut : décris ici le ticket à implémenter (JSON littéral). Édite simplement le payload pour adapter à ta demande, le préset tourne immédiatement sans plugin tracker. Pour brancher un vrai tracker (Jira / GitHub / Linear) : change le type de ce step en `ApiCall` (bouton 🌐 ci-dessus) et configure le plugin — `{{steps.fetch_issue.data}}` continuera à alimenter l\'étape `implement`.',
  'wiz.preset.autoDev.implementPrompt': 'Implémente la demande suivante :\n---\n{{steps.fetch_issue.data}}\n---\n\nSi une review précédente t\'a laissé du feedback :\n{{state.last_review}}\n\nTiens compte du feedback ci-dessus pour cette nouvelle itération.\nSi aucune review n\'a eu lieu encore, ce bloc est vide — c\'est ton premier passage.',
  'wiz.preset.autoDev.reviewPrompt': 'Review l\'implémentation produite par `implement` et le résultat des tests `run_tests`.\n\nTests output : {{steps.run_tests.data.stdout}}\nTests exit code : {{steps.run_tests.data.exit_code}}\n\nSi tout est OK : termine ta réponse par [SIGNAL: APPROVED].\nSinon : rédige un feedback actionnable, écris exactement à la fin (et UNIQUEMENT à la fin) :\n---STATE:last_review=<ton feedback en une ligne>---\npuis termine par [SIGNAL: NEEDS_CHANGES].',
  'wiz.preset.autoDev.notifyDoneBody': '✅ Workflow Auto-Dev terminé : {{steps.review.summary}}',
  'wiz.preset.autoDev.rollbackBody': '❌ Auto-Dev a échoué sur `{{failed_step.name}}` — détail : {{failed_step.output}}',
  'wiz.preset.prGate.issueKeyLabel': 'Clé du ticket / issue (optionnel)',
  'wiz.preset.prGate.issueKeyPlaceholder': 'EW-1234  ·  org/repo#42  ·  KRN-7',
  'wiz.preset.prGate.issueKeyDesc': 'Optionnel — utilisé uniquement si tu transformes l\'étape `fetch_issue` en ApiCall (référence `{{issue_key}}` dans `api_path_params` ou `api_query` du plugin tracker).',
  'wiz.preset.prGate.fetchIssueDesc': 'Fixture par défaut : décris ici le ticket à implémenter (JSON littéral). Édite simplement le payload pour adapter à ta demande, le préset tourne immédiatement sans plugin tracker. Pour brancher un vrai tracker (Jira / GitHub / Linear) : change le type de ce step en `ApiCall` (bouton 🌐 ci-dessus) et configure le plugin — `{{steps.fetch_issue.data}}` continuera à alimenter l\'étape `implement`.',
  'wiz.preset.prGate.implementPrompt': 'Implémente la demande suivante :\n---\n{{steps.fetch_issue.data}}\n---\n\nSors un résumé court dans `summary` et le diff brut dans `data.diff` pour que la review humaine ait du contexte.',
  'wiz.preset.shared.runTestsDesc': 'Pré-rempli avec un script bash multi-stacks qui détecte automatiquement Makefile / cargo / npm / composer / pytest. Pour une commande simple, change `exec_command` ET `exec_args` séparément (PAS de chaîne complète dans un seul champ) : Make → command `make` + args `["test"]`, Node → command `npm` + args `["test"]`, Python → command `pytest` + args `[]`. Pour une ligne shell, utilise command `bash` + args `["-c", "ta-commande-complète"]`. Le binaire choisi doit figurer dans `exec_allowlist`.',
  'wiz.preset.prGate.gateMessage': '## Validation pré-merge\n\n**Implémentation :** {{steps.implement.summary}}\n\n**Tests :** exit `{{steps.run_tests.data.exit_code}}`\n\nApprouver pour lancer le merge automatique. Demander des changements pour relancer `implement`.',
  'wiz.preset.prGate.mergePrompt': 'Merge la branche dans main. Vérifie qu\'il n\'y a pas de conflit, puis push. Sors le SHA du merge commit dans `data.merge_sha`.',
  'wiz.preset.prGate.rollbackBody': '❌ PR Gate échoué : `{{failed_step.name}}` — {{failed_step.output}}',
  'wiz.preset.deployRollback.successBody': '🚀 Déploiement terminé sans incident.',
  'wiz.preset.deployRollback.alertBody': '🚨 ALERTE PROD : étape `{{failed_step.name}}` a planté. Rollback en cours...',
  'wiz.preset.deployRollback.postMortemPrompt': 'Analyse l\'échec et produis un post-mortem en 3 sections :\n\n## Étape qui a planté\n{{failed_step.name}}\n\n## Erreur observée\n{{failed_step.output}}\n\n## Hypothèses de cause racine\n(à toi de jouer — propose 2-3 pistes priorisées)',
  'wiz.preset.featurePlanner.title': 'Feature / Epic Planner',
  'wiz.preset.featurePlanner.desc': 'Découpe une epic ou une feature en sous-tâches typées (auto_ai vs human_action) avec dépendances. Tracker-agnostique (Jira / GitHub / Linear via les MCP installés).',
  'wiz.preset.featurePlanner.analyzePrompt': `Tu es un Epic Planner. L'utilisateur a passé l'URL : {{epic_url}}.

# 1. Détecte le tracker
Lis le host de l'URL :
- atlassian.net → Jira (MCP Atlassian)
- github.com/.../issues/ → GitHub (MCP GitHub)
- linear.app → Linear (MCP Linear)
- autre → décris ce qu'il te faudrait, ne lance rien et émets [SIGNAL: NO_RESULTS]

# 2. Récupère le contenu
Via le MCP correspondant : titre, description, project key, état actuel, sous-tâches déjà existantes (tu en auras besoin pour l'idempotence à l'étape suivante).

# 3. Découpe en sous-tâches
Contraintes :
- Chaque sous-tâche tient dans une PR (1-3 jours de boulot max).
- Tag chaque sous-tâche :
  * \`auto_ai\` — pur boulot de code (créer des fichiers, lancer des tests, ouvrir une PR). Faisable par un agent autonome.
  * \`human_action\` — config UI (CMS, dashboard infra), décision archi/naming, info externe (URLs, credentials, accès…).
- Identifie les dépendances : A bloque B si B dépend logiquement que A soit fait avant.
- Documente CHAQUE décision prise par toi-même quand l'epic était ambigu (sprint cible, ordre de phases, naming, etc.).
- Identifie les infos manquantes que seul un humain peut fournir.

# 4. Output STRUCTURED (obligatoire)
\`\`\`json
{
  "data": {
    "tracker": "jira" | "github" | "linear",
    "source_key": "EW-7247",
    "sub_tasks": [
      { "id": 1, "title": "...", "description": "...", "type": "auto_ai", "blocked_by": [] }
    ],
    "decisions_taken": ["J'ai supposé que la phase 0 doit être complétée avant la phase 1 parce que..."],
    "missing_info": ["URL Adobe DTM AN — à fournir par la team Data"]
  },
  "status": "OK",
  "summary": "Plan : 12 sous-tâches (4 human_action, 8 auto_ai). 2 infos manquantes flaguées."
}
\`\`\`

Termine par [SIGNAL: PLAN_READY].`,
  'wiz.preset.featurePlanner.gateMessage': `## Plan proposé pour {{epic_url}}

{{steps.analyze_epic.summary}}

### Décisions prises automatiquement
{{steps.analyze_epic.data.decisions_taken}}

### Infos manquantes (à fournir par humain)
{{steps.analyze_epic.data.missing_info}}

### Sous-tâches
{{steps.analyze_epic.data.sub_tasks}}

**Approuver** → l'agent crée les tickets dans le tracker.
**Demander des changements** → retour à l'étape de planification avec ton feedback.`,
  'wiz.preset.featurePlanner.setLinksPrompt': `Les sous-tâches viennent d'être créées en bulk via un BatchApiCall. Tu vas maintenant poser les liens \`blocks\` / \`is blocked by\` entre elles.

# Données d'entrée
- Tracker : {{steps.analyze_epic.data.tracker}}
- Plan original (avec les \`blocked_by\` déclarés) : {{steps.analyze_epic.data.sub_tasks}}
- Résultats de création (un item par sous-tâche, avec \`input.id\` et \`response\` = la nouvelle key Jira/GitHub) : {{steps.create_tickets.data.items}}

# Mapping
1. Pour chaque sous-tâche du plan original avec un \`blocked_by\` non vide :
   - Retrouve la key créée correspondante dans \`steps.create_tickets.data.items\` (match sur \`input.id\` ↔ plan.id)
   - Pour chaque \`blocked_by\` (id de sous-tâche bloquante), retrouve aussi sa key créée
   - Ajoute un lien "is blocked by" via le MCP : la sous-tâche \`current\` est bloquée par \`predecessor\`

# Idempotence
Avant d'ajouter un lien, vérifie si le lien existe déjà via le MCP (la création a pu être ré-exécutée). N'ajoute que ce qui manque. Pas d'erreur si le lien existe déjà.

# Erreurs
Si un item de \`steps.create_tickets.data.items\` a \`status: ERROR\`, **ignore-le** pour le mapping (on ne peut pas lier vers un ticket qui n'a pas été créé). Note-les dans le \`summary\` final pour que l'humain ait la liste des tickets manquants à créer manuellement.

# Output STRUCTURED final
\`\`\`json
{
  "data": {
    "links_added": 5,
    "links_already_existed": 2,
    "skipped_failed_items": []
  },
  "status": "OK",
  "summary": "Feature Planner : 28 tickets créés, 5 liens blocks posés, 2 liens existaient déjà."
}
\`\`\`

Termine par [SIGNAL: OK].`,
  'wiz.preset.featurePlanner.notifyBody': '🗺️ Feature Planner terminé pour {{epic_url}} — {{steps.set_links.summary}}',
  'wiz.preset.dailyHostAudit.title': 'Audit quotidien sur liste fixe',
  'wiz.preset.dailyHostAudit.desc': 'Source de données déterministe (5 hosts en dur) → fan-out d\'un Quick Prompt audit sur chacun → notification Slack. Démontre la brique `JsonData` : pas besoin de monter d\'API pour une liste figée.',
  'wiz.preset.dailyHostAudit.hostListDesc': 'Liste figée des hosts à auditer. Édite le payload JSON pour adapter à ton contexte (locales, environnements, sous-domaines, etc.). Zéro token, zéro réseau — la valeur est passée telle quelle au step suivant via `{{steps.host-list.data}}`.',
  'wiz.preset.dailyHostAudit.auditEachDesc': 'Fan-out sur la liste ci-dessus. Choisis un Quick Prompt existant (ou crée-en un) qui audite UN host. Chaque exécution reçoit `{{batch.item.host}}` dans son prompt rendu.',
  'wiz.preset.dailyHostAudit.notifyBody': '🌍 Audit quotidien terminé : {{steps.audit-each-host.summary}}',
  'wiz.preset.ticketToPr.title': 'Ticket Autopilot — du ticket à la PR',
  'wiz.preset.ticketToPr.desc': 'Pipeline complet : récupère un ticket, analyse + plan, implémente avec TDD strict, teste, review par un agent différent, crée la PR, attend ta validation finale. Skills méthodologiques de obra/superpowers (TDD, debugging, verification, code-review). Édite le step `fetch_issue` pour brancher ton tracker (Jira / GitHub) ou laisse la fixture pour tester.',
  'wiz.preset.ticketToPr.fetchIssueDesc': 'Source du ticket à implémenter. Fixture par défaut (édite le payload), ou switch en `ApiCall` (bouton 🌐) pointé sur ton plugin tracker quand tu en as un. Le résultat alimente l\'étape `analyze` via `{{steps.fetch_issue.data}}`.',
  'wiz.preset.ticketToPr.analyzePrompt': 'Analyse le ticket suivant et produis un plan structuré :\n---\n{{steps.fetch_issue.data}}\n---\n\n# Tu vas charger automatiquement les skills `writing-plans` et `brainstorming` :\n- Explore l\'intent du ticket avant l\'implémentation (3-5 questions critiques)\n- Liste les hypothèses + les points d\'incertitude\n- Découpe en sous-tâches (1-3 jours chacune max)\n- Identifie les risques et la stratégie de test\n\n# Output Structured :\n```json\n{\n  "data": {\n    "summary": "<résumé 1 phrase>",\n    "complexity": "low | medium | high",\n    "questions": ["..."],\n    "subtasks": [{"id": 1, "title": "...", "description": "..."}],\n    "test_strategy": "..."\n  },\n  "status": "OK",\n  "summary": "Plan en N sous-tâches, complexité <X>"\n}\n```',
  'wiz.preset.ticketToPr.planGateMessage': '## Validation du plan\n\n**Récap :** {{steps.analyze.summary}}\n\n**Sous-tâches :**\n{{steps.analyze.data.subtasks}}\n\n**Stratégie de test :**\n{{steps.analyze.data.test_strategy}}\n\nApprouve pour lancer l\'implémentation, ou demande des changements pour relancer l\'analyse avec ton feedback.',
  'wiz.preset.ticketToPr.implementPrompt': 'Implémente le plan validé :\n---\n{{steps.analyze.data}}\n---\n\nSi une review précédente t\'a laissé du feedback à adresser :\n{{state.last_review}}\n\n# Tu vas charger automatiquement :\n- `test-driven-development` (rituel red-green-refactor strict)\n- `systematic-debugging` (root-cause à 4 phases si un test casse)\n- `verification-before-completion` (no claim sans evidence)\n- `receiving-code-review` (technique pour appliquer les retours review)\n\n# Procédure :\n1. Pour CHAQUE sous-tâche du plan : écris d\'abord les tests (red), puis le code minimal (green), puis refactor.\n2. Lance les tests à chaque étape, vérifie l\'output réellement.\n3. Si un test casse de manière inattendue, applique systematic-debugging (pas de fix au pifomètre).\n4. À la fin, output `[SIGNAL: CONTINUE]` pour passer aux tests d\'intégration.',
  'wiz.preset.ticketToPr.reviewPrompt': 'Review l\'implémentation produite par `implement` et le résultat des tests `run_tests`.\n\nContexte :\n- Plan original : {{steps.analyze.summary}}\n- Tests output : {{steps.run_tests.data.stdout}}\n- Tests exit code : {{steps.run_tests.data.exit_code}}\n\n# Tu vas charger automatiquement les skills `requesting-code-review` et `verification-before-completion`.\n\n# Procédure :\n1. Vérifie que l\'implémentation couvre TOUTES les sous-tâches du plan (pas une partielle).\n2. Vérifie les tests : ils testent un comportement réel, pas juste des mocks ?\n3. YAGNI check : l\'implémentation n\'a pas ajouté de complexité non demandée ?\n4. Sécu : injections, fuites de secrets, validation d\'entrées ?\n5. Edge cases : null, empty, unicode, large input ?\n\n# Verdict :\nSi tout OK, termine ta réponse par exactement `[SIGNAL: APPROVED]`.\nSinon, écris un feedback actionnable, écris exactement à la fin (et UNIQUEMENT à la fin) :\n```\n---STATE:last_review=<feedback en 1-3 lignes>---\n[SIGNAL: NEEDS_CHANGES]\n```',
  'wiz.preset.ticketToPr.createPrPrompt': 'L\'implémentation est validée et tous les tests passent. Crée la PR.\n\nContexte :\n- Ticket : {{steps.fetch_issue.data}}\n- Récap implémentation : {{steps.implement.summary}}\n- Review approuvée : {{steps.review.summary}}\n\n# Tu vas charger automatiquement `finishing-a-development-branch` et `verification-before-completion`.\n\n# Procédure :\n1. Vérifie une dernière fois que les tests passent (commande exacte, output complet).\n2. Push la branche actuelle.\n3. Crée la PR via `gh pr create` avec :\n   - Titre cohérent (référence le ticket)\n   - Body structuré : Summary (3 bullets) + Test Plan (checklist)\n4. Émets les markers Kronn pour exposer l\'URL au step suivant — chaque marker sur sa propre ligne, exactement ce format :\n   ```\n   ---STATE:pr_url=<url>---\n   ---STATE:pr_number=<n>---\n   ```\n   (Important : `state.pr_url=<url>` SANS les `---STATE:...---` ne sera PAS lu par Kronn.)\n5. Termine par `[SIGNAL: PR_CREATED]`.',
  'wiz.preset.ticketToPr.readyGateMessage': '## PR prête au merge\n\n**PR :** {{state.pr_url}}\n**Récap :** {{steps.review.summary}}\n\nApprouve pour finaliser (notif de succès), ou demande des changements pour relancer `implement` avec ton feedback (le `state.last_review` sera consommé par l\'agent).',
  'wiz.preset.ticketToPr.notifyDoneBody': '✅ Ticket Autopilot terminé : {{state.pr_url}} — {{steps.review.summary}}',
  'wiz.preset.ticketToPr.rollbackBody': '❌ Ticket Autopilot a échoué sur `{{failed_step.name}}` — détail : {{failed_step.output}}',
  // 0.8.3 — Feasibility-Gated AutoPilot (auto-proposé après l'audit IA)
  'wiz.preset.feasibilityAutopilot.title': 'Big-ticket AutoPilot — feasibility-gated',
  'wiz.preset.feasibilityAutopilot.desc': 'Pour les gros tickets : un step `triage` classe chaque sous-tâche en clear / decided / mocked / blocked AVANT toute ligne de code. La gate humaine valide le plan, puis `implement` insère des markers `KRONN-(ASSUMED|MOCKED|TODO)` à chaque liberté tracée. `run_tests` Exec lance la vraie suite, `drift_check` Exec liste les markers dans la PR finale. Token-cost minimal — seuls triage / implement / pr_draft sont Agent.',
  'wiz.preset.feasibilityAutopilot.fetchIssueDesc': 'Source du ticket. JsonData fixture par défaut ; le wizard swap automatiquement en ApiCall si un plugin tracker (Jira/GitHub/GitLab) est wiré sur le projet. Le step suivant lit `{{steps.fetch_issue.data}}` (objet complet — l\'agent navigue les champs selon la source : `body` pour le fixture, `fields.description` ou `renderedFields.description` pour Jira).',
  'wiz.preset.feasibilityAutopilot.triageDesc': '[TRIAGE] Audit de faisabilité — classe chaque sous-tâche en clear / decided / mocked / blocked. L\'agent ne CODE pas ici ; il produit un manifest JSON validé par schema (on_invalid=Fail). Chaque entry porte un `id` stable que `implement` utilisera comme decision_id dans les markers.',
  'wiz.preset.feasibilityAutopilot.triagePrompt': 'You are the TRIAGE step of a Feasibility-Gated AutoPilot run.\n\nTicket payload (from `fetch_issue` — full data object, navigate as needed for `body` / `description` / `fields.description` / `renderedFields.description` depending on source):\n---\n{{steps.fetch_issue.data}}\n---\n\nRead the project (current cwd is its worktree). Classify every sub-task into clear / decided / mocked / blocked per the schema. Emit the JSON manifest only — no markdown commentary outside the envelope.',
  'wiz.preset.feasibilityAutopilot.gateDesc': 'Revue humaine du manifest triage avant le code. `gate_request_changes_target: triage` permet de boucler max 5 fois.',
  'wiz.preset.feasibilityAutopilot.gateMessage': 'Triage manifest produit. Revois les 4 catégories ci-dessous avant d\'approuver :\n\n{{steps.triage.data}}\n\n- Approve : lance l\'implémentation telle quelle.\n- Request changes : renvoie au triage avec tes notes (max 5 boucles).\n- Reject : abandonne le run.',
  'wiz.preset.feasibilityAutopilot.implementDesc': 'Implémentation contrainte par le manifest validé. Chaque entry decided/mocked/blocked DOIT avoir son marker `KRONN-*` dans le code. `[SIGNAL: BLOCKED <id>]` → Goto(triage) si l\'agent découvre une impossibilité (cap 3 itérations).',
  'wiz.preset.feasibilityAutopilot.implementPrompt': 'You are the IMPLEMENT step of a Feasibility-Gated AutoPilot run.\n\nThe triage step produced and a human (or auto-budget) approved this manifest:\n\nCLEAR entries (implement directly, no marker needed):\n{{steps.triage.data.clear}}\n\nDECIDED entries (implement the `chosen` option, insert `// KRONN-ASSUMED(<id>): <chosen> — <why>` at the primary touch point):\n{{steps.triage.data.decided}}\n\nMOCKED entries (implement the `placeholder` strategy, insert `// KRONN-MOCKED(<id>): <strategy>`):\n{{steps.triage.data.mocked}}\n\nBLOCKED entries (do NOT implement the feature, only insert `// KRONN-TODO(<id>): waiting on <needed_from> — <why>` where the feature would have been):\n{{steps.triage.data.blocked}}\n\nFiles the manifest claims you\'ll touch:\n{{steps.triage.data.files_touched}}\n\nRULES:\n1. Stay within the files_touched list unless absolutely necessary.\n2. Every entry in decided/mocked/blocked MUST have its corresponding KRONN-* marker in the code, referencing the `id` from the manifest. The next step (`drift_check`) will grep for these — anything missing will be visible in the PR.\n3. If you discover an entry classified as `clear` or `decided` is actually impossible to implement, STOP immediately and emit `[SIGNAL: BLOCKED <id>]` as the last line of your response. The workflow will jump back to triage. Do NOT silently mock or fake it.\n4. Do not invent values that aren\'t in the manifest (URLs, secrets, IDs). If the manifest says it\'s mocked/blocked, respect that.\n5. Do NOT write tests in this step. The next step (`run_tests`) is an Exec that runs the project\'s existing test suite — keep your scope to implementation.\n\nUse your tools to edit files. End your response with a brief summary of what you implemented + the [SIGNAL: ...] line.',
  'wiz.preset.feasibilityAutopilot.driftCheckDesc': 'Liste tous les markers `KRONN-(ASSUMED|MOCKED|TODO)` du worktree. 0 token. La sortie est embarquée verbatim dans la PR par `pr_draft` via `{{steps.drift_check.output}}`.',
  'wiz.preset.feasibilityAutopilot.prDraftDesc': 'Produit le body de PR en agrégeant manifest + verdict des tests + markers drift_check. Section "Decisions taken" met en évidence les libertés tracées que le reviewer doit challenger.',
  'wiz.preset.feasibilityAutopilot.prDraftPrompt': 'You are the PR-DRAFT step of a Feasibility-Gated AutoPilot run.\n\nProduce a PR description in Markdown with this structure:\n\n## Ticket\n{{steps.fetch_issue.data.key}}\n\n## Summary\n<1-3 sentences>\n\n## Implemented (from triage)\n<list of CLEAR + DECIDED + MOCKED entries with their KRONN-* markers>\n\n## Still open (blocked)\n<list of BLOCKED entries with `needed_from` — these are explicit asks to other teams>\n\n## Decisions taken (review carefully)\n<bullets of DECIDED entries: what was chosen vs alternatives, why>\n\n## Mocks to replace later\n<bullets of MOCKED entries with `revisit_when`>\n\n## Test verdict\n<one-line summary of `{{steps.run_tests.output}}` — passed / skipped / errored>\n\n## Markers audit (drift_check output)\n<embed `{{steps.drift_check.output}}` verbatim inside a ``` code fence so reviewers see every traced freedom>\n\n## Test plan\n<bullet list of how to verify each implemented entry manually, beyond what the test suite covers>\n\nPull values from `{{steps.triage.data}}`. End with `[SIGNAL: CONTINUE]`.',
  'wiz.addRollbackStep': 'Ajouter une étape de rollback',
  'wiz.removeRollbackStep': 'Supprimer cette étape de rollback',
  'wf.launchModalTitle': 'Lancer « {name} »',
  'wf.launchModalHint': 'Remplis les variables ci-dessous, elles deviendront `{{nom}}` dans tes step prompts.',
  'wf.launchModalRequired': 'Variable(s) obligatoire(s) manquante(s) : {names}',
  'wf.launchModalGo': 'Lancer',
  'wiz.notifyTitle': 'Webhook / Notification',
  'wiz.notifyHint': 'Appel HTTP direct depuis Kronn, sans agent. Les templates {{steps.X.output}} fonctionnent dans l\'URL et le body.',
  'wiz.notifyUrl': 'URL du webhook',
  'wiz.notifyMethod': 'Méthode',
  'wiz.notifyBody': 'Corps de la requête',
  'wiz.batchQPTitle': 'Batch Quick Prompt',
  'wiz.batchQPHint': 'Lance un Quick Prompt sur chaque item d\'une liste produite par un step précédent (fetch Jira, GitHub, etc.). Le workflow attend par défaut que toutes les discussions enfants se terminent.',
  'wiz.batchQPNoQPs': 'Aucun Quick Prompt disponible. Crée-en un d\'abord dans l\'onglet Quick Prompts.',
  'wiz.batchQPPicker': 'Quick Prompt à lancer',
  'wiz.batchQPPickerEmpty': 'sélectionner un QP',
  'wiz.notifyMissingUrl': 'URL webhook manquante',
  'wiz.batchQPRequired': 'Sélectionne un Quick Prompt — c\'est lui qui sera lancé sur chaque item.',
  'wiz.batchQPVarSubst': 'Chaque item de la liste remplira la variable {{{0}}} du Quick Prompt.',
  'wiz.batchItemsFrom': 'Liste des items à traiter',
  'wiz.batchItemsFromRequired': 'Indique où trouver la liste — généralement l\'output d\'un step précédent.',
  'wiz.batchItemsFromHelper': 'Clique sur un step précédent pour le remplir automatiquement :',
  'wiz.batchItemsFromFromStep': '← Depuis {0}',
  'wiz.batchItemsFromPickStep': 'Utiliser l\'output du step "{0}"',
  'wiz.batchFirstStepWarning': 'Un step batch en première position n\'a pas de step précédent à chaîner. Ajoute un step "fetch" avant pour récupérer la liste d\'items, ou hardcode la liste dans le champ ci-dessous.',
  'wiz.batchAutoPrevNotice': '↑ Étape « {name} » auto-configurée pour alimenter ce batch',
  'wiz.batchWaitForCompletion': 'Attendre la fin de toutes les discussions',
  'wiz.batchMaxItems': 'Max items',
  'wiz.batchWorktree': 'Isoler les modifications de chaque agent',
  'wiz.batchWorktreeHint': 'Chaque agent travaille dans sa propre copie du projet pour ne pas écraser le travail des autres. Cochez si les agents vont modifier des fichiers en parallèle. Laissez décoché pour les analyses (rapports, audits) — c\'est plus rapide.',
  'wiz.batchWorktreeNoProject': 'Ce Quick Prompt n\'est lié à aucun projet — il n\'y a pas de code git à isoler.',
  'wiz.batchChain': 'Enchaîner d\'autres Quick Prompts (optionnel)',
  'wiz.batchChainHint': 'Après le QP initial, ces QPs seront envoyés séquentiellement comme messages utilisateur dans CHAQUE discussion batch. Chaque QP reçoit le MÊME item du batch (ex: "EW-1234") dans sa première variable — idéal pour "analyse → review → résumé" sur le même ticket. Tu peux aussi réutiliser la réponse de l\'agent précédent via `{{previous_qp.output}}` pour des flux "brief → plan → tickets". QPs avec 2+ variables non éligibles. Le batch ne sera marqué terminé qu\'une fois toute la chaîne exécutée.',
  'wiz.batchChainAdd': '+ Ajouter un QP à la chaîne',
  'wiz.batchChainRemove': 'Retirer de la chaîne',
  'wiz.batchChainMoveUp': 'Remonter dans la chaîne',
  'wiz.batchChainMoveDown': 'Descendre dans la chaîne',
  'wiz.batchChainEmpty': 'Aucun QP sans variable disponible à chaîner.',
  'wiz.stepDescription': 'Description',
  'wiz.stepDescriptionPlaceholder': 'Que fait ce step en 1 ligne ? Ex : « Récupère les tickets Jira ouverts » ou « Analyse les logs de tests » — affiché dans la liste, optionnel.',
  'wiz.insertStepHere': 'Insérer un step ici',
  'wiz.moveStepUp': 'Monter ce step',
  'wiz.moveStepDown': 'Descendre ce step',
  'wiz.ifContainsPlaceholder': 'Mot-clé à matcher dans la sortie de l\'agent (ex : APPROVED, NEEDS_CHANGES, ERROR…)',
  'wiz.removeCondition': 'Supprimer cette condition',
  'wiz.wfVariablesTitle': 'Variables de lancement',
  'wiz.wfVariablesHint': 'Variables demandées au moment du lancement manuel — comme les Quick Prompts. Les valeurs deviennent `{{nom}}` dans tes step prompts. Optionnel — si ton workflow tire son contexte d\'un step amont (ApiCall) ou d\'un trigger, laisse vide.',
  'wiz.addVariable': 'Ajouter une variable',
  'wiz.removeVariable': 'Supprimer cette variable',
  'wiz.undeclaredVarsTitle': 'Variables non-déclarées détectées',
  'wiz.undeclaredVarsHint': 'Ces `{{var}}` ne matchent aucun step antérieur, state, iter, artifact ou variable de lancement. Au runtime elles rendront vide. Déclare-les ou corrige le nom.',
  'wiz.undeclaredAddVar': 'Ajouter aux variables',
  'wiz.undeclaredAddVarHint': 'Ajoute « {name} » comme variable de lancement (demandée au lancement manuel).',
  'wiz.undeclaredUnknownStep': 'Step inconnu — vérifie le nom',
  'wiz.undeclaredFailedOutsideRollback': '`failed_step.*` n\'est utilisable que dans une étape de rollback',
  'wiz.pollInterval': 'Intervalle de poll (cron)',
  'wiz.availableVars': 'Variables disponibles',
  'wiz.triggerVars': 'Variables de trigger (Tracker)',
  'wiz.issueTitle': "Titre de l'issue",
  'wiz.issueBody': "Contenu de l'issue",
  'wiz.issueNumber': 'Numéro (#42)',
  'wiz.issueUrl': "Lien vers l'issue",
  'wiz.issueLabels': 'Labels (virgule)',
  'wiz.stepChaining': 'Chaînage entre steps',
  'wiz.prevOutput': 'Sortie du step précédent',
  'wiz.namedOutput': "Sortie d'un step par nom",
  'wiz.iterHint': 'Compteur d\'itération du step (1, 2, 3…) — utile dans les boucles Goto',
  'wiz.stateHint': 'Variable d\'état durable, écrite par les agents via ---STATE:clé=valeur---',
  'wiz.stateChipsLoopOut': 'Boucle détectée — passe ton verdict au step de retour :',
  'wiz.stateChipsLoopIn': 'Reçoit le verdict d\'une boucle — lis le contexte précédent :',
  'wiz.stateChipWrite': 'écrire `state.{key}`',
  'wiz.stateChipWriteHint': 'Ajoute en fin de prompt l\'instruction d\'écrire `---STATE:{key}=...---` pour passer ton verdict à l\'étape qui reboucle.',
  'wiz.stateChipReadHint': 'Insère `{{state.{key}}}` au curseur — la valeur écrite par l\'étape précédente de la boucle.',
  'wiz.stateInstructionBlock': 'À la fin de ta réponse (et UNIQUEMENT à la fin), écris exactement :\n---STATE:{key}=<ton verdict / feedback en une ligne>---\n\nCe bloc sera lu par l\'étape « {target} » au prochain tour de boucle.',
  'wiz.gotoMaxIterPlaceholder': 'N',
  'wiz.gotoMaxIterHint': 'Au-delà de N itérations sur ce Goto, le runner sort de la boucle et continue. Vide = pas de limite par-edge (seulement la limite globale du workflow s\'applique).',
  'wiz.gotoMaxIterLabel': 'max',
  'wiz.gotoMaxIterUnit': 'itérations',
  'wiz.gotoTargetSelect': 'choisir un step',
  'wiz.condActionStop': 'Stop — termine le run',
  'wiz.condActionSkip': 'Skip — saute le step suivant',
  'wiz.condActionGoto': 'Goto — retourne à un step antérieur',
  'wiz.rollbackFiresFailed': 'Failed (échec runtime)',
  'wiz.rollbackSkipsCancelled': 'Cancelled (tu as cliqué « Arrêter »)',
  'wiz.rollbackSkipsGuard': 'StoppedByGuard (limite atteinte)',
  'wiz.rollbackSkipsReject': 'Gate Reject (tu as rejeté)',
  'wiz.availableSignals': 'Signaux disponibles (conditions)',
  'wiz.signalNoResults': "Aucun résultat à reporter",
  'wiz.signalContinue': "Continuer normalement (défaut)",
  'wiz.clickToInsert': 'Cliquer pour insérer',
  'wiz.example': 'Exemple : workflow multi-step',
  'wiz.stepName': 'Nom du step',
  'wiz.advanced': 'Avancé',
  'wiz.mode': 'Mode',
  'wiz.debateAgents': 'Agents du débat',
  'wiz.maxRounds': 'Rounds max',
  'wiz.agentSettings': 'Agent settings (optionnel)',
  'wiz.model': 'Model',
  'wiz.modelPlaceholder': 'ex: o3',
  'wiz.stallTimeout': 'Stall timeout (sec)',
  'wiz.delayAfter': 'Délai après (sec)',
  'wiz.retry': 'Retry',
  'wiz.conditions': 'Conditions (on_result)',
  'wiz.showMore': 'Voir plus',
  'wiz.showLess': 'Réduire',
  'wiz.cronSwitchSimple': 'Mode simple',
  'wiz.testStep': 'Tester',
  'wiz.testInfo': 'Testez cette étape isolément. En mode lecture seule, l\'agent décrit ce qu\'il ferait sans rien modifier (pas de commentaire Jira, pas de commit, pas d\'écriture). Décochez pour exécuter réellement.',
  'wiz.testMockInput': 'Données de l\'étape précédente (mock)',
  'wiz.testMockPlaceholder': 'Collez ici le JSON ou texte que l\'étape précédente aurait produit...',
  'wiz.testDryRun': 'Lecture seule (dry-run)',
  'wiz.testRun': 'Lancer le test',
  'wiz.testRunning': 'En cours...',
  'wiz.testCancel': 'Annuler',
  'wiz.testLogConnecting': 'Connexion à l\'agent...',
  'wiz.testLogStarted': 'Étape "{0}" lancée — l\'agent travaille...',
  'wiz.testLogDone': 'Terminé — statut : {0}',
  'wiz.ifContains': 'si contient',
  'wiz.noResultsPlaceholder': 'NO_RESULTS (obligatoire)',
  'wiz.noResultsStop': 'Aucun résultat → Stop',
  'wiz.addStep': 'Ajouter un step',
  'wiz.errorNoName': 'Nom du workflow requis',
  'wiz.errorNoPrompt': 'Prompt manquant pour "{0}"',
  'wiz.agentQpPicker': 'Depuis un Quick Prompt existant',
  'wiz.agentQpPickerInline': '— Aucun (prompt inline ci-dessous) —',
  'wiz.agentQpInheritedFrom': 'Hérité de « {0} »',
  'wiz.agentQpInheritedHint': 'Le `prompt_template`, le `tier` et les `skill_ids` du QP sont chargés au run-time. Les variables `{{var}}` sont résolues avec le contexte du workflow (launch variables / state / previous_step…).',
  'wiz.agentQpOverridePlaceholder': 'Override du prompt — laisse vide pour garder celui du Quick Prompt. Remplis pour écraser uniquement dans ce step.',
  'wiz.qrefOverrideActive': 'override actif',
  'wiz.qrefOverrideActiveHint': 'Tu as renseigné un override ci-dessous. Il écrase la valeur correspondante du QP/QA pour ce step uniquement.',
  'wiz.qrefPreview': 'Prompt :',
  'wiz.qrefVars': 'Variables :',
  'wiz.qrefEmpty': '(prompt vide)',
  'wiz.qrefOverridePromptToggle': 'Personnaliser le prompt pour ce step (override)',
  'wiz.errorBatchNoQP': 'Quick Prompt non sélectionné pour "{0}"',
  'wiz.errorBatchNoItemsFrom': 'Liste des items manquante pour "{0}" (ex: {{steps.step1.data.tickets}})',
  'wiz.errorApiNoPlugin': 'Plugin API non sélectionné pour "{0}"',
  'wiz.errorApiNoEndpoint': 'Endpoint manquant pour "{0}"',
  'wiz.errorNotifyNoUrl': 'URL de webhook manquante pour "{0}"',
  'wiz.errorGateNoMessage': 'Message de validation manquant pour "{0}"',
  'wiz.errorExecNoCommand': 'Commande shell manquante pour "{0}"',
  'wiz.errorNoCondition': 'Condition vide dans "{0}" (condition #{1})',
  'wiz.security': 'Sécurité',
  'wiz.sandbox': 'Sandbox (Docker)',
  'wiz.requireApproval': 'Approbation requise',
  'wiz.maxFiles': 'Max fichiers modifiés',
  'wiz.maxLines': 'Max lignes modifiées',
  'wiz.concurrency': 'Limite de concurrence (runs simultanés)',
  'wiz.hooks': 'Hooks de workspace (optionnel)',
  'wiz.hooksHint': 'Commandes shell exécutées à chaque phase du worktree git',
  'wiz.hookAfterCreate': 'Après création',
  'wiz.hookBeforeRun': 'Avant exécution',
  'wiz.hookAfterRun': 'Après exécution',
  'wiz.hookBeforeRemove': 'Avant suppression',
  'wiz.previous': 'Précédent',
  'wiz.next': 'Suivant',
  'wiz.create': 'Créer',
  'wiz.saveError': 'Échec de l\'enregistrement. Vérifie la config de tes étapes.',
  'wiz.save': 'Enregistrer',

  // ── Skills (WHAT) ──
  'skills.title': 'Skills',
  'skills.active': 'Skills actifs',
  'skills.add': 'Ajouter un skill',
  'skills.remove': 'Retirer',
  'skills.builtin': 'Intégré',
  'skills.custom': 'Personnalisé',
  'skills.language': 'Langage',
  'skills.domain': 'Domaine',
  'skills.business': 'Business',
  'skills.noSkills': 'Aucun skill sélectionné',
  'skills.selectSkills': 'Sélectionner des skills...',
  'skills.createCustom': 'Créer un skill personnalisé',
  'skills.name': 'Nom',
  'skills.icon': 'Icône',
  'skills.category': 'Catégorie',
  'skills.description': 'Description courte',
  'skills.descriptionPlaceholder': 'Ce que ce skill apporte en une phrase',
  'skills.content': 'Contenu (system prompt)',
  'skills.deleteConfirm': 'Supprimer ce skill ?',
  'skills.editCustom': 'Modifier ce skill',
  'skills.saveChanges': 'Enregistrer',
  'skills.projectDefaults': 'Skills par défaut',
  'skills.projectDefaultsHint': 'Pré-sélectionnés pour les discussions et workflows de ce projet',
  'skills.searchProjects': 'Rechercher un projet…',

  // ── Profiles (WHO) ──
  'profiles.title': 'Profils agent',
  'profiles.select': 'Profil agent',
  'profiles.none': 'Aucun profil',
  'profiles.builtin': 'Intégré',
  'profiles.clickToEditName': 'Cliquer pour modifier le prénom',
  'profiles.custom': 'Personnalisé',
  'profiles.technical': 'Technique',
  'profiles.business': 'Business',
  'profiles.meta': 'Meta',
  'profiles.createCustom': 'Créer un profil personnalisé',
  'profiles.name': 'Nom',
  'profiles.personaName': 'Prénom',
  'profiles.role': 'Rôle',
  'profiles.avatar': 'Avatar',
  'profiles.color': 'Couleur',
  'profiles.category': 'Catégorie',
  'profiles.persona': 'Persona (system prompt)',
  'profiles.defaultEngine': 'Agent par défaut',
  'profiles.deleteConfirm': 'Supprimer ce profil ?',
  'profiles.projectDefault': 'Profil par défaut du projet',
  'profiles.projectDefaultHint': 'Pré-sélectionné pour les nouvelles discussions de ce projet',
  'profiles.searchProjects': 'Rechercher un projet…',

  // ── Directives (HOW) ──
  'directives.title': 'Directives',
  'directives.active': 'Directives actives',
  'directives.add': 'Ajouter une directive',
  'directives.remove': 'Retirer',
  'directives.builtin': 'Intégré',
  'directives.custom': 'Personnalisé',
  'directives.source': 'Source',
  'skills.source': 'Source',
  'directives.output': 'Sortie',
  'directives.language': 'Langue',
  'directives.noDirectives': 'Aucune directive sélectionnée',
  'directives.selectDirectives': 'Sélectionner des directives...',
  'directives.createCustom': 'Créer une directive personnalisée',
  'directives.name': 'Nom',
  'directives.icon': 'Icône',
  'directives.category': 'Catégorie',
  'directives.description': 'Description courte',
  'directives.descriptionPlaceholder': 'Ce que cette directive change dans le comportement',
  'directives.content': 'Contenu (system prompt)',
  'directives.conflicts': 'Conflits',
  'directives.deleteConfirm': 'Supprimer cette directive ?',
  'directives.conflictWarning': 'Conflit détecté avec :',

  // ── Setup Wizard ──
  'setup.step.agents': 'Agents',
  'setup.step.repos': 'Dépôts',
  'setup.step.done': 'Terminé',
  'setup.subtitle': 'Enter the grid. Command your agents.',
  'setup.aiAgents': 'Agents IA',
  'setup.refresh': 'Rafraîchir',
  'setup.agentsDetected': '{0} agent{1} détecté{2} sur votre système.',
  'setup.noAgentDetected': 'Aucun agent détecté — installez-en au moins un.',
  'setup.install': 'Installer',
  'setup.enabled': 'Activé',
  'setup.disabled': 'Désactivé',
  'setup.continue': 'Continuer',
  'setup.skip': 'Passer cette étape',
  'setup.back': 'Retour',
  'setup.detectedRepos': 'Dépôts détectés',
  'setup.rescan': 'Re-scanner',
  'setup.scanningRepos': 'Scan des dépôts git...',
  'setup.reposFound': '{0} dépôt{1} git trouvé{2}.',
  'setup.hiddenRepos': '+ {0} caché{1}',
  'setup.noRepoFound': 'Aucun dépôt git trouvé. Indiquez un chemin à scanner.',
  'setup.addPath': 'Ajouter un autre chemin',
  'setup.scan': 'Scanner',
  'setup.hide': 'Masquer',
  'setup.show': 'Voir',
  'setup.hiddenReposToggle': '{0} les {1} dépôt{2} caché{3}',
  'setup.hidden': 'caché',
  'setup.configDone': 'Configuration terminée',
  'setup.summary': '{0} agent{1}, {2} dépôt{3} détecté{4}.',
  'setup.goToDashboard': 'Accéder au dashboard',
  'setup.preparingDashboard': 'Préparation du dashboard...',
  'setup.completingStep1': 'Sauvegarde de la configuration...',
  'setup.completingStep2': 'Enregistrement des projets détectés...',
  'setup.completingStep3': 'Initialisation du dashboard...',
  'setup.detecting': 'Détection des agents...',
  'setup.detectingHint': 'Analyse de votre système (PATH, WSL, npm)... cela peut prendre quelques secondes.',
  'setup.detectionFailed': 'Detection failed',
  'setup.installFailed': 'Install failed',
  'setup.scanFailed': 'Scan failed',

  // ── Settings: Voice ──
  'settings.voice': 'Voice',
  'settings.sttLabel': 'Speech-to-Text (Whisper)',
  'settings.sttDesc': '100% local via Whisper WASM. Le modèle est téléchargé au premier usage puis caché dans le navigateur.',
  'settings.ttsLabel': 'Text-to-Speech (Piper)',
  'settings.ttsDesc': '100% local via Piper WASM. Activable via le bouton volume dans les discussions. Le modèle est téléchargé au premier usage (~50 MB) puis caché dans le navigateur.',
  'settings.stallTimeout': "Timeout d'inactivité agent",
  'settings.stallTimeoutHint': "Durée maximale sans sortie avant d'interrompre l'agent. Augmentez si vos agents lancent des commandes longues (tests, builds).",
  'settings.stallTimeoutWarning': "Un timeout élevé laisse plus de temps aux agents pour exécuter des opérations longues, ce qui peut entraîner une consommation de tokens plus importante. À utiliser pour les builds et suites de tests conséquentes.",
  'settings.debugSection': 'Debug',
  'settings.debugMode': 'Mode debug',
  'settings.debugModeHint': "Active les logs détaillés du backend (détection agents, scan projets, résolution de chemins hôte). Utile pour diagnostiquer les soucis macOS / multi-plateforme. Pour forcer ponctuellement sans modifier la config : ./kronn start --debug",
  'settings.debugModeRestart': "Le nouveau niveau de log s'applique au prochain redémarrage du backend. Utilise ./kronn restart pour redémarrer.",
  'settings.debugLogsTitle': 'Logs backend (temps réel)',
  'settings.debugLogsEmpty': 'Aucun log capturé pour le moment. Interagis avec l\'app pour déclencher des events.',
  'settings.debugLogsHint': "Source: ringbuffer en RAM alimenté par chaque event tracing. Pas de fichier sur disque. Ctrl+F dans le viewer pour rechercher (grep côté client).",
  'settings.debugLogsRefresh': 'Rafraîchir',
  'settings.debugLogsStartFollow': 'Suivre',
  'settings.debugLogsStopFollow': 'Pause',
  'settings.debugLogsCopy': 'Copier',
  'settings.debugLogsClear': 'Vider',
  'settings.debugBufferedCount': '{0} / {1} lignes',
  'settings.debugReportCta': 'Signaler un bug sur GitHub',
  'settings.debugReportHint': 'Ouvre un nouvel onglet avec les logs pré-remplis (secrets masqués). Tu peux éditer avant de soumettre.',
  'settings.debugReportIssueList': 'Voir les issues existantes',
  'settings.debugLiveLabel': 'LIVE',
  'common.on': 'Activé',
  'common.off': 'Désactivé',

  // ── Discussions: Voice & misc ──
  'disc.recording': 'Enregistrement en cours...',
  'disc.transcribing': 'Transcription en cours...',
  'disc.resumeListening': "Reprise de l'ecoute...",
  'disc.cancelVoice': 'Annuler',
  'disc.sendVoice': 'Envoyer',
  'disc.stopRecording': 'Stop',
  'disc.micStop': 'Arreter',
  'disc.micDictate': 'Dicter',
  'disc.draftRestored': 'Brouillon restauré · écrit {0}',
  'disc.draftDismiss': 'Supprimer le brouillon',
  'disc.voiceModeOn': 'Mode conversation vocale',
  'disc.voiceModeOff': 'Desactiver le mode conversation',
  'disc.logs': 'Logs',
  'disc.editTimeout': 'Modifier le timeout',
  'disc.ttsDisable': 'Desactiver la lecture vocale',
  'disc.ttsEnable': 'Activer la lecture vocale',

  // ── Common ──
  'common.cancel': 'Annuler',
  'common.dismiss': 'Fermer',
  'common.save': 'Sauvegarder',
  'common.delete': 'Supprimer',
  'common.close': 'Fermer',
  'common.add': 'Ajouter',
  'common.copy': 'Copier',
  'common.loading': 'Chargement...',
  'common.seeMore': 'Voir plus',
  'common.seeLess': 'Voir moins',

  // Workflow execution guards (0.7.0)
  'wf.guards.title': 'Limites d\'exécution',
  'wf.guards.description': 'Filets de sécurité contre les boucles, les runs sans fin et les budgets imprévus. Laisse vide pour utiliser les valeurs par défaut.',
  'wf.guards.summaryDefaults': 'Défauts : {0} min · {1} appels IA · {2} revisites/step',
  'wf.guards.summaryActive': 'Limites personnalisées',
  'wf.guards.timeoutLabel': 'Durée max',
  'wf.guards.timeoutUnit': '(minutes)',
  'wf.guards.timeoutHint': 'Défaut {0} min — temps total écoulé du run.',
  'wf.guards.maxLlmCallsLabel': 'Appels IA max',
  'wf.guards.maxLlmCallsHint': 'Défaut {0} — Agent = 1, BatchQP = 1, ApiCall/Notify = 0.',
  'wf.guards.loopDetectionLabel': 'Détection de boucle',
  'wf.guards.loopDetectionHint': 'Défaut {0} — nombre max de revisites du même step (Goto).',
  'wf.guards.stoppedBy.title': 'Stoppé par garde-fou',
  'wf.guards.stoppedBy.timeout': 'Durée max atteinte ({0} s)',
  'wf.guards.stoppedBy.maxLlmCalls': 'Trop d\'appels IA ({0} sur {1})',
  'wf.guards.stoppedBy.loopDetection': 'Boucle détectée sur le step « {0} » ({1} fois)',

  // ── Stale-stream watchdog (TD-20260504) ──
  'discussions.streamRecovered': 'Connexion perdue avec l\'agent — état le plus récent rechargé',

  // ── Migration ai/ → docs/ banner ──
  'migration.title': 'Convention `ai/` héritée détectée',
  'migration.desc': 'Ce projet utilise encore `ai/index.md`. Migre vers la convention `docs/AGENTS.md` (Kronn 0.7.1+) — `git mv` préserve l\'historique, les liens internes sont réécrits.',
  'migration.symlink': 'Créer un symlink `ai → docs` pour la rétro-compatibilité',
  'migration.cta': 'Migrer vers docs/',
  'migration.ctaPending': 'Migration en cours…',
  'migration.ctaDone': 'Migration terminée',
  'migration.inProgress': 'Déplacement des fichiers, réécriture des liens…',
  'migration.successInline': '{0} fichiers déplacés — `docs/AGENTS.md` prêt',
  'migration.successToast': 'Migration réussie ({0} fichiers déplacés)',
  'migration.alreadyToast': 'Projet déjà migré',
  'migration.failedToast': 'Échec de la migration de {0}',
  'migration.failedGeneric': 'Migration échouée — voir les logs serveur',

  // ── User context (cross-project agent prompts) ──
  'userContext.title': 'Mes contextes',
  'userContext.subtitle': 'Notes markdown injectées dans tous les agents, quels que soient le projet et le CLI. Stockées dans ~/.kronn/user-context/.',
  'userContext.newNamePlaceholder': 'nom-du-fichier (ajoutera .md automatiquement)',
  'userContext.newNameAria': 'Nom du nouveau fichier de contexte',
  'userContext.add': 'Ajouter',
  'userContext.deleteConfirm': 'Supprimer le fichier « {0} » ?',
  'userContext.deleteAria': 'Supprimer {0}',
  'userContext.delete': 'Supprimer',
  'userContext.loading': 'Chargement…',
  'userContext.empty': 'Aucun fichier. Ajoute-en un pour partager des notes avec tous tes agents.',
  'userContext.save': 'Enregistrer',
  'userContext.editingAria': 'Édition de {0}',
};

const en: TranslationDict = {
  // ── Nav & Tabs ──
  'nav.projects': 'Projects',
  'nav.discussions': 'Discussions',
  'nav.mcps': 'Plugins',
  'nav.workflows': 'Automation',
  'nav.config': 'Config',
  'nav.scan': 'Scan',

  // ── Projects page ──
  'projects.title': 'Projects',
  'projects.aiReady': 'AI-ready',
  'projects.hidden': 'hidden',
  'projects.hiddenPlural': 'hidden',
  'projects.hideHidden': 'Hide hidden projects',
  'projects.showHidden': 'Show hidden projects',
  'projects.search': 'Search a project...',
  'projects.group.local': 'Local projects',
  'projects.group.other': 'Other',
  'projects.showMore': 'Show more ({0} project{1} remaining)',
  'projects.collapse': 'Collapse',
  'projects.empty': 'No projects. Click "Scan" to detect your repositories.',
  'projects.emptyHint': 'No projects yet. Use the + button at the top to add a folder, clone a repo, or create a new project.',
  'projects.emptySearch': 'No matching project.',
  'projects.manageMcps': 'Manage in Plugins',
  'projects.noMcp': 'No plugin — configure',
  'projects.mcpHint.beforeAudit': 'Add a few MCP plugins before running briefing or audit — they make the results much richer.',
  'projects.mcpHint.cta': 'Add a plugin',
  'projects.delete': 'Delete',
  'projects.deleteConfirm': 'Remove this project from Kronn?',
  'projects.deleteHard': 'Delete permanently',
  'projects.deleteHardWarn': 'This action is irreversible. The project folder will be deleted from disk.',
  'projects.deleteHardConfirmLabel': 'Type the project name to confirm',
  'projects.deleteSoft': 'Remove from Kronn',
  'projects.skills': 'Skills',
  'projects.noSkills': 'No skills available — create in Config',
  // 0.8.3 — Linked repos / companion projects
  'linkedRepos.title': 'Linked repos',
  'linkedRepos.hint': 'Companion repos (API, IaC, design…) that agents need to know about for cross-repo context. Injected into every audit / discussion / workflow system prompt.',
  'linkedRepos.empty': 'No linked repos yet — add one so agents have cross-repo context.',
  'linkedRepos.addLink': 'Add a linked repo',
  'linkedRepos.addBtn': 'Add',
  'linkedRepos.remove': 'Remove',
  'linkedRepos.confirmRemove': 'Remove this linked repo? Agents will no longer see it in their context.',
  'linkedRepos.namePlaceholder': 'Name (e.g. backend-api)',
  'linkedRepos.locationPlaceholder': 'Path or URL (e.g. /home/user/Repos/backend-api or https://github.com/org/backend-api)',
  'linkedRepos.descriptionPlaceholder': 'What is this repo for? (optional — e.g. "GraphQL schema for the frontend")',
  'linkedRepos.requiredField': 'Name and location are required.',
  'linkedRepos.maxReached': 'Limit reached (max 20 linked repos).',
  'linkedRepos.saveError': 'Failed to save linked repos.',
  // 0.8.6 (#27) — autocomplete picker
  'linkedRepos.pickerLabel': 'Your Kronn projects — click to auto-fill',
  'linkedRepos.pickerMoreHint': '{0} more',
  'linkedRepos.pickerShowLess': 'Show less',
  'projects.workflows': 'Workflows',
  'projects.noWorkflows': 'No workflow — create',
  'projects.loading': 'Loading projects…',
  'projects.scanning': 'Scanning…',
  'projects.scanFound': '{0} new project(s) detected',
  'projects.scanNone': 'No new projects detected',
  'projects.scanError': 'Scan error',
  'projects.bootstrap': 'Add a project',
  'projects.bootstrap.name': 'Project name',
  'projects.bootstrap.desc': 'Project description',
  'projects.bootstrap.descPlaceholder': 'Describe your project: goals, features, constraints...',
  'projects.bootstrap.docs': 'Documents (optional)',
  'projects.bootstrap.docsHint': 'Drag & drop or click — architecture, specs, PRD...',
  'projects.bootstrap.options': 'Advanced options',
  'projects.bootstrap.noCreateRepo': 'Do not create a Git repo',
  'projects.bootstrap.createRepoVia': 'Create repo via',
  'projects.bootstrap.noGeneratePlan': 'Do not generate plan/issues',
  'projects.bootstrap.generatePlanVia': 'Plan & issues via',
  'projects.bootstrap.creating': 'Creating...',
  'projects.bootstrap.start': 'Create and bootstrap',
  'projects.bootstrap.mcps': 'Plugins to enable',
  'projects.addFolder': 'Local folder',
  'projects.folder.hint': 'Add a folder as a project — no git required. If a .git is present, Kronn auto-detects it.',
  'projects.folder.path': 'Folder path',
  'projects.folder.name': 'Name (optional, inferred from folder)',
  'projects.folder.namePlaceholder': 'Auto-inferred if empty',
  'projects.folder.add': 'Add',
  'projects.folder.success': 'Project added',

  'projects.clone': 'Clone a repository',
  'projects.clone.url': 'Repository URL',
  'projects.clone.urlPlaceholder': 'https://github.com/... or git@github.com:...',
  'projects.clone.name': 'Name (optional)',
  'projects.clone.start': 'Clone',
  'projects.clone.cloning': 'Cloning...',
  'projects.clone.success': 'Repository cloned successfully',
  'projects.clone.discover': 'Detect my repositories',
  'projects.clone.discovering': 'Detecting...',
  'projects.clone.discovered': '{0} repo(s) detected from {1}',
  'projects.clone.noToken': 'No GitHub/GitLab token found. Configure the GitHub or GitLab plugin in the Plugins tab.',
  'projects.clone.alreadyCloned': 'Already in Kronn',
  'projects.clone.stars': 'stars',
  'projects.clone.searchRepos': 'Filter repositories...',
  'projects.clone.selectSources': 'Sources to query',
  'projects.docAi': 'Project documentation',
  'projects.techDebtBadge': '{0} tech-debt item(s) to address — see docs/tech-debt/',
  'projects.docAi.selectFile': 'Select a file to view',
  'projects.docAi.loading': 'Loading...',
  'projects.docAi.empty': 'No project documentation available',
  'projects.docAi.search': 'Search...',
  'projects.docAi.noResults': 'No results',
  'projects.docAi.file': 'file',
  'projects.docAi.files': 'files',
  'projects.docAi.discuss': 'Discuss this file',
  'projects.docAi.fixThis': 'Fix this issue',
  'projects.docAi.fixThisPrompt': "Here is a documented tech-debt entry in `{0}` — help me resolve it. Read the file, propose a short plan (1-3 steps), then execute the code changes needed to fix it. Once fixed, update the TD-* file (status → resolved, or delete it) and the matching row in `docs/inconsistencies-tech-debt.md`.\n\nStart:",
  'projects.docAi.banner.runAudit': "Run an AI audit to fully (re)document the project and surface its tech debt and inconsistencies.",
  'projects.docAi.banner.auditRunning': "Audit in progress — the documentation is being built step by step. The files below fill in as the audit advances; remaining placeholders will be replaced before completion.",
  'projects.docAi.banner.bootstrapDone': "Bootstrap done. Run the full audit to generate the detailed project documentation.",
  'projects.docAi.banner.validate': "Validate the audit to get an up-to-date, more relevant documentation (the AI will ask about ambiguities).",
  // 0.8.4 (#298) — post-audit step recap panel
  'projects.docAi.auditRecap.title': 'Last audit breakdown',
  'projects.docAi.auditRecap.stepsLabel': 'steps',
  'projects.docAi.auditRecap.runsLabel': 'audits',
  'projects.docAi.auditRecap.filterAll': 'All',
  'projects.docAi.auditRecap.showMoreChips': '+ {0} older',
  'projects.docAi.auditRecap.showLessChips': '– Collapse',
  'projects.docAi.auditRecap.openDrawer': 'View previous audits ({0})',
  'projects.docAi.auditRecap.drawerTitle': 'Audit history',
  'projects.docAi.auditRecap.metaDurationTooltip': 'Total audit duration (from launch to end of Step 10).',
  'projects.docAi.auditRecap.metaTdTooltip': 'Number of Tech Debts (TDs) detected by this audit. Click the card to see the per-step breakdown.',
  'projects.docAi.auditRecap.metaHealthTooltip': 'Project health score (0-100). Computed from TD severity: 100 = no critical debt, 0 = critical saturation. Weights: Critical = -12, High = -5, Medium = -2, Low = -0.3.',
  'projects.docAi.auditRecap.colStepTooltip': 'Step number in the audit pipeline (1-10 for a global audit, 1-1 for a targeted one). Click to sort.',
  'projects.docAi.auditRecap.colFileTooltip': 'The `docs/*.md` file produced or refined by this step. The 🔧 marker means the file was repaired from the template (placeholder leak detected).',
  'projects.docAi.auditRecap.colDurationTooltip': 'Duration of this step only (Δ between step_start and step_done). Click to sort — useful for spotting the slowest step.',
  'projects.docAi.auditRecap.colTokensTooltip': 'Tokens consumed by this step (max of input + output, not cumulative). Click to sort — useful for spotting the most expensive step.',
  'projects.docAi.auditRecap.totalsPrefix': 'Total:',
  'projects.docAi.auditRecap.colFile': 'File',
  'projects.docAi.auditRecap.colDuration': 'Duration',
  'projects.docAi.auditRecap.colTokens': 'Tokens',
  'projects.docAi.auditRecap.empty': 'No per-step metrics for this audit (pre-0.8.4 run).',
  'projects.docAi.auditRecap.repairedHint': 'This step wrote an empty file or a placeholder — repaired from the template.',
  // 0.8.4 (#287) — sub-audit modal
  'audit.kindSelector.tooltip': "Audit type to launch. Targeted audits (Security, RGAA, Database…) unlock after the global audit is done.",
  'audit.kindSelector.reAuditHint': "Re-run a thematic audit (security, RGAA, database…) to dig deeper into one topic — or re-run the global audit to refresh the whole doc.",
  'audit.kindSelector.launchLabel': "Launch: {0}",
  'audit.kind.Full': "Global audit (10 steps)",
  'audit.kind.Security': "Security",
  'audit.kind.Docker': "Docker",
  'audit.kind.Performance': "Performance",
  'audit.kind.Accessibility': "Accessibility (WCAG 2.1)",
  'audit.kind.Rgaa': "RGAA 4.1 (France)",
  'audit.kind.Database': "Database",
  'audit.kind.ApiDesign': "API Design",
  'audit.kind.afterFull': "(after global audit)",
  'audit.subAudit.modalLabel': 'Audit type picker',
  'audit.subAudit.title': 'Which audit do you want to run?',
  'audit.subAudit.intro': 'Pick a global audit (the 10 steps, ~20 min) or a targeted audit on one dimension (1 step, faster, better for drilling down).',
  'audit.subAudit.fullTitle': 'Global audit (10 steps)',
  'audit.subAudit.fullDesc': 'Full pipeline: project docs, repo-map, architecture, Mermaid diagrams, tech-debt, cluster recommendations.',
  'audit.subAudit.targetedSectionTitle': 'Targeted audits (1 step)',
  'audit.subAudit.cta': 'Targeted audit',
  'audit.subAudit.kind.Security.title': 'Security',
  'audit.subAudit.kind.Security.desc': 'Auth, secrets, input validation, headers, vulnerable dependencies.',
  'audit.subAudit.kind.Docker.title': 'Docker',
  'audit.subAudit.kind.Docker.desc': 'Dockerfile, multi-stage, layer caching, image size, healthcheck, secrets.',
  'audit.subAudit.kind.Performance.title': 'Performance',
  'audit.subAudit.kind.Performance.desc': 'Hot paths, bundle size, lazy-load, N+1 queries, missing indexes.',
  'audit.subAudit.kind.Accessibility.title': 'Accessibility (WCAG 2.1)',
  'audit.subAudit.kind.Accessibility.desc': 'Semantic HTML, ARIA, focus, contrast, alternatives. International standard.',
  'audit.subAudit.kind.Rgaa.title': 'RGAA 4.1 (France)',
  'audit.subAudit.kind.Rgaa.desc': "France's mandatory accessibility norm (décret 2019-768). Stricter than WCAG. Includes a manual-audit + training section.",
  'audit.subAudit.kind.Database.title': 'Database',
  'audit.subAudit.kind.Database.desc': 'Schema, migrations, indexes, ORM N+1, referential integrity.',
  'audit.subAudit.kind.ApiDesign.title': 'API Design',
  'audit.subAudit.kind.ApiDesign.desc': 'Contract, versioning, pagination, authn/authz, rate limiting, doc drift.',
  'projects.docAi.discussPrompt': 'I would like to discuss the content of the file `{0}` from the project documentation. Here are my observations:\n\n',
  'projects.status.valid': 'valid',
  'projects.status.validating': 'validating...',
  'projects.status.auditOk': 'audit OK',
  'projects.status.template': 'template',
  'projects.status.none': 'none',
  'projects.status.bootstrapped': 'MVP defined',
  'projects.status.bootstrapping': 'bootstrap...',

  // ── AI Audit ──
  'audit.noTemplate': 'No template installed. Install the template to structure the project documentation.',
  'audit.installTemplate': 'Install AI template',
  'audit.description': 'The AI audit analyzes the entire project in 10 steps and fills the docs/ documentation.',
  'audit.warning': 'Estimated duration: ~20 minutes. High token usage.',
  'audit.launch': 'Launch AI audit',
  'audit.startFullAudit': 'Start AI Audit',
  'audit.resumeFromStep': 'Resume at step {0}/10',
  'audit.resumeFromTooltip': 'Previous audit was interrupted after step {0}. Already-completed steps will be skipped to save tokens + time.',
  'audit.fullAuditDesc': 'Installs the template, runs a 10-step audit, then creates the validation discussion.',
  'audit.briefingLabel': 'Project briefing (optional)',
  'audit.briefingPlaceholder': 'Describe your project briefly: stack, conventions, key points...',
  'audit.fullAuditDone': 'Audit complete! Validation discussion created.',
  'audit.templateStep': 'Installing template...',
  'audit.step': 'Step {0}/{1} — {2}',
  'audit.elapsed': '⏱ {0}',
  'audit.elapsedTooltip': 'Time elapsed since audit start (client-side ticker, anchored on the server\'s started_at).',
  'audit.lastStepTokens': '💬 {0} tk',
  'audit.lastStepTokensTooltip': 'Tokens spent on the most recently finished step (max(input+output) — agents report cumulative usage per call, so we take the max, not a sum).',
  'audit.totalTokens': 'Σ {0} tk',
  'audit.totalTokensTooltip': 'Cumulative tokens across every finished step since audit start. To spot the most expensive step: watch the delta between two consecutive step_done events.',
  'audit.currentTool': '🔧 {0}',
  'audit.currentToolTooltip': 'Tool the agent just started calling (Read, Glob, Bash, mcp__...). Last-write-wins — the chip updates on every new tool and clears at step_done.',
  'audit.validationInProgress': 'Validation in progress — {0} messages exchanged.',
  'audit.activityInProgress': 'Activity in progress on this project',
  'audit.validationHint': 'Validation completes from the discussion when the AI confirms all items are resolved.',
  'audit.resumeValidation': 'Resume validation',
  'audit.readyToValidate': 'Project documentation generated. Validate the audit to resolve ambiguities with the AI.',
  'audit.validate': 'Validate audit',
  'audit.done': 'Audit validated. Project documentation ready in docs/.',
  'audit.markValid': 'Mark audit as validated',
  'audit.validationComplete': 'The AI confirms that validation is complete.',
  'audit.autopilotCtaText': 'Audit validated. Wire AutoPilot to process this project\'s tickets automatically.',
  'audit.autopilotCtaBtn': 'Setup AutoPilot',
  'audit.trackerHint': 'Audit goes deeper with a tracker wired (GitHub / GitLab / Jira / Linear) — Phase 3 offers ticket creation and AutoPilot runs on real issues.',
  'audit.trackerHintConfigure': 'Configure a tracker',
  'audit.trackerHintDismiss': 'Don\'t show again for this project',
  'audit.validationTitle': 'AI Audit Validation',
  'audit.validationPrompt': 'Here is the project documentation (docs/ folder). Follow the strict 4-phase validation protocol. Do NOT emit KRONN:VALIDATION_COMPLETE until ALL 4 phases are done.\n\n## Phase 1 — Auto-fix (autonomous)\nRead the source code and fix what you can: orphan TODO markers, skeleton files, outdated info. Update docs/ files directly.\n\n## Phase 2 — Ambiguity questions (interactive)\nAsk your questions one by one. After each answer, update docs/ files before the next question. If an answer reveals new unknowns, ask follow-ups.\n\n## Phase 3 — Tech debt review (interactive)\nReview EVERY entry in docs/inconsistencies-tech-debt.md and docs/tech-debt/. For each item: is the assessment accurate? What priority? Should a ticket be created?\n\n## Phase 4 — Documentation challenge (interactive)\nAsk me 2-3 practical questions a new developer would ask. Check if the docs answer them. If insufficient, update and ask another.\n\nOnly when all 4 phases are complete, end with: "KRONN:VALIDATION_COMPLETE".',
  'audit.validationFilesFilled': 'Filled doc files:',
  'audit.validationFilesIncomplete': 'Incomplete doc files:',
  'audit.validationFilesNone': '(none)',
  'audit.validationTodos': 'Remaining TODO markers ({0}):',
  'audit.cancelAudit': 'Cancel',
  'audit.cancelled': 'Audit cancelled. All files have been removed.',
  'audit.streamError': 'Audit stopped on an error: {0}',
  'audit.stepWarning': '⚠ Step `{0}` produced no useful output — {1}. {2}',
  'audit.stepWarningRepaired': 'Template restored; re-run the audit to recover the content.',
  'audit.stepWarningNotRepaired': 'No template to restore; check the file manually.',
  'audit.viewTechDebts': 'View {0} TDs →',
  'audit.viewTechDebtsAfterValidation': 'View all Tech Debts for this project',
  'audit.partialStreamError': 'Partial audit stopped on an error: {0}',
  'audit.bootstrapInProgress': 'Bootstrap in progress...',
  'audit.resumeBootstrap': 'Resume bootstrap',
  'audit.bootstrapComplete': 'Bootstrap is complete. The project MVP is defined.',
  'audit.markBootstrapped': 'Mark as defined',
  'audit.bootstrapDone': 'MVP defined. Run the audit to generate the complete project documentation.',
  'audit.staleSections': '{0} stale section(s)',
  'audit.updateStale': 'Update ({0} sections)',
  'audit.noStale': 'Audit up to date',
  'audit.auditDate': 'Audited on {0}',
  'audit.startBriefing': 'Briefing (AI)',
  'audit.briefingDesc': 'Quick discussion (5 min) to give context to the audit.',
  'briefing.formBtn': 'Set up briefing',
  'briefing.formBtnTooltip': 'Fill the 6 questions in the form. On save, an AI agent reviews your answers and only asks for clarifications on ambiguous points, then writes the final briefing.',
  'briefing.formTitle': 'Project briefing',
  'briefing.formIntro': 'Answer the 6 questions below. On save, an AI agent reviews your answers, asks for clarifications only if needed, then writes `docs/briefing.md`. No need to split this in two steps.',
  'briefing.saveAndReviewBtn': 'Save + AI review',
  'briefing.savedButNoDiscToast': 'Briefing saved, but the AI review could not spawn. The audit will still use your answers.',
  'briefing.q1Purpose': '1. What does this project do?',
  'briefing.q1Placeholder': 'One sentence. What it does for its users.',
  'briefing.q2Team': '2. Who works on it?',
  'briefing.q2Placeholder': 'solo / small team / large team',
  'briefing.q3Maturity': '3. What stage is it at?',
  'briefing.q3Placeholder': 'prototype, MVP, production, legacy, rewrite…',
  'briefing.q4Dependencies': '4. Key external dependencies?',
  'briefing.q4Placeholder': 'Examples: "PostgreSQL on AWS RDS", "user-service API on gitlab.company.com/org/repo" — or "none".',
  'briefing.q5Traps': '5. What would a new contributor get wrong on day one?',
  'briefing.q5Placeholder': 'traps, implicit rules, fragile areas',
  'briefing.q6Additional': '6. Anything else the audit should know?',
  'briefing.q6Placeholder': 'Optional — keep it short.',
  'briefing.saveBtn': 'Save briefing',
  'briefing.savedToast': 'Briefing saved (docs/briefing.md).',
  'briefing.missingFields': 'Mandatory fields missing: {0}',
  'audit.briefingDone': 'Briefing complete!',
  'audit.resumeBriefing': 'Resume briefing',
  'audit.goToProject': 'Go to project',
  'audit.runningMcpFilterBanner': 'AI audit in progress on this project — some MCPs are temporarily disabled to speed up the audit. Normal MCPs come back automatically once the audit finishes. If you need the full set now, wait a few minutes or open this discussion on a different project.',
  'audit.activeAuditsTitle': 'Audits in progress',
  'audit.activeAuditsEmpty': 'No audit in progress',
  'audit.activeAuditsStep': 'Step {0}/{1}',
  'audit.runningTooltip': '{0} audit{1} in progress',
  'audit.viewAllProjects': 'View all projects',
  'mermaid.parseError': 'Could not render this Mermaid diagram — check the syntax below.',
  'mermaid.errorDetails': 'Error details',
  'mermaid.showSource': 'Show source',
  'mermaid.hideSource': 'Hide source',
  'mermaid.fullscreen': 'Fullscreen',
  'mermaid.closeFullscreen': 'Close fullscreen',
  'mermaid.print': 'Print',
  'audit.unauditedWarning': 'This project has no validated AI audit yet. Start with a project briefing to give business context — the audit then produces a single source of truth shared by EVERY agent (Claude Code, Cursor, Vibe, Ollama…) that survives session resets, agent switches, and team turnover.',
  'audit.unauditedWarningWithBriefing': 'Briefing done, but the AI audit hasn\'t run yet. Launch it to generate the Kronn documentation (`docs/AGENTS.md` + structure) — the **cross-agent source of truth** read identically by Claude Code, Cursor, Vibe, Ollama and others. Prevents agent-to-agent drift and preserves project knowledge beyond any single session.',
  'audit.unauditedCtaBriefing': 'Brief the project',
  'audit.unauditedCtaLaunch': 'Launch AI audit',
  'audit.auditInProgress': 'Audit in progress...',
  'audit.auditDoneResume': 'Audit complete! Validation is ready.',

  // ── Discussions ──
  'disc.new': 'New',
  'disc.batchFromWorkflowRun': 'run #{0} · {1}',
  'disc.batchFromWorkflow': '{0}',
  'disc.batchParentClickHint': 'Open this batch\'s parent workflow',
  'disc.batchDeleteHint': 'Delete this batch ({0} discussions)',
  'disc.batchRetryHint': 'Re-launch this batch ({0} discussions, identical copy)',
  'disc.batchRetryConfirm': 'Re-launch this batch?\n\n« {1} » will fan out {0} new discussions with the same prompts. The old batch stays in place — delete it manually if needed.',
  'disc.batchRetryToast': 'Batch re-launched ({0} discussions created)',
  'disc.batchRetryError': 'Retry failed: {0}',
  'disc.batchRetryEmpty': 'No items to retry (child discussions may have been deleted).',
  'disc.batchDeleteConfirm': 'Delete this batch and its {0} discussions?\n\n« {1} »\n\nThis cannot be undone.',
  'disc.batchDeletedToast': 'Batch deleted ({0} discussions removed)',
  'disc.batchDeleteError': 'Delete failed: {0}',
  'disc.stopAgent': 'Stop the agent',
  'disc.stopAgentToast': 'Agent stopped',
  'disc.stopAgentNothing': 'Nothing to stop — the agent already finished',
  'disc.stopAgentError': 'Stop failed: {0}',
  'disc.partialRecoveredToast': '{0} discussion(s) restored after restart — interrupted agent responses recovered',
  'disc.partialPendingPrompt': 'A previous agent response is still being recovered.\n\nForce recovery now and send your message?',
  'disc.partialDismissed': 'Recovery forced — you can resend your message',
  'disc.searchPlaceholder': 'Search...',
  'disc.searchClear': 'Clear search',
  // 0.8.4 (#294) — cross-agent source binding (badge + filter)
  'disc.source.importedHint': 'Discussion imported from {0}.',
  'disc.source.divergedHint': '{0}: diverged (edited inside Kronn after import — a re-push would overwrite these changes).',
  'disc.source.filterTooltip': 'Filter by source CLI (Claude Code, Cursor, …)',
  'disc.source.filterAll': 'All sources',
  'disc.source.filterFrom': 'From {0}',
  'disc.markAllRead': 'Mark all as read',
  'disc.markAllReadTooltip': 'Mark every discussion ({0} unread messages) as read. Includes archived discussions and batches.',
  'disc.newTitle': 'New discussion',
  'disc.project': 'Project (optional)',
  'disc.noProject': 'No project',
  'disc.agent': 'Agent',
  'disc.noAgent': 'No agent installed',
  'disc.title': 'Title (optional)',
  'disc.titlePlaceholder': 'Auto-generated from prompt...',
  'disc.prompt': 'Initial prompt',
  'disc.promptPlaceholder': 'Describe what you want...',
  'disc.attachFiles': 'Attach context files',
  'disc.start': 'Start discussion',
  // 0.8.6 phase 2 — disc-first refactor
  'disc.launchAgentNow': 'Launch an agent right away',
  'disc.launchAgentNowHint': 'Uncheck to draft your brief without an agent, or to open a multi-agent room (Claude + Codex...) you invite later from the header.',
  'disc.discFirstHint': 'No agent will be launched. You can invite Claude, Codex, Gemini etc. from the discussion header.',
  'disc.discFirstDefaultTitle': 'Agentless discussion',
  'disc.createEmpty': 'Create discussion',
  'disc.discFirstCreatedToast': 'Discussion created. Click "+ Invite" in the header to add an agent.',
  'disc.participantsEmpty': 'No agent attached. Click "+ Invite" to add a participant.',
  'disc.invitePeer': '+ Invite',
  'disc.invitePeerTooltip': 'Generate a token to paste into another MCP-capable CLI (Claude, Codex, Gemini…) so it joins this discussion.',
  'disc.inviteFailed': 'Invite failed: {0}',
  'disc.inviteCopied': 'Instruction copied to clipboard.',
  'disc.inviteCopyFailed': "Couldn't copy — copy the command manually.",
  'disc.inviteModalTitle': 'Invite an agent to this discussion',
  'disc.inviteModalIntro': 'Paste this instruction in each MCP-capable CLI you want to invite. The same token works for N agents during the next {0} min:',
  'disc.inviteModalClose': 'Close',
  'disc.inviteCopyBtn': 'Copy instruction',
  'disc.inviteExpiresHint': 'Expires at: {0}',
  'disc.auditWarn': 'The AI will analyze the full project documentation and code. This first analysis may take a few minutes and consume tokens.',
  'disc.auditHint': 'There may be several questions. The conversation can be resumed at any time from the Discussions tab.',
  'disc.advancedOptions': 'Skills, Profiles & Directives',
  'disc.mcps': 'Available plugins',
  'disc.mcpSearch': 'Filter plugins…',
  'disc.noMcps': 'No plugins configured',
  'disc.editConfig': 'Edit profiles, skills & directives',
  'git.title': 'Modified files',
  'git.refresh': 'Refresh',
  'git.noChanges': 'No changes',
  'git.filesChanged': 'file(s) changed',
  'git.committedOnBranch': '{0} file(s) already committed on this branch (vs {1})',
  'git.onDefaultBranch': 'You are on the default branch',
  'git.createBranch': 'Create branch',
  'git.branchName': 'Branch name...',
  'git.commit': 'Commit',
  'git.commitMessage': 'Commit message...',
  'git.amend': 'Amend',
  'git.sign': 'Sign (-S)',
  'git.commitSelected': 'Commit {0} file(s)',
  'git.push': 'Push',
  'git.pushSuccess': 'Push successful',
  'git.createPr': 'Create PR',
  'git.createMr': 'Create MR',
  'git.prTitle': 'PR title',
  'git.prEdit': 'Edit',
  'git.prPreview': 'Preview',
  'git.prBodyPlaceholder': 'PR description (Markdown)...',
  'git.prTemplateProject': 'project template',
  'git.prTemplateKronn': 'Kronn template',
  'git.submitPr': 'Create Pull Request',
  'git.submitMr': 'Create Merge Request',
  'git.selectAll': 'Select all',
  'git.deselectAll': 'Deselect all',
  'git.filesBtn': 'Files',
  'git.pendingFilesTooltip': '{0} file(s) pending commit',
  // ── Test mode (worktree swap) ──
  'testMode.cta': 'Try this version',
  'testMode.ctaTooltip': 'Load the discussion\'s code into your main repo — checks out the branch and pauses the agent while you try it.',
  'testMode.bannerHeadline': 'You are trying this discussion\'s version in your IDE — the agent is paused.',
  'testMode.bannerRestore': 'will restore to `{0}`',
  'testMode.exit': 'Stop testing',
  'testMode.exitTooltip': 'Go back to your previous state and re-create the discussion\'s worktree.',
  'testMode.exiting': 'Exiting…',
  'testMode.enterSuccess': 'Test mode on — main repo is now on {0}',
  'testMode.enterError': 'Could not enter test mode: {0}',
  'testMode.exitSuccess': 'Test mode off — back on {0}',
  'testMode.exitError': 'Error while exiting test mode: {0}',
  'testMode.modal.worktreeDirtyTitle': 'Uncommitted agent changes',
  'testMode.modal.worktreeDirtyExplain': 'The agent left uncommitted changes in its worktree. They will be lost if you test now — commit them first, or ask the agent to do it.',
  'testMode.modal.mainDirtyTitle': 'Uncommitted work on `{0}`',
  'testMode.modal.mainDirtyExplain': 'Your main repo has uncommitted changes. What do you want to do before switching to the agent\'s version?',
  'testMode.modal.detachedTitle': 'Detached HEAD',
  'testMode.modal.detachedExplain': 'Your main repo is not on a named branch. After the test we\'ll take you back if you were on one.',
  'testMode.modal.fallbackTitle': 'Cannot enter test mode',
  'testMode.modal.openGitPanel': 'Open the git panel',
  'testMode.modal.stashAndProceed': 'Stash and proceed',
  'testMode.modal.stashing': 'Stashing…',
  'testMode.modal.commitFirst': 'Commit first',
  'testMode.modal.proceedAnyway': 'Proceed anyway',
  'testMode.modal.cancel': 'Cancel',
  'testMode.modal.filesMore': '… and {0} more',
  'git.terminal': 'Terminal',
  'git.terminalPlaceholder': 'git log --oneline -5',
  'git.terminalRestricted': 'Terminal disabled — enable full_access on an agent in Config',
  'disc.empty': 'No discussions.\nClick "New" to start.',
  'disc.selectOrCreate': 'Select or create a discussion',
  'disc.mentionHint': '{0} to target an agent...',
  'disc.messagePlaceholder': 'Your message...',
  'disc.attachFile': 'Add context file',
  'disc.general': 'General',
  'disc.local': 'Local',
  'disc.system': 'System',
  'disc.summaryCached': 'Summary',
  'disc.viewSummary': 'View',
  'disc.hideSummary': 'Hide',
  'disc.thinking': '{0} is thinking...',
  'disc.running': 'Agent running...',
  'disc.debateLaunching': 'Debate booting up… (agent CLIs can take 5-30s to start, especially Codex on cold-start)',
  'disc.debateAgentEmpty': '{0} finished without producing any content — CLI may have crashed or hit a rate limit. Re-launch the debate to retry.',
  'disc.newContent': 'New content',
  'disc.badgeInfo.showDetails': 'Show details',
  'disc.badgeInfo.profileKind': 'AI profile',
  'disc.badgeInfo.skillKind': 'Skill',
  'disc.badgeInfo.directiveKind': 'Directive',
  'disc.badgeInfo.tokens': 'tokens',
  'disc.badgeInfo.builtin': 'built-in',
  'disc.badgeInfo.notFound': 'Item not found (may have been deleted).',
  'disc.synthesis': 'Synthesis',
  'disc.cancel': 'Cancel',
  'disc.resend': 'Resend',
  'disc.editResend': 'Edit and resend',
  'disc.retryResponse': 'Retry response',
  'disc.copyMessage': 'Copy message',
  'disc.copy': 'Copy',
  'disc.copied': 'Copied!',
  'disc.confirmDelete': 'Delete this discussion? This action cannot be undone.',
  'disc.overrideKey': 'Activate or Override API Key',
  'disc.orCheckAgent': 'or check that your local agent is connected',
  'disc.stopThinking': 'Stop thinking',
  'disc.agentDisabled': 'Agent {0} is currently disabled or uninstalled.',
  'disc.agentDisabledLink': 'Manage agents',
  'disc.archive': 'Archive',
  'disc.favorites': 'Favorites',
  'disc.pin': 'Pin to favorites',
  'disc.unpin': 'Unpin from favorites',
  'disc.pinError': 'Error: {0}',
  'disc.chainQP': 'Chain a Quick Prompt after the response',
  'disc.cancelQueuedQP': 'Cancel queued Quick Prompt (click)',
  'disc.delete': 'Delete',
  'disc.archived': 'Archives',
  'disc.showMore': 'older discussions',
  'disc.unseenMessagesTooltip': '{0} unread message{1}',
  'disc.scrollToTop': 'Jump to top',
  'disc.scrollToBottom': 'Jump to bottom',
  'disc.introspectionPillTooltip': '{0} history-tool call{1} (kronn-internal MCP)',
  // 0.8.5 — disc-id pill in ChatHeader + sidebar tooltip + search by id prefix.
  'disc.idPillTooltip': 'Full ID: {0} · click to copy',
  'disc.idCopied': 'ID copied to clipboard',
  'disc.idCopyFailed': 'Copy failed (clipboard access denied)',
  'disc.titleHoverTooltip': '{0}\nID: {1}',
  'disc.summaryStrategyLabel': 'Auto-summary',
  'disc.summaryStrategy.Auto.label': '⚡ Auto',
  'disc.summaryStrategy.Auto.hint': 'Auto-fire every N messages based on the agent\'s context budget. Best for debate or small models.',
  'disc.summaryStrategy.OnDemand.label': '🤖 On demand',
  'disc.summaryStrategy.OnDemand.hint': 'No auto-fire. The agent can request a summary explicitly (tool surface coming soon). Best for big-context models (Claude Opus, GPT-5).',
  'disc.summaryStrategy.Off.label': '🚫 Off',
  'disc.summaryStrategy.Off.hint': 'No summarisation. Raw transcript until context saturates. Saves all summary token cost — may truncate on very long threads.',
  'disc.introspectionUnsupportedNote': '{0} doesn\'t read MCP — the kronn-internal history tools (disc_meta / disc_get_message / disc_summarize) won\'t be called. The agent falls back to the raw transcript in its context window. Pick `Auto` to keep an auto-generated summary as a safety net for long threads.',
  'workflows.runningTooltip': '{0} workflow{1} currently running',
  'disc.unarchive': 'Unarchive',
  'disc.editTitle': 'Rename',
  'disc.workspaceLabel': 'Workspace',
  'disc.workspaceDirect': 'On project',
  'disc.workspaceIsolated': 'Isolated (worktree)',
  'disc.workspaceDirectDesc': 'Work directly on the project directory',
  'disc.workspaceIsolatedDesc': 'Create a dedicated branch in a git worktree',
  'disc.workspaceIsolatedNeedsRepo': 'Requires a project linked to a git repo',
  'disc.branchName': 'Branch name',
  'disc.baseBranch': 'Base branch',
  'disc.isolatedBadge': 'isolated',
  'disc.worktreeUnlock': 'Release branch',
  'disc.worktreeLock': 'Lock branch',
  'disc.worktreeUnlocked': 'unlocked',
  'disc.switchAgent': 'Switch agent',
  'disc.modelTier': 'AI mode',
  'settings.identity': 'Identity',
  'settings.identityHint': 'Your nickname and avatar, shown in conversations.',
  'settings.pseudo': 'Nickname',
  'settings.avatarEmail': 'Email (Gravatar)',
  'settings.avatarHint': 'Used to display your Gravatar avatar. Optional.',
  'settings.bio': 'Bio',
  'settings.bioPlaceholder': 'Ex: Product Manager at Acme Corp, specialized in analytics and growth...',
  'settings.bioHint': 'Shared with agents at the start of each new discussion. Helps agents tailor their responses to your profile.',
  'settings.globalContext': 'Global context',
  'settings.globalContextHint': 'This text is shared with all your agents at the start of every discussion. Great for your business glossary, internal conventions, or anything your agents should always know.',
  'settings.globalContextPlaceholder': '## Glossary\n- CMS: our custom CMS based on...\n- DAM: Digital Asset Manager\n\n## Stack\n- Frontend: Next.js 14\n- Backend: Node.js + Express\n\n## Conventions\n- Tests required for every change\n- PR review by at least 1 person',
  'settings.globalContextSaved': 'Global context saved',
  'settings.gcModeAlways': 'Always inject',
  'settings.gcModeNoProject': 'Only without project',
  'settings.gcModeNever': 'Disabled',

  // ── Guided tour v2 (0.3.6 — 17 steps) ──
  'tour.welcome.title': 'Welcome to Kronn!',
  'tour.welcome.desc': 'In 2 minutes, you\'ll know how to pilot your AI agents. Escape to exit, ? to replay.',
  'tour.conceptProject.title': 'Your projects',
  'tour.conceptProject.desc': 'A project = a folder or repository that gives context to your agents. Code, docs, configs — everything starts here.',
  'tour.scan.title': 'Auto scanner',
  'tour.scan.desc': 'Detects git repositories already on your machine. One click and you\'re done.',
  'tour.newProject.title': 'Add a project',
  'tour.newProject.desc': 'Click this button to see the creation options.',
  'tour.modalOverview.title': '3 ways to start',
  'tour.modalOverview.desc': 'Bootstrap — create from scratch with AI\n\nClone — import an existing Git repo\n\nFolder — add a local directory without git',
  'tour.modalClose.title': 'Got it!',
  'tour.modalClose.desc': 'Let\'s continue the tour — the modal closes automatically.',
  'tour.navPlugins.title': 'Plugins',
  'tour.navPlugins.desc': 'Connect your tools: GitHub, Jira, Slack... 56 plugins available to empower your agents.',
  'tour.addPlugin.title': 'Add a plugin',
  'tour.addPlugin.desc': 'This button opens the catalog of 53 available plugins. Search by name (e.g. "github", "jira") and activate them on your projects. Each active plugin will be accessible by your agents.',
  'tour.navDiscussions.title': 'Discussions',
  'tour.navDiscussions.desc': 'This is where you talk to your AI agents, in the context of your projects.',
  'tour.sidebar.title': 'Your conversations',
  'tour.sidebar.desc': 'Organized by project. Workflow discussions are grouped in folders.',
  'tour.newDisc.title': 'Start a discussion',
  'tour.newDisc.desc': 'Click to open the form. You\'ll pick an agent, a project, and skills.',
  'tour.discForm.title': 'The form',
  'tour.discForm.desc': 'Agent, project, skills, attached files — everything is configured here before launching the conversation.',
  'tour.discProfiles.title': 'Frame the agent with a profile',
  'tour.discProfiles.desc': 'Each discussion can be framed by a profile (Architect, Tech-Lead, Mentor, QA…). They set the agent\'s tone, method and role — same question, very different answers depending on which profile is active. Click here to open the list.',
  'tour.discProfileChip.title': 'Pick a profile',
  'tour.discProfileChip.desc': 'Hover a profile to read its full description. Click a profile to activate it and continue. Create or edit your own profiles in Settings > Skills, Profiles & Directives.',
  'tour.navAutomation.title': 'Automation',
  'tour.navAutomation.desc': 'Two tools: Quick Prompts (reusable templates) and Workflows (multi-step automations, cron).',
  'tour.navConfig.title': 'Configuration',
  'tour.navConfig.desc': 'Your API keys, custom profiles, skills, and the global context shared with all agents.',
  'tour.usage.title': 'Usage tracking',
  'tour.usage.desc': 'See your token consumption by agent and model. Essential to control costs.',
  'tour.done.title': 'You\'re all set!',
  'tour.done.desc': 'Start by launching a discussion with an agent. The ? button in the top right replays this tour anytime.\n\n🎁 A little gift for making it this far: an easter egg is hidden in Settings — enter the Konami code `↑↑↓↓←→←→BA` at the right spot, then type `kronnMatrix` in the field that appears. Shhh. 🤫',
  'tour.next': 'Next',
  'tour.prev': 'Previous',
  'tour.skip': 'Skip tour',
  'tour.finish': 'Finish',
  'tour.clickHint': 'Click the highlighted element to continue',
  'tour.replayHint': 'Replay guided tour',
  'tour.replay': 'Replay guided tour',

  // ── Ollama (0.4.0) ──
  'ollama.online': 'Online',
  'ollama.offline': 'Server stopped',
  'ollama.unreachable': 'Unreachable',
  'ollama.notInstalled': 'Not installed',
  'ollama.models': 'model(s)',
  'ollama.refresh': 'Refresh',
  'ollama.installTitle': 'Install Ollama',
  'ollama.installDesc': 'Ollama lets you run AI models locally on your machine, for free and without internet.',
  'ollama.launchTitle': 'Start Ollama',
  'ollama.launchDesc': 'The Ollama server is not running. Run: ollama serve',
  'ollama.pullTitle': 'Download a model',
  'ollama.pullDesc': 'Ollama is online but no models are installed. Run one of the following commands in your terminal:',
  'ollama.installedModels': 'Installed models',
  'ollama.defaultTag': 'default',
  'ollama.pickDefaultHint': 'Click a model to set it as the default.',
  'ollama.pickedDefaultHint': 'Current default: {0}',
  'ollama.pullMoreHint': 'To add more models: ollama pull <name> — see ollama.com/library',
  'ollama.canirunHint': 'canirun.ai — check if your hardware can run a given model',
  'contacts.title': 'Contacts',
  'contacts.pending': 'pending',
  'contacts.inviteCode': 'Invite code',
  'contacts.inviteHint': 'Share this code so other Kronn users can add you as a contact.',
  'contacts.add': 'Add contact',
  'contacts.addPlaceholder': 'kronn:pseudo@host:port',
  'contacts.wsConnected': 'Connected in real-time',
  'contacts.wsDisconnected': 'Disconnected',
  'contacts.added': 'Contact added',
  'contacts.addError': 'Failed to add contact',
  'contacts.warnGeneric': 'Contact added but unreachable — check that both machines are on the same network.',
  'contacts.warn.TAILSCALE_MISSING': 'Contact added but unreachable — this contact uses Tailscale, but Tailscale is not active here. Install or connect Tailscale.',
  'contacts.warn.TAILSCALE_UNREACHABLE': 'Contact added but unreachable — Tailscale is active but the contact is not responding. Check that they are online.',
  'contacts.warn.LAN_UNREACHABLE': 'Contact added but unreachable — this contact is on a local network. Check that you are on the same Wi-Fi/LAN.',
  'contacts.warn.NETWORK_UNREACHABLE': 'Contact added but unreachable — check the address and that the contact is online.',
  'contacts.delete': 'Delete',
  'contacts.deleteConfirm': 'Delete this contact? You will no longer be able to message them or share discussions.',
  'contacts.deleted': 'Contact deleted',
  'contacts.sharedByPeer': '{0} shared "{1}"',
  'contacts.deleteError': 'Failed to delete contact',
  'contacts.tailscaleHint': 'Tailscale detected — this code works anywhere, even if you change networks.',
  'contacts.detectedIps': 'Detected IP addresses',
  'contacts.usedInCode': 'used in code',
  'contacts.clickToUse': 'Click to use this IP in the invite code',
  'contacts.ipSelected': 'IP selected for invite code',
  'contacts.guideTitle': 'How to connect two Kronn instances',
  'contacts.guideStep1': 'Both machines must be on the same network (LAN, corporate VPN, or Tailscale).',
  'contacts.guideStep2': 'Copy your invite code above.',
  'contacts.guideStep3': 'Send it to your contact (via email, chat, etc.).',
  'contacts.guideStep4': 'Your contact pastes it in Discussions > Contacts > "+" button.',
  'contacts.guideNetworkTitle': 'Which network to use?',
  'contacts.guideNetwork': 'Corporate VPN (AWS VPN, OpenVPN...): use your VPN IP in the Domain field below. Tailscale (free): install on both machines, IP is auto-detected. Same WiFi: works too, but IP changes when you switch networks.',
  'disc.tier.economy': 'Eco',
  'disc.tier.default': 'Standard',
  'disc.tier.reasoning': 'Advanced',

  // ── Debate ──
  'debate.title': 'Multi-agent debate',
  'debate.header': 'Inter-agent debate',
  'debate.instructions': 'Select at least 2 agents. They will debate for {0} round{1}, then the main agent will synthesize.',
  'debate.main': 'main',
  'debate.launch': 'Launch debate ({0} agents)',
  'debate.rounds': 'Rounds',

  // ── Config ──
  'config.appearance': 'Appearance',
  'config.appearanceHint': 'Choose the visual theme for the interface.',
  'config.themeSystem': 'System',
  'config.themeLight': 'Light',
  'config.themeDark': 'Dark',
  'config.themeMatrix': 'Matrix',
  'config.themeSakura': 'Sakura',
  'config.themeGotham': 'Gotham',
  'config.secretLabel': 'Secret code:',
  'config.secretPlaceholder': '••••••••',
  'config.secretSubmit': 'Unlock',
  'config.secretUnlocked': 'Theme "{0}" unlocked.',
  'config.secretInvalid': 'Invalid code.',
  'config.konamiReveal': '🎮 Secret mode activated',
  'config.batmanRecruited': '🦇 Batman joined the Batcave',
  'skills.autoActivated': '⚡ Skill "{0}" auto-activated',
  'skills.autoTriggerOn': 'Auto-activation ON',
  'skills.autoTriggerOff': 'Auto-activation OFF',
  'skills.autoTriggerHint': 'Whether this skill auto-activates when you type certain keywords (e.g. "PDF", "report"). Click to disable — the skill is still available manually.',
  'skills.autoToggleFailed': 'Could not toggle auto-activation.',
  'config.subtitle': 'General Kronn settings',
  'config.uiLanguage': 'Interface language',
  'config.uiLanguageHint': 'Display language for the Kronn interface.',
  'config.outputLanguage': 'Output language',
  'config.outputLanguageHint': 'Language in which agents will respond by default.',
  'config.scanPaths': 'Scan folders',
  'config.scanPathsHint': 'Root directories where Kronn looks for your git repositories.',
  'config.scanPathPlaceholder': '/home/user/projects',
  'config.path': 'folder',
  'config.pathsPlural': 'folders',
  'config.scanIgnore': 'Ignore patterns',
  'config.scanIgnoreHint': 'Directory names to ignore during scan (node_modules, .git, etc.).',
  'config.scanIgnorePlaceholder': 'e.g.: vendor',
  'config.pattern': 'pattern',
  'config.patternsPlural': 'patterns',
  'config.scanDepth': 'Scan depth',
  'config.scanDepthHint': 'Number of subdirectory levels to explore when searching for git repos (2-10).',
  'config.agents': 'Agents',
  'config.installed': 'installed',
  'config.installedPlural': 'installed',
  'config.refresh': 'Refresh',
  'config.fullAccess': 'Allows web access, file modifications and command execution.',
  'config.autoApply': 'Automatically applies changes without confirmation.',
  'config.restrictedAgent': 'Agent {0} does not have "full access" mode enabled — it won\'t be able to modify files.',
  'config.restrictedAgentLink': 'Enable in Config > Agents',
  'config.restrictedDebate': 'Some agents don\'t have "full access" mode — they won\'t be able to modify files.',
  'config.restrictedStep': 'This agent does not have "full access" mode — it won\'t be able to modify files.',
  'config.fullAccessBadge': 'Full access',
  'config.uninstallConfirm': 'Uninstall {0}?',
  'config.uninstall': 'Uninstall',
  'config.hostManaged': 'Installed on host',
  'config.uninstallFailed': 'Uninstall failed — remove manually on host',
  'config.installFailed': 'Failed to install {0}: {1}',
  'config.getKey': 'Get an API key',
  'config.wslWarning': 'WSL detected — prefer installing agents directly on WSL, Kronn will detect them automatically. Docker-installed agents are lost on rebuild.',
  // ── RTK (compression hook for shell outputs) ──
  'config.rtk.title': 'Eco mode',
  'config.rtk.explainer': 'Agents run lots of commands (git, cargo, ls, tests) that produce verbose output. Kronn can compress those outputs before they reach the AI, saving up to 90% tokens with no quality loss.',
  'config.rtk.poweredBy': 'Powered by',
  'config.rtk.openSource': 'open source',
  'config.rtk.stateAll': 'Active on every compatible agent',
  'config.rtk.statePartial': 'Active on {0}/{1} compatible agents',
  'config.rtk.stateNone': 'Not activated yet',
  'config.rtk.notInstalled': 'RTK is not installed on this machine',
  'config.rtk.savings': '~{0} tokens saved this month',
  'config.rtk.activateAll': 'Enable on every compatible agent',
  'config.rtk.activateRemaining': 'Enable on the {0} remaining',
  'config.rtk.activating': 'Activating…',
  'config.rtk.installCta': 'Install RTK then activate',
  'config.rtk.installHelp': 'RTK needs to be installed on your machine. Open a terminal and paste the command below, or ask your tech-savvy colleague to install it for you.',
  'config.rtk.installCommand': 'Install command',
  'config.rtk.viewOnGithub': 'View RTK on GitHub',
  'config.rtk.viewDocs': 'Documentation',
  'config.rtk.badgeActive': 'RTK active',
  'config.rtk.badgeMissing': 'RTK — hook not configured',
  'config.rtk.badgeNotInstalled': 'RTK not installed',
  'config.rtk.badgeNotApplicable': 'Not supported by RTK',
  'config.rtk.aboutLink': 'About RTK',
  'agentRuntimeWarning.vibe.sdk_fallback': 'Vibe SDK incompatible — Kronn falls back to direct Mistral API (~4 s faster but no local tools: bash, files, MCP). Upgrade `mistral-vibe` or ignore.',
  'app.updateAvailable': 'Kronn {0} → {1} available',
  'app.updateOpenRelease': 'View release',
  'app.updateDismiss': 'Dismiss until the next version',
  'app.backendOffline': 'Backend unreachable',
  'app.backendOfflineTitle': 'The Kronn backend isn\'t responding — actions will fail until it\'s restarted.',
  'disc.kronnToolResult': 'View result',
  'config.rtk.activateError': 'RTK activation failed. Check console for details.',
  'config.rtk.activateSuccess': 'RTK activated.',
  'config.rtk.activatePartial': 'RTK partially activated — {0} of {1} agents failed (see details).',
  'config.rtk.activateNoTarget': 'Nothing to enable — every supported agent is already configured.',
  'config.rtk.deactivate': 'Disable RTK ({0})',
  'config.rtk.deactivateHint': 'Remove the RTK hook from agent configs — reversible via "Activate".',
  'config.rtk.deactivateError': 'RTK deactivation failed. Check console for details.',
  'config.rtk.deactivateSuccess': 'RTK deactivated.',
  'config.rtk.detailsToggle': 'Details',
  'config.rtk.statTokens': 'Tokens saved',
  'config.rtk.statRatio': 'Average ratio',
  'config.rtk.statSamples': 'Commands compressed',
  'config.rtk.sobrietyTitle': 'About "eco mode"',
  'config.rtk.sobrietyBody': 'The most sustainable use is not to use AI at all. If you do use it, RTK compresses shell outputs before they reach the model — fewer tokens, less energy, same quality.',
  'config.rtk.updateAvailable': '{0} available',
  'config.rtk.updateAvailableTitle': 'RTK {0} → {1}',
  'config.rtk.updateAvailableAria': 'RTK update from {0} to {1} available',
  'config.rtk.updateModalTitle': 'Update RTK',
  'config.rtk.updateModalBody': 'Installed: {0} — Latest known: {1}. Run the command below (the script is idempotent and upgrades in place).',
  'config.agentUpdateAvailableAria': 'Update for {0} to {1} available',
  'config.agentUpdateAvailableTitle': 'Installed {0} → latest {1}',
  'config.agentUpdateModalTitle': 'Update {0}',
  'config.agentUpdateModalBody': 'Installed: {0} — Latest known: {1}. Re-run the install command to upgrade (npm/curl/uv installs over the top).',
  'config.enabled': 'Active',
  'config.disabled': 'Paused',
  'config.toggleEnable': 'Re-enable',
  'config.toggleDisable': 'Pause',
  'config.apiKeys': 'API Keys (optional)',
  'config.apiKeysHint1': 'Your local agents use their own authentication (subscriptions).',
  'config.apiKeysHint2': 'If you want to use a specific API key instead, you can enter it here as an override.',
  'config.localAuth': 'local auth',
  'config.tokenCostHint': 'Estimated token cost when injected into agent prompt',
  'config.viewModels': 'View models',
  'config.account': 'Account',
  'config.viewAccount': 'View {0} account / usage',
  'config.defaultModel': 'Default',
  'config.saveError': 'Save error',
  'config.tokenUsage': 'Token usage',
  'config.estimateTokenUsage': 'Estimated token usage',
  'config.enableOverride': 'Enable API key override',
  'config.disableOverride': 'Disable override (use local auth)',
  'config.syncTokensConfirm': 'Update the API key in all installed agent configs?',
  'config.syncTokensDone': 'Key synced: {0}',
  'config.syncTokensNone': 'No agents to sync',
  'config.bestPractices': 'Best practices by provider',
  'config.discoverKeys': 'Auto-detect',
  'config.discoverKeysFound': '{0} key(s) found, {1} imported',
  'config.discoverKeysNone': 'No keys detected',
  'config.addKey': 'Add a key',
  'config.keyName': 'Key name',
  'config.defaultKeyName': 'Personal API Key',
  'config.deleteKey': 'Delete this key',
  'config.deleteKeyConfirm': 'Delete key "{0}"?',
  'config.activateKey': 'Activate this key',
  'config.overrideActive': 'override active',
  'config.overrideDisabled': 'override disabled',
  'config.save': 'Save',
  'config.saved': 'Saved!',
  'config.server': 'Server & Security',
  'config.authToken': 'API Token',
  'config.authEnabled': 'active',
  'config.authDisabled': 'disabled',
  'config.authActivate': 'Enable authentication',
  'config.authActivated': 'Authentication enabled! Token copied to clipboard.',
  'config.authDisabledHint': 'The API is open without authentication. Enable a token to secure access.',
  'config.authHint': 'Use this token in the Authorization: Bearer <token> header to authenticate API calls.',
  'config.authCopied': 'Token copied',
  'config.authRegenConfirm': 'Regenerate token? Existing clients will need to reconnect.',
  'config.authRegenerated': 'Token regenerated',
  'config.authRegenError': 'Error regenerating token',
  'config.regenerate': 'Regenerate',
  'config.domain': 'Custom domain',
  'config.domainHint': 'E.g. kronn.local — used for CORS and TLS. Requires restart.',
  'config.domainSave': 'Save domain',
  'config.domainSaved': 'Domain saved (restart required)',
  'config.domainError': 'Save error',
  'config.maxAgents': 'Max concurrent agents',
  'config.maxAgentsHint': 'Maximum parallel agent processes. Restart required to apply.',
  'config.usage': 'Usage',
  'config.usageEmpty': 'No usage data available.',
  'config.usageTotalTokens': 'Total tokens',
  'config.usageTotalCost': 'Estimated cost',
  'config.usageDiscussions': 'Discussions',
  'config.usageWorkflows': 'Workflows',
  'config.usageByProvider': 'By provider',
  'config.usageByProject': 'By project',
  'config.usageTopDiscussions': 'Top discussions',
  'config.usageTopWorkflows': 'Top workflows',
  'config.usageDaily': 'History (30 days)',
  'config.usageFilterAll': 'All',
  'config.usageFilterDisc': 'Disc.',
  'config.usageFilterWf': 'Wf.',
  'config.database': 'Database',
  'config.dbProjects': 'Projects',
  'config.dbDiscussions': 'Discussions',
  'config.dbMessages': 'Messages',
  'config.dbMcps': 'Plugins',
  'config.dbWorkflows': 'Workflows',
  'config.dbSkills': 'Custom skills',
  'config.dbProfiles': 'Custom profiles',
  'config.dbDirectives': 'Custom directives',
  'config.export': 'Export',
  'config.import': 'Import',
  'config.dbBackup': 'DB snapshot',
  'config.dbBackupSuccess': 'Snapshot written: {0}',
  'config.dbBackupError': 'Snapshot failed — see console',
  'config.importInvalid': 'Invalid file',
  'config.importConfirm': 'This will replace all current data. Continue?',
  'config.importError': 'Error during import',
  'config.importSuccess': 'Import successful',
  'config.importPathWarning': '{0} project(s) with invalid paths — remap them in the Projects page',
  'config.exportApiWarning': 'Authentication tokens are not included in the export. Save them before migrating.',
  'config.exportApiWarningLink': 'View my tokens →',
  'config.resetHint': 'Reset configuration and relaunch the setup wizard.',
  'config.resetConfirm': "WARNING: this will delete all configuration (AI provider keys, preferences, agents).\n\nRemember to:\n- Export your data (Config → Database → Export)\n- Save your API keys and authentication token\n\nThis action is irreversible. Continue?",
  'config.reset': 'Reset configuration',
  'config.configFile': 'Config file',

  // ── Plugins ──
  'mcp.title': 'Plugins',
  'mcp.subtitle': '(MCP / API)',
  'mcp.add': 'Add',
  'mcp.detect': 'Sync',
  'mcp.syncing': 'Syncing…',
  'mcp.incomplete.title': '{0} plugin(s) not operational',
  'mcp.incomplete.hint': 'Their config is incomplete. Kronn does NOT load them into projects so they do not break agents at boot. Click a row to fix.',
  'mcp.search': 'Search plugin or project...',
  'mcp.searchRegistry': 'Search plugin...',
  'mcp.sortAz': 'Sort A → Z',
  'mcp.sortZa': 'Sort Z → A',
  'mcp.configure': 'Configure {0}',
  'mcp.addTitle': 'Add a plugin',
  'mcp.addAnother': 'Add another {0} config',
  'mcp.label': 'Name / Label',
  'mcp.envVars': 'Environment variables',
  'mcp.custom.tileTitle': 'Custom API',
  'mcp.custom.tileCat': 'Your own API',
  'mcp.custom.tileDesc': 'Define your own REST API: name, base URL, free-form description, optional docs link, and any fields the agent needs (tokens, IDs, headers).',
  'mcp.custom.tileBadge': 'Defined by you',
  'mcp.custom.name': 'API name',
  'mcp.custom.namePlaceholder': 'e.g. Salesforce Sales API',
  'mcp.custom.baseUrl': 'Base URL',
  'mcp.custom.baseUrlPlaceholder': 'https://my-org.salesforce.com/services/data/v59.0',
  'mcp.custom.description': 'Describe the API',
  'mcp.custom.descriptionPlaceholder': 'What this API does. Auth notes. Anything the agent should know — it will also fetch the docs link below.',
  'mcp.custom.docsUrl': 'Documentation link (optional)',
  'mcp.custom.fields': 'Fields',
  'mcp.custom.fieldsHint': 'One {Label, Value} pair per field. Kronn auto-derives the env key (e.g. "Bearer Token" → BEARER_TOKEN).',
  'mcp.custom.fieldLabel': 'Label (e.g. Bearer Token)',
  'mcp.custom.fieldValue': 'Value',
  'mcp.custom.fieldAdd': 'Add a field',
  'mcp.custom.fieldRemove': 'Remove this field',
  // 0.8.6 — edit mode: secrets-preserved reassurance
  'mcp.custom.fieldValueStored': '•••• stored encrypted',
  'mcp.custom.fieldValueStoredTooltip': 'Value preserved. Edit it via "Edit secrets" on the plugin drawer.',
  'mcp.custom.fieldValueEditElsewhere': '→ Edit the value via "Edit secrets"',
  'mcp.custom.fieldsHintEditMode': 'You can modify the plugin STRUCTURE (field names, endpoints, auth) AND credential VALUES here. Existing values are pre-filled (masked) — leave them as-is to keep them, or type over to replace.',
  'mcp.custom.envViewOnlyHint': 'Read-only view. To modify values, click "Edit plugin".',
  'mcp.custom.save': 'Save',
  // 0.8.6 — Edit existing Custom plugin
  'mcp.custom.saveEdit': 'Save changes',
  'mcp.custom.editSpec': 'Edit plugin',
  // 0.8.6 (#29) — banner for legacy Custom plugins with no endpoints.
  'mcp.custom.autodiscoveryTitle': 'No endpoints declared',
  'mcp.custom.autodiscoveryHint': 'Without endpoints, agents have to read the docs on every call. Ask the AI to propose a list from `docs_url`.',
  'mcp.custom.autodiscoveryCta': 'Ask the AI to help',
  'mcp.custom.updated': 'API "{0}" updated',
  'mcp.custom.specSavedEnvFailed': 'Spec saved, but credential values update failed: {0}',
  'mcp.custom.errorName': 'API name is required',
  'mcp.custom.errorBaseUrl': 'Base URL is required',
  'mcp.custom.created': 'API "{0}" added',
  'mcp.custom.error': 'Failed: {0}',
  // 0.8.6 (#33) — Custom plugin import / export (clipboard JSON).
  'mcp.custom.copyAsJson': 'Copy as JSON',
  'mcp.custom.copied': 'Plugin JSON copied to clipboard (no credentials included)',
  'mcp.custom.copyManual': 'Copy this JSON manually:',
  'mcp.custom.exportTitle': 'Export "{0}"',
  'mcp.custom.exportHint': 'Here is the plugin JSON (no credentials). It is being copied to the clipboard; otherwise use Ctrl+C / Cmd+C on the text below.',
  'mcp.custom.copyManualInstruction': 'Clipboard write refused. Select the text and copy it manually.',
  'mcp.custom.exportError': 'This plugin has no exportable spec',
  'mcp.custom.importTileTitle': 'Import from JSON',
  'mcp.custom.importTileDesc': 'Paste an export from another Kronn (spec only, never credentials).',
  'mcp.custom.importPasteLabel': 'Paste the plugin JSON',
  'mcp.custom.importPlaceholder': '{ "name": "...", "base_url": "https://...", "endpoints": [ ... ] }',
  'mcp.custom.importPasteFromClipboard': 'Paste from clipboard',
  'mcp.custom.importPasteUnavailable': 'Clipboard access denied — paste manually',
  'mcp.custom.importSubmit': 'Import plugin',
  'mcp.custom.importSecretsHint': 'Credentials are never imported. Fill them in via "Edit secrets" afterwards.',
  'mcp.custom.importErrorParse': 'Invalid JSON — check for missing commas or quotes',
  'mcp.custom.importErrorShape': 'JSON must be an object, not an array or primitive value',
  'mcp.custom.importErrorName': 'Missing or empty "name" field',
  'mcp.custom.importErrorBaseUrl': 'Missing or empty "base_url" field',
  'mcp.custom.imported': 'Plugin "{0}" imported — fill in its credentials',
  // ── 0.8.6 — Endpoints declared at create time ──
  'mcp.custom.endpoints.header': 'Endpoints',
  'mcp.custom.endpoints.emptyHint': 'No endpoints declared: your agents will read the docs every time. Fill `docs_url` above then ask the AI to fill this list ("Build with AI").',
  'mcp.custom.endpoints.populatedHint': '{0} endpoint(s) declared. Agents can call them directly via the `api_call` MCP tool (no credentials in the prompt).',
  'mcp.custom.endpoints.methodLabel': 'HTTP method',
  'mcp.custom.endpoints.descPlaceholder': "Short description (e.g. 'List widgets')",
  'mcp.custom.endpoints.add': 'Add an endpoint',
  'mcp.custom.endpoints.remove': 'Remove this endpoint',
  // 0.8.6 — Auth picker
  'mcp.custom.auth.header': 'Authentication',
  'mcp.custom.auth.hint': 'How Kronn injects credentials on every call. Pick the pattern the API uses. The AI helper can fill this from the docs.',
  'mcp.custom.auth.kind.none': 'None (public endpoints)',
  'mcp.custom.auth.kind.bearer': 'Bearer (Authorization: Bearer <token>)',
  'mcp.custom.auth.kind.tokenExchange': 'Token exchange (POST creds → access_token → Bearer)',
  'mcp.custom.auth.kind.other': 'Other (JSON edit required)',
  'mcp.custom.auth.kind.otherWarning': '⚠ This plugin uses an auth scheme not yet exposed in the picker (ApiKeyQuery / Header / Basic / OAuth2). Kept as-is — manual edit required to change it.',
  'mcp.custom.auth.bearer.envKey': 'Field containing the token',
  'mcp.custom.auth.bearer.envKeyPlaceholder': '— pick a field —',
  'mcp.custom.auth.tokenExchange.hint': 'The API requires a 2-step exchange: POST creds → response contains an access_token → used as Bearer on subsequent calls. Kronn caches the token and refreshes before expiry.',
  'mcp.custom.auth.tokenExchange.endpoint': 'Exchange endpoint (relative to base_url)',
  'mcp.custom.auth.tokenExchange.method': 'HTTP method',
  'mcp.custom.auth.tokenExchange.bodyFormat': 'Body format',
  'mcp.custom.auth.tokenExchange.bodyTemplate': 'Body template (JSON)',
  'mcp.custom.auth.tokenExchange.bodyTemplateHint': 'Use ${ENV.FIELD_NAME} to reference a stored credential. Didomi example: {"type": "api-key", "key": "${ENV.API_KEY}", "secret": "${ENV.API_SECRET}"}',
  'mcp.custom.auth.tokenExchange.tokenJsonpath': 'JSONPath of the token in the response',
  'mcp.custom.auth.tokenExchange.ttl': 'Token TTL (seconds — 3600 = 1h, 0 = no cache)',
  'mcp.custom.auth.tokenExchange.inject': 'How to inject the token in subsequent calls',
  // 0.8.6 — orphan env warning
  'mcp.envOrphanHint': "Values stored under old field names ({0}) became inaccessible after your rename. They'll be deleted on next Save. Re-paste your credentials under the new names.",
  // ── Custom API AI helper (chat bubble) ──
  'mcp.custom.helper.trigger': 'Build with AI',
  'mcp.custom.helper.triggerHint': 'Ask a local agent to pre-fill the form from a curl, a docs link, or a free-form description',
  'mcp.custom.helper.discTitle': 'Custom API config helper (ephemeral)',
  'mcp.custom.helper.bubbleTitle': 'API config helper',
  'mcp.custom.helper.ephemeral': 'ephemeral',
  'mcp.custom.helper.minimize': 'Minimize',
  'mcp.custom.helper.restore': 'Reopen helper',
  'mcp.custom.helper.close': 'Close (deletes the conversation)',
  'mcp.custom.helper.switchAgent': 'Switch agent (resets the conversation)',
  'mcp.custom.helper.thinking': 'Agent is thinking…',
  'mcp.custom.helper.inputPlaceholder': 'Paste a curl, a docs link, or describe your API…',
  'mcp.custom.helper.send': 'Send',
  'mcp.custom.helper.stop': 'Stop',
  'mcp.custom.helper.noAgents': 'No agents installed. Configure at least one in Settings → Agents.',
  'mcp.custom.helper.contextHint': 'What the agent already sees in your form',
  'mcp.custom.helper.welcome': 'Hi! How would you like to describe your API? Pick a shortcut or just type.',
  'mcp.custom.helper.starter.curl': 'Paste a curl',
  'mcp.custom.helper.starter.curlPrompt': "Here's a curl example that calls the API I need to configure. Extract the name, base URL, required fields (tokens, IDs, headers) and propose a Kronn config:\n\n```\n",
  'mcp.custom.helper.starter.docs': 'Docs link',
  'mcp.custom.helper.starter.docsPrompt': "Here's the API docs URL. Read it and propose a Kronn config (name, base_url, description, required fields):\n",
  'mcp.custom.helper.starter.describe': 'Describe freely',
  'mcp.custom.helper.starter.describePrompt': 'I want to configure an API for ',
  'mcp.custom.helper.suggestion': 'Proposal',
  'mcp.custom.helper.apply': 'Apply',
  'mcp.custom.helper.applied': '✓ Applied',
  'mcp.custom.helper.ctx.header': '=== CURRENT FORM STATE ===',
  'mcp.custom.helper.ctx.empty': '(empty)',
  'mcp.custom.helper.ctx.unnamed': 'Custom API',
  'mcp.custom.helper.ctx.noFields': '  (no fields declared)',
  'mcp.custom.helper.ctx.fieldsCount': '{0} field(s)',
  'mcp.custom.helper.ctx.noEndpoints': '  (no endpoints declared — fetch docs_url then emit them)',
  'mcp.custom.helper.sys.role': 'You are a Kronn assistant specialized in building custom REST API configs. You help the user fill a form describing a third-party API that Kronn will drive.',
  'mcp.custom.helper.sys.boundaries': 'The form has 4 main fields: name (short, e.g. "Salesforce Sales API"), base_url (root URL), description (free-form, what the API does + auth notes), docs_url (docs link, optional), and a list of fields ({label, value} pairs for tokens/IDs/headers the user will provide).',
  'mcp.custom.helper.sys.action': 'When you have enough info to propose a config (even partially), emit a KRONN:APPLY block in the format below. You can emit multiple blocks across the conversation.',
  'mcp.custom.helper.sys.format': "Exact format for proposals (the block MUST start with 'KRONN:APPLY' followed by a ```json fence):",
  'mcp.custom.helper.sys.partial': "You can emit a partial proposal (e.g. just name + base_url) if you have those but not the fields yet. NEVER guess real secret values; leave value=\"\" for fields, the user types their credentials themselves.",
  'mcp.custom.helper.sys.endpoints': "ENDPOINTS — when `docs_url` is set in the formSnapshot, use your WebFetch tool (if available) to fetch the docs, then emit an `endpoints` field in your KRONN:APPLY with the 5-15 most useful endpoints (each: `{path, method, description}`). Prefer read endpoints (GET) unless the user explicitly asked for a write operation. Short description (≤120 chars). Don't re-emit endpoints already visible in the formSnapshot. If you don't have WebFetch, tell the user and ask them to paste the important paths.",
  'mcp.custom.helper.sys.style': 'Stay concise. Ask one question at a time if you need to clarify. Always note what is missing to finalize the config.',
  'mcp.custom.helper.sys.starter': 'Hi! Describe the API you want to configure: paste a curl, a docs link, or explain in natural language.',
  'mcp.custom.helper.sys.userQuestion': '=== USER QUESTION ===',
  'mcp.setup': 'Setup',
  'mcp.getToken': 'Get a token',
  'mcp.value': 'value...',
  'mcp.globalAll': 'Global (all projects)',
  'mcp.addBtn': 'Add',
  'mcp.back': 'Back',
  'mcp.alreadyAdded': 'already added',
  'mcp.keys': 'key',
  'mcp.keysPlural': 'keys',
  'mcp.clickToRename': 'Click to rename',
  'mcp.editKeys': 'Edit keys',
  'mcp.close': 'Close',
  'mcp.show': 'Show',
  'mcp.hide': 'Hide',
  'mcp.saving': 'Saving...',
  'mcp.save': 'Save',
  'mcp.cancel': 'Cancel',
  'mcp.revealWarning': 'Current values could not be decrypted. Enter new values below.',
  'mcp.secretsBroken': 'This plugin\u2019s secrets are corrupted — click to re-enter values',
  'mcp.secretsBrokenShort': 'Secrets',
  'mcp.disableGlobal': 'Disable global — keep only checked projects',
  'mcp.enableGlobal': 'Enable on all projects',
  'mcp.general': 'General',
  'mcp.disableGeneral': 'Disable for discussions without a project',
  'mcp.enableGeneral': 'Enable for discussions without a project',
  'mcp.editContext': 'Edit context of {0} for {1}',
  'mcp.customized': '(customized)',
  'mcp.default': '(default)',
  'mcp.moreProjects': '+{0} projects',
  'mcp.lessProjects': 'Collapse',
  'mcp.deleteConfig': 'Delete this config',
  'mcp.deleteConfigConfirm': 'Delete config "{0}"? This is irreversible — keys, linked projects and custom contexts will be lost.',
  'mcp.deleteConfigSuccess': 'Config "{0}" deleted',
  'mcp.deleteConfigError': 'Delete failed: {0}',
  'mcp.empty': 'No plugin configured. Click "Add" to install one or "Sync" to scan your projects.',
  'mcp.contextTitle': 'Plugin context — {0}',
  'mcp.contextInfo': 'Project: {0} · File: docs/operations/mcp-servers/{1}.md',
  'mcp.contextPlaceholder': 'Instructions for agents using this plugin in this project...',
  'mcp.config': 'config',
  'mcp.configPlural': 'configs',
  'mcp.server': 'plugin',
  'mcp.serverPlural': 'plugins',
  'mcp.global': 'global',
  'mcp.globalPlural': 'global',
  'mcp.project': 'project',
  'mcp.projectPlural': 'projects',
  'mcp.cat.all': 'All',
  'mcp.cat.gitCode': 'Git & Code',
  'mcp.cat.databases': 'Databases',
  'mcp.cat.cloud': 'Cloud & Infra',
  'mcp.cat.search': 'Search & Web',
  'mcp.cat.monitoring': 'Monitoring',
  'mcp.cat.communication': 'Communication',
  'mcp.cat.projectMgmt': 'Project Management',
  'mcp.cat.design': 'Design',
  'mcp.cat.utilities': 'Utilities',
  'mcp.cat.other': 'Other',
  'mcp.setupRequired': 'Setup required',
  'mcp.official': 'Official',
  'mcp.community': 'Community',
  'mcp.kind.mcp': 'MCP',
  'mcp.kind.api': 'API',
  'mcp.kind.hybrid': 'MCP + API',
  'mcp.kind.all': 'All',
  'mcp.kind.mcpTooltip': 'MCP plugin — the agent uses dedicated tools exposed by the MCP server.',
  'mcp.kind.apiTooltip': 'API plugin — the agent calls a REST API via curl (no MCP tools).',
  'mcp.kind.hybridTooltip': 'Hybrid plugin — exposes both an MCP and a REST API. The agent picks the best tool.',
  'mcp.kind.allTooltip': 'All plugins (MCP + API + hybrid).',
  'mcp.by': 'by',
  'mcp.mcpLoad': 'MCP load',
  'mcp.mcpLoadOk': 'MCP load OK',
  'mcp.mcpLoadWarn': 'Many MCPs — possible slowdown',
  'mcp.mcpLoadDanger': 'Too many MCPs — agent timeout risk',
  'mcp.scope': 'Scope',

  // ── MCP setup help (i18n overrides for token_help) ──
  'mcp.help.mcp-fastly': "Requires the Fastly CLI and the fastly-mcp binary.\n\n1. Install the CLI: https://developer.fastly.com/reference/cli/\n2. Install the MCP server: https://github.com/fastly/mcp/releases\n3. Create an authentication profile:\n   fastly profile create my-profile\n   (the name is your choice — e.g. \"prod\", \"company\"…)\n4. Paste your API token when prompted.\n\nNo environment variable needed — auth is read from CLI profiles.",
  'mcp.help.mcp-gitlab': "Requires the GitLab CLI (glab).\n\n1. Install the CLI: https://gitlab.com/gitlab-org/cli/-/releases\n   brew install glab (macOS/Linux) or winget install glab (Windows)\n2. Create a Personal Access Token (api scope) on your GitLab\n3. Fill in the fields below:\n   GITLAB_TOKEN: your PAT\n   GITLAB_HOST: your GitLab hostname (e.g. gitlab.company.com)\n   Leave GITLAB_HOST empty for gitlab.com.\n\nKronn passes these values to glab automatically — no need to run glab auth login.",
  'mcp.help.mcp-microsoft-365': "Connects Outlook (mail, calendar), Teams, OneDrive and OneNote via Microsoft Graph.\nCommunity server: @softeria/ms-365-mcp-server (--org-mode for work accounts).\nNo official Microsoft MCP server available yet.\n\nOption 1 — Default Softeria app (quick):\nLeave fields empty. On first launch, the agent shows a code and asks you to visit https://microsoft.com/devicelogin. Your Microsoft admin may block third-party apps.\n\nOption 2 — Your own Azure app (recommended for organizations):\n1. portal.azure.com → Entra ID → App registrations → New registration\n2. Name: \"Kronn MCP\", type: Accounts in this org only\n3. Copy Application (client) ID → MS365_MCP_CLIENT_ID\n4. Copy Directory (tenant) ID → MS365_MCP_TENANT_ID\n5. Authentication → Check \"Allow public client flows\" (required!)\n6. API permissions → Microsoft Graph → Delegated permissions:\n   Mail.Read, Mail.Send, Calendars.ReadWrite, Chat.Read, Files.ReadWrite, User.Read\n7. Grant admin consent if required\n\nAuthentication:\nOn first launch, the server displays a code and a URL.\nOpen https://microsoft.com/devicelogin in a browser, enter the code, sign in.\nTokens are cached locally — no need to re-authenticate each time.\n\nIf you get \"Failed to reconnect\":\nnpx @softeria/ms-365-mcp-server --org-mode --logout\nnpx @softeria/ms-365-mcp-server --org-mode --login\n\nWorks with all Kronn agents (cross-agent).",

  // ── Workflows ──
  'wf.title': 'Automation',
  'wf.subtitle': 'Workflows, quick prompts',
  'wf.new': 'New workflow',
  'wf.newHint': 'Build a workflow manually, step by step. You control every detail.',
  'wf.tabWorkflows': 'Workflows',
  'wf.tabQuickPrompts': 'Quick Prompts',
  'wf.tabQuickApis': 'Quick APIs',
  'qa.new': 'New Quick API',
  'qa.empty': 'No Quick APIs',
  'qa.emptyHint': 'Build reusable API calls (POST Jira issue, get GitHub PR…) with variables {{title}}, {{key}}…',
  'qa.name': 'Name',
  'qa.namePlaceholder': 'Create Jira ticket',
  'qa.descriptionLabel': 'Description (recommended)',
  'qa.descriptionPlaceholder': 'Creates a ticket in project EW with a title + description provided as variables.',
  'qa.variables': 'Detected variables',
  'qa.varLabel': 'Display label',
  'qa.varPlaceholder': 'Placeholder',
  'qa.varRequired': 'Required',
  'qa.varDescriptionPlaceholder': 'Description (optional)',
  'qa.varsHint': 'Type `{{name}}` in any field above (endpoint, body, headers, query) — variables will appear here.',
  'qa.save': 'Save',
  'qa.edit': 'Edit',
  'qa.delete': 'Delete',
  'qa.deleteConfirm': 'Delete the Quick API "{name}"?',
  'qa.export': 'Export (JSON)',
  'qa.exportDone': '"{name}" exported ✓',
  'qa.vars': '{0} variable(s)',
  'qa.noPluginsWarning': 'No API plugin is configured on this host. Create one first (Atlassian, GitHub, SpeedCurve, …) in the MCPs / Plugins tab, then come back.',
  'qa.noPluginsCta': 'Configure a plugin →',
  'qa.saveBlockedHint': 'Required fields missing',
  'qa.fieldName': 'name',
  'qa.fieldPlugin': 'API plugin',
  'qa.fieldConfig': 'plugin config',
  'qa.fieldEndpoint': 'endpoint',
  'qa.launch': 'Launch (direct run)',
  'qa.runTitle': 'Launch "{name}"',
  'qa.runNoVars': 'This Quick API has no variables to fill.',
  'qa.runGo': 'Run',
  'qa.runFailed': 'Run failed',
  'qa.batch.launch': 'Batch (fan-out N parallel calls)',
  'qa.batch.cancel': 'Cancel',
  'qa.batch.go': 'Launch batch',
  'qa.batch.inputLabel': 'One `{0}` value per line (or comma/semicolon-separated)',
  'qa.batch.inputPlaceholder': 'www.euronews.com\nde.euronews.com\nfr.euronews.com',
  'qa.batch.jsonInputLabel': 'JSON array of objects — one per call',
  'qa.batch.jsonInputHint': 'Expected variables: {0}. Example: `[{"host":"de.example.com","limit":"5"}, ...]`',
  'qa.batch.jsonInputPlaceholder': '[\n  {"host":"www.example.com","limit":"5"},\n  {"host":"de.example.com","limit":"5"}\n]',
  'qa.batch.countHint': '{0} call(s) will fire',
  'qa.batch.jsonCountHint': '{0} call(s) parsed from JSON',
  'qa.batch.resultSummary': '{0}/{1} OK · {2} failure(s) ·',
  'qa.batch.colInput': 'Input',
  'qa.batch.colStatus': 'Status',
  'qa.batch.colResult': 'Result',
  'qa.batch.expandRow': 'show all',
  'qa.batch.collapseRow': 'collapse',
  'qa.batch.errorEmpty': 'No items to launch.',
  'qa.batch.errorJsonArray': 'JSON must be an array (`[...]`).',
  'qa.batch.errorJsonParse': 'Invalid JSON: {0}',
  'qp.new': 'New prompt',
  'qp.empty': 'No quick prompts',
  'qp.emptyHint': 'Create reusable prompts with variables {{ticket}}, {{url}}...',
  'qp.name': 'Name',
  'qp.namePlaceholder': 'Analyze Jira ticket',
  'qp.prompt': 'Prompt',
  'qp.promptPlaceholder': 'Analyze ticket {{ticket}} on Jira and identify...',
  'qp.addVariable': '+ Variable',
  'qp.varName': 'Name',
  'qp.varLabel': 'Display label',
  'qp.varPlaceholder': 'Placeholder',
  'qp.save': 'Save',
  'qp.saveBlockedHint': 'Required fields missing',
  'qp.bindingsLabel': 'Bindings (skills · profiles · directives)',
  'qp.improveWithAi': 'Improve this QP with AI',
  'qp.improveInitialPrompt': 'Audit and improve the Quick Prompt "{0}" below. Follow the protocol: audit table, recommendations, then the full refactored QP as a JSON block followed by the KRONN:QP_IMPROVED signal.',
  'qp.improveVisibleIntro': '✨ Auditing and improving the Quick Prompt "{0}"…',
  'qp.history.openLabel': '🕒 {0} version(s)',
  'qp.history.openTitle': 'Version history for "{0}"',
  'qp.history.drawerTitle': 'History — {0}',
  'qp.history.empty': 'No archived versions yet. Versions are snapshotted on every QP edit.',
  'qp.history.current': 'current',
  'qp.history.launchesTooltip': 'Number of discussions launched with this QP version.',
  'qp.history.avgTokensTooltip': 'Avg tokens of the first agent reply (before any follow-up turns).',
  'qp.history.avgDurationTooltip': 'Avg wall-clock duration of the first agent reply.',
  'qp.history.avgCostTooltip': 'Avg USD cost of the first agent reply.',
  'qp.history.showDiff': 'Show diff v{0} → v{1}',
  'qp.history.hideDiff': 'Hide diff',
  'qp.history.diffPrev': 'v{0} (previous)',
  'qp.history.diffNext': 'v{0} (this version)',
  'qp.history.btnTitle': 'View history and stats',
  'qp.history.deleteAria': 'Delete version {0}',
  'qp.history.deleteConfirm': 'Delete version v{0}? Launches tied to this version will no longer be attributed to the QP (the discussions themselves stay intact).',
  'qp.metrics.chipTooltip': 'Current version (v{0}) stats: avg first-agent-reply over {1} launch(es).',
  'qp.launch.missingRequired': 'Required fields missing: {0}',
  'qp.improvedReady': '✨ Improved QP ready — one click to deploy.',
  'qp.deployImproved': 'Deploy improved QP',
  'qp.deployInProgress': 'Deploying…',
  'qp.deployFailed': 'Deploy failed: {0}',
  'qp.deploySuccess': 'QP deployed as v{0}',
  'qp.deployedAtVersion': '✅ QP deployed as v{0}',
  'qp.improveCatalogHeader': 'Available catalog (use it in the refactor)',
  'qp.improveCatalogIntro': 'Below is the list of skills, profiles, and directives installed on this Kronn instance. Add them to the refactored QP\'s `skill_ids` / `profile_ids` / `directive_ids` when relevant — always preserve existing bindings unless they\'re clearly off-topic.',
  'qp.improveCatalogSkills': 'Skills',
  'qp.improveCatalogProfiles': 'Profiles',
  'qp.improveCatalogDirectives': 'Directives',
  'qp.improveCatalogEmpty': '_None available in this category._',
  'qp.fieldName': 'name',
  'qp.fieldPrompt': 'prompt',
  'qp.launch': 'Launch',
  'qp.agentBadgeTooltip': 'QP bound to the {0} agent',
  'qp.compareAgents.button': 'Compare across {0} installed agents',
  'qp.compareAgents.tooltip': 'Run this QP in parallel across all {0} installed agents — one disc per agent, grouped in a batch.',
  'qp.compareAgents.cta': 'Compare ({0})',
  'qp.compareAgents.selectorLabel': 'Compare on:',
  'qp.compareAgents.selectAll': 'All',
  'qp.compareAgents.selectNone': 'None',
  'qp.vars': '{0} variable(s)',
  'qp.delete': 'Delete',
  'qp.deleteConfirm': 'Delete this quick prompt?',
  'qp.syntaxHint': 'Variables: {{name}}. Conditional sections: {{#name}}text if filled{{/name}}',
  'qp.descriptionLabel': 'Description (optional — helps batch workflows understand what this prompt does)',
  'qp.descriptionPlaceholder': 'Ex: Technical analysis of a Jira ticket for scoping (tech view, effort, open questions)',
  'qp.variables': 'Variables',
  'qp.varRequired': 'Required',
  'qp.varDescriptionPlaceholder': 'What is this variable for? (ex: Jira ticket ID, e.g. EW-7182)',
  'qp.batch.launch': 'Run as batch',
  'qp.batch.worktree': 'Isolate each discussion in a git worktree',
  'qp.batch.worktreeHint': 'Each child discussion runs in its own git worktree on a dedicated branch. Required when the agents write code in parallel.',
  'qp.batch.worktreeNoProject': 'This Quick Prompt has no project (git repo) attached. Link it to a project to enable this option.',
  'qp.batch.cancel': 'Cancel',
  'qp.batch.inputLabel': 'List of values for "{0}" (one per line OR comma/semicolon-separated)',
  'qp.batch.inputPlaceholder': 'EW-7100, EW-7101, EW-7102\nOR one value per line',
  'qp.batch.countHint': '{0} discussion(s) will be created in parallel (max 5 at a time)',
  'qp.batch.confirm': "Launch {0} discussions in parallel with \"{1}\"?\n\nEach discussion consumes tokens. This may cost money.",
  'qp.batch.emptyInput': 'Paste at least one value (one per line).',
  'qp.batch.tooManyItems': 'Too many lines ({0}). Maximum 50 per batch.',
  'qp.batch.needsVariable': "This Quick Prompt has no variables — can't batch. Add a {{key}} variable to the template.",
  'qp.batch.failed': 'Batch launch failed: {0}',
  'qp.batch.toast.ok': '{0} — done ({1} OK)',
  'qp.batch.toast.partial': '{0} — {1} OK, {2} errors',
  'bootstrap.repoReady': 'Git repository created — ready for validation',
  'bootstrap.repoValidated': 'Repository validated. Now analyze the project architecture from the provided documents.',
  'bootstrap.analyzeArchitecture': 'Validate → Analyze architecture',
  'bootstrap.architectureReady': 'Architecture analyzed — ready for validation',
  'bootstrap.architectureValidated': 'Architecture validated. Generate the project plan with epics, stories and estimates.',
  'bootstrap.generatePlan': 'Validate → Generate plan',
  'bootstrap.planReady': 'Project plan generated — ready for validation',
  'bootstrap.planValidated': 'Plan validated. Create issues on the project tracker.',
  'bootstrap.createIssues': 'Validate → Create issues',
  'bootstrap.issuesCreated': 'Issues created on the tracker',
  'bootstrap.viewProject': 'View project',
  'bootstrap.startDev': 'Start dev on issue #1',
  'bootstrap.devKickoffTitle': 'Dev kickoff — issue #1',
  'bootstrap.devKickoffPrompt': 'Open issue #1 in the project tracker and start working on it. Read the description, ask any clarifying questions if needed, then implement the solution step by step. Use the tracker MCP to update the issue status as you progress, and commit logically scoped changes as you go.',
  'wf.createWithAI': 'Create with AI',
  'wf.createWithAIHint': 'Describe your need in plain language, the AI generates the full workflow. Faster for complex workflows, but you should review the result.',
  'wf.aiArchitectPrompt': 'I want to create a new automated workflow. Help me design it step by step.',
  'wf.aiWorkflowReady': 'AI-designed workflow — ready to deploy',
  // 0.8.3 — bundle CTA. {0}=QPs, {1}=QAs, {2}=Custom APIs.
  'wf.aiBundleReady': 'AI-designed bundle — ready to deploy ({0} Quick Prompts · {1} Quick APIs · {2} Custom APIs)',
  // 0.8.3 — bundle button. {0}=total extras (QPs+QAs+CustomAPIs).
  'wf.createBundleBtn': 'Create everything (1 workflow + {0} artifacts)',
  'wf.createThisWorkflow': 'Create this workflow',
  'wf.workflowCreated': 'Workflow created successfully',
  'wf.workflowCreateError': 'Error creating workflow',
  'wf.empty': 'No workflow configured',
  'wf.emptyHint': 'Create a workflow to automate your tasks',
  'wf.active': 'Active',
  'wf.inactive': 'Inactive',
  'wf.trigger': 'Trigger',
  'wf.delete': 'Delete',
  'wf.selectOne': 'Select a workflow',
  'wf.unknown': 'Unknown',
  'wf.edit': 'Edit',
  'wf.back': 'Back',
  'wf.refresh': 'Refresh',
  'wf.launch': 'Trigger',
  'wf.running': 'Running',
  'wf.runDone': 'Run finished — {0}',
  'wf.runWaiting': 'Awaiting your decision',
  'wf.stepsExecuted': '{0} steps executed',
  'wf.deleteAll': 'Delete all',
  'wf.noRuns': 'No runs',
  'wf.skipNext': 'Skip next step',
  'wf.manual': 'Manual',
  'wf.batchStepQpMissing': 'Quick Prompt not found (id: {0}). Was it deleted?',
  'wf.batchStepItemsLabel': 'Items from:',
  'wf.batchStepWaits': '⏳ Waits for all discussions to finish',
  'wf.batchStepFireForget': '⚡ Fire-and-forget (do not wait)',
  'wf.batchStepMaxItems': 'Max {0} items',
  'wf.batchStepWorktree': 'Isolated worktree per discussion',
  'wf.batchStepChainLabel': 'Chain:',
  'wf.runBatchChip': '📋 View the {0} conversations',
  'wf.runBatchChipHint': 'Jump to the Discussions sidebar and open the batch this run created',
  'wf.cancelRun': 'Stop',
  'wf.cancelRunConfirm': 'Stop this run and all its child discussions in progress?',
  'wf.cancelRunError': 'Cancel failed. Check the console for details.',
  'wf.activeRunsTitle': 'Active runs',
  'wf.activeRunsEmpty': 'No active runs',
  'wf.cancelling': 'Stopping…',
  'wf.viewAllWorkflows': 'View all workflows',
  'wf.elapsed': '{0} ago',
  // ─── ApiCall step (désagentification — 0.5.2) ──────────────────────
  'wf.apicall.title': 'Fetch data',
  'wf.apicall.subtitle': 'Direct API call — zero tokens consumed',
  'wf.apicall.pluginPicker': 'API plugin',
  'wf.apicall.pluginPickerHint': 'Pick an API configured on this project',
  'wf.apicall.qaPicker': 'From an existing Quick API',
  'wf.apicall.qaPickerInline': '— None (inline config below) —',
  'wf.apicall.qaInheritedFrom': 'Inherited from « {0} »',
  'wf.apicall.qaInheritedHint': 'The call inherits the Quick API\'s config. Click ✏️ below to override one or more fields for this step only.',
  'wf.apicall.qaDetach': 'Detach from Quick API (inline mode)',
  'wf.apicall.qaOverrideToggle': 'Customize this call for this step (override)',
  'wf.apicall.qaSummaryEndpoint': 'Call:',
  'wf.apicall.qaSummaryExtract': 'Extract:',
  'wf.apicall.endpointPicker': 'Endpoint',
  'wf.apicall.endpointPlaceholder': 'e.g. /repos/owner/repo/issues — click to pick from list',
  'wf.apicall.pathParamsTitle': 'Path parameters',
  'wf.apicall.pathParamsHint': 'Auto-detected from the endpoint. Fill each token — Kronn substitutes them in the URL at request time.',
  'wf.apicall.pathParamsPlaceholder': 'Value for {0}',
  'wf.apicall.pathParamsPreview': 'Resolved URL',
  'wf.apicall.methodLabel': 'Method',
  'wf.apicall.queryParams': 'Query parameters',
  'wf.apicall.headers': 'Headers',
  'wf.apicall.body': 'JSON body',
  'wf.apicall.testBtn': 'Test the call',
  'wf.apicall.testing': 'Testing…',
  'wf.apicall.testHint': 'Issues a real request with the configured credentials',
  'wf.apicall.testVarsTitle': 'Variables to provide for the test',
  'wf.apicall.testVarsHint': 'Your config contains unresolved `{{var}}` placeholders. Give them a test value for this request only (the saved config is not modified).',
  'wf.apicall.testVarsPlaceholder': 'test value',
  'wf.apicall.testVarsGo': 'Test with these values',
  'wf.apicall.responseTitle': 'JSON response',
  'wf.apicall.responseEmpty': 'Run a test to see the response here',
  'wf.apicall.clickToPickHint': '💡 Click a key inside an array → all items. A value → that specific one. [N] → iterate.',
  'wf.apicall.suggest.title': 'Suggestions for this response',
  'wf.apicall.suggest.iterate': 'Iterate over the {0} items',
  'wf.apicall.suggest.allField': 'All "{0}" ({1} values)',
  'wf.apicall.suggest.firstItem': 'The 1st item',
  'wf.apicall.suggest.counter': 'Counter "{0}" = {1}',
  'wf.apicall.extractSection': 'What comes out of here',
  'wf.apicall.extractSectionHint': 'The next step will receive the extracted value via {{steps.name.data}}',
  'wf.apicall.extractData': 'Data',
  'wf.apicall.extractStatus': 'Status',
  'wf.apicall.extractSummary': 'Summary',
  'wf.apicall.pathLabel': 'Path (JSONPath)',
  'wf.apicall.pathPlaceholder': 'e.g. $.issues[*].key',
  'wf.apicall.preview': 'Preview',
  'wf.apicall.previewEmpty': 'No result for this path',
  'wf.apicall.previewType': 'Type',
  'wf.apicall.errorInvalidPath': 'Invalid JSONPath',
  'wf.apicall.examples': 'Examples',
  'wf.apicall.advancedToggle': 'Advanced options (body, method, timeout, retries, output var)',
  'wf.apicall.exampleAllIds': 'All IDs',
  'wf.apicall.exampleFirst': 'First element',
  'wf.apicall.exampleCount': 'Total count',
  'wf.apicall.failOnEmpty': 'Mark as NO_RESULTS when empty',
  'wf.apicall.failOnEmptyHint': 'Triggers Skip/Stop/Goto conditions of the next step',
  'wf.apicall.fallbackLabel': 'Fallback value (when empty)',
  'wf.apicall.nextStepBatchOk': 'The next step expects an array — Data is an array ✓',
  'wf.apicall.nextStepBatchMismatch': 'The next step (batch) expects an array, but Data is {0}',
  'wf.apicall.nextStepBatchWrap': 'Wrap in an array',
  'wf.apicall.tokensSaved': '0 tokens consumed by this step',
  'wf.apicall.timeoutLabel': 'Timeout (ms)',
  'wf.apicall.retriesLabel': 'Retries (5xx / 429)',
  'wf.apicall.outputVarLabel': 'Variable name',
  'wf.apicall.outputVarHint': 'Available in next steps via {{steps.<name>.data}}',
  'wf.apicall.testSuccess': 'Call succeeded in {0} ms',
  'wf.apicall.testFailed': 'Failed',
  'wf.apicall.notSupported': 'No API plugin configured on this project',
  'wf.apicall.authManagedTitle': 'Auth — handled by Kronn',
  'wf.apicall.authManagedHint': 'These fields are injected at runtime from the credentials you entered in Settings → APIs. Nothing to repeat here.',
  'wf.apicall.authManagedReveal': 'Reveal the env variable name',
  'wf.apicall.testNeedsProjectLink': 'This plugin config isn\'t linked to any project yet — go to Settings → APIs and tick at least one project to enable testing.',
  // ── AI helper bubble (0.5.2) ──
  'wf.apicall.helper.trigger': 'AI config helper',
  'wf.apicall.helper.triggerHint': 'Ask a local agent to help pick the endpoint and extract path',
  'wf.apicall.helper.triggerDisabled': 'Pick an API above first — without a selected plugin the agent has no spec to read',
  'wf.apicall.helper.pickAgent': 'Which agent helps?',
  'wf.apicall.helper.switchAgent': 'Switch agent (resets the conversation)',
  'wf.apicall.helper.welcome': 'Hi! What would you like to do? Pick a starter or type your question.',
  'wf.apicall.helper.starter.build': 'Help me build the call',
  'wf.apicall.helper.starter.buildPrompt': 'Help me build this API call. I want to ',
  'wf.apicall.helper.starter.endpoint': 'Suggest an endpoint',
  'wf.apicall.helper.starter.endpointPrompt': 'Suggest an endpoint for ',
  'wf.apicall.helper.starter.body': 'Help with the body',
  'wf.apicall.helper.starter.bodyPrompt': 'Help me build the JSON body for ',
  'wf.apicall.helper.cancel': 'Cancel',
  'wf.apicall.helper.discTitle': 'Config helper (ephemeral)',
  'wf.apicall.helper.bubbleTitle': 'Config helper',
  'wf.apicall.helper.ephemeral': 'ephemeral',
  'wf.apicall.helper.minimize': 'Minimize',
  'wf.apicall.helper.restore': 'Reopen helper',
  'wf.apicall.helper.close': 'Close (deletes the conversation)',
  'wf.apicall.helper.thinking': 'Agent thinking…',
  'wf.apicall.helper.inputPlaceholder': 'Describe what you want to fetch…',
  'wf.apicall.helper.send': 'Send',
  'wf.apicall.helper.stop': 'Stop',
  'wf.apicall.helper.suggestion': 'Suggestion',
  'wf.apicall.helper.apply': 'Apply',
  'wf.apicall.helper.applied': 'Applied ✓',
  'wf.apicall.helper.noAgents': 'No agent installed on this host.',
  'wf.apicall.helper.contextHint': 'The context (API, endpoint, query, last test) is attached to every message sent to the agent.',
  'wf.apicall.helper.contextNoApi': 'No API selected',
  'wf.apicall.helper.contextLastErr': 'last test: ❌',
  'wf.apicall.helper.contextLastOk': 'last test: ✓',
  // System prompt strings — drive the agent's reply language. Changing the UI locale
  // changes which language the agent answers in for the API helper conversation.
  'wf.apicall.helper.sys.unknown': '(unknown)',
  'wf.apicall.helper.sys.noPlugin': '(no plugin selected)',
  'wf.apicall.helper.sys.none': '(none)',
  'wf.apicall.helper.sys.default': '(default)',
  'wf.apicall.helper.sys.empty': '(empty)',
  'wf.apicall.helper.sys.ctxHeader': '### CURRENT CONTEXT (snapshot at send time)',
  'wf.apicall.helper.sys.ctxLastFail': '### LAST TEST → FAILURE',
  'wf.apicall.helper.sys.ctxLastOk': '### LAST TEST → SUCCESS (response excerpt)',
  'wf.apicall.helper.sys.userQuestion': '### USER QUESTION',
  'wf.apicall.helper.sys.role': `# Role
You are a configuration assistant for a "Fetch data" step (\`StepType::ApiCall\`)
in a Kronn workflow. This step performs a direct HTTP call (no LLM tokens consumed)
and extracts a value via JSONPath, which pipes into the next step.`,
  'wf.apicall.helper.sys.boundaries': `# What you can and CANNOT do
- ✅ Read the API spec below, the current step state, the last test result.
- ✅ Suggest config changes (endpoint, method, query, headers, body, extract).
- ❌ You have **NO** tool to call the API yourself. No Bash, no MCP, no fetch.
     Kronn (Rust backend) makes the call when the user clicks "Test the call".
- ❌ NEVER suggest "checking online" via an MCP — it is not available in this
     conversation. If a failure cause eludes you, point to the official docs or
     the provider's dashboard.`,
  'wf.apicall.helper.sys.action': `# Your only useful action: propose a \`KRONN:APPLY\`
When you suggest a config, write EXACTLY this format (otherwise the UI cannot read it):

KRONN:APPLY
\`\`\`json
{ "endpoint": "/path/v4", "query": { "k": "v" }, "extract": "$.data[*]" }
\`\`\`

Allowed fields: \`endpoint\`, \`method\`, \`query\`, \`headers\`, \`body\`, \`extract\`.
You may set just ONE field — the rest of the step is preserved.
No more than one \`KRONN:APPLY\` block per message (otherwise the user cannot pick).`,
  'wf.apicall.helper.sys.debug': `# Debug method (apply as soon as a test fails)
1. Read the \`### LAST TEST → FAILURE\` block — it contains \`HTTP <status> on <method> <url> — <body excerpt>\`.
2. Note the **status** (4xx vs 5xx → 4xx = your config is at fault, 5xx = server-side).
3. Note the **query params** in the composed URL (the apikey is masked — that is normal and EXPECTED).
4. Cross-reference the plugin TIPS below + the allowed endpoints.
5. Propose **ONE** targeted change (a single KRONN:APPLY). No salvo of 3 simultaneous hypotheses.
6. If the failure persists after 2 attempts, advise the user to check the provider's dashboard
   (docs URL below) — do not loop.`,
  'wf.apicall.helper.sys.endpointsHeader': '# ALLOWED endpoints (never invent outside this list)',
  'wf.apicall.helper.sys.endpointsEmpty': '(none — the user has not picked an API yet)',
  'wf.apicall.helper.sys.tipsHeader': '# PLUGIN TIPS — {0}',
  'wf.apicall.helper.sys.docsHeader': '# Official docs',
  'wf.apicall.helper.sys.style': `# Style
- English, ≤ 3 lines per message.
- No filler, no "let me analyze", no "here's what I suggest:" — straight to the point.
- The apikey/token shown as \`••••••••\` or \`***\` in logs are MASKED ONLY FOR DISPLAY.
  The real value the user configured IS sent by Kronn. If asked "are you sure the key
  is being sent?" — answer YES without reservation, and redirect the diagnosis elsewhere
  (host, endpoint, token scope).`,
  'wf.apicall.helper.sys.starter': 'To start, ask the user what they want to fetch.',
  'wf.apicall.helper.sys.authHeader': `### AUTH — handled automatically by Kronn (DO NOT suggest)
The backend injects these fields at request time, using the key the user
entered in Settings → APIs. ⚠️ NEVER put them back in a KRONN:APPLY
suggestion; that would overwrite the real value with a placeholder
("YOUR_API_KEY", etc.) and break auth.`,
  'wf.apicall.helper.sys.authQueryItem': '- query param `{0}` (value pulled from env `{1}`, already configured in Kronn)',
  'wf.apicall.helper.sys.authHeaderItem': '- header `{0}` (value pulled from env `{1}`, already configured in Kronn)',
  'wiz.testBatchStep': 'Test (dry-run)',
  'wiz.testBatchInfo': 'Preview what this batch step would do without creating anything: items list, prompt rendered for the 1st item, QP/agent, workspace mode.',
  'wiz.testBatchAlwaysDryRun': 'Always dry-run — no discussion will be created.',
  'wiz.testBatchOk': 'Configuration valid',
  'wiz.testBatchErrors': '{0} blocking error(s):',
  'wiz.testBatchItemsCount': 'item(s) would be launched',
  'wiz.testBatchCappedAt': 'preview capped at the first {0}',
  'wiz.testBatchFillsVar': 'fills {{{0}}}',
  'wiz.testBatchWorkspace': 'Workspace',
  'wiz.testBatchWaits': 'waits for completion',
  'wiz.testBatchFireForget': 'fire-and-forget',
  'wiz.testBatchItemsSample': 'Extracted items:',
  'wiz.testBatchSamplePromptFor': 'Rendered prompt for the 1st item ({0}):',
  'wiz.testMockPrefilled': 'auto-filled from « {0} » test result',
  'wiz.testMockPrefilledExtract': 'extracted from « {0} » test (last line)',
  'wiz.testMockPrefilledFull': 'full result from « {0} »',
  'wiz.testMockShowFull': '↻ Show full output',
  'wiz.testMockShowExtract': '↻ Keep only the last line',
  'wiz.testMockUsePrev': 'Use result from « {0} »',
  'wiz.testBatchWarnings': '{0} warning(s):',
  'wiz.testBatchItemToggle': 'Show the rendered prompt for this item',
  'wiz.testBatchItemPrompt': 'Prompt sent to the agent:',
  'wiz.testBatchItemDryRunBtn': 'Run dry-run',
  'wiz.testBatchItemDryRunLoading': 'Dry-run running…',
  'wiz.testBatchItemDryRunHint': 'Runs the agent in read-only mode on this item — describes what it would do without changing anything',
  'wiz.testBatchItemDryRunResult': 'What the agent would do:',

  // ── Structured agent questions (0.3.5) ──
  'agentQuestions.hint': 'The agent is asking you questions. Fill each field and click Reply — faster than typing everything in the chat.',
  'agentQuestions.submit': 'Reply',
  'agentQuestions.fillAll': 'Fill every field to send',

  'wf.inProgress': 'in progress...',
  'wf.pending': 'pending',
  'wf.live.stepNoOutput': '(this step produced no content)',
  'wf.live.stepStreamingWaiting': 'Agent starting up… (no chunks received yet)',
  'wf.live.totalElapsedHint': 'Total time elapsed since the run was triggered.',
  'wf.liveStep.agent': 'Agent thinking',
  'wf.liveStep.api': 'HTTP call in flight',
  'wf.liveStep.notify': 'Sending webhook',
  'wf.liveStep.batch': 'Fanning out',
  'wf.liveStep.gate': 'Awaiting human decision',
  'wf.liveStep.exec': 'Shell command running',
  'wf.runStatusToReview': 'TO REVIEW',
  'wf.pausedJustNow': 'just now',
  'wf.pausedMinutes': 'paused for {0} min',
  'wf.pausedHours': 'paused for {0}h',
  'wf.pausedHoursMinutes': 'paused for {0}h{1}',
  'wf.pausedDays': 'paused for {0}d',
  'wf.pausedZeroTokens': '0 tokens consumed during the pause',
  'wf.pausedZeroTokensHint': 'No LLM calls fire during a Gate pause — the meter stops until the operator decides.',
  'wf.tokensTotal': 'tokens (run total)',
  'wf.tokensTotalHint': 'Sum of tokens used by every step in this run since it started. Includes the agent steps; gates always contribute 0.',
  'wf.stepTokensSuffix': 'tk',
  'wf.stepTokensHint': 'Tokens consumed by this step. Gates / Exec / ApiCall / Notify burn nothing — they stay at 0. Agent / BatchQuickPrompt steps scale with prompt + response length.',
  'wf.gate.title': 'Human decision required',
  'wf.gate.defaultMessage': 'Approval required to continue.',
  'wf.gate.commentPlaceholder': 'Comment (required for "Request changes")…',
  'wf.gate.commentRequired': 'A comment is required when requesting changes.',
  'wf.gate.approve': 'Approve',
  'wf.gate.requestChanges': 'Request changes',
  'wf.gate.reject': 'Reject',
  'wf.gate.triage.clear': 'Clear',
  'wf.gate.triage.decided': 'Decided (review)',
  'wf.gate.triage.mocked': 'Mocked (replace later)',
  'wf.gate.triage.blocked': 'Blocked (waiting)',
  'wf.gate.triage.chosen': 'Chosen:',
  'wf.gate.triage.why': 'Why:',
  'wf.gate.triage.placeholder': 'Placeholder:',
  'wf.gate.triage.strategy': 'Strategy:',
  'wf.gate.triage.revisitWhen': 'Revisit when:',
  'wf.gate.triage.neededFrom': 'Needed from:',
  'wf.gate.triage.workaround': 'Workaround:',
  'wf.gate.triage.optionsConsidered': '{0} rejected alternative(s)',
  'wf.gate.triage.filesTouched': '{0} file(s) planned',
  'wf.deleteRun': 'Delete this run',
  'wf.deleteWorkflowConfirm': 'Delete this workflow? This is irreversible — all runs and their child discussions will be lost.',
  'wf.deleteRunConfirm': 'Delete this run? Its worktree and artifacts will be removed.',
  'wf.deleteAllRunsConfirm': 'Delete ALL runs of this workflow? Irreversible — {0} run(s) + worktrees will be purged.',
  'wf.runWorktreePath': 'Worktree:',
  'wf.runWorktreeCopy': 'Copy path',
  'wf.produced.title': '{0} preserved branch(es)',
  'wf.produced.aheadCount': '{0} commit(s) ahead',
  'wf.produced.pushed': 'pushed',
  'wf.produced.localOnly': 'local only',
  'wf.produced.copyCheckout': 'Copy git checkout',
  'wf.produced.copyDiff': 'Copy git diff',
  'wf.produced.copyCd': 'Copy cd',
  'wf.produced.test': 'Test in worktree',
  'wf.produced.testHint': 'Spawn an isolated worktree on this commit so you can test without touching main',
  'wf.produced.creating': 'Creating…',
  'wf.produced.cleanup': 'Cleanup',
  'wf.export': 'Export',
  'wf.exportHint': 'Download this workflow as JSON (with its referenced Quick Prompts). Shareable across Kronn instances.',
  'wf.exportDone': 'Workflow "{name}" exported',
  'wf.import': 'Import',
  'wf.importHint': 'Import a workflow exported from Kronn (.json). Drag-and-drop or file picker.',
  'qp.export': 'Export this Quick Prompt',
  'qp.exportDone': 'Quick Prompt "{name}" exported',
  'qp.import': 'Import',
  'qp.importHint': 'Import a Quick Prompt exported from Kronn (.json).',
  'imp.workflowTitle': 'Import a workflow',
  'imp.qpTitle': 'Import a Quick Prompt',
  'imp.dropOrPick': 'Drop a file here, or pick one',
  'imp.fileHint': 'Expected: .kronn-workflow.json or .kronn-qp.json',
  'imp.pickFile': 'Pick a file',
  'imp.cancel': 'Cancel',
  'imp.confirm': 'Import',
  'imp.pickAnother': '← Pick another file',
  'imp.previewName': 'Name',
  'imp.previewSteps': 'Steps',
  'imp.previewVars': 'Variables',
  'imp.previewBundledQps': 'Bundled Quick Prompts',
  'imp.targetProject': 'Attach to project (optional)',
  'imp.noProject': 'None (unattached workflow)',
  'imp.errorNotJson': 'This file is not a .json — check the format.',
  'imp.errorReadFailed': 'Failed to read the file.',
  'imp.errorJsonParse': 'Invalid JSON — the file is corrupt or incomplete.',
  'imp.errorWrongKind': 'Wrong export type: expected `{expected}`, got `{got}`. Did you swap a workflow and a Quick Prompt?',
  'imp.workflowDone': 'Workflow "{name}" imported',
  'imp.qpDone': 'Quick Prompt "{name}" imported',
  'wf.noOutput': '(no output)',
  'wf.status': 'Status',
  'wf.duration': 'Duration',
  'wf.noProject': 'No project',

  // ── Workflow wizard ──
  'wiz.modeSimple': 'Simple',
  'wiz.modeAdvanced': 'Advanced',
  'wiz.helpModeSimple': 'Single step: one agent, one task. Good for 80% of cases.',
  'wiz.helpModeAdvanced': 'Multi-step pipelines, conditions, scheduled triggers, batch.',
  'wiz.helpSimpleVsAdvanced': 'Simple = 1 step, 1 agent, 1 task (e.g. "generate a weekly report"). Advanced = multi-step pipelines with conditions (e.g. "fetch tickets → batch → notify"). You can always switch to Advanced later.',
  'wiz.task': 'Task',
  'wiz.agentLabel': 'AI Agent',
  'wiz.helpAgent': 'The AI model that will run the task. Claude Code is usually the most capable; Codex and Gemini are alternatives. Each agent must be installed separately on your machine.',
  'wiz.promptLabel': 'What should the agent do?',
  'wiz.helpPromptSimple': 'Describe the task in plain language, as if talking to a colleague. You can use variables like {{issue.title}} if the workflow is triggered by a tracker issue.',
  'wiz.promptSimplePlaceholder': 'Describe the task in plain language...',
  'wiz.starterLabel': 'Or start from an example:',
  'wiz.helpStarters': 'Click an example to pre-fill the name and prompt. You can edit them freely before saving.',
  'wiz.starter.codeReview': 'Code review',
  'wiz.starter.codeReviewPrompt': 'Analyze the latest commits on the main branch. For each modified file, identify:\n- Quality issues (duplicated code, excessive complexity)\n- Potential security flaws\n- Project convention violations\n\nGenerate a Markdown report with issues sorted by severity (critical / warning / suggestion).',
  'wiz.starter.changelog': 'Auto changelog',
  'wiz.starter.changelogPrompt': 'Analyze all commits since the last release tag. For each commit, identify whether it is a new feature, bug fix, improvement, or technical change.\n\nGenerate a structured Markdown changelog with sections: New Features, Bug Fixes, Improvements, Technical.',
  'wiz.starter.techDebt': 'Tech debt detection',
  'wiz.starter.techDebtPrompt': 'Scan the project source code and identify technical debt:\n- Remaining TODO/FIXME/HACK comments\n- Outdated dependencies or known vulnerabilities\n- Files over 500 lines without tests\n- Dead code or unused imports\n\nGenerate a prioritized report with estimated effort (S/M/L) for each item.',
  'wiz.starter.testCoverage': 'Test coverage audit',
  'wiz.starter.testCoveragePrompt': 'Analyze the project structure and identify critical files and functions without tests:\n- API endpoints without integration tests\n- Utility functions without unit tests\n- UI components with interactions but no tests\n\nGenerate a prioritized list of the most important missing tests, with a test skeleton for each.',
  'wiz.starter.docUpdate': 'Documentation update',
  'wiz.starter.docUpdatePrompt': 'Compare existing documentation (README, docs/, comments) with the current code. Identify:\n- Outdated or incorrect sections\n- New features not yet documented\n- Installation or configuration instructions that need updating\n\nPropose corrections for each file.',
  'wiz.starter.securityScan': 'Security scan',
  'wiz.starter.securityScanPrompt': 'Perform a security audit of the project:\n- Check dependencies for known CVEs\n- Look for hardcoded secrets or tokens in the code\n- Identify potential injections (SQL, XSS, command injection)\n- Verify permissions and authentication configuration\n\nGenerate a report with vulnerabilities sorted by criticality (CRITICAL / HIGH / MEDIUM / LOW).',
  'wiz.triggerLabel': 'Trigger',
  'wiz.triggerWhenLabel': 'When does it run?',
  'wiz.triggerManual': 'Manually',
  'wiz.triggerScheduled': 'On a schedule',
  'wiz.triggerTracker': 'When an issue arrives',
  'wiz.helpTriggerSimple': 'Manually: you click "Run" when you need it. On a schedule: the workflow runs by itself on a recurring schedule (e.g. every Monday 9am).',
  'wiz.helpTriggerAdvanced': 'Three trigger modes: Manually (you click), On a schedule (recurring times), or When an issue arrives (GitHub/Jira tracker, filtered by labels).',
  'wiz.helpTriggerManual': 'You launch the workflow yourself from this page by clicking "Run".',
  'wiz.helpTriggerCron': 'The workflow runs automatically on a schedule (e.g. every Monday 9am, or every 10 minutes).',
  'wiz.helpTriggerTracker': 'The workflow fires when a new issue appears on GitHub (or another tracker) with certain labels. Ideal for triage or auto-fix.',
  'wiz.suggestionsCount': '{0} suggestion(s) available',
  'wiz.suggestionsTitle': 'Suggestions for this project',
  // 0.8.5 — unified QuickStart picker (replaces 3 separate UI sections).
  'wiz.quickstart.toggle': '✨ {0} ready-made templates available',
  'wiz.quickstart.title': 'Quick start',
  'wiz.quickstart.hint': 'Pick a template to pre-fill the workflow.',
  'wiz.quickstart.collapse': 'Collapse',
  'wiz.quickstart.searchPlaceholder': 'Search templates…',
  'wiz.quickstart.filterComplexity': 'Complexity:',
  'wiz.quickstart.filterSource': 'Source:',
  'wiz.quickstart.complexity.simple': 'Simple',
  'wiz.quickstart.complexity.intermediate': 'Intermediate',
  'wiz.quickstart.complexity.advanced': 'Advanced',
  'wiz.quickstart.source.starter': 'Plugin demo',
  'wiz.quickstart.source.project-suggestion': 'Project suggestion',
  'wiz.quickstart.source.preset': 'Preset',
  'wiz.quickstart.complexityTooltip': 'Estimated configuration depth',
  'wiz.quickstart.sourceTooltip': 'Where this template comes from',
  'wiz.quickstart.stepsCount': '{0} step(s)',
  'wiz.quickstart.empty': 'No template matches. Try removing filters or start from scratch by typing a name.',
  'wiz.quickstart.loading': 'Loading suggestions…',
  'wiz.quickstart.apply': 'Use this template',
  'wiz.quickstart.notApplicable': 'Unavailable: {0}',
  'wiz.quickstart.disabledNoName': 'Enter a workflow name before selecting a ready-made template.',
  'wiz.activate': 'Activate',
  'wiz.importDraft': 'Import as draft',
  'wiz.outputFormat': 'Output format',
  'wiz.outputFree': 'Free text',
  'wiz.outputStructured': 'Structured',
  'wiz.outputStructuredHint': 'Output will be wrapped in JSON (data + status + summary). Next step can access {{previous_step.data}} instead of raw text.',
  'wiz.outputFreeHint': 'Raw text. Next step can only read {{previous_step.output}} — no .data / .summary / .status fields.',
  'wiz.infos': 'Info',
  'wiz.trigger': 'Trigger',
  'wiz.steps': 'Steps',
  'wiz.config': 'Config',
  'wiz.summary': 'Summary',
  'wiz.name': 'Workflow name',
  'wiz.namePlaceholder': 'e.g.: Auto-fix 5xx errors',
  'wiz.nameRequired': 'Give your workflow a name to move to the next step.',
  'wiz.project': 'Project',
  'wiz.projectOptional': 'Project (optional)',
  'wiz.noProject': 'No project',
  'wiz.triggerType': 'Trigger type',
  'wiz.frequency': 'Frequency',
  'wiz.every': 'Every',
  'wiz.minutes': 'minutes',
  'wiz.hours': 'hours',
  'wiz.days': 'days',
  'wiz.weeks': 'weeks',
  'wiz.months': 'months',
  'wiz.at': 'at',
  'wiz.cronWeekdaysLabel': 'Only specific days?',
  'wiz.helpCronWeekdays': 'Check the days the workflow should run. Leave all unchecked to run EVERY day. e.g. check Mon and Fri → the workflow will only run on Mondays and Fridays.',
  'wiz.cronWeekdaysAll': 'every day',
  'wiz.cronWeekdaysEveryday': 'Runs every day',
  'wiz.cronWeekdaysPreview': 'Runs only on: {0}',
  'wiz.cronEveryDisabledHint': 'Disabled because specific days are selected. Uncheck all days to re-enable.',
  'wiz.weekday.0': 'Sunday',
  'wiz.weekday.1': 'Monday',
  'wiz.weekday.2': 'Tuesday',
  'wiz.weekday.3': 'Wednesday',
  'wiz.weekday.4': 'Thursday',
  'wiz.weekday.5': 'Friday',
  'wiz.weekday.6': 'Saturday',
  'wiz.weekdayShort.0': 'Sun',
  'wiz.weekdayShort.1': 'Mon',
  'wiz.weekdayShort.2': 'Tue',
  'wiz.weekdayShort.3': 'Wed',
  'wiz.weekdayShort.4': 'Thu',
  'wiz.weekdayShort.5': 'Fri',
  'wiz.weekdayShort.6': 'Sat',
  'wiz.labels': 'Labels (comma-separated)',
  'wiz.labelsPlaceholder': 'bug-5xx, auto-fix',
  'wiz.stepType': 'Step type',
  'wiz.stepSwapConfirm': 'Convert this step from "{0}" to "{1}"? Fields specific to the current type (prompt, profile, skills, plugin/endpoint, JSON payload…) will be cleared.',
  'wiz.stepTypeAgent': 'Agent',
  'wiz.stepTypeAgentHint': 'An AI agent that reasons, reads code, writes files, calls MCPs. The most flexible step — but consumes tokens.',
  'wiz.stepTypeApiCall': 'API',
  'wiz.stepTypeApiCallHint': 'Direct API call (Chartbeat, Jira, Cloudflare…) — zero tokens. Fetches data and pipes to the next step.',
  'wiz.stepTypeBatchQP': 'Batch QP',
  'wiz.stepTypeBatchQPHint': 'Fan out a Quick Prompt over a list of items produced by a previous step.',
  'wiz.stepTypeNotify': 'Notify',
  'wiz.stepTypeNotifyHint': 'Direct HTTP call (webhook) — zero tokens. Send to Slack, Teams, or trigger a downstream pipeline.',
  'wiz.stepTypeGate': 'Gate',
  'wiz.stepTypeGateHint': 'Pause the run for human approval — zero tokens. Operator approves, requests changes, or rejects from RunDetail.',
  'wiz.stepTypeExec': 'Exec',
  'wiz.stepTypeExecHint': 'Run a whitelisted binary directly (cargo test, npm build…) — zero tokens, never via shell, args passed as literal argv.',
  'wiz.stepTypeBatchApi': 'Batch API',
  'wiz.stepTypeBatchApiHint': 'Fan out an API call over a list of items, in parallel. Zero tokens — perfect for creating N tickets / posting N comments / updating N statuses at once.',
  'wiz.stepTypeJsonData': 'JSON',
  'wiz.stepTypeJsonDataHint': 'Deterministic data source — emits a literal JSON payload. Zero tokens, zero network. Great for feeding a Batch step with a fixed list (10 hosts, 5 regions…) without standing up an API, or as a dev fixture.',
  'wiz.jsonDataTitle': 'JSON Data',
  'wiz.jsonDataHint': 'The payload is emitted as-is to the next step via `{{steps.<name>.data}}`. Useful for a BatchQuickPrompt on a fixed list (10 hosts, 5 regions…) without standing up an API.',
  'wiz.jsonDataPayload': 'JSON payload',
  'wiz.jsonDataPlaceholder': '[\n  { "host": "fr.example.com" },\n  { "host": "de.example.com" },\n  { "host": "en.example.com" }\n]',
  'wiz.jsonDataParseError': 'Invalid JSON:',
  'wiz.jsonDataNoTemplating': 'No runtime templating — the value is returned verbatim. For a dynamic payload, use an Agent or ApiCall step.',
  'wiz.jsonDataSummaryArray': '{0} item(s) — will be consumed by a downstream Batch step',
  'wiz.jsonDataSummaryObject': '1 object, {0} field(s)',
  'wiz.jsonDataSummaryScalar': '1 scalar value',
  'wiz.jsonDataMissingPayload': 'no payload',
  'wiz.errorJsonDataNoPayload': 'JSON step « {0} » has no payload — paste valid JSON in the textarea.',
  'wiz.batchApiTitle': 'Batch API call',
  'wiz.batchApiHint': 'Reuses the API config below for each item in the list. Each item exposes `{{batch.item.<field>}}` in body, headers, query, etc. Items run in parallel, capped by the concurrent limit.',
  'wiz.batchApiItemsFrom': 'Items source',
  'wiz.batchApiItemsFromPlaceholder': '{{steps.plan.data.sub_tasks}} or {{steps.plan.data}} or a raw JSON array',
  'wiz.batchApiConcurrentLimit': 'Concurrency',
  'wiz.batchApiConcurrentLimitHint': 'Max parallel calls (default 5, max 20)',
  'wiz.batchApiMaxItems': 'Max items',
  'wiz.batchApiMaxItemsHint': 'Safety cap (default 50)',
  'wiz.batchApiMissingConfig': 'incomplete config',
  'wiz.batchApiQaPicker': 'API config source',
  'wiz.batchApiQaPickerInline': '— Inline (config defined here) —',
  'wiz.batchApiQaPickerHint': 'You can reference a saved Quick API: its config will be used at runtime. The fields below remain per-field overrides.',
  'wiz.execTitle': 'Direct shell execution (Exec)',
  'wiz.execHint': 'Run a binary from the workflow allowlist inside the workspace. Args are templated but passed literally (no shell interpretation). Timeout enforced.',
  'wiz.execWorktreeHint': 'This command sees the changes made by previous steps.',
  'wiz.execWorktreeHintFirst': 'This command runs in a fresh git worktree created for this run (isolated repo copy under .kronn/worktrees/).',
  'wiz.execNoProjectWarn': 'No project linked: the command runs in Kronn\'s current directory, with no worktree isolation. Link a project to enable per-run worktree.',
  'wiz.worktreeHint': 'Each run executes in an isolated git copy. Steps share the same files.',
  'wiz.worktreeIconTooltip': 'Git worktree: an isolated copy of the repo, created for the run\'s lifetime, under .kronn/worktrees/.',
  'wiz.execCommand': 'Binary',
  'wiz.execCommandSelect': 'pick a binary',
  'wiz.execArgs': 'Arguments (one per line)',
  'wiz.execArgsPlaceholder': 'test\n--\n{{steps.fetch.summary}}',
  'wiz.execTimeoutSecs': 'Timeout (seconds, default 300, max 1800)',
  'wiz.execSetupToggle': 'This workflow runs in a git worktree — install dependencies first',
  'wiz.execSetupHint': 'The worktree has tracked files (composer.json, package.json, …) but NOT generated dirs (vendor/, node_modules/, target/). Check to run an install BEFORE your main command.',
  'wiz.execSetupPreset': 'Common pattern (auto-fills the fields below)',
  'wiz.execSetupPresetPick': 'Pick a preset',
  'wiz.execSetupCommand': 'Setup command',
  'wiz.execSetupArgs': 'Setup args (one per line)',
  'wiz.execAllowlistEmpty': 'Exec is disabled: no command allowed for this workflow.',
  'wiz.execAllowlistConfigureNow': 'Configure allowlist →',
  'wiz.execAllowlistTitle': 'Exec allowlist',
  'wiz.execAllowlistHint': 'Binaries that Exec steps may invoke in this workflow. Empty = Exec disabled. Bare names only (no paths, no shell metas). Comma-separated.',
  'wiz.execAllowlistPlaceholder': 'npm, cargo, make, pytest',
  'wiz.gateTitle': 'Human pause (Gate)',
  'wiz.gateHint': 'Run stops at this step awaiting a decision (approve, request changes, reject). {{steps.X.summary}} templates supported in the message.',
  'wiz.gateMessage': 'Message shown to the operator',
  'wiz.gateMessagePlaceholder': 'Approve audit `{{steps.audit.summary}}`?',
  'wiz.gateRequestChangesTarget': '"Request changes" target',
  'wiz.gateRequestChangesDefault': '(default: previous step)',
  'wiz.gateNotifyUrl': 'Notify this URL when the Gate fires (optional)',
  'wiz.gateNotifyUrlPlaceholder': 'https://hooks.slack.com/services/... or {{state.slack_url}}',
  'wiz.gateNotifyUrlHint': 'Best-effort POST with {run_id, workflow_name, step_name, message}. Templates supported. Failures logged, never blocking.',
  'wiz.rollbackTitle': 'Rollback / compensation (on failure)',
  'wiz.rollbackHint': 'Steps run when the workflow ends Failed. You can reference {{failed_step.name}} and {{failed_step.output}}. Notify, Agent and ApiCall are supported (Gate is excluded — it would deadlock an already-Failed run).',
  'wiz.rollbackAgentPromptPlaceholder': 'Analyze the failure above: {{failed_step.output}}. Produce a 3-line post-mortem.',
  'wiz.presetsTitle': 'Start from a pattern',
  'wiz.presetsHint': '— or start from a blank workflow below',
  'wiz.presetsBlankHint': 'You can also scroll down and build your workflow step by step. Presets are fully editable after applying.',
  'wiz.preset.autoDev.title': 'Auto-Dev with tests',
  'wiz.preset.autoDev.desc': 'Implement, test, review, loop until OK or max 5 iterations. Review feedback is passed back to the next implement via state.',
  'wiz.preset.prGate.title': 'PR pipeline with human Gate',
  'wiz.preset.prGate.desc': 'Implement, run tests, wait for human approval (with webhook notification), merge if approved.',
  'wiz.preset.deployRollback.title': 'Deploy with rollback',
  'wiz.preset.deployRollback.desc': 'Build, smoke tests, deploy. On failure: alert ops + automatic post-mortem agent.',
  'wiz.preset.autoDev.issueKeyLabel': 'Ticket / issue key (optional)',
  'wiz.preset.autoDev.issueKeyPlaceholder': 'EW-1234  ·  org/repo#42  ·  KRN-7',
  'wiz.preset.autoDev.issueKeyDesc': 'Optional — only used if you switch the `fetch_issue` step to ApiCall (reference `{{issue_key}}` in `api_path_params` or `api_query` of the tracker plugin).',
  'wiz.preset.autoDev.fetchIssueDesc': 'Default fixture: describe the ticket to implement here (literal JSON). Just edit the payload to fit your request, the preset runs immediately without any tracker plugin. To wire a real tracker (Jira / GitHub / Linear): switch this step to `ApiCall` (🌐 button above) and configure the plugin — `{{steps.fetch_issue.data}}` will keep feeding the `implement` step.',
  'wiz.preset.autoDev.implementPrompt': 'Implement the following request:\n---\n{{steps.fetch_issue.data}}\n---\n\nIf a previous review left feedback:\n{{state.last_review}}\n\nTake the feedback above into account for this new iteration.\nIf no review happened yet, this block is empty — it\'s your first pass.',
  'wiz.preset.autoDev.reviewPrompt': 'Review the implementation produced by `implement` and the result of the `run_tests` step.\n\nTests output: {{steps.run_tests.data.stdout}}\nTests exit code: {{steps.run_tests.data.exit_code}}\n\nIf everything is OK: end your reply with [SIGNAL: APPROVED].\nOtherwise: write actionable feedback, then exactly at the end (and ONLY at the end):\n---STATE:last_review=<your feedback in one line>---\nthen end with [SIGNAL: NEEDS_CHANGES].',
  'wiz.preset.autoDev.notifyDoneBody': '✅ Auto-Dev workflow complete: {{steps.review.summary}}',
  'wiz.preset.autoDev.rollbackBody': '❌ Auto-Dev failed at `{{failed_step.name}}` — details: {{failed_step.output}}',
  'wiz.preset.prGate.issueKeyLabel': 'Ticket / issue key (optional)',
  'wiz.preset.prGate.issueKeyPlaceholder': 'EW-1234  ·  org/repo#42  ·  KRN-7',
  'wiz.preset.prGate.issueKeyDesc': 'Optional — only used if you switch the `fetch_issue` step to ApiCall (reference `{{issue_key}}` in `api_path_params` or `api_query` of the tracker plugin).',
  'wiz.preset.prGate.fetchIssueDesc': 'Default fixture: describe the ticket to implement here (literal JSON). Just edit the payload to fit your request, the preset runs immediately without any tracker plugin. To wire a real tracker (Jira / GitHub / Linear): switch this step to `ApiCall` (🌐 button above) and configure the plugin — `{{steps.fetch_issue.data}}` will keep feeding the `implement` step.',
  'wiz.preset.prGate.implementPrompt': 'Implement the following request:\n---\n{{steps.fetch_issue.data}}\n---\n\nOutput a short summary in `summary` and the raw diff in `data.diff` so the human reviewer has context.',
  'wiz.preset.shared.runTestsDesc': 'Pre-filled with a multi-stack bash script that auto-detects Makefile / cargo / npm / composer / pytest. For a simple command, edit `exec_command` AND `exec_args` separately (DO NOT put the whole line in one field): Make → command `make` + args `["test"]`, Node → command `npm` + args `["test"]`, Python → command `pytest` + args `[]`. For a shell one-liner, use command `bash` + args `["-c", "your-full-command"]`. The chosen binary must be in `exec_allowlist`.',
  'wiz.preset.prGate.gateMessage': '## Pre-merge approval\n\n**Implementation:** {{steps.implement.summary}}\n\n**Tests:** exit `{{steps.run_tests.data.exit_code}}`\n\nApprove to trigger the automatic merge. Request changes to run `implement` again.',
  'wiz.preset.prGate.mergePrompt': 'Merge the branch into main. Check there is no conflict, then push. Output the merge commit SHA in `data.merge_sha`.',
  'wiz.preset.prGate.rollbackBody': '❌ PR Gate failed: `{{failed_step.name}}` — {{failed_step.output}}',
  'wiz.preset.deployRollback.successBody': '🚀 Deployment complete, no incident.',
  'wiz.preset.deployRollback.alertBody': '🚨 PROD ALERT: step `{{failed_step.name}}` failed. Rolling back...',
  'wiz.preset.deployRollback.postMortemPrompt': 'Analyze the failure and produce a post-mortem in 3 sections:\n\n## Step that failed\n{{failed_step.name}}\n\n## Observed error\n{{failed_step.output}}\n\n## Root cause hypotheses\n(your turn — propose 2-3 prioritized leads)',
  'wiz.preset.featurePlanner.title': 'Feature / Epic Planner',
  'wiz.preset.featurePlanner.desc': 'Break an epic or feature into typed sub-tasks (auto_ai vs human_action) with dependencies. Tracker-agnostic (Jira / GitHub / Linear via the installed MCPs).',
  'wiz.preset.featurePlanner.analyzePrompt': `You are an Epic Planner. The user passed the URL: {{epic_url}}.

# 1. Detect the tracker
Read the URL host:
- atlassian.net → Jira (Atlassian MCP)
- github.com/.../issues/ → GitHub (GitHub MCP)
- linear.app → Linear MCP
- other → describe what you'd need, do nothing, end with [SIGNAL: NO_RESULTS]

# 2. Fetch the content
Via the matching MCP: title, description, project key, current state, and existing sub-tasks (you'll need them for idempotency next step).

# 3. Break it down
Constraints:
- Each sub-task must fit in a single PR (1-3 days of work max).
- Tag each sub-task:
  * \`auto_ai\` — pure code work (writing files, running tests, opening a PR). Doable by an autonomous agent.
  * \`human_action\` — UI config (CMS, infra dashboard), architecture/naming decision, external info (URLs, credentials, accesses…).
- Identify dependencies: A blocks B if B logically depends on A being done first.
- Document EACH decision you took yourself when the epic was ambiguous (target sprint, phase ordering, naming, etc.).
- Identify missing info that only a human can provide.

# 4. STRUCTURED output (mandatory)
\`\`\`json
{
  "data": {
    "tracker": "jira" | "github" | "linear",
    "source_key": "EW-7247",
    "sub_tasks": [
      { "id": 1, "title": "...", "description": "...", "type": "auto_ai", "blocked_by": [] }
    ],
    "decisions_taken": ["I assumed phase 0 must complete before phase 1 because..."],
    "missing_info": ["Adobe DTM AN URL — needed from the Data team"]
  },
  "status": "OK",
  "summary": "Plan: 12 sub-tasks (4 human_action, 8 auto_ai). 2 missing-info items flagged."
}
\`\`\`

End with [SIGNAL: PLAN_READY].`,
  'wiz.preset.featurePlanner.gateMessage': `## Proposed plan for {{epic_url}}

{{steps.analyze_epic.summary}}

### Decisions auto-taken
{{steps.analyze_epic.data.decisions_taken}}

### Missing info (human-provided)
{{steps.analyze_epic.data.missing_info}}

### Sub-tasks
{{steps.analyze_epic.data.sub_tasks}}

**Approve** → the agent creates the tickets in the tracker.
**Request changes** → goes back to the planning step with your feedback.`,
  'wiz.preset.featurePlanner.setLinksPrompt': `Sub-tasks have just been bulk-created via a BatchApiCall. You'll now set the \`blocks\` / \`is blocked by\` links between them.

# Inputs
- Tracker: {{steps.analyze_epic.data.tracker}}
- Original plan (with declared \`blocked_by\`): {{steps.analyze_epic.data.sub_tasks}}
- Creation results (one item per sub-task, with \`input.id\` and \`response\` = the new Jira/GitHub key): {{steps.create_tickets.data.items}}

# Mapping
1. For each sub-task in the original plan with non-empty \`blocked_by\`:
   - Find the matching created key in \`steps.create_tickets.data.items\` (match on \`input.id\` ↔ plan.id)
   - For each \`blocked_by\` (id of the blocking sub-task), find its created key as well
   - Add a "is blocked by" link via the MCP: the \`current\` sub-task is blocked by \`predecessor\`

# Idempotency
Before adding a link, check whether it already exists via the MCP (the creation may have been re-run). Only add what's missing. No error if the link already exists.

# Errors
If an item in \`steps.create_tickets.data.items\` has \`status: ERROR\`, **skip it** for the mapping (you can't link to a ticket that wasn't created). List them in the final \`summary\` so the human has the missing tickets to create manually.

# Final STRUCTURED output
\`\`\`json
{
  "data": {
    "links_added": 5,
    "links_already_existed": 2,
    "skipped_failed_items": []
  },
  "status": "OK",
  "summary": "Feature Planner: 28 tickets created, 5 blocks links set, 2 already existed."
}
\`\`\`

End with [SIGNAL: OK].`,
  'wiz.preset.featurePlanner.notifyBody': '🗺️ Feature Planner finished for {{epic_url}} — {{steps.set_links.summary}}',
  'wiz.preset.dailyHostAudit.title': 'Daily audit on fixed list',
  'wiz.preset.dailyHostAudit.desc': 'Deterministic data source (5 hardcoded hosts) → fan out an audit Quick Prompt on each → Slack notification. Showcases the `JsonData` brick: no need to stand up an API for a fixed list.',
  'wiz.preset.dailyHostAudit.hostListDesc': 'Hardcoded list of hosts to audit. Edit the JSON payload to fit your context (locales, environments, subdomains, etc.). Zero tokens, zero network — the value is passed as-is to the next step via `{{steps.host-list.data}}`.',
  'wiz.preset.dailyHostAudit.auditEachDesc': 'Fan-out on the list above. Pick an existing Quick Prompt (or create one) that audits ONE host. Each run receives `{{batch.item.host}}` in its rendered prompt.',
  'wiz.preset.dailyHostAudit.notifyBody': '🌍 Daily audit complete: {{steps.audit-each-host.summary}}',
  'wiz.preset.ticketToPr.title': 'Ticket Autopilot — from ticket to PR',
  'wiz.preset.ticketToPr.desc': 'Full pipeline: fetch a ticket, analyze + plan, implement with strict TDD, run tests, review by a different agent, create the PR, wait for your final approval. Methodology skills from obra/superpowers (TDD, debugging, verification, code-review). Edit the `fetch_issue` step to wire your tracker (Jira / GitHub) or keep the fixture to test.',
  'wiz.preset.ticketToPr.fetchIssueDesc': 'Source of the ticket to implement. Default fixture (edit the payload), or switch to `ApiCall` (🌐 button) pointed at your tracker plugin once you have one. The result feeds the `analyze` step via `{{steps.fetch_issue.data}}`.',
  'wiz.preset.ticketToPr.analyzePrompt': 'Analyze the following ticket and produce a structured plan:\n---\n{{steps.fetch_issue.data}}\n---\n\n# You will auto-load the `writing-plans` and `brainstorming` skills:\n- Explore the ticket\'s intent before implementation (3-5 critical questions)\n- List assumptions + uncertainty points\n- Break into subtasks (1-3 days each max)\n- Identify risks and test strategy\n\n# Structured output:\n```json\n{\n  "data": {\n    "summary": "<one-line summary>",\n    "complexity": "low | medium | high",\n    "questions": ["..."],\n    "subtasks": [{"id": 1, "title": "...", "description": "..."}],\n    "test_strategy": "..."\n  },\n  "status": "OK",\n  "summary": "Plan in N subtasks, complexity <X>"\n}\n```',
  'wiz.preset.ticketToPr.planGateMessage': '## Plan validation\n\n**Recap:** {{steps.analyze.summary}}\n\n**Subtasks:**\n{{steps.analyze.data.subtasks}}\n\n**Test strategy:**\n{{steps.analyze.data.test_strategy}}\n\nApprove to start implementation, or request changes to re-run analysis with your feedback.',
  'wiz.preset.ticketToPr.implementPrompt': 'Implement the validated plan:\n---\n{{steps.analyze.data}}\n---\n\nIf a previous review left feedback to address:\n{{state.last_review}}\n\n# You will auto-load:\n- `test-driven-development` (strict red-green-refactor ritual)\n- `systematic-debugging` (4-phase root-cause when a test breaks)\n- `verification-before-completion` (no claim without evidence)\n- `receiving-code-review` (technique for applying review feedback)\n\n# Procedure:\n1. For EACH subtask in the plan: write tests first (red), then minimal code (green), then refactor.\n2. Run tests at each step, actually verify the output.\n3. If a test breaks unexpectedly, apply systematic-debugging (no shotgun fixes).\n4. At the end, output `[SIGNAL: CONTINUE]` to move to integration tests.',
  'wiz.preset.ticketToPr.reviewPrompt': 'Review the implementation produced by `implement` and the result of the `run_tests` step.\n\nContext:\n- Original plan: {{steps.analyze.summary}}\n- Tests output: {{steps.run_tests.data.stdout}}\n- Tests exit code: {{steps.run_tests.data.exit_code}}\n\n# You will auto-load `requesting-code-review` and `verification-before-completion` skills.\n\n# Procedure:\n1. Verify the implementation covers ALL subtasks in the plan (not partial).\n2. Verify tests: do they test real behavior, not just mocks?\n3. YAGNI check: did the implementation add unrequested complexity?\n4. Security: injections, secret leaks, input validation?\n5. Edge cases: null, empty, unicode, large input?\n\n# Verdict:\nIf everything OK, end your reply with exactly `[SIGNAL: APPROVED]`.\nOtherwise, write actionable feedback, then exactly at the end (and ONLY at the end):\n```\n---STATE:last_review=<feedback in 1-3 lines>---\n[SIGNAL: NEEDS_CHANGES]\n```',
  'wiz.preset.ticketToPr.createPrPrompt': 'The implementation is validated and all tests pass. Create the PR.\n\nContext:\n- Ticket: {{steps.fetch_issue.data}}\n- Implementation recap: {{steps.implement.summary}}\n- Approved review: {{steps.review.summary}}\n\n# You will auto-load `finishing-a-development-branch` and `verification-before-completion`.\n\n# Procedure:\n1. Verify one last time that tests pass (exact command, complete output).\n2. Push the current branch.\n3. Create the PR via `gh pr create` with:\n   - Coherent title (reference the ticket)\n   - Structured body: Summary (3 bullets) + Test Plan (checklist)\n4. Emit the Kronn STATE markers so the next step can consume the URL — each on its own line, exactly this format:\n   ```\n   ---STATE:pr_url=<url>---\n   ---STATE:pr_number=<n>---\n   ```\n   (Important: writing `state.pr_url=<url>` WITHOUT the `---STATE:...---` markers will NOT be parsed by Kronn.)\n5. End with `[SIGNAL: PR_CREATED]`.',
  'wiz.preset.ticketToPr.readyGateMessage': '## PR ready to merge\n\n**PR:** {{state.pr_url}}\n**Recap:** {{steps.review.summary}}\n\nApprove to finalize (success notification), or request changes to re-run `implement` with your feedback (`state.last_review` will be consumed by the agent).',
  'wiz.preset.ticketToPr.notifyDoneBody': '✅ Ticket Autopilot complete: {{state.pr_url}} — {{steps.review.summary}}',
  'wiz.preset.ticketToPr.rollbackBody': '❌ Ticket Autopilot failed at `{{failed_step.name}}` — details: {{failed_step.output}}',
  // 0.8.3 — Feasibility-Gated AutoPilot (auto-proposed after AI audit)
  'wiz.preset.feasibilityAutopilot.title': 'Big-ticket AutoPilot — feasibility-gated',
  'wiz.preset.feasibilityAutopilot.desc': 'For big tickets: a `triage` step classifies every sub-task into clear / decided / mocked / blocked BEFORE any code is written. The human gate validates the plan, then `implement` inserts `KRONN-(ASSUMED|MOCKED|TODO)` markers at every traced freedom. `run_tests` Exec runs the real suite, `drift_check` Exec lists the markers in the final PR. Minimal token cost — only triage / implement / pr_draft are Agent.',
  'wiz.preset.feasibilityAutopilot.fetchIssueDesc': 'Ticket source. JsonData fixture by default; the wizard auto-swaps to ApiCall when a tracker plugin (Jira/GitHub/GitLab) is wired for the project. The next step reads `{{steps.fetch_issue.data}}` (full object — the agent navigates the fields depending on source: `body` for the fixture, `fields.description` or `renderedFields.description` for Jira).',
  'wiz.preset.feasibilityAutopilot.triageDesc': '[TRIAGE] Feasibility audit — classifies every sub-task into clear / decided / mocked / blocked. The agent does NOT write code here; it produces a schema-validated JSON manifest (on_invalid=Fail). Each entry has a stable `id` that `implement` will use as the decision_id in markers.',
  'wiz.preset.feasibilityAutopilot.triagePrompt': 'You are the TRIAGE step of a Feasibility-Gated AutoPilot run.\n\nTicket payload (from `fetch_issue` — full data object, navigate as needed for `body` / `description` / `fields.description` / `renderedFields.description` depending on source):\n---\n{{steps.fetch_issue.data}}\n---\n\nRead the project (current cwd is its worktree). Classify every sub-task into clear / decided / mocked / blocked per the schema. Emit the JSON manifest only — no markdown commentary outside the envelope.',
  'wiz.preset.feasibilityAutopilot.gateDesc': 'Human review of the triage manifest before code. `gate_request_changes_target: triage` allows up to 5 loops.',
  'wiz.preset.feasibilityAutopilot.gateMessage': 'Triage manifest produced. Review the four categories below before approving:\n\n{{steps.triage.data}}\n\n- Approve: continue to implementation as-is.\n- Request changes: send back to triage with your notes (loops up to 5 times).\n- Reject: abandon the run.',
  'wiz.preset.feasibilityAutopilot.implementDesc': 'Implementation constrained by the validated manifest. Every decided/mocked/blocked entry MUST have its `KRONN-*` marker in the code. `[SIGNAL: BLOCKED <id>]` → Goto(triage) if the agent discovers an impossibility (cap 3 iterations).',
  'wiz.preset.feasibilityAutopilot.implementPrompt': 'You are the IMPLEMENT step of a Feasibility-Gated AutoPilot run.\n\nThe triage step produced and a human (or auto-budget) approved this manifest:\n\nCLEAR entries (implement directly, no marker needed):\n{{steps.triage.data.clear}}\n\nDECIDED entries (implement the `chosen` option, insert `// KRONN-ASSUMED(<id>): <chosen> — <why>` at the primary touch point):\n{{steps.triage.data.decided}}\n\nMOCKED entries (implement the `placeholder` strategy, insert `// KRONN-MOCKED(<id>): <strategy>`):\n{{steps.triage.data.mocked}}\n\nBLOCKED entries (do NOT implement the feature, only insert `// KRONN-TODO(<id>): waiting on <needed_from> — <why>` where the feature would have been):\n{{steps.triage.data.blocked}}\n\nFiles the manifest claims you\'ll touch:\n{{steps.triage.data.files_touched}}\n\nRULES:\n1. Stay within the files_touched list unless absolutely necessary.\n2. Every entry in decided/mocked/blocked MUST have its corresponding KRONN-* marker in the code, referencing the `id` from the manifest. The next step (`drift_check`) will grep for these — anything missing will be visible in the PR.\n3. If you discover an entry classified as `clear` or `decided` is actually impossible to implement, STOP immediately and emit `[SIGNAL: BLOCKED <id>]` as the last line of your response. The workflow will jump back to triage. Do NOT silently mock or fake it.\n4. Do not invent values that aren\'t in the manifest (URLs, secrets, IDs). If the manifest says it\'s mocked/blocked, respect that.\n5. Do NOT write tests in this step. The next step (`run_tests`) is an Exec that runs the project\'s existing test suite — keep your scope to implementation.\n\nUse your tools to edit files. End your response with a brief summary of what you implemented + the [SIGNAL: ...] line.',
  'wiz.preset.feasibilityAutopilot.driftCheckDesc': 'Lists every `KRONN-(ASSUMED|MOCKED|TODO)` marker in the worktree. 0 tokens. The output is embedded verbatim into the PR by `pr_draft` via `{{steps.drift_check.output}}`.',
  'wiz.preset.feasibilityAutopilot.prDraftDesc': 'Produces the PR body aggregating manifest + test verdict + drift_check markers. The "Decisions taken" section highlights traced freedoms the reviewer must challenge.',
  'wiz.preset.feasibilityAutopilot.prDraftPrompt': 'You are the PR-DRAFT step of a Feasibility-Gated AutoPilot run.\n\nProduce a PR description in Markdown with this structure:\n\n## Ticket\n{{steps.fetch_issue.data.key}}\n\n## Summary\n<1-3 sentences>\n\n## Implemented (from triage)\n<list of CLEAR + DECIDED + MOCKED entries with their KRONN-* markers>\n\n## Still open (blocked)\n<list of BLOCKED entries with `needed_from` — these are explicit asks to other teams>\n\n## Decisions taken (review carefully)\n<bullets of DECIDED entries: what was chosen vs alternatives, why>\n\n## Mocks to replace later\n<bullets of MOCKED entries with `revisit_when`>\n\n## Test verdict\n<one-line summary of `{{steps.run_tests.output}}` — passed / skipped / errored>\n\n## Markers audit (drift_check output)\n<embed `{{steps.drift_check.output}}` verbatim inside a ``` code fence so reviewers see every traced freedom>\n\n## Test plan\n<bullet list of how to verify each implemented entry manually, beyond what the test suite covers>\n\nPull values from `{{steps.triage.data}}`. End with `[SIGNAL: CONTINUE]`.',
  'wiz.addRollbackStep': 'Add rollback step',
  'wiz.removeRollbackStep': 'Remove this rollback step',
  'wf.launchModalTitle': 'Launch "{name}"',
  'wf.launchModalHint': 'Fill the variables below — they become `{{name}}` in your step prompts.',
  'wf.launchModalRequired': 'Required variable(s) missing: {names}',
  'wf.launchModalGo': 'Launch',
  'wiz.notifyTitle': 'Webhook / Notification',
  'wiz.notifyHint': 'Direct HTTP call from Kronn, no agent. {{steps.X.output}} templates work in URL and body.',
  'wiz.notifyUrl': 'Webhook URL',
  'wiz.notifyMethod': 'Method',
  'wiz.notifyBody': 'Request body',
  'wiz.batchQPTitle': 'Batch Quick Prompt',
  'wiz.batchQPHint': 'Runs a Quick Prompt on each item from a list produced by a previous step (Jira fetch, GitHub, etc.). By default the workflow waits for all child discussions to finish.',
  'wiz.batchQPNoQPs': 'No Quick Prompts available. Create one first in the Quick Prompts tab.',
  'wiz.batchQPPicker': 'Quick Prompt to launch',
  'wiz.batchQPPickerEmpty': 'pick a QP',
  'wiz.notifyMissingUrl': 'webhook URL missing',
  'wiz.batchQPRequired': 'Pick a Quick Prompt — this is what will run on each item.',
  'wiz.batchQPVarSubst': 'Each item in the list fills the {{{0}}} variable of the Quick Prompt.',
  'wiz.batchItemsFrom': 'Items list to process',
  'wiz.batchItemsFromRequired': 'Tell us where to find the list — usually from a previous step\'s output.',
  'wiz.batchItemsFromHelper': 'Click a previous step to auto-fill:',
  'wiz.batchItemsFromFromStep': '← From {0}',
  'wiz.batchItemsFromPickStep': 'Use the output of step "{0}"',
  'wiz.batchFirstStepWarning': 'A batch step in the first slot has no previous step to chain from. Add a "fetch" step before it to pull the list, or hardcode the list in the field below.',
  'wiz.batchAutoPrevNotice': '↑ Step "{name}" auto-configured to feed this batch',
  'wiz.batchWaitForCompletion': 'Wait for all discussions to finish',
  'wiz.batchMaxItems': 'Max items',
  'wiz.batchWorktree': 'Isolate each agent\'s changes',
  'wiz.batchWorktreeHint': 'Each agent works in its own copy of the project so they don\'t overwrite each other. Check this if agents will modify files in parallel. Leave unchecked for analyses (reports, audits) — it\'s faster.',
  'wiz.batchWorktreeNoProject': 'This Quick Prompt is not linked to any project — there is no code to isolate.',
  'wiz.batchChain': 'Chain more Quick Prompts (optional)',
  'wiz.batchChainHint': 'After the initial QP, these QPs will be sent sequentially as user messages in EACH batch discussion. Each chain QP receives the SAME batch item (e.g. "EW-1234") in its first variable — perfect for "analyse → review → summary" on the same ticket. You can also reuse the previous agent reply via `{{previous_qp.output}}` for "brief → plan → tickets" flows. QPs with 2+ variables are not eligible. The batch is only marked complete once every chain finishes.',
  'wiz.batchChainAdd': '+ Add a QP to the chain',
  'wiz.batchChainRemove': 'Remove from chain',
  'wiz.batchChainMoveUp': 'Move up in chain',
  'wiz.batchChainMoveDown': 'Move down in chain',
  'wiz.batchChainEmpty': 'No variable-less QP available to chain.',
  'wiz.stepDescription': 'Description',
  'wiz.stepDescriptionPlaceholder': 'What does this step do, in 1 line? E.g. "Fetch open Jira tickets" or "Analyze test logs" — shown in the list, optional.',
  'wiz.insertStepHere': 'Insert a step here',
  'wiz.moveStepUp': 'Move step up',
  'wiz.moveStepDown': 'Move step down',
  'wiz.ifContainsPlaceholder': 'Keyword to match in the agent output (e.g. APPROVED, NEEDS_CHANGES, ERROR…)',
  'wiz.removeCondition': 'Remove this condition',
  'wiz.wfVariablesTitle': 'Launch variables',
  'wiz.wfVariablesHint': 'Variables prompted at manual launch time — like Quick Prompts. Values become `{{name}}` in your step prompts. Optional — leave empty if your workflow gets its context from an upstream step (ApiCall) or a trigger.',
  'wiz.addVariable': 'Add variable',
  'wiz.removeVariable': 'Remove this variable',
  'wiz.undeclaredVarsTitle': 'Undeclared variables detected',
  'wiz.undeclaredVarsHint': 'These `{{var}}` don\'t match any earlier step, state, iter, artifact or launch variable. They\'ll render empty at runtime. Declare them or fix the name.',
  'wiz.undeclaredAddVar': 'Add to variables',
  'wiz.undeclaredAddVarHint': 'Add "{name}" as a launch variable (asked at manual launch).',
  'wiz.undeclaredUnknownStep': 'Unknown step — check the name',
  'wiz.undeclaredFailedOutsideRollback': '`failed_step.*` is only usable in a rollback step',
  'wiz.pollInterval': 'Poll interval (cron)',
  'wiz.availableVars': 'Available variables',
  'wiz.triggerVars': 'Trigger variables (Tracker)',
  'wiz.issueTitle': 'Issue title',
  'wiz.issueBody': 'Issue body',
  'wiz.issueNumber': 'Number (#42)',
  'wiz.issueUrl': 'Link to issue',
  'wiz.issueLabels': 'Labels (comma-separated)',
  'wiz.stepChaining': 'Step chaining',
  'wiz.prevOutput': 'Previous step output',
  'wiz.namedOutput': 'Named step output',
  'wiz.iterHint': 'Step iteration counter (1, 2, 3…) — useful in Goto loops',
  'wiz.stateHint': 'Durable state variable, agents write via ---STATE:key=value---',
  'wiz.stateChipsLoopOut': 'Loop detected — pass your verdict to the rewind step:',
  'wiz.stateChipsLoopIn': 'Receives a loop verdict — read the previous context:',
  'wiz.stateChipWrite': 'write `state.{key}`',
  'wiz.stateChipWriteHint': 'Appends an instruction to the prompt to write `---STATE:{key}=...---` so the rewind step can read your verdict.',
  'wiz.stateChipReadHint': 'Inserts `{{state.{key}}}` at the cursor — the value the previous loop step wrote.',
  'wiz.stateInstructionBlock': 'At the end of your reply (and ONLY at the end), write exactly:\n---STATE:{key}=<your verdict / feedback in one line>---\n\nThis block will be read by step "{target}" on the next loop iteration.',
  'wiz.gotoMaxIterPlaceholder': 'N',
  'wiz.gotoMaxIterHint': 'Past N iterations of this Goto, the runner exits the loop and continues. Empty = no per-edge limit (only the workflow-level guard applies).',
  'wiz.gotoMaxIterLabel': 'max',
  'wiz.gotoMaxIterUnit': 'iterations',
  'wiz.gotoTargetSelect': 'pick a step',
  'wiz.condActionStop': 'Stop — end the run',
  'wiz.condActionSkip': 'Skip — skip the next step',
  'wiz.condActionGoto': 'Goto — jump back to an earlier step',
  'wiz.rollbackFiresFailed': 'Failed (runtime error)',
  'wiz.rollbackSkipsCancelled': 'Cancelled (you clicked "Stop")',
  'wiz.rollbackSkipsGuard': 'StoppedByGuard (limit reached)',
  'wiz.rollbackSkipsReject': 'Gate Reject (you rejected)',
  'wiz.availableSignals': 'Available signals (conditions)',
  'wiz.signalNoResults': 'Nothing found or nothing to report',
  'wiz.signalContinue': 'Continue normally (default)',
  'wiz.clickToInsert': 'Click to insert',
  'wiz.example': 'Example: multi-step workflow',
  'wiz.stepName': 'Step name',
  'wiz.advanced': 'Advanced',
  'wiz.mode': 'Mode',
  'wiz.debateAgents': 'Debate agents',
  'wiz.maxRounds': 'Max rounds',
  'wiz.agentSettings': 'Agent settings (optional)',
  'wiz.model': 'Model',
  'wiz.modelPlaceholder': 'e.g.: o3',
  'wiz.stallTimeout': 'Stall timeout (sec)',
  'wiz.delayAfter': 'Delay after (sec)',
  'wiz.retry': 'Retry',
  'wiz.conditions': 'Conditions (on_result)',
  'wiz.showMore': 'Show more',
  'wiz.showLess': 'Show less',
  'wiz.cronSwitchSimple': 'Simple mode',
  'wiz.testStep': 'Test',
  'wiz.testInfo': 'Test this step in isolation. In read-only mode, the agent describes what it would do without making any changes (no Jira comments, no commits, no writes). Uncheck to execute for real.',
  'wiz.testMockInput': 'Previous step data (mock)',
  'wiz.testMockPlaceholder': 'Paste the JSON or text the previous step would have produced...',
  'wiz.testDryRun': 'Read-only (dry-run)',
  'wiz.testRun': 'Run test',
  'wiz.testRunning': 'Running...',
  'wiz.testCancel': 'Cancel',
  'wiz.testLogConnecting': 'Connecting to agent...',
  'wiz.testLogStarted': 'Step "{0}" started — agent is working...',
  'wiz.testLogDone': 'Done — status: {0}',
  'wiz.ifContains': 'if contains',
  'wiz.noResultsPlaceholder': 'NO_RESULTS (required)',
  'wiz.noResultsStop': 'No results → Stop',
  'wiz.addStep': 'Add a step',
  'wiz.errorNoName': 'Workflow name required',
  'wiz.errorNoPrompt': 'Prompt missing for "{0}"',
  'wiz.agentQpPicker': 'From an existing Quick Prompt',
  'wiz.agentQpPickerInline': '— None (inline prompt below) —',
  'wiz.agentQpInheritedFrom': 'Inherited from « {0} »',
  'wiz.agentQpInheritedHint': 'The QP\'s `prompt_template`, `tier`, and `skill_ids` are loaded at run-time. `{{var}}` placeholders are resolved against the workflow context (launch variables / state / previous_step…).',
  'wiz.agentQpOverridePlaceholder': 'Prompt override — leave empty to keep the Quick Prompt\'s prompt. Fill to override for this step only.',
  'wiz.qrefOverrideActive': 'override active',
  'wiz.qrefOverrideActiveHint': 'You filled an override below. It overrides the corresponding QP/QA value for this step only.',
  'wiz.qrefPreview': 'Prompt:',
  'wiz.qrefVars': 'Variables:',
  'wiz.qrefEmpty': '(empty prompt)',
  'wiz.qrefOverridePromptToggle': 'Customize the prompt for this step (override)',
  'wiz.errorBatchNoQP': 'No Quick Prompt selected for "{0}"',
  'wiz.errorBatchNoItemsFrom': 'Items list missing for "{0}" (e.g. {{steps.step1.data.tickets}})',
  'wiz.errorApiNoPlugin': 'API plugin not selected for "{0}"',
  'wiz.errorApiNoEndpoint': 'Endpoint missing for "{0}"',
  'wiz.errorNotifyNoUrl': 'Webhook URL missing for "{0}"',
  'wiz.errorGateNoMessage': 'Approval message missing for "{0}"',
  'wiz.errorExecNoCommand': 'Shell command missing for "{0}"',
  'wiz.errorNoCondition': 'Empty condition in "{0}" (condition #{1})',
  'wiz.security': 'Security',
  'wiz.sandbox': 'Sandbox (Docker)',
  'wiz.requireApproval': 'Approval required',
  'wiz.maxFiles': 'Max files modified',
  'wiz.maxLines': 'Max lines modified',
  'wiz.concurrency': 'Concurrency limit (simultaneous runs)',
  'wiz.hooks': 'Workspace hooks (optional)',
  'wiz.hooksHint': 'Shell commands executed at each phase of the git worktree',
  'wiz.hookAfterCreate': 'After creation',
  'wiz.hookBeforeRun': 'Before execution',
  'wiz.hookAfterRun': 'After execution',
  'wiz.hookBeforeRemove': 'Before removal',
  'wiz.previous': 'Previous',
  'wiz.next': 'Next',
  'wiz.create': 'Create',
  'wiz.saveError': 'Save failed. Check your step config.',
  'wiz.save': 'Save',

  // ── Skills (WHAT) ──
  'skills.title': 'Skills',
  'skills.active': 'Active skills',
  'skills.add': 'Add skill',
  'skills.remove': 'Remove',
  'skills.builtin': 'Built-in',
  'skills.custom': 'Custom',
  'skills.language': 'Language',
  'skills.domain': 'Domain',
  'skills.business': 'Business',
  'skills.noSkills': 'No skills selected',
  'skills.selectSkills': 'Select skills...',
  'skills.createCustom': 'Create custom skill',
  'skills.name': 'Name',
  'skills.icon': 'Icon',
  'skills.category': 'Category',
  'skills.description': 'Short description',
  'skills.descriptionPlaceholder': 'What this skill brings in one sentence',
  'skills.content': 'Content (system prompt)',
  'skills.deleteConfirm': 'Delete this skill?',
  'skills.editCustom': 'Edit this skill',
  'skills.saveChanges': 'Save changes',
  'skills.projectDefaults': 'Default skills',
  'skills.projectDefaultsHint': 'Pre-selected for discussions and workflows of this project',
  'skills.searchProjects': 'Search projects…',

  // ── Profiles (WHO) ──
  'profiles.title': 'Agent Profiles',
  'profiles.select': 'Agent profile',
  'profiles.none': 'No profile',
  'profiles.builtin': 'Built-in',
  'profiles.clickToEditName': 'Click to edit persona name',
  'profiles.custom': 'Custom',
  'profiles.technical': 'Technical',
  'profiles.business': 'Business',
  'profiles.meta': 'Meta',
  'profiles.createCustom': 'Create custom profile',
  'profiles.name': 'Name',
  'profiles.personaName': 'First name',
  'profiles.role': 'Rôle',
  'profiles.avatar': 'Avatar',
  'profiles.color': 'Color',
  'profiles.category': 'Category',
  'profiles.persona': 'Persona (system prompt)',
  'profiles.defaultEngine': 'Default agent',
  'profiles.deleteConfirm': 'Delete this profile?',
  'profiles.projectDefault': 'Default project profile',
  'profiles.projectDefaultHint': 'Pre-selected for new discussions of this project',
  'profiles.searchProjects': 'Search projects…',

  // ── Directives (HOW) ──
  'directives.title': 'Directives',
  'directives.active': 'Active directives',
  'directives.add': 'Add directive',
  'directives.remove': 'Remove',
  'directives.builtin': 'Built-in',
  'directives.custom': 'Custom',
  'directives.source': 'Source',
  'skills.source': 'Source',
  'directives.output': 'Output',
  'directives.language': 'Language',
  'directives.noDirectives': 'No directives selected',
  'directives.selectDirectives': 'Select directives...',
  'directives.createCustom': 'Create custom directive',
  'directives.name': 'Name',
  'directives.icon': 'Icon',
  'directives.category': 'Category',
  'directives.description': 'Short description',
  'directives.descriptionPlaceholder': 'What this directive changes in behavior',
  'directives.content': 'Content (system prompt)',
  'directives.conflicts': 'Conflicts',
  'directives.deleteConfirm': 'Delete this directive?',
  'directives.conflictWarning': 'Conflict detected with:',

  // ── Setup Wizard ──
  'setup.step.agents': 'Agents',
  'setup.step.repos': 'Repositories',
  'setup.step.done': 'Done',
  'setup.subtitle': 'Enter the grid. Command your agents.',
  'setup.aiAgents': 'AI Agents',
  'setup.refresh': 'Refresh',
  'setup.agentsDetected': '{0} agent{1} detected on your system.',
  'setup.noAgentDetected': 'No agent detected — install at least one.',
  'setup.install': 'Install',
  'setup.enabled': 'Enabled',
  'setup.disabled': 'Disabled',
  'setup.continue': 'Continue',
  'setup.skip': 'Skip this step',
  'setup.back': 'Back',
  'setup.detectedRepos': 'Detected repositories',
  'setup.rescan': 'Re-scan',
  'setup.scanningRepos': 'Scanning git repositories...',
  'setup.reposFound': '{0} git repositor{1} found.',
  'setup.hiddenRepos': '+ {0} hidden',
  'setup.noRepoFound': 'No git repository found. Specify a path to scan.',
  'setup.addPath': 'Add another path',
  'setup.scan': 'Scan',
  'setup.hide': 'Hide',
  'setup.show': 'Show',
  'setup.hiddenReposToggle': '{0} {1} hidden repositor{2}',
  'setup.hidden': 'hidden',
  'setup.configDone': 'Setup complete',
  'setup.summary': '{0} agent{1}, {2} repositor{3} detected.',
  'setup.goToDashboard': 'Go to dashboard',
  'setup.preparingDashboard': 'Preparing dashboard...',
  'setup.completingStep1': 'Saving configuration...',
  'setup.completingStep2': 'Registering detected projects...',
  'setup.completingStep3': 'Initializing dashboard...',
  'setup.detecting': 'Detecting agents...',
  'setup.detectingHint': 'Scanning your system (PATH, WSL, npm)... this may take a few seconds.',
  'setup.detectionFailed': 'Detection failed',
  'setup.installFailed': 'Install failed',
  'setup.scanFailed': 'Scan failed',

  // ── Settings: Voice ──
  'settings.voice': 'Voice',
  'settings.sttLabel': 'Speech-to-Text (Whisper)',
  'settings.sttDesc': '100% local via Whisper WASM. The model is downloaded on first use then cached in the browser.',
  'settings.ttsLabel': 'Text-to-Speech (Piper)',
  'settings.ttsDesc': '100% local via Piper WASM. Toggle via the volume button in discussions. The model is downloaded on first use (~50 MB) then cached in the browser.',
  'settings.stallTimeout': 'Agent inactivity timeout',
  'settings.stallTimeoutHint': 'Maximum duration without output before stopping the agent. Increase if your agents run long commands (tests, builds).',
  'settings.stallTimeoutWarning': 'A high timeout gives agents more time to run long operations, which may increase token consumption. Use for large builds and test suites.',
  'settings.debugSection': 'Debug',
  'settings.debugMode': 'Debug mode',
  'settings.debugModeHint': "Enables verbose backend logs (agent detection, project scan, host-path resolution). Useful for diagnosing macOS / cross-platform issues. To force a one-shot without persisting: ./kronn start --debug",
  'settings.debugModeRestart': 'The new log level takes effect on next backend restart. Use ./kronn restart to restart.',
  'settings.debugLogsTitle': 'Backend logs (live)',
  'settings.debugLogsEmpty': 'No logs captured yet. Interact with the app to trigger events.',
  'settings.debugLogsHint': 'Source: in-memory ringbuffer fed by every tracing event. No file on disk. Use Ctrl+F inside the viewer to search.',
  'settings.debugLogsRefresh': 'Refresh',
  'settings.debugLogsStartFollow': 'Follow',
  'settings.debugLogsStopFollow': 'Pause',
  'settings.debugLogsCopy': 'Copy',
  'settings.debugLogsClear': 'Clear',
  'settings.debugBufferedCount': '{0} / {1} lines',
  'settings.debugReportCta': 'Report a bug on GitHub',
  'settings.debugReportHint': 'Opens a new tab with the logs pre-filled (secrets redacted). Review before submitting.',
  'settings.debugReportIssueList': 'View existing issues',
  'settings.debugLiveLabel': 'LIVE',
  'common.on': 'On',
  'common.off': 'Off',

  // ── Discussions: Voice & misc ──
  'disc.recording': 'Recording...',
  'disc.transcribing': 'Transcribing...',
  'disc.resumeListening': 'Resuming listening...',
  'disc.cancelVoice': 'Cancel',
  'disc.sendVoice': 'Send',
  'disc.stopRecording': 'Stop',
  'disc.micStop': 'Stop',
  'disc.micDictate': 'Dictate',
  'disc.draftRestored': 'Draft restored · written {0}',
  'disc.draftDismiss': 'Discard draft',
  'disc.voiceModeOn': 'Voice conversation mode',
  'disc.voiceModeOff': 'Disable conversation mode',
  'disc.logs': 'Logs',
  'disc.editTimeout': 'Edit timeout',
  'disc.ttsDisable': 'Disable voice playback',
  'disc.ttsEnable': 'Enable voice playback',

  // ── Common ──
  'common.cancel': 'Cancel',
  'common.dismiss': 'Dismiss',
  'common.save': 'Save',
  'common.delete': 'Delete',
  'common.close': 'Close',
  'common.add': 'Add',
  'common.copy': 'Copy',
  'common.loading': 'Loading...',
  'common.seeMore': 'See more',
  'common.seeLess': 'See less',

  // Workflow execution guards (0.7.0)
  'wf.guards.title': 'Execution limits',
  'wf.guards.description': 'Safety net against runaway loops, infinite runs and unexpected budgets. Leave blank to use defaults.',
  'wf.guards.summaryDefaults': 'Defaults: {0} min · {1} AI calls · {2} revisits/step',
  'wf.guards.summaryActive': 'Custom limits',
  'wf.guards.timeoutLabel': 'Max duration',
  'wf.guards.timeoutUnit': '(minutes)',
  'wf.guards.timeoutHint': 'Default {0} min — total wall-clock elapsed.',
  'wf.guards.maxLlmCallsLabel': 'Max AI calls',
  'wf.guards.maxLlmCallsHint': 'Default {0} — Agent = 1, BatchQP = 1, ApiCall/Notify = 0.',
  'wf.guards.loopDetectionLabel': 'Loop detection',
  'wf.guards.loopDetectionHint': 'Default {0} — max revisits of the same step via Goto.',
  'wf.guards.stoppedBy.title': 'Stopped by guard',
  'wf.guards.stoppedBy.timeout': 'Max duration reached ({0} s)',
  'wf.guards.stoppedBy.maxLlmCalls': 'Too many AI calls ({0} of {1})',
  'wf.guards.stoppedBy.loopDetection': 'Loop detected on step "{0}" ({1} times)',

  // ── Stale-stream watchdog (TD-20260504) ──
  'discussions.streamRecovered': 'Connection to the agent was lost — latest state reloaded',

  // ── Migration ai/ → docs/ banner ──
  'migration.title': 'Legacy `ai/` convention detected',
  'migration.desc': 'This project still uses `ai/index.md`. Migrate to the `docs/AGENTS.md` convention (Kronn 0.7.1+) — `git mv` preserves history, internal refs are rewritten.',
  'migration.symlink': 'Create an `ai → docs` symlink for retro-compatibility',
  'migration.cta': 'Migrate to docs/',
  'migration.ctaPending': 'Migrating…',
  'migration.ctaDone': 'Migration complete',
  'migration.inProgress': 'Moving files, rewriting refs…',
  'migration.successInline': '{0} files moved — `docs/AGENTS.md` ready',
  'migration.successToast': 'Migration succeeded ({0} files moved)',
  'migration.alreadyToast': 'Project already migrated',
  'migration.failedToast': 'Migration failed for {0}',
  'migration.failedGeneric': 'Migration failed — check server logs',

  // ── User context (cross-project agent prompts) ──
  'userContext.title': 'My contexts',
  'userContext.subtitle': 'Markdown notes injected into every agent, regardless of project or CLI. Stored in ~/.kronn/user-context/.',
  'userContext.newNamePlaceholder': 'file-name (will append .md)',
  'userContext.newNameAria': 'New context file name',
  'userContext.add': 'Add',
  'userContext.deleteConfirm': 'Delete file "{0}"?',
  'userContext.deleteAria': 'Delete {0}',
  'userContext.delete': 'Delete',
  'userContext.loading': 'Loading…',
  'userContext.empty': 'No files yet. Add one to share notes with all your agents.',
  'userContext.save': 'Save',
  'userContext.editingAria': 'Editing {0}',
};

const es: TranslationDict = {
  // ── Nav & Tabs ──
  'nav.projects': 'Proyectos',
  'nav.discussions': 'Discusiones',
  'nav.mcps': 'Plugins',
  'nav.workflows': 'Automatización',
  'nav.config': 'Config',
  'nav.scan': 'Escanear',

  // ── Projects page ──
  'projects.title': 'Proyectos',
  'projects.aiReady': 'AI-ready',
  'projects.hidden': 'oculto',
  'projects.hiddenPlural': 'ocultos',
  'projects.hideHidden': 'Ocultar proyectos ocultos',
  'projects.showHidden': 'Mostrar proyectos ocultos',
  'projects.search': 'Buscar un proyecto...',
  'projects.group.local': 'Proyectos locales',
  'projects.group.other': 'Otros',
  'projects.showMore': 'Ver más ({0} proyecto{1} restante{2})',
  'projects.collapse': 'Reducir',
  'projects.empty': 'Sin proyectos. Haga clic en "Escanear" para detectar sus repositorios.',
  'projects.emptyHint': 'Sin proyectos por ahora. Use el botón + arriba para agregar una carpeta, clonar un repositorio o crear un proyecto nuevo.',
  'projects.emptySearch': 'Ningún proyecto encontrado.',
  'projects.manageMcps': 'Gestionar en Plugins',
  'projects.noMcp': 'Sin plugin — configurar',
  'projects.mcpHint.beforeAudit': 'Añade algunos plugins MCP antes de lanzar briefing o auditoría — enriquecen mucho los resultados.',
  'projects.mcpHint.cta': 'Añadir un plugin',
  'projects.delete': 'Eliminar',
  'projects.deleteConfirm': 'Eliminar este proyecto de Kronn?',
  'projects.deleteHard': 'Eliminar permanentemente',
  'projects.deleteHardWarn': 'Esta acción es irreversible. La carpeta del proyecto será eliminada del disco.',
  'projects.deleteHardConfirmLabel': 'Escriba el nombre del proyecto para confirmar',
  'projects.deleteSoft': 'Retirar de Kronn',
  'projects.skills': 'Skills',
  'projects.noSkills': 'Sin skills disponibles — crear en Config',
  // 0.8.3 — Linked repos / companion projects
  'linkedRepos.title': 'Repos vinculados',
  'linkedRepos.hint': 'Repos compañeros (API, IaC, design…) que los agentes deben conocer para el contexto cross-repo. Inyectados en el system prompt de cada audit / discusión / workflow.',
  'linkedRepos.empty': 'Sin repos vinculados — añade uno para dar contexto cross-repo a los agentes.',
  'linkedRepos.addLink': 'Añadir un repo vinculado',
  'linkedRepos.addBtn': 'Añadir',
  'linkedRepos.remove': 'Quitar',
  'linkedRepos.confirmRemove': '¿Quitar este repo vinculado? Los agentes ya no lo verán en su contexto.',
  'linkedRepos.namePlaceholder': 'Nombre (ej. backend-api)',
  'linkedRepos.locationPlaceholder': 'Ruta o URL (ej. /home/user/Repos/backend-api o https://github.com/org/backend-api)',
  'linkedRepos.descriptionPlaceholder': '¿Para qué sirve este repo? (opcional — ej. "GraphQL schema del frontend")',
  'linkedRepos.requiredField': 'El nombre y la ubicación son obligatorios.',
  'linkedRepos.maxReached': 'Límite alcanzado (máx. 20 repos vinculados).',
  'linkedRepos.saveError': 'Error al guardar los repos vinculados.',
  // 0.8.6 (#27) — autocomplete picker
  'linkedRepos.pickerLabel': 'Tus proyectos Kronn — haz clic para auto-rellenar',
  'linkedRepos.pickerMoreHint': '{0} más',
  'linkedRepos.pickerShowLess': 'Mostrar menos',
  'projects.workflows': 'Workflows',
  'projects.noWorkflows': 'Sin workflow — crear',
  'projects.loading': 'Cargando proyectos…',
  'projects.scanning': 'Escaneando…',
  'projects.scanFound': '{0} nuevo(s) proyecto(s) detectado(s)',
  'projects.scanNone': 'Ningún proyecto nuevo detectado',
  'projects.scanError': 'Error durante el escaneo',
  'projects.bootstrap': 'Agregar un proyecto',
  'projects.bootstrap.name': 'Nombre del proyecto',
  'projects.bootstrap.desc': 'Descripción del proyecto',
  'projects.bootstrap.descPlaceholder': 'Describe tu proyecto: objetivos, funcionalidades, restricciones...',
  'projects.bootstrap.docs': 'Documentos (opcional)',
  'projects.bootstrap.docsHint': 'Arrastre o haga clic — arquitectura, specs, PRD...',
  'projects.bootstrap.options': 'Opciones avanzadas',
  'projects.bootstrap.noCreateRepo': 'No crear repositorio Git',
  'projects.bootstrap.createRepoVia': 'Crear repositorio vía',
  'projects.bootstrap.noGeneratePlan': 'No generar plan/issues',
  'projects.bootstrap.generatePlanVia': 'Plan & issues vía',
  'projects.bootstrap.creating': 'Creando...',
  'projects.bootstrap.start': 'Crear y lanzar bootstrap',
  'projects.bootstrap.mcps': 'Plugins a activar',
  'projects.addFolder': 'Carpeta local',
  'projects.folder.hint': 'Agrega una carpeta como proyecto — no se requiere git. Si hay un .git presente, Kronn lo detecta automáticamente.',
  'projects.folder.path': 'Ruta de la carpeta',
  'projects.folder.name': 'Nombre (opcional, deducido de la carpeta)',
  'projects.folder.namePlaceholder': 'Deducido automáticamente si está vacío',
  'projects.folder.add': 'Agregar',
  'projects.folder.success': 'Proyecto agregado',

  'projects.clone': 'Clonar un repositorio',
  'projects.clone.url': 'URL del repositorio',
  'projects.clone.urlPlaceholder': 'https://github.com/... o git@github.com:...',
  'projects.clone.name': 'Nombre (opcional)',
  'projects.clone.start': 'Clonar',
  'projects.clone.cloning': 'Clonando...',
  'projects.clone.success': 'Repositorio clonado con éxito',
  'projects.clone.discover': 'Detectar mis repositorios',
  'projects.clone.discovering': 'Detectando...',
  'projects.clone.discovered': '{0} repositorio(s) detectado(s) desde {1}',
  'projects.clone.noToken': 'No se encontró token GitHub/GitLab. Configure el plugin GitHub o GitLab en la pestaña Plugins.',
  'projects.clone.alreadyCloned': 'Ya en Kronn',
  'projects.clone.stars': 'estrellas',
  'projects.clone.searchRepos': 'Filtrar repositorios...',
  'projects.clone.selectSources': 'Fuentes a consultar',
  'projects.docAi': 'Documentación del proyecto',
  'projects.techDebtBadge': '{0} elemento(s) de deuda técnica por tratar — ver docs/tech-debt/',
  'projects.docAi.selectFile': 'Seleccione un archivo para verlo',
  'projects.docAi.loading': 'Cargando...',
  'projects.docAi.empty': 'Sin documentación de proyecto disponible',
  'projects.docAi.search': 'Buscar...',
  'projects.docAi.noResults': 'Sin resultados',
  'projects.docAi.file': 'archivo',
  'projects.docAi.files': 'archivos',
  'projects.docAi.discuss': 'Discutir este archivo',
  'projects.docAi.fixThis': 'Resolver este problema',
  'projects.docAi.fixThisPrompt': "Aquí hay una deuda técnica documentada en `{0}` — ayúdame a resolverla. Lee el archivo, propón un plan corto (1-3 pasos), luego ejecuta los cambios de código necesarios para arreglarlo. Una vez resuelto, actualiza el archivo TD-* (estado → resuelto, o elimínalo) y la fila correspondiente en `docs/inconsistencies-tech-debt.md`.\n\nInicio:",
  'projects.docAi.banner.runAudit': "Lanza una auditoría IA para (re)documentar el proyecto completo y descubrir su deuda técnica y sus incoherencias.",
  'projects.docAi.banner.auditRunning': "Auditoría en curso — la documentación se construye paso a paso. Los archivos abajo se rellenan a medida que avanza; los placeholders restantes serán reemplazados antes del final.",
  'projects.docAi.banner.bootstrapDone': "Bootstrap terminado. Lanza la auditoría completa para generar la documentación detallada del proyecto.",
  'projects.docAi.banner.validate': "Valida la auditoría para tener una documentación actualizada y más relevante (la IA hace preguntas sobre las ambigüedades).",
  // 0.8.4 (#298) — post-audit step recap panel
  'projects.docAi.auditRecap.title': 'Detalles de la última auditoría',
  'projects.docAi.auditRecap.stepsLabel': 'pasos',
  'projects.docAi.auditRecap.runsLabel': 'auditorías',
  'projects.docAi.auditRecap.filterAll': 'Todas',
  'projects.docAi.auditRecap.showMoreChips': '+ {0} más antiguas',
  'projects.docAi.auditRecap.showLessChips': '– Reducir',
  'projects.docAi.auditRecap.openDrawer': 'Ver auditorías previas ({0})',
  'projects.docAi.auditRecap.drawerTitle': 'Historial de auditorías',
  'projects.docAi.auditRecap.metaDurationTooltip': 'Duración total de la auditoría (desde el lanzamiento hasta el final del Step 10).',
  'projects.docAi.auditRecap.metaTdTooltip': 'Número de Tech Debts (TDs) detectadas. Haz clic en la tarjeta para ver el detalle por etapa.',
  'projects.docAi.auditRecap.metaHealthTooltip': 'Puntuación de salud del proyecto (0-100). Calculada desde la severidad de las TDs: 100 = sin deuda crítica, 0 = saturación crítica. Pesos: Critical = -12, High = -5, Medium = -2, Low = -0.3.',
  'projects.docAi.auditRecap.colStepTooltip': 'Número de etapa en el pipeline (1-10 para auditoría global, 1-1 para focalizada). Haz clic para ordenar.',
  'projects.docAi.auditRecap.colFileTooltip': 'Archivo `docs/*.md` producido o refinado por esta etapa. El marcador 🔧 indica reparación desde plantilla (placeholder leak detectado).',
  'projects.docAi.auditRecap.colDurationTooltip': 'Duración de esta etapa únicamente (Δ entre step_start y step_done). Haz clic para ordenar — útil para identificar la etapa más lenta.',
  'projects.docAi.auditRecap.colTokensTooltip': 'Tokens consumidos por esta etapa (máx. input + output, no acumulativo). Haz clic para ordenar — útil para identificar la etapa más cara.',
  'projects.docAi.auditRecap.totalsPrefix': 'Total:',
  'projects.docAi.auditRecap.colFile': 'Archivo',
  'projects.docAi.auditRecap.colDuration': 'Duración',
  'projects.docAi.auditRecap.colTokens': 'Tokens',
  'projects.docAi.auditRecap.empty': 'Sin métricas por paso para esta auditoría (corrida pre-0.8.4).',
  'projects.docAi.auditRecap.repairedHint': 'Este paso escribió un archivo vacío o un placeholder — reparado desde la plantilla.',
  // 0.8.4 (#287) — sub-audit modal
  'audit.kindSelector.tooltip': "Tipo de auditoría a lanzar. Las auditorías focalizadas (Seguridad, RGAA, Database…) se desbloquean tras la auditoría global.",
  'audit.kindSelector.reAuditHint': "Vuelve a lanzar una auditoría temática (seguridad, RGAA, base de datos…) para profundizar en un punto — o relanza la global para refrescar toda la documentación.",
  'audit.kindSelector.launchLabel': "Lanzar: {0}",
  'audit.kind.Full': "Auditoría global (10 etapas)",
  'audit.kind.Security': "Seguridad",
  'audit.kind.Docker': "Docker",
  'audit.kind.Performance': "Rendimiento",
  'audit.kind.Accessibility': "Accesibilidad (WCAG 2.1)",
  'audit.kind.Rgaa': "RGAA 4.1 (Francia)",
  'audit.kind.Database': "Base de datos",
  'audit.kind.ApiDesign': "Diseño de API",
  'audit.kind.afterFull': "(tras auditoría global)",
  'audit.subAudit.modalLabel': 'Elegir tipo de auditoría',
  'audit.subAudit.title': '¿Qué auditoría lanzar?',
  'audit.subAudit.intro': 'Elige una auditoría global (las 10 etapas, ~20 min) o una auditoría focalizada en una dimensión (1 etapa, más rápida).',
  'audit.subAudit.fullTitle': 'Auditoría global (10 etapas)',
  'audit.subAudit.fullDesc': 'Pipeline completo: docs del proyecto, repo-map, arquitectura, diagramas Mermaid, deuda técnica, recomendaciones.',
  'audit.subAudit.targetedSectionTitle': 'Auditorías focalizadas (1 etapa)',
  'audit.subAudit.cta': 'Auditoría focalizada',
  'audit.subAudit.kind.Security.title': 'Seguridad',
  'audit.subAudit.kind.Security.desc': 'Auth, secretos, validación de entradas, headers, dependencias vulnerables.',
  'audit.subAudit.kind.Docker.title': 'Docker',
  'audit.subAudit.kind.Docker.desc': 'Dockerfile, multi-stage, layer caching, tamaño imagen, healthcheck, secretos.',
  'audit.subAudit.kind.Performance.title': 'Rendimiento',
  'audit.subAudit.kind.Performance.desc': 'Hot paths, bundle size, lazy-load, queries N+1, índices faltantes.',
  'audit.subAudit.kind.Accessibility.title': 'Accesibilidad (WCAG 2.1)',
  'audit.subAudit.kind.Accessibility.desc': 'HTML semántico, ARIA, focus, contraste, alternativas. Estándar internacional.',
  'audit.subAudit.kind.Rgaa.title': 'RGAA 4.1 (Francia)',
  'audit.subAudit.kind.Rgaa.desc': 'Norma francesa obligatoria (decreto 2019-768). Más estricta que WCAG. Incluye un apartado audit manual + formación.',
  'audit.subAudit.kind.Database.title': 'Base de datos',
  'audit.subAudit.kind.Database.desc': 'Schema, migraciones, índices, ORM N+1, integridad referencial.',
  'audit.subAudit.kind.ApiDesign.title': 'Diseño de API',
  'audit.subAudit.kind.ApiDesign.desc': 'Contrato, versionado, paginación, authn/authz, rate limiting, doc drift.',
  'projects.docAi.discussPrompt': 'Quiero discutir el contenido del archivo `{0}` de la documentación del proyecto. Aquí mis observaciones:\n\n',
  'projects.status.valid': 'validado',
  'projects.status.validating': 'validando...',
  'projects.status.auditOk': 'audit OK',
  'projects.status.template': 'template',
  'projects.status.none': 'ninguno',
  'projects.status.bootstrapped': 'MVP definido',
  'projects.status.bootstrapping': 'bootstrap...',

  // ── AI Audit ──
  'audit.noTemplate': 'No hay template instalado. Instale el template para estructurar la documentación del proyecto.',
  'audit.installTemplate': 'Instalar template AI',
  'audit.description': 'La auditoría AI analiza todo el proyecto en 10 pasos y completa la doc docs/.',
  'audit.warning': 'Duración estimada: ~20 minutos. Alto consumo de tokens.',
  'audit.launch': 'Lanzar auditoría AI',
  'audit.startFullAudit': 'Iniciar Auditoría AI',
  'audit.resumeFromStep': 'Reanudar en el paso {0}/10',
  'audit.resumeFromTooltip': 'La auditoría anterior se interrumpió tras el paso {0}. Los pasos ya completados se saltarán para ahorrar tokens y tiempo.',
  'audit.fullAuditDesc': 'Instala la plantilla, ejecuta una auditoría de 10 pasos y crea la discusión de validación.',
  'audit.briefingLabel': 'Briefing del proyecto (opcional)',
  'audit.briefingPlaceholder': 'Describa su proyecto brevemente: stack, convenciones, puntos clave...',
  'audit.fullAuditDone': 'Auditoría completada! Discusión de validación creada.',
  'audit.templateStep': 'Instalando plantilla...',
  'audit.step': 'Paso {0}/{1} — {2}',
  'audit.elapsed': '⏱ {0}',
  'audit.elapsedTooltip': 'Tiempo transcurrido desde el inicio de la auditoría (contador del lado del cliente, anclado en started_at del servidor).',
  'audit.lastStepTokens': '💬 {0} tk',
  'audit.lastStepTokensTooltip': 'Tokens gastados en el último paso finalizado (max(input+output) — los agentes reportan uso acumulado por llamada, por eso tomamos el máximo, no una suma).',
  'audit.totalTokens': 'Σ {0} tk',
  'audit.totalTokensTooltip': 'Tokens acumulados de todos los pasos finalizados desde el inicio de la auditoría. Para identificar el paso más costoso: observa el delta entre dos step_done consecutivos.',
  'audit.currentTool': '🔧 {0}',
  'audit.currentToolTooltip': 'Herramienta que el agente acaba de invocar (Read, Glob, Bash, mcp__...). Last-write-wins — el chip se actualiza con cada nueva herramienta y se borra al step_done.',
  'audit.validationInProgress': 'Validación en curso — {0} mensajes intercambiados.',
  'audit.activityInProgress': 'Actividad en curso en este proyecto',
  'audit.validationHint': 'La validación se completa desde la discusión cuando la IA confirma que todo está resuelto.',
  'audit.resumeValidation': 'Reanudar validación',
  'audit.readyToValidate': 'Documentación del proyecto generada. Valide la auditoría para resolver ambigüedades con la IA.',
  'audit.validate': 'Validar auditoría',
  'audit.done': 'Auditoría validada. Documentación del proyecto lista en docs/.',
  'audit.markValid': 'Marcar auditoría como validada',
  'audit.validationComplete': 'La IA confirma que la validación está completa.',
  'audit.autopilotCtaText': 'Auditoría validada. Conecta AutoPilot para procesar los tickets de este proyecto automáticamente.',
  'audit.autopilotCtaBtn': 'Configurar AutoPilot',
  'audit.trackerHint': 'Auditoría más completa con un tracker conectado (GitHub / GitLab / Jira / Linear) — la Fase 3 propone crear tickets y AutoPilot trabaja sobre issues reales.',
  'audit.trackerHintConfigure': 'Configurar un tracker',
  'audit.trackerHintDismiss': 'No mostrar más para este proyecto',
  'audit.validationTitle': 'Validación auditoría AI',
  'audit.validationPrompt': 'Aquí está la documentación del proyecto (carpeta docs/). Sigue el protocolo estricto de validación en 4 fases. NO emitas KRONN:VALIDATION_COMPLETE hasta que las 4 fases estén completas.\n\n## Fase 1 — Auto-corrección (autónoma)\nLee el código fuente y corrige lo que puedas: marcadores TODO huérfanos, archivos esqueleto, info desactualizada. Actualiza los archivos docs/ directamente.\n\n## Fase 2 — Preguntas de ambigüedad (interactiva)\nHaz tus preguntas una por una. Con cada respuesta, actualiza los archivos docs/ antes de la siguiente pregunta. Si una respuesta revela nuevas incógnitas, haz preguntas de seguimiento.\n\n## Fase 3 — Revisión deuda técnica (interactiva)\nRevisa CADA entrada de docs/inconsistencies-tech-debt.md y docs/tech-debt/. Para cada item: ¿es correcta la evaluación? ¿Qué prioridad? ¿Crear un ticket?\n\n## Fase 4 — Desafío documentación (interactiva)\nHazme 2-3 preguntas prácticas que un dev nuevo haría. Verifica si la doc las responde. Si insuficiente, actualiza y haz otra pregunta.\n\nSolo cuando las 4 fases estén completas, termina con: "KRONN:VALIDATION_COMPLETE".',
  'audit.validationFilesFilled': 'Archivos de doc completados:',
  'audit.validationFilesIncomplete': 'Archivos de doc incompletos:',
  'audit.validationFilesNone': '(ninguno)',
  'audit.validationTodos': 'Marcadores TODO restantes ({0}):',
  'audit.cancelAudit': 'Cancelar',
  'audit.cancelled': 'Auditoría cancelada. Todos los archivos han sido eliminados.',
  'audit.streamError': 'La auditoría se detuvo por un error: {0}',
  'audit.stepWarning': '⚠ El paso `{0}` no produjo nada útil — {1}. {2}',
  'audit.stepWarningRepaired': 'Plantilla restaurada; relanza la auditoría para recuperar el contenido.',
  'audit.stepWarningNotRepaired': 'No hay plantilla para restaurar; revisa el archivo manualmente.',
  'audit.viewTechDebts': 'Ver {0} TDs →',
  'audit.viewTechDebtsAfterValidation': 'Ver todos los Tech Debts del proyecto',
  'audit.partialStreamError': 'La auditoría parcial se detuvo por un error: {0}',
  'audit.bootstrapInProgress': 'Bootstrap en curso...',
  'audit.resumeBootstrap': 'Reanudar bootstrap',
  'audit.bootstrapComplete': 'El bootstrap está completo. El MVP del proyecto está definido.',
  'audit.markBootstrapped': 'Marcar como definido',
  'audit.bootstrapDone': 'MVP definido. Ejecute la auditoría para generar la documentación del proyecto completa.',
  'audit.staleSections': '{0} sección(es) obsoleta(s)',
  'audit.updateStale': 'Actualizar ({0} secciones)',
  'audit.noStale': 'Auditoría actualizada',
  'audit.auditDate': 'Auditado el {0}',
  'audit.startBriefing': 'Briefing (IA)',
  'audit.briefingDesc': 'Discusión rápida (5 min) para dar contexto a la auditoría.',
  'briefing.formBtn': 'Configurar briefing',
  'briefing.formBtnTooltip': 'Rellena las 6 preguntas del formulario. Al guardar, un agente IA revisa tus respuestas y solo pregunta aclaraciones sobre los puntos ambiguos, luego escribe el briefing final.',
  'briefing.formTitle': 'Briefing del proyecto',
  'briefing.formIntro': 'Responde las 6 preguntas. Al guardar, un agente IA revisa tus respuestas, pide aclaraciones solo si es necesario, y escribe `docs/briefing.md`. No hay que hacerlo en dos pasos.',
  'briefing.saveAndReviewBtn': 'Guardar + revisión IA',
  'briefing.savedButNoDiscToast': 'Briefing guardado, pero la revisión IA no pudo iniciarse. La auditoría usará tus respuestas igualmente.',
  'briefing.q1Purpose': '1. ¿Qué hace este proyecto?',
  'briefing.q1Placeholder': 'Una frase. Qué hace para sus usuarios.',
  'briefing.q2Team': '2. ¿Quién trabaja en él?',
  'briefing.q2Placeholder': 'solo / equipo pequeño / equipo grande',
  'briefing.q3Maturity': '3. ¿En qué etapa está?',
  'briefing.q3Placeholder': 'prototipo, MVP, producción, legacy, rewrite…',
  'briefing.q4Dependencies': '4. ¿Dependencias externas clave?',
  'briefing.q4Placeholder': 'Ejemplos: "PostgreSQL en AWS RDS", "user-service API en gitlab.company.com/org/repo" — o "ninguna".',
  'briefing.q5Traps': '5. ¿Qué haría mal un nuevo colaborador el primer día?',
  'briefing.q5Placeholder': 'trampas, reglas implícitas, áreas frágiles',
  'briefing.q6Additional': '6. ¿Algo más que la auditoría debería saber?',
  'briefing.q6Placeholder': 'Opcional — sé breve.',
  'briefing.saveBtn': 'Guardar briefing',
  'briefing.savedToast': 'Briefing guardado (docs/briefing.md).',
  'briefing.missingFields': 'Faltan campos obligatorios: {0}',
  'audit.briefingDone': 'Briefing completado!',
  'audit.resumeBriefing': 'Reanudar briefing',
  'audit.goToProject': 'Ver proyecto',
  'audit.runningMcpFilterBanner': 'Auditoría IA en curso en este proyecto — algunos MCPs están temporalmente deshabilitados para acelerar la auditoría. Los MCPs habituales volverán automáticamente al finalizar la auditoría. Si necesitas el set completo ahora, espera unos minutos o abre esta discusión en otro proyecto.',
  'audit.activeAuditsTitle': 'Auditorías en curso',
  'audit.activeAuditsEmpty': 'Ninguna auditoría en curso',
  'audit.activeAuditsStep': 'Paso {0}/{1}',
  'audit.runningTooltip': '{0} auditoría{1} en curso',
  'audit.viewAllProjects': 'Ver todos los proyectos',
  'mermaid.parseError': 'No se pudo renderizar este diagrama Mermaid — revisa la sintaxis abajo.',
  'mermaid.errorDetails': 'Detalles del error',
  'mermaid.showSource': 'Ver código fuente',
  'mermaid.hideSource': 'Ocultar código fuente',
  'mermaid.fullscreen': 'Pantalla completa',
  'mermaid.closeFullscreen': 'Cerrar pantalla completa',
  'mermaid.print': 'Imprimir',
  'audit.unauditedWarning': 'Este proyecto aún no tiene una auditoría IA validada. Comienza con un briefing del proyecto para dar contexto de negocio — la auditoría genera luego una fuente de verdad compartida por TODOS los agentes (Claude Code, Cursor, Vibe, Ollama…) que sobrevive a los cambios de agente, de sesión y de equipo.',
  'audit.unauditedWarningWithBriefing': 'Briefing realizado, pero la auditoría IA aún no se ha lanzado. Lánzala para generar la documentación Kronn (`docs/AGENTS.md` + estructura) que sirve de fuente de verdad **entre agentes**: Claude Code, Cursor, Vibe, Ollama y otros leen el mismo contexto. Evita divergencias entre agentes y preserva el conocimiento del proyecto más allá de una sesión.',
  'audit.unauditedCtaBriefing': 'Hacer el briefing',
  'audit.unauditedCtaLaunch': 'Lanzar auditoría IA',
  'audit.auditInProgress': 'Auditoría en curso...',
  'audit.auditDoneResume': 'Auditoría completada! La validación está lista.',

  // ── Discussions ──
  'disc.new': 'Nueva',
  'disc.batchFromWorkflowRun': 'run #{0} · {1}',
  'disc.batchFromWorkflow': '{0}',
  'disc.batchParentClickHint': 'Abrir el workflow padre de este batch',
  'disc.batchDeleteHint': 'Eliminar este batch ({0} discusiones)',
  'disc.batchRetryHint': 'Relanzar este batch ({0} discusiones, copia idéntica)',
  'disc.batchRetryConfirm': '¿Relanzar este batch?\n\n« {1} » creará {0} nuevas discusiones con los mismos prompts. El batch anterior se mantiene — bórralo manualmente si necesario.',
  'disc.batchRetryToast': 'Batch relanzado ({0} discusiones creadas)',
  'disc.batchRetryError': 'Reintento fallido: {0}',
  'disc.batchRetryEmpty': 'No hay items para reintentar (las discusiones pueden haber sido eliminadas).',
  'disc.batchDeleteConfirm': '¿Eliminar este batch y sus {0} discusiones?\n\n« {1} »\n\nEsta acción es irreversible.',
  'disc.batchDeletedToast': 'Batch eliminado ({0} discusiones eliminadas)',
  'disc.batchDeleteError': 'Error al eliminar: {0}',
  'disc.stopAgent': 'Detener el agente',
  'disc.stopAgentToast': 'Agente detenido',
  'disc.stopAgentNothing': 'Nada que detener — el agente ya terminó',
  'disc.stopAgentError': 'Error al detener: {0}',
  'disc.partialRecoveredToast': '{0} discusión(es) restauradas tras reinicio — respuestas de agente interrumpidas recuperadas',
  'disc.partialPendingPrompt': 'Una respuesta de agente anterior está en recuperación.\n\n¿Forzar recuperación ahora y enviar tu mensaje?',
  'disc.partialDismissed': 'Recuperación forzada — puedes reenviar tu mensaje',
  'disc.searchPlaceholder': 'Buscar...',
  'disc.searchClear': 'Borrar búsqueda',
  // 0.8.4 (#294) — cross-agent source binding (badge + filter)
  'disc.source.importedHint': 'Discusión importada desde {0}.',
  'disc.source.divergedHint': '{0}: divergió (editada en Kronn después de la importación — un re-push sobrescribiría estos cambios).',
  'disc.source.filterTooltip': 'Filtrar por CLI fuente (Claude Code, Cursor, …)',
  'disc.source.filterAll': 'Todas las fuentes',
  'disc.source.filterFrom': 'Desde {0}',
  'disc.markAllRead': 'Marcar todo como leído',
  'disc.markAllReadTooltip': 'Marcar todas las discusiones ({0} mensajes no leídos) como leídas. Incluye discusiones archivadas y lotes.',
  'disc.newTitle': 'Nueva discusión',
  'disc.project': 'Proyecto (opcional)',
  'disc.noProject': 'Sin proyecto',
  'disc.agent': 'Agente',
  'disc.noAgent': 'Ningún agente instalado',
  'disc.title': 'Título (opcional)',
  'disc.titlePlaceholder': 'Auto-generado desde el prompt...',
  'disc.prompt': 'Prompt inicial',
  'disc.promptPlaceholder': 'Describa lo que desea...',
  'disc.attachFiles': 'Adjuntar archivos de contexto',
  'disc.start': 'Iniciar discusión',
  // 0.8.6 phase 2 — disc-first refactor
  'disc.launchAgentNow': 'Lanzar un agente ahora',
  'disc.launchAgentNowHint': 'Desmarca para preparar tu brief sin agente, o para abrir una sala multi-agente (Claude + Codex...) que invitas luego desde el header.',
  'disc.discFirstHint': 'No se lanzará ningún agente. Puedes invitar a Claude, Codex, Gemini etc. desde el header de la discusión.',
  'disc.discFirstDefaultTitle': 'Discusión sin agente',
  'disc.createEmpty': 'Crear discusión',
  'disc.discFirstCreatedToast': 'Discusión creada. Pulsa «+ Invitar» en el header para añadir un agente.',
  'disc.participantsEmpty': 'Ningún agente adjunto. Pulsa «+ Invitar» para añadir un participante.',
  'disc.invitePeer': '+ Invitar',
  'disc.invitePeerTooltip': 'Genera un token para pegar en otro CLI compatible MCP (Claude, Codex, Gemini…) que se unirá a esta discusión.',
  'disc.inviteFailed': 'Invitación fallida: {0}',
  'disc.inviteCopied': 'Instrucción copiada al portapapeles.',
  'disc.inviteCopyFailed': 'No se pudo copiar — copia el comando manualmente.',
  'disc.inviteModalTitle': 'Invitar a un agente a esta discusión',
  'disc.inviteModalIntro': 'Pega esta instrucción en cada CLI compatible MCP que quieras invitar. El mismo token vale para N agentes durante {0} min:',
  'disc.inviteModalClose': 'Cerrar',
  'disc.inviteCopyBtn': 'Copiar instrucción',
  'disc.inviteExpiresHint': 'Expira a las: {0}',
  'disc.auditWarn': 'La IA analizará toda la documentación del proyecto y el código. Este primer análisis puede tardar unos minutos y consumir tokens.',
  'disc.auditHint': 'Puede haber varias preguntas. La conversación se puede reanudar en cualquier momento desde la pestaña Discusiones.',
  'disc.advancedOptions': 'Skills, Perfiles y Directivas',
  'disc.mcps': 'Plugins disponibles',
  'disc.mcpSearch': 'Filtrar plugins…',
  'disc.noMcps': 'Ningún plugin configurado',
  'disc.editConfig': 'Editar perfiles, skills y directivas',
  'git.title': 'Archivos modificados',
  'git.refresh': 'Actualizar',
  'git.noChanges': 'Sin cambios',
  'git.filesChanged': 'archivo(s) modificado(s)',
  'git.committedOnBranch': '{0} archivo(s) ya confirmado(s) en esta rama (vs {1})',
  'git.onDefaultBranch': 'Está en la rama principal',
  'git.createBranch': 'Crear rama',
  'git.branchName': 'Nombre de la rama...',
  'git.commit': 'Commit',
  'git.commitMessage': 'Mensaje del commit...',
  'git.amend': 'Amend',
  'git.sign': 'Firmar (-S)',
  'git.commitSelected': 'Commitear {0} archivo(s)',
  'git.push': 'Push',
  'git.pushSuccess': 'Push exitoso',
  'git.createPr': 'Crear PR',
  'git.createMr': 'Crear MR',
  'git.prTitle': 'Título del PR',
  'git.prEdit': 'Editar',
  'git.prPreview': 'Vista previa',
  'git.prBodyPlaceholder': 'Descripción del PR (Markdown)...',
  'git.prTemplateProject': 'plantilla proyecto',
  'git.prTemplateKronn': 'plantilla Kronn',
  'git.submitPr': 'Crear Pull Request',
  'git.submitMr': 'Crear Merge Request',
  'git.selectAll': 'Seleccionar todo',
  'git.deselectAll': 'Deseleccionar todo',
  'git.filesBtn': 'Archivos',
  'git.pendingFilesTooltip': '{0} archivo(s) pendiente(s) de commit',
  // ── Test mode (worktree swap) ──
  'testMode.cta': 'Probar esta versión',
  'testMode.ctaTooltip': 'Carga el código de la discusión en tu repo principal — checkout de la rama y pausa el agente mientras pruebas.',
  'testMode.bannerHeadline': 'Estás probando la versión de esta discusión en tu IDE — el agente está en pausa.',
  'testMode.bannerRestore': 'volverá a `{0}`',
  'testMode.exit': 'Parar la prueba',
  'testMode.exitTooltip': 'Volver a tu estado anterior y recrear el worktree de la discusión.',
  'testMode.exiting': 'Saliendo…',
  'testMode.enterSuccess': 'Modo prueba activo — tu repo está en {0}',
  'testMode.enterError': 'No se pudo entrar en modo prueba: {0}',
  'testMode.exitSuccess': 'Modo prueba desactivado — de vuelta en {0}',
  'testMode.exitError': 'Error al salir del modo prueba: {0}',
  'testMode.modal.worktreeDirtyTitle': 'Cambios del agente sin commit',
  'testMode.modal.worktreeDirtyExplain': 'El agente dejó cambios sin commit en su worktree. Se perderán si pruebas ahora — haz commit primero, o pídele al agente que lo haga.',
  'testMode.modal.mainDirtyTitle': 'Trabajo sin commit en `{0}`',
  'testMode.modal.mainDirtyExplain': 'Tu repo principal tiene cambios sin commit. ¿Qué quieres hacer antes de cambiar a la versión del agente?',
  'testMode.modal.detachedTitle': 'HEAD separado',
  'testMode.modal.detachedExplain': 'Tu repo principal no está en una rama con nombre. Después de la prueba, volveremos a ella si la tenías.',
  'testMode.modal.fallbackTitle': 'No se puede entrar en modo prueba',
  'testMode.modal.openGitPanel': 'Abrir el panel git',
  'testMode.modal.stashAndProceed': 'Stashear y continuar',
  'testMode.modal.stashing': 'Stasheando…',
  'testMode.modal.commitFirst': 'Commit primero',
  'testMode.modal.proceedAnyway': 'Continuar igual',
  'testMode.modal.cancel': 'Cancelar',
  'testMode.modal.filesMore': '… y {0} más',
  'git.terminal': 'Terminal',
  'git.terminalPlaceholder': 'git log --oneline -5',
  'git.terminalRestricted': 'Terminal desactivado — active full_access en un agente en Config',
  'disc.empty': 'Sin discusiones.\nHaga clic en "Nueva" para comenzar.',
  'disc.selectOrCreate': 'Seleccione o cree una discusión',
  'disc.mentionHint': '{0} para dirigirse a un agente...',
  'disc.messagePlaceholder': 'Su mensaje...',
  'disc.attachFile': 'Agregar archivo de contexto',
  'disc.general': 'General',
  'disc.local': 'Local',
  'disc.system': 'Sistema',
  'disc.summaryCached': 'Síntesis',
  'disc.viewSummary': 'Ver',
  'disc.hideSummary': 'Ocultar',
  'disc.thinking': '{0} está pensando...',
  'disc.running': 'Agente en ejecución...',
  'disc.debateLaunching': 'Debate iniciando… (las CLI agents pueden tardar 5-30s en arrancar, especialmente Codex en cold-start)',
  'disc.debateAgentEmpty': '{0} terminó sin producir contenido — el CLI pudo haberse caído o alcanzado un rate limit. Vuelve a lanzar el debate para reintentar.',
  'disc.newContent': 'Nuevo contenido',
  'disc.badgeInfo.showDetails': 'Mostrar detalles',
  'disc.badgeInfo.profileKind': 'Perfil IA',
  'disc.badgeInfo.skillKind': 'Skill',
  'disc.badgeInfo.directiveKind': 'Directiva',
  'disc.badgeInfo.tokens': 'tokens',
  'disc.badgeInfo.builtin': 'integrado',
  'disc.badgeInfo.notFound': 'Elemento no encontrado (puede haber sido eliminado).',
  'disc.synthesis': 'Síntesis',
  'disc.cancel': 'Cancelar',
  'disc.resend': 'Reenviar',
  'disc.editResend': 'Editar y reenviar',
  'disc.retryResponse': 'Reintentar respuesta',
  'disc.copyMessage': 'Copiar mensaje',
  'disc.copy': 'Copiar',
  'disc.copied': 'Copiado!',
  'disc.confirmDelete': 'Eliminar esta discusión? Esta acción es irreversible.',
  'disc.overrideKey': 'Activar o reemplazar clave API',
  'disc.orCheckAgent': 'o verifique que su agente local está conectado',
  'disc.stopThinking': 'Detener pensamiento',
  'disc.agentDisabled': 'El agente {0} está actualmente desactivado o desinstalado.',
  'disc.agentDisabledLink': 'Gestionar agentes',
  'disc.archive': 'Archivar',
  'disc.favorites': 'Favoritos',
  'disc.pin': 'Fijar en favoritos',
  'disc.unpin': 'Quitar de favoritos',
  'disc.pinError': 'Error: {0}',
  'disc.chainQP': 'Encadenar un Quick Prompt tras la respuesta',
  'disc.cancelQueuedQP': 'Cancelar Quick Prompt en espera (clic)',
  'disc.delete': 'Eliminar',
  'disc.showMore': 'discusiones más antiguas',
  'disc.unseenMessagesTooltip': '{0} mensaje{1} sin leer',
  'disc.scrollToTop': 'Ir al principio',
  'disc.scrollToBottom': 'Ir al final',
  'disc.introspectionPillTooltip': '{0} llamada{1} a herramientas de historial (kronn-internal MCP)',
  // 0.8.5 — disc-id pill in ChatHeader + sidebar tooltip + search by id prefix.
  'disc.idPillTooltip': 'ID completo: {0} · clic para copiar',
  'disc.idCopied': 'ID copiado al portapapeles',
  'disc.idCopyFailed': 'Copia fallida (acceso al portapapeles denegado)',
  'disc.titleHoverTooltip': '{0}\nID: {1}',
  'disc.summaryStrategyLabel': 'Resumen auto',
  'disc.summaryStrategy.Auto.label': '⚡ Auto',
  'disc.summaryStrategy.Auto.hint': 'Resumen automático cada N mensajes según el contexto del agente. Ideal para debate o modelos pequeños.',
  'disc.summaryStrategy.OnDemand.label': '🤖 Bajo demanda',
  'disc.summaryStrategy.OnDemand.hint': 'Sin resumen automático. El agente puede pedirlo explícitamente (próximamente). Adecuado para grandes contextos.',
  'disc.summaryStrategy.Off.label': '🚫 Desactivado',
  'disc.summaryStrategy.Off.hint': 'Sin resumen. Conversación cruda hasta saturar el contexto. Evita todo coste de resumen.',
  'disc.introspectionUnsupportedNote': '{0} no lee MCP — las herramientas de historial kronn-internal (disc_meta / disc_get_message / disc_summarize) no se llamarán. El agente recurre a la transcripción cruda en su ventana de contexto. Elige `Auto` para conservar un resumen automático como red de seguridad en discusiones largas.',
  'workflows.runningTooltip': '{0} workflow{1} en ejecución',
  'disc.archived': 'Archivos',
  'disc.unarchive': 'Desarchivar',
  'disc.editTitle': 'Renombrar',
  'disc.workspaceLabel': 'Espacio de trabajo',
  'disc.workspaceDirect': 'En el proyecto',
  'disc.workspaceIsolated': 'Aislado (worktree)',
  'disc.workspaceDirectDesc': 'Trabajar directamente en el directorio del proyecto',
  'disc.workspaceIsolatedDesc': 'Crear una rama dedicada en un worktree git',
  'disc.workspaceIsolatedNeedsRepo': 'Requiere un proyecto vinculado a un repo git',
  'disc.branchName': 'Nombre de la rama',
  'disc.baseBranch': 'Rama base',
  'disc.isolatedBadge': 'aislado',
  'disc.worktreeUnlock': 'Liberar rama',
  'disc.worktreeLock': 'Bloquear rama',
  'disc.worktreeUnlocked': 'desbloqueado',
  'disc.switchAgent': 'Cambiar agente',
  'disc.modelTier': 'Modo IA',
  'settings.identity': 'Identidad',
  'settings.identityHint': 'Tu apodo y avatar, mostrados en las conversaciones.',
  'settings.pseudo': 'Apodo',
  'settings.avatarEmail': 'Email (Gravatar)',
  'settings.avatarHint': 'Utilizado para mostrar tu avatar Gravatar. Opcional.',
  'settings.bio': 'Bio',
  'settings.bioPlaceholder': 'Ej: Product Manager en Acme Corp, especializado en analytics y growth...',
  'settings.bioHint': 'Compartido con los agentes al inicio de cada nueva discusión. Ayuda a los agentes a adaptar sus respuestas a tu perfil.',
  'settings.globalContext': 'Contexto global',
  'settings.globalContextHint': 'Este texto se comparte con todos sus agentes al inicio de cada discusión. Ideal para su glosario empresarial, convenciones internas o cualquier información que sus agentes deban conocer siempre.',
  'settings.globalContextPlaceholder': '## Glosario\n- CMS: nuestro CMS personalizado basado en...\n- DAM: Digital Asset Manager\n\n## Stack\n- Frontend: Next.js 14\n- Backend: Node.js + Express\n\n## Convenciones\n- Tests obligatorios para cada cambio\n- Revisión de PR por al menos 1 persona',
  'settings.globalContextSaved': 'Contexto global guardado',
  'settings.gcModeAlways': 'Siempre inyectar',
  'settings.gcModeNoProject': 'Solo sin proyecto',
  'settings.gcModeNever': 'Desactivado',

  // ── Guided tour v2 (0.3.6 — 17 steps) ──
  'tour.welcome.title': 'Bienvenido a Kronn!',
  'tour.welcome.desc': 'En 2 minutos, sabrá cómo pilotar sus agentes IA. Escape para salir, ? para repetir.',
  'tour.conceptProject.title': 'Sus proyectos',
  'tour.conceptProject.desc': 'Un proyecto = una carpeta o repositorio que da contexto a sus agentes. Código, docs, configs — todo empieza aquí.',
  'tour.scan.title': 'Escáner automático',
  'tour.scan.desc': 'Detecta los repositorios git presentes en su máquina. Un clic y listo.',
  'tour.newProject.title': 'Agregue un proyecto',
  'tour.newProject.desc': 'Haga clic en este botón para ver las opciones de creación.',
  'tour.modalOverview.title': '3 formas de empezar',
  'tour.modalOverview.desc': 'Bootstrap — cree desde cero con IA\n\nClone — importe un repo Git existente\n\nCarpeta — agregue un directorio local sin git',
  'tour.modalClose.title': '¡Entendido!',
  'tour.modalClose.desc': 'Continuamos el tour — el modal se cierra automáticamente.',
  'tour.navPlugins.title': 'Plugins',
  'tour.navPlugins.desc': 'Conecte sus herramientas: GitHub, Jira, Slack... 56 plugins disponibles para potenciar sus agentes.',
  'tour.addPlugin.title': 'Agregar un plugin',
  'tour.addPlugin.desc': 'Este botón abre el catálogo de 56 plugins disponibles. Busque por nombre (ej: "github", "jira") y actívelos en sus proyectos. Cada plugin activo será accesible por sus agentes.',
  'tour.navDiscussions.title': 'Discusiones',
  'tour.navDiscussions.desc': 'Aquí habla con sus agentes IA, en el contexto de sus proyectos.',
  'tour.sidebar.title': 'Sus conversaciones',
  'tour.sidebar.desc': 'Organizadas por proyecto. Las discusiones de workflows se agrupan en carpetas.',
  'tour.newDisc.title': 'Inicie una discusión',
  'tour.newDisc.desc': 'Haga clic para abrir el formulario. Elegirá un agente, un proyecto y skills.',
  'tour.discForm.title': 'El formulario',
  'tour.discForm.desc': 'Agente, proyecto, skills, archivos adjuntos — todo se configura aquí antes de iniciar la conversación.',
  'tour.discProfiles.title': 'Enmarca el agente con un perfil',
  'tour.discProfiles.desc': 'Cada discusión puede ser enmarcada por un perfil (Arquitecto, Tech-Lead, Mentor, QA…). Definen el tono, el método y el rol que adopta el agente — misma pregunta, respuestas muy distintas según el perfil. Haz clic aquí para abrir la lista.',
  'tour.discProfileChip.title': 'Elige un perfil',
  'tour.discProfileChip.desc': 'Pasa el cursor sobre un perfil para ver su descripción completa. Haz clic en un perfil para activarlo y continuar. Puedes crear o modificar tus propios perfiles en Settings > Skills, Perfiles y Directivas.',
  'tour.navAutomation.title': 'Automatización',
  'tour.navAutomation.desc': 'Dos herramientas: Quick Prompts (plantillas reutilizables) y Workflows (automatizaciones multi-paso, cron).',
  'tour.navConfig.title': 'Configuración',
  'tour.navConfig.desc': 'Sus claves API, perfiles, skills personalizados y el contexto global compartido con todos los agentes.',
  'tour.usage.title': 'Seguimiento de consumo',
  'tour.usage.desc': 'Vea su consumo de tokens por agente y modelo. Esencial para controlar costos.',
  'tour.done.title': '¡Está listo!',
  'tour.done.desc': 'Comience lanzando una discusión con un agente. El botón ? arriba a la derecha repite este tour en cualquier momento.\n\n🎁 Un pequeño regalo por haber llegado hasta aquí: un easter egg está escondido en Settings — introduce el Konami code `↑↑↓↓←→←→BA` en el lugar correcto, luego escribe `kronnMatrix` en el campo que aparece. Shhh. 🤫',
  'tour.next': 'Siguiente',
  'tour.prev': 'Anterior',
  'tour.skip': 'Saltar tour',
  'tour.finish': 'Terminar',
  'tour.clickHint': 'Haga clic en el elemento resaltado para continuar',
  'tour.replayHint': 'Repetir tour guiado',
  'tour.replay': 'Repetir tour guiado',

  // ── Ollama (0.4.0) ──
  'ollama.online': 'En línea',
  'ollama.offline': 'Servidor detenido',
  'ollama.unreachable': 'Inalcanzable',
  'ollama.notInstalled': 'No instalado',
  'ollama.models': 'modelo(s)',
  'ollama.refresh': 'Actualizar',
  'ollama.installTitle': 'Instalar Ollama',
  'ollama.installDesc': 'Ollama permite ejecutar modelos de IA localmente en su máquina, gratis y sin conexión a internet.',
  'ollama.launchTitle': 'Iniciar Ollama',
  'ollama.launchDesc': 'El servidor Ollama no está iniciado. Ejecute: ollama serve',
  'ollama.pullTitle': 'Descargar un modelo',
  'ollama.pullDesc': 'Ollama está en línea pero no hay modelos instalados. Ejecute uno de los siguientes comandos en su terminal:',
  'ollama.installedModels': 'Modelos instalados',
  'ollama.defaultTag': 'predeterminado',
  'ollama.pickDefaultHint': 'Haz clic en un modelo para definirlo como predeterminado.',
  'ollama.pickedDefaultHint': 'Predeterminado actual: {0}',
  'ollama.pullMoreHint': 'Para agregar más modelos: ollama pull <nombre> — ver ollama.com/library',
  'ollama.canirunHint': 'canirun.ai — comprueba si tu hardware puede ejecutar un modelo',
  'contacts.title': 'Contactos',
  'contacts.pending': 'pendiente',
  'contacts.inviteCode': 'Código de invitación',
  'contacts.inviteHint': 'Comparte este código para que otros usuarios de Kronn puedan agregarte.',
  'contacts.add': 'Agregar contacto',
  'contacts.addPlaceholder': 'kronn:pseudo@host:port',
  'contacts.wsConnected': 'Conectado en tiempo real',
  'contacts.wsDisconnected': 'Desconectado',
  'contacts.added': 'Contacto agregado',
  'contacts.addError': 'No se pudo agregar el contacto',
  'contacts.warnGeneric': 'Contacto agregado pero inalcanzable — verifique que ambas máquinas estén en la misma red.',
  'contacts.warn.TAILSCALE_MISSING': 'Contacto agregado pero inalcanzable — este contacto usa Tailscale, pero Tailscale no está activo aquí. Instale o conecte Tailscale.',
  'contacts.warn.TAILSCALE_UNREACHABLE': 'Contacto agregado pero inalcanzable — Tailscale está activo pero el contacto no responde. Verifique que esté en línea.',
  'contacts.warn.LAN_UNREACHABLE': 'Contacto agregado pero inalcanzable — este contacto está en una red local. Verifique que esté en la misma red Wi-Fi/LAN.',
  'contacts.warn.NETWORK_UNREACHABLE': 'Contacto agregado pero inalcanzable — verifique la dirección y que el contacto esté en línea.',
  'contacts.delete': 'Eliminar',
  'contacts.deleteConfirm': '¿Eliminar este contacto? Ya no podrás enviarle mensajes ni compartir discusiones.',
  'contacts.deleted': 'Contacto eliminado',
  'contacts.sharedByPeer': '{0} compartió "{1}"',
  'contacts.deleteError': 'No se pudo eliminar el contacto',
  'contacts.tailscaleHint': 'Tailscale detectado — este código funciona en cualquier red.',
  'contacts.detectedIps': 'Direcciones IP detectadas',
  'contacts.usedInCode': 'usado en el código',
  'contacts.clickToUse': 'Haz clic para usar esta IP en el código de invitación',
  'contacts.ipSelected': 'IP seleccionada para el código de invitación',
  'contacts.guideTitle': 'Cómo conectar dos instancias Kronn',
  'contacts.guideStep1': 'Las dos máquinas deben estar en la misma red (LAN, VPN corporativo o Tailscale).',
  'contacts.guideStep2': 'Copia tu código de invitación de arriba.',
  'contacts.guideStep3': 'Envíalo a tu contacto (por email, chat, etc.).',
  'contacts.guideStep4': 'Tu contacto lo pega en Discusiones > Contactos > botón "+".',
  'contacts.guideNetworkTitle': 'Qué red usar?',
  'contacts.guideNetwork': 'VPN corporativo (AWS VPN, OpenVPN...): usa tu IP VPN en el campo Dominio. Tailscale (gratis): instálalo en ambas máquinas, la IP se detecta automáticamente. Mismo WiFi: funciona, pero la IP cambia al cambiar de red.',
  'disc.tier.economy': 'Eco',
  'disc.tier.default': 'Estándar',
  'disc.tier.reasoning': 'Avanzado',

  // ── Debate ──
  'debate.title': 'Debate multi-agente',
  'debate.header': 'Debate inter-agentes',
  'debate.instructions': 'Seleccione al menos 2 agentes. Debatirán durante {0} ronda{1} y luego el agente principal hará la síntesis.',
  'debate.main': 'principal',
  'debate.launch': 'Lanzar debate ({0} agentes)',
  'debate.rounds': 'Rondas',

  // ── Config ──
  'config.appearance': 'Apariencia',
  'config.appearanceHint': 'Elige el tema visual de la interfaz.',
  'config.themeSystem': 'Sistema',
  'config.themeLight': 'Claro',
  'config.themeDark': 'Oscuro',
  'config.themeMatrix': 'Matrix',
  'config.themeSakura': 'Sakura',
  'config.themeGotham': 'Gotham',
  'config.secretLabel': 'Código secreto:',
  'config.secretPlaceholder': '••••••••',
  'config.secretSubmit': 'Desbloquear',
  'config.secretUnlocked': 'Tema «{0}» desbloqueado.',
  'config.secretInvalid': 'Código inválido.',
  'config.konamiReveal': '🎮 Modo secreto activado',
  'config.batmanRecruited': '🦇 Batman se unió a la Batcave',
  'skills.autoActivated': '⚡ Skill «{0}» activado automáticamente',
  'skills.autoTriggerOn': 'Auto-activación ON',
  'skills.autoTriggerOff': 'Auto-activación OFF',
  'skills.autoTriggerHint': 'Si este skill se activa automáticamente cuando escribes ciertas palabras clave (ej: «PDF», «informe»). Haz clic para desactivar — el skill sigue disponible manualmente.',
  'skills.autoToggleFailed': 'No se pudo cambiar la auto-activación.',
  'config.subtitle': 'Configuración general de Kronn',
  'config.uiLanguage': 'Idioma de la interfaz',
  'config.uiLanguageHint': 'Idioma de visualización de la interfaz Kronn.',
  'config.outputLanguage': 'Idioma de salida',
  'config.outputLanguageHint': 'Idioma en el que los agentes responderán por defecto.',
  'config.scanPaths': 'Carpetas a escanear',
  'config.scanPathsHint': 'Carpetas raíz donde Kronn busca sus repositorios git.',
  'config.scanPathPlaceholder': '/home/user/projects',
  'config.path': 'carpeta',
  'config.pathsPlural': 'carpetas',
  'config.scanIgnore': 'Patrones a ignorar',
  'config.scanIgnoreHint': 'Nombres de carpetas a ignorar durante el escaneo (node_modules, .git, etc.).',
  'config.scanIgnorePlaceholder': 'ej: vendor',
  'config.pattern': 'patrón',
  'config.patternsPlural': 'patrones',
  'config.scanDepth': 'Profundidad de escaneo',
  'config.scanDepthHint': 'Número de niveles de subdirectorios a explorar para encontrar repositorios git (2-10).',
  'config.agents': 'Agentes',
  'config.installed': 'instalado',
  'config.installedPlural': 'instalados',
  'config.refresh': 'Actualizar',
  'config.fullAccess': 'Permite acceso web, modificación de archivos y ejecución de comandos.',
  'config.autoApply': 'Aplica automáticamente los cambios sin confirmación.',
  'config.restrictedAgent': 'El agente {0} no tiene el modo "full access" activado — no podrá modificar archivos.',
  'config.restrictedAgentLink': 'Activar en Config > Agents',
  'config.restrictedDebate': 'Algunos agentes no tienen el modo "full access" — no podrán modificar archivos.',
  'config.restrictedStep': 'Este agente no tiene el modo "full access" — no podrá modificar archivos.',
  'config.fullAccessBadge': 'Full access',
  'config.uninstallConfirm': 'Desinstalar {0}?',
  'config.uninstall': 'Desinstalar',
  'config.hostManaged': 'Instalado en el host',
  'config.uninstallFailed': 'Error al desinstalar — eliminar manualmente en el host',
  'config.installFailed': 'Error al instalar {0}: {1}',
  'config.getKey': 'Obtener una clave API',
  'config.wslWarning': 'WSL detectado — instale los agentes directamente en WSL, Kronn los detectará automáticamente. Los agentes instalados en Docker se pierden al reconstruir.',
  // ── RTK (compression hook for shell outputs) ──
  'config.rtk.title': 'Modo económico',
  'config.rtk.explainer': 'Los agentes lanzan muchos comandos (git, cargo, ls, tests) que producen texto muy voluminoso. Kronn puede comprimir esas salidas antes de que lleguen a la IA, ahorrando hasta un 90% de tokens sin pérdida de calidad.',
  'config.rtk.poweredBy': 'Hecho con',
  'config.rtk.openSource': 'open source',
  'config.rtk.stateAll': 'Activo en todos los agentes compatibles',
  'config.rtk.statePartial': 'Activo en {0}/{1} agentes compatibles',
  'config.rtk.stateNone': 'Aún no activado',
  'config.rtk.notInstalled': 'RTK no está instalado en este equipo',
  'config.rtk.savings': '~{0} tokens ahorrados este mes',
  'config.rtk.activateAll': 'Activar en todos los agentes compatibles',
  'config.rtk.activateRemaining': 'Activar en los {0} restantes',
  'config.rtk.activating': 'Activando…',
  'config.rtk.installCta': 'Instalar RTK y activar',
  'config.rtk.installHelp': 'RTK debe instalarse en tu equipo. Abre una terminal y pega el comando siguiente, o pide a tu colega técnico que lo instale por ti.',
  'config.rtk.installCommand': 'Comando de instalación',
  'config.rtk.viewOnGithub': 'Ver RTK en GitHub',
  'config.rtk.viewDocs': 'Documentación',
  'config.rtk.badgeActive': 'RTK activo',
  'config.rtk.badgeMissing': 'RTK — hook no configurado',
  'config.rtk.badgeNotInstalled': 'RTK no instalado',
  'config.rtk.badgeNotApplicable': 'No compatible con RTK',
  'config.rtk.aboutLink': 'Acerca de RTK',
  'agentRuntimeWarning.vibe.sdk_fallback': 'SDK de Vibe incompatible — Kronn usa la API directa de Mistral (~4 s más rápido pero sin herramientas locales: bash, archivos, MCP). Actualiza `mistral-vibe` o ignora.',
  'app.updateAvailable': 'Kronn {0} → {1} disponible',
  'app.updateOpenRelease': 'Ver la release',
  'app.updateDismiss': 'Ocultar hasta la próxima versión',
  'app.backendOffline': 'Backend inaccesible',
  'app.backendOfflineTitle': 'El backend de Kronn no responde — las acciones fallarán hasta que se reinicie.',
  'disc.kronnToolResult': 'Ver respuesta',
  'config.rtk.activateError': 'Error al activar RTK. Revisa la consola.',
  'config.rtk.activatePartial': 'RTK activado parcialmente — {0} de {1} agentes fallaron (ver detalles).',
  'config.rtk.activateNoTarget': 'Nada que activar — todos los agentes compatibles ya están configurados.',
  'config.rtk.deactivate': 'Desactivar RTK ({0})',
  'config.rtk.deactivateHint': 'Quita el hook RTK de las configuraciones — reversible vía "Activar".',
  'config.rtk.deactivateError': 'Error al desactivar RTK. Revisa la consola.',
  'config.rtk.deactivateSuccess': 'RTK desactivado.',
  'config.rtk.activateSuccess': 'RTK activado.',
  'config.rtk.detailsToggle': 'Detalles',
  'config.rtk.statTokens': 'Tokens ahorrados',
  'config.rtk.statRatio': 'Ratio medio',
  'config.rtk.statSamples': 'Comandos comprimidos',
  'config.rtk.sobrietyTitle': 'Sobre el "modo económico"',
  'config.rtk.updateAvailable': '{0} disponible',
  'config.rtk.updateAvailableTitle': 'RTK {0} → {1}',
  'config.rtk.updateAvailableAria': 'Actualización de RTK {0} a {1} disponible',
  'config.rtk.updateModalTitle': 'Actualizar RTK',
  'config.rtk.updateModalBody': 'Instalado: {0} — Última versión conocida: {1}. Ejecuta el comando de abajo (el script es idempotente y actualiza en su lugar).',
  'config.agentUpdateAvailableAria': 'Actualización de {0} a {1} disponible',
  'config.agentUpdateAvailableTitle': 'Instalado {0} → última {1}',
  'config.agentUpdateModalTitle': 'Actualizar {0}',
  'config.agentUpdateModalBody': 'Instalado: {0} — Última versión conocida: {1}. Vuelve a ejecutar el comando de instalación para actualizar (npm/curl/uv instalan encima).',
  'config.rtk.sobrietyBody': 'El uso más sostenible es no utilizar IA. Si la utilizas, RTK comprime las salidas del shell antes de que lleguen al modelo — menos tokens, menos energía, misma calidad.',
  'config.enabled': 'Activo',
  'config.disabled': 'En pausa',
  'config.toggleEnable': 'Reactivar',
  'config.toggleDisable': 'Pausar',
  'config.apiKeys': 'Claves API (opcional)',
  'config.apiKeysHint1': 'Sus agentes locales usan su propia autenticación (suscripciones).',
  'config.apiKeysHint2': 'Si desea usar una clave API específica, puede ingresarla aquí como override.',
  'config.localAuth': 'auth local',
  'config.tokenCostHint': 'Costo estimado en tokens al inyectar en el prompt del agente',
  'config.viewModels': 'Ver modelos',
  'config.account': 'Cuenta',
  'config.viewAccount': 'Ver cuenta / consumo de {0}',
  'config.defaultModel': 'Por defecto',
  'config.saveError': 'Error al guardar',
  'config.tokenUsage': 'Consumo de tokens',
  'config.estimateTokenUsage': 'Estimación de tokens',
  'config.enableOverride': 'Activar override de clave API',
  'config.disableOverride': 'Desactivar override (usar auth local)',
  'config.syncTokensConfirm': 'Actualizar la clave API en la config de todos los agentes instalados?',
  'config.syncTokensDone': 'Clave sincronizada: {0}',
  'config.syncTokensNone': 'Ningún agente para sincronizar',
  'config.bestPractices': 'Buenas prácticas por proveedor',
  'config.discoverKeys': 'Auto-detectar',
  'config.discoverKeysFound': '{0} clave(s) encontrada(s), {1} importada(s)',
  'config.discoverKeysNone': 'Ninguna clave detectada',
  'config.addKey': 'Agregar una clave',
  'config.keyName': 'Nombre de la clave',
  'config.defaultKeyName': 'Clave API personal',
  'config.deleteKey': 'Eliminar esta clave',
  'config.deleteKeyConfirm': 'Eliminar clave "{0}"?',
  'config.activateKey': 'Activar esta clave',
  'config.overrideActive': 'override activo',
  'config.overrideDisabled': 'override desactivado',
  'config.save': 'Guardar',
  'config.saved': 'Guardado!',
  'config.server': 'Servidor y seguridad',
  'config.authToken': 'Token API',
  'config.authEnabled': 'activo',
  'config.authDisabled': 'desactivado',
  'config.authActivate': 'Activar autenticación',
  'config.authActivated': 'Autenticación activada! Token copiado al portapapeles.',
  'config.authDisabledHint': 'La API está abierta sin autenticación. Active un token para asegurar el acceso.',
  'config.authHint': 'Usa este token en el header Authorization: Bearer <token> para autenticar las llamadas API.',
  'config.authCopied': 'Token copiado',
  'config.authRegenConfirm': 'Regenerar token? Los clientes existentes deberán reconectarse.',
  'config.authRegenerated': 'Token regenerado',
  'config.authRegenError': 'Error al regenerar',
  'config.regenerate': 'Regenerar',
  'config.domain': 'Dominio personalizado',
  'config.domainHint': 'Ej: kronn.local — usado para CORS y TLS. Requiere reinicio.',
  'config.domainSave': 'Guardar dominio',
  'config.domainSaved': 'Dominio guardado (reinicio necesario)',
  'config.domainError': 'Error al guardar',
  'config.maxAgents': 'Agentes simultáneos max',
  'config.maxAgentsHint': 'Número máximo de procesos agentes en paralelo. Reinicio necesario.',
  'config.usage': 'Uso',
  'config.usageEmpty': 'Sin datos de consumo disponibles.',
  'config.usageTotalTokens': 'Tokens totales',
  'config.usageTotalCost': 'Costo estimado',
  'config.usageDiscussions': 'Discusiones',
  'config.usageWorkflows': 'Workflows',
  'config.usageByProvider': 'Por proveedor',
  'config.usageByProject': 'Por proyecto',
  'config.usageTopDiscussions': 'Top discusiones',
  'config.usageTopWorkflows': 'Top workflows',
  'config.usageDaily': 'Historial (30 dias)',
  'config.usageFilterAll': 'Todo',
  'config.usageFilterDisc': 'Disc.',
  'config.usageFilterWf': 'Wf.',
  'config.database': 'Base de datos',
  'config.dbProjects': 'Proyectos',
  'config.dbDiscussions': 'Discusiones',
  'config.dbMessages': 'Mensajes',
  'config.dbMcps': 'Plugins',
  'config.dbWorkflows': 'Workflows',
  'config.dbSkills': 'Skills custom',
  'config.dbProfiles': 'Perfiles custom',
  'config.dbDirectives': 'Directivas custom',
  'config.export': 'Exportar',
  'config.dbBackup': 'Snapshot DB',
  'config.dbBackupSuccess': 'Snapshot guardado: {0}',
  'config.dbBackupError': 'Error de snapshot — ver consola',
  'config.import': 'Importar',
  'config.importInvalid': 'Archivo inválido',
  'config.importConfirm': 'Esto reemplazará todos los datos actuales. Continuar?',
  'config.importError': 'Error durante la importación',
  'config.importSuccess': 'Importación exitosa',
  'config.importPathWarning': '{0} proyecto(s) con rutas inválidas — remapéalos en la página Proyectos',
  'config.exportApiWarning': 'Los tokens de autenticación no se incluyen en la exportación. Guárdalos antes de migrar.',
  'config.exportApiWarningLink': 'Ver mis tokens →',
  'config.resetHint': 'Restablecer la configuración y relanzar el asistente de setup.',
  'config.resetConfirm': "ATENCIÓN: esto eliminará toda la configuración (claves de proveedores IA, preferencias, agentes).\n\nRecuerde:\n- Exportar sus datos (Config → Base de datos → Exportar)\n- Guardar sus claves API y su token de autenticación\n\nEsta acción es irreversible. ¿Continuar?",
  'config.reset': 'Restablecer configuración',
  'config.configFile': 'Archivo de config',

  // ── Plugins ──
  'mcp.title': 'Plugins',
  'mcp.subtitle': '(MCP / API)',
  'mcp.incomplete.title': '{0} plugin(s) no operativo(s)',
  'mcp.incomplete.hint': 'Su configuración está incompleta. Kronn NO los carga en los proyectos para no romper los agentes al arrancar. Haz clic en una fila para corregir.',
  'mcp.add': 'Agregar',
  'mcp.detect': 'Sincronizar',
  'mcp.syncing': 'Sincronizando…',
  'mcp.search': 'Buscar plugin o proyecto...',
  'mcp.searchRegistry': 'Buscar plugin...',
  'mcp.sortAz': 'Ordenar A → Z',
  'mcp.sortZa': 'Ordenar Z → A',
  'mcp.configure': 'Configurar {0}',
  'mcp.addTitle': 'Agregar un plugin',
  'mcp.addAnother': 'Agregar otra config {0}',
  'mcp.label': 'Nombre / Label',
  'mcp.custom.tileTitle': 'Custom API',
  'mcp.custom.tileCat': 'Tu propia API',
  'mcp.custom.tileDesc': 'Define tu propia API REST: nombre, URL base, descripción libre, enlace de docs opcional y los campos que el agente necesita (tokens, IDs, headers).',
  'mcp.custom.tileBadge': 'Definida por ti',
  'mcp.custom.name': 'Nombre de la API',
  'mcp.custom.namePlaceholder': 'ej. Salesforce Sales API',
  'mcp.custom.baseUrl': 'URL base',
  'mcp.custom.baseUrlPlaceholder': 'https://my-org.salesforce.com/services/data/v59.0',
  'mcp.custom.description': 'Describe la API',
  'mcp.custom.descriptionPlaceholder': 'Qué hace esta API. Notas sobre auth. Lo que el agente deba saber — también leerá el enlace de docs.',
  'mcp.custom.docsUrl': 'Enlace de documentación (opcional)',
  'mcp.custom.fields': 'Campos',
  'mcp.custom.fieldsHint': 'Un par {Etiqueta, Valor} por campo. Kronn deriva la clave de entorno (p. ej. "Bearer Token" → BEARER_TOKEN).',
  'mcp.custom.fieldLabel': 'Etiqueta (p. ej. Bearer Token)',
  'mcp.custom.fieldValue': 'Valor',
  'mcp.custom.fieldAdd': 'Añadir un campo',
  'mcp.custom.fieldRemove': 'Quitar este campo',
  // 0.8.6 — edit mode: secrets-preserved reassurance
  'mcp.custom.fieldValueStored': '•••• guardado cifrado',
  'mcp.custom.fieldValueStoredTooltip': 'Valor preservado. Modifícalo vía «Editar los secretos» en el drawer del plugin.',
  'mcp.custom.fieldValueEditElsewhere': '→ Edita el valor vía «Editar los secretos»',
  'mcp.custom.fieldsHintEditMode': 'Puedes modificar aquí la ESTRUCTURA del plugin (nombres de campos, endpoints, auth) Y los VALORES de credenciales. Los valores existentes están pre-rellenados (enmascarados) — déjalos tal cual para conservarlos, o teclea encima para reemplazar.',
  'mcp.custom.envViewOnlyHint': 'Vista de solo lectura. Para modificar los valores, haz clic «Modificar el plugin».',
  'mcp.custom.save': 'Guardar',
  // 0.8.6 — Edit existing Custom plugin
  'mcp.custom.saveEdit': 'Guardar cambios',
  'mcp.custom.editSpec': 'Modificar el plugin',
  // 0.8.6 (#29) — banner for legacy Custom plugins with no endpoints.
  'mcp.custom.autodiscoveryTitle': 'Ningún endpoint declarado',
  'mcp.custom.autodiscoveryHint': 'Sin endpoints, los agentes deben leer la documentación en cada llamada. Pide a la IA que proponga una lista desde `docs_url`.',
  'mcp.custom.autodiscoveryCta': 'Pedir ayuda a la IA',
  'mcp.custom.updated': 'API «{0}» actualizada',
  'mcp.custom.specSavedEnvFailed': 'Spec guardada, pero los valores de credenciales fallaron: {0}',
  'mcp.custom.errorName': 'El nombre de la API es obligatorio',
  'mcp.custom.errorBaseUrl': 'La URL base es obligatoria',
  'mcp.custom.created': 'API «{0}» añadida',
  'mcp.custom.error': 'Error: {0}',
  // 0.8.6 (#33) — Custom plugin import / export (clipboard JSON).
  'mcp.custom.copyAsJson': 'Copiar como JSON',
  'mcp.custom.copied': 'JSON del plugin copiado al portapapeles (sin credenciales)',
  'mcp.custom.copyManual': 'Copia este JSON manualmente:',
  'mcp.custom.exportTitle': 'Exportar «{0}»',
  'mcp.custom.exportHint': 'Aquí está el JSON del plugin (sin credenciales). Se está copiando al portapapeles; si no, usa Ctrl+C / Cmd+C en el texto de abajo.',
  'mcp.custom.copyManualInstruction': 'El portapapeles rechazó la escritura. Selecciona el texto y cópialo manualmente.',
  'mcp.custom.exportError': 'Este plugin no tiene una spec exportable',
  'mcp.custom.importTileTitle': 'Importar desde JSON',
  'mcp.custom.importTileDesc': 'Pega una exportación de otro Kronn (solo spec, nunca credenciales).',
  'mcp.custom.importPasteLabel': 'Pega el JSON del plugin',
  'mcp.custom.importPlaceholder': '{ "name": "...", "base_url": "https://...", "endpoints": [ ... ] }',
  'mcp.custom.importPasteFromClipboard': 'Pegar desde el portapapeles',
  'mcp.custom.importPasteUnavailable': 'Acceso al portapapeles denegado — pega manualmente',
  'mcp.custom.importSubmit': 'Importar plugin',
  'mcp.custom.importSecretsHint': 'Las credenciales nunca se importan. Rellénalas luego con «Editar secretos».',
  'mcp.custom.importErrorParse': 'JSON inválido — revisa si faltan comas o comillas',
  'mcp.custom.importErrorShape': 'El JSON debe ser un objeto, no un array ni un valor primitivo',
  'mcp.custom.importErrorName': 'Falta el campo «name» o está vacío',
  'mcp.custom.importErrorBaseUrl': 'Falta el campo «base_url» o está vacío',
  'mcp.custom.imported': 'Plugin «{0}» importado — rellena sus credenciales',
  // ── 0.8.6 — Endpoints declared at create time ──
  'mcp.custom.endpoints.header': 'Endpoints',
  'mcp.custom.endpoints.emptyHint': 'Sin endpoints declarados: tus agentes leerán la doc cada vez. Rellena `docs_url` arriba, luego pide a la IA que rellene esta lista («Construir con IA»).',
  'mcp.custom.endpoints.populatedHint': '{0} endpoint(s) declarados. Los agentes los pueden llamar directamente vía la herramienta MCP `api_call` (sin credenciales en el prompt).',
  'mcp.custom.endpoints.methodLabel': 'Método HTTP',
  'mcp.custom.endpoints.descPlaceholder': "Descripción corta (p. ej. 'Listar widgets')",
  'mcp.custom.endpoints.add': 'Añadir un endpoint',
  'mcp.custom.endpoints.remove': 'Quitar este endpoint',
  // 0.8.6 — Auth picker
  'mcp.custom.auth.header': 'Autenticación',
  'mcp.custom.auth.hint': 'Cómo Kronn inyecta las credenciales en cada llamada. Elige el patrón que usa la API. El AI helper puede rellenar esta sección desde la doc.',
  'mcp.custom.auth.kind.none': 'Ninguna (endpoints públicos)',
  'mcp.custom.auth.kind.bearer': 'Bearer (Authorization: Bearer <token>)',
  'mcp.custom.auth.kind.tokenExchange': 'Token exchange (POST creds → access_token → Bearer)',
  'mcp.custom.auth.kind.other': 'Otra (edición JSON requerida)',
  'mcp.custom.auth.kind.otherWarning': '⚠ Este plugin usa un esquema de auth aún no expuesto en el picker (ApiKeyQuery / Header / Basic / OAuth2). Conservado tal cual — edición manual requerida para cambiarlo.',
  'mcp.custom.auth.bearer.envKey': 'Campo que contiene el token',
  'mcp.custom.auth.bearer.envKeyPlaceholder': '— elige un campo —',
  'mcp.custom.auth.tokenExchange.hint': 'La API requiere un intercambio en 2 pasos: POST creds → respuesta contiene un access_token → usado como Bearer en llamadas siguientes. Kronn cachea el token y lo refresca antes de expirar.',
  'mcp.custom.auth.tokenExchange.endpoint': 'Endpoint de intercambio (relativo a base_url)',
  'mcp.custom.auth.tokenExchange.method': 'Método HTTP',
  'mcp.custom.auth.tokenExchange.bodyFormat': 'Formato del body',
  'mcp.custom.auth.tokenExchange.bodyTemplate': 'Plantilla del body (JSON)',
  'mcp.custom.auth.tokenExchange.bodyTemplateHint': 'Usa ${ENV.NOMBRE_CAMPO} para referenciar una credencial almacenada. Ejemplo Didomi: {"type": "api-key", "key": "${ENV.API_KEY}", "secret": "${ENV.API_SECRET}"}',
  'mcp.custom.auth.tokenExchange.tokenJsonpath': 'JSONPath del token en la respuesta',
  'mcp.custom.auth.tokenExchange.ttl': 'TTL del token (segundos — 3600 = 1h, 0 = sin caché)',
  'mcp.custom.auth.tokenExchange.inject': 'Cómo inyectar el token en llamadas siguientes',
  // 0.8.6 — orphan env warning
  'mcp.envOrphanHint': "Valores almacenados bajo nombres de campo antiguos ({0}) quedaron inaccesibles tras tu rename. Se eliminarán al próximo Save. Repega tus credenciales con los nuevos nombres.",
  // ── Custom API AI helper (chat bubble) ──
  'mcp.custom.helper.trigger': 'Construir con IA',
  'mcp.custom.helper.triggerHint': 'Pídele a un agente local que rellene el formulario desde un curl, un enlace de docs o una descripción libre',
  'mcp.custom.helper.discTitle': 'Asistente config API custom (efímero)',
  'mcp.custom.helper.bubbleTitle': 'Asistente config API',
  'mcp.custom.helper.ephemeral': 'efímero',
  'mcp.custom.helper.minimize': 'Minimizar',
  'mcp.custom.helper.restore': 'Reabrir asistente',
  'mcp.custom.helper.close': 'Cerrar (elimina la conversación)',
  'mcp.custom.helper.switchAgent': 'Cambiar de agente (reinicia la conversación)',
  'mcp.custom.helper.thinking': 'El agente está pensando…',
  'mcp.custom.helper.inputPlaceholder': 'Pega un curl, un enlace de docs, o describe tu API…',
  'mcp.custom.helper.send': 'Enviar',
  'mcp.custom.helper.stop': 'Parar',
  'mcp.custom.helper.noAgents': 'No hay agentes instalados. Configura al menos uno en Settings → Agents.',
  'mcp.custom.helper.contextHint': 'Lo que el agente ya ve en tu formulario',
  'mcp.custom.helper.welcome': '¡Hola! ¿Cómo quieres describir tu API? Elige un atajo o escribe libremente.',
  'mcp.custom.helper.starter.curl': 'Pegar un curl',
  'mcp.custom.helper.starter.curlPrompt': "Aquí tienes un curl de ejemplo que llama a la API que necesito configurar. Extrae el nombre, base URL, campos requeridos (tokens, IDs, headers) y propón una config Kronn:\n\n```\n",
  'mcp.custom.helper.starter.docs': 'Enlace de docs',
  'mcp.custom.helper.starter.docsPrompt': "Aquí está la URL de docs. Léela y propón una config Kronn (name, base_url, description, campos requeridos):\n",
  'mcp.custom.helper.starter.describe': 'Describir libremente',
  'mcp.custom.helper.starter.describePrompt': 'Quiero configurar una API para ',
  'mcp.custom.helper.suggestion': 'Propuesta',
  'mcp.custom.helper.apply': 'Aplicar',
  'mcp.custom.helper.applied': '✓ Aplicado',
  'mcp.custom.helper.ctx.header': '=== ESTADO ACTUAL DEL FORMULARIO ===',
  'mcp.custom.helper.ctx.empty': '(vacío)',
  'mcp.custom.helper.ctx.unnamed': 'Custom API',
  'mcp.custom.helper.ctx.noFields': '  (sin campos declarados)',
  'mcp.custom.helper.ctx.noEndpoints': '  (sin endpoints declarados — lee docs_url y emítelos)',
  'mcp.custom.helper.ctx.fieldsCount': '{0} campo(s)',
  'mcp.custom.helper.sys.role': 'Eres un asistente Kronn especializado en crear configs de APIs REST custom. Ayudas al usuario a rellenar un formulario que describe una API de terceros que Kronn pilotará.',
  'mcp.custom.helper.sys.boundaries': 'El formulario tiene 4 campos principales: name (corto, p. ej. "Salesforce Sales API"), base_url (URL raíz), description (libre, lo que hace la API + notas de auth), docs_url (enlace docs, opcional), y una lista de fields (pares {label, value} para tokens/IDs/headers que el usuario proporcionará).',
  'mcp.custom.helper.sys.action': 'Cuando tengas suficiente info para proponer una config (aunque sea parcial), emite un bloque KRONN:APPLY en el formato de abajo. Puedes emitir múltiples bloques durante la conversación.',
  'mcp.custom.helper.sys.format': "Formato exacto para las propuestas (el bloque DEBE empezar por 'KRONN:APPLY' seguido de una cerca ```json):",
  'mcp.custom.helper.sys.partial': "Puedes emitir una propuesta parcial (p. ej. solo name + base_url) si tienes esas pero aún no los fields. NUNCA adivines valores reales de secretos; deja value=\"\" para los fields, el usuario teclea sus credenciales él mismo.",
  'mcp.custom.helper.sys.endpoints': "ENDPOINTS — cuando `docs_url` está en el formSnapshot, usa tu herramienta WebFetch (si está disponible) para leer la doc, luego emite un campo `endpoints` en tu KRONN:APPLY con los 5-15 endpoints más útiles (cada uno: `{path, method, description}`). Prefiere endpoints de lectura (GET) salvo que el usuario haya pedido una operación de escritura. Descripción corta (≤120 chars). No re-emitas endpoints ya visibles en el formSnapshot. Si no tienes WebFetch, dilo al usuario y pídele que pegue los paths importantes.",
  'mcp.custom.helper.sys.style': 'Mantente conciso. Haz una pregunta a la vez si necesitas clarificar. Indica siempre qué falta para finalizar la config.',
  'mcp.custom.helper.sys.starter': 'Hola! Describe la API que quieres configurar: pega un curl, un enlace de docs, o explica en lenguaje natural.',
  'mcp.custom.helper.sys.userQuestion': '=== PREGUNTA USUARIO ===',
  'mcp.envVars': 'Variables de entorno',
  'mcp.setup': 'Configuración',
  'mcp.getToken': 'Obtener un token',
  'mcp.value': 'valor...',
  'mcp.globalAll': 'Global (todos los proyectos)',
  'mcp.addBtn': 'Agregar',
  'mcp.back': 'Volver',
  'mcp.alreadyAdded': 'ya presente',
  'mcp.keys': 'clave',
  'mcp.keysPlural': 'claves',
  'mcp.clickToRename': 'Clic para renombrar',
  'mcp.editKeys': 'Editar claves',
  'mcp.close': 'Cerrar',
  'mcp.show': 'Mostrar',
  'mcp.hide': 'Ocultar',
  'mcp.saving': 'Guardando...',
  'mcp.save': 'Guardar',
  'mcp.cancel': 'Cancelar',
  'mcp.revealWarning': 'No se pudieron descifrar los valores actuales. Ingrese los nuevos valores a continuación.',
  'mcp.secretsBroken': 'Los secretos de este plugin están corruptos — haga clic para volver a ingresarlos',
  'mcp.secretsBrokenShort': 'Secretos',
  'mcp.disableGlobal': 'Desactivar global — mantener solo proyectos marcados',
  'mcp.enableGlobal': 'Activar en todos los proyectos',
  'mcp.general': 'General',
  'mcp.disableGeneral': 'Desactivar para discusiones sin proyecto',
  'mcp.enableGeneral': 'Activar para discusiones sin proyecto',
  'mcp.editContext': 'Editar contexto de {0} para {1}',
  'mcp.customized': '(personalizado)',
  'mcp.default': '(por defecto)',
  'mcp.moreProjects': '+{0} proyectos',
  'mcp.lessProjects': 'Reducir',
  'mcp.deleteConfig': 'Eliminar esta config',
  'mcp.deleteConfigConfirm': '¿Eliminar la config "{0}"? Esta acción es irreversible — claves, proyectos vinculados y contextos personalizados se perderán.',
  'mcp.deleteConfigSuccess': 'Config "{0}" eliminada',
  'mcp.deleteConfigError': 'Eliminación fallida: {0}',
  'mcp.empty': 'Ningún plugin configurado. Haga clic en "Agregar" para instalar uno o "Sincronizar" para escanear sus proyectos.',
  'mcp.contextTitle': 'Contexto plugin — {0}',
  'mcp.contextInfo': 'Proyecto: {0} · Archivo: docs/operations/mcp-servers/{1}.md',
  'mcp.contextPlaceholder': 'Instrucciones para agentes que usan este plugin en este proyecto...',
  'mcp.config': 'config',
  'mcp.configPlural': 'configs',
  'mcp.server': 'plugin',
  'mcp.serverPlural': 'plugins',
  'mcp.global': 'global',
  'mcp.globalPlural': 'globales',
  'mcp.project': 'proyecto',
  'mcp.projectPlural': 'proyectos',
  'mcp.cat.all': 'Todos',
  'mcp.cat.gitCode': 'Git & Code',
  'mcp.cat.databases': 'Bases de datos',
  'mcp.cat.cloud': 'Cloud & Infra',
  'mcp.cat.search': 'Búsqueda & Web',
  'mcp.cat.monitoring': 'Monitoreo',
  'mcp.cat.communication': 'Comunicación',
  'mcp.cat.projectMgmt': 'Gestión de proyectos',
  'mcp.cat.design': 'Diseño',
  'mcp.cat.utilities': 'Utilidades',
  'mcp.cat.other': 'Otros',
  'mcp.setupRequired': 'Configuración requerida',
  'mcp.official': 'Oficial',
  'mcp.community': 'Comunitario',
  'mcp.kind.mcp': 'MCP',
  'mcp.kind.api': 'API',
  'mcp.kind.hybrid': 'MCP + API',
  'mcp.kind.all': 'Todos',
  'mcp.kind.mcpTooltip': 'Plugin MCP — el agente usa herramientas dedicadas expuestas por el servidor MCP.',
  'mcp.kind.apiTooltip': 'Plugin API — el agente llama a una API REST vía curl (sin herramientas MCP).',
  'mcp.kind.hybridTooltip': 'Plugin híbrido — expone un MCP y una API REST. El agente elige según necesidad.',
  'mcp.kind.allTooltip': 'Todos los plugins (MCP + API + híbridos).',
  'mcp.by': 'por',
  'mcp.mcpLoad': 'Carga MCP',
  'mcp.mcpLoadOk': 'Carga MCP OK',
  'mcp.mcpLoadWarn': 'Muchos MCPs — posible ralentización',
  'mcp.mcpLoadDanger': 'Demasiados MCPs — riesgo de timeout',
  'mcp.scope': 'Alcance',

  // ── MCP setup help (i18n overrides for token_help) ──
  'mcp.help.mcp-fastly': "Requiere el Fastly CLI y el binario fastly-mcp.\n\n1. Instalar el CLI: https://developer.fastly.com/reference/cli/\n2. Instalar el servidor MCP: https://github.com/fastly/mcp/releases\n3. Crear un perfil de autenticación:\n   fastly profile create mi-perfil\n   (el nombre es libre — ej: \"prod\", \"company\"…)\n4. Pegar su API token cuando se solicite.\n\nNo se necesita variable de entorno — la auth se lee de los perfiles CLI.",
  'mcp.help.mcp-gitlab': "Requiere el CLI de GitLab (glab).\n\n1. Instalar el CLI: https://gitlab.com/gitlab-org/cli/-/releases\n   brew install glab (macOS/Linux) o winget install glab (Windows)\n2. Crear un Personal Access Token (scope api) en su GitLab\n3. Rellenar los campos abajo:\n   GITLAB_TOKEN: su PAT\n   GITLAB_HOST: el hostname de su GitLab (ej: gitlab.company.com)\n   Dejar GITLAB_HOST vacío para gitlab.com.\n\nKronn pasa estos valores a glab automáticamente — no es necesario ejecutar glab auth login.",
  'mcp.help.mcp-microsoft-365': "Conecta Outlook (correo, calendario), Teams, OneDrive y OneNote vía Microsoft Graph.\nServidor comunitario: @softeria/ms-365-mcp-server (--org-mode para cuentas profesionales).\nNo hay servidor MCP oficial de Microsoft por el momento.\n\nOpción 1 — App Softeria por defecto (rápido):\nDejar los campos vacíos. Al primer uso, el agente muestra un código y pide ir a https://microsoft.com/devicelogin. Su admin Microsoft puede bloquear apps de terceros.\n\nOpción 2 — Su propia app Azure (recomendado para organizaciones):\n1. portal.azure.com → Entra ID → Registros de aplicaciones → Nuevo registro\n2. Nombre: \"Kronn MCP\", tipo: Cuentas en esta org solamente\n3. Copiar ID de aplicación (cliente) → MS365_MCP_CLIENT_ID\n4. Copiar ID de directorio (inquilino) → MS365_MCP_TENANT_ID\n5. Autenticación → Marcar \"Permitir flujos de cliente público\" (¡obligatorio!)\n6. Permisos de API → Microsoft Graph → Permisos delegados:\n   Mail.Read, Mail.Send, Calendars.ReadWrite, Chat.Read, Files.ReadWrite, User.Read\n7. Conceder consentimiento del admin si es necesario\n\nAutenticación:\nAl primer uso, el servidor muestra un código y una URL.\nAbrir https://microsoft.com/devicelogin en un navegador, introducir el código, iniciar sesión.\nLos tokens se guardan localmente — no necesita re-autenticarse cada vez.\n\nSi obtiene \"Failed to reconnect\":\nnpx @softeria/ms-365-mcp-server --org-mode --logout\nnpx @softeria/ms-365-mcp-server --org-mode --login\n\nFunciona con todos los agentes Kronn (cross-agent).",

  // ── Workflows ──
  'wf.title': 'Automatización',
  'wf.subtitle': 'Workflows, quick prompts',
  'wf.new': 'Nuevo workflow',
  'wf.newHint': 'Crea un workflow manualmente, paso a paso. Controlas cada detalle.',
  'wf.tabWorkflows': 'Workflows',
  'wf.tabQuickPrompts': 'Quick Prompts',
  'wf.tabQuickApis': 'Quick APIs',
  'qa.new': 'Nuevo Quick API',
  'qa.empty': 'Ningún Quick API',
  'qa.emptyHint': 'Crea llamadas API reutilizables (POST Jira issue, get GitHub PR…) con variables {{title}}, {{key}}…',
  'qa.name': 'Nombre',
  'qa.namePlaceholder': 'Crear ticket Jira',
  'qa.descriptionLabel': 'Descripción (recomendada)',
  'qa.descriptionPlaceholder': 'Crea un ticket en el proyecto EW con título + descripción aportados como variables.',
  'qa.variables': 'Variables detectadas',
  'qa.varLabel': 'Etiqueta mostrada',
  'qa.varPlaceholder': 'Placeholder',
  'qa.varRequired': 'Obligatoria',
  'qa.varDescriptionPlaceholder': 'Descripción (opcional)',
  'qa.varsHint': 'Escribe `{{nombre}}` en cualquier campo arriba (endpoint, body, headers, query) — las variables aparecerán aquí.',
  'qa.save': 'Guardar',
  'qa.edit': 'Editar',
  'qa.delete': 'Eliminar',
  'qa.deleteConfirm': '¿Eliminar el Quick API «{name}»?',
  'qa.export': 'Exportar (JSON)',
  'qa.exportDone': '«{name}» exportado ✓',
  'qa.vars': '{0} variable(s)',
  'qa.noPluginsWarning': 'Ningún plugin API configurado en este host. Crea uno primero (Atlassian, GitHub, SpeedCurve, …) en la pestaña MCPs / Plugins, luego vuelve aquí.',
  'qa.noPluginsCta': 'Configurar un plugin →',
  'qa.saveBlockedHint': 'Campos obligatorios faltantes',
  'qa.fieldName': 'nombre',
  'qa.fieldPlugin': 'plugin API',
  'qa.fieldConfig': 'config plugin',
  'qa.fieldEndpoint': 'endpoint',
  'qa.launch': 'Ejecutar (directo)',
  'qa.runTitle': 'Ejecutar «{name}»',
  'qa.runNoVars': 'Este Quick API no tiene variables que rellenar.',
  'qa.runGo': 'Ejecutar',
  'qa.runFailed': 'Ejecución fallida',
  'qa.batch.launch': 'Batch (lanzar N llamadas en paralelo)',
  'qa.batch.cancel': 'Cancelar',
  'qa.batch.go': 'Lanzar el batch',
  'qa.batch.inputLabel': 'Un valor de `{0}` por línea (o separados por , o ;)',
  'qa.batch.inputPlaceholder': 'www.euronews.com\nde.euronews.com\nfr.euronews.com',
  'qa.batch.jsonInputLabel': 'Array JSON de objetos — uno por llamada',
  'qa.batch.jsonInputHint': 'Variables esperadas: {0}. Ejemplo: `[{"host":"de.example.com","limit":"5"}, ...]`',
  'qa.batch.jsonInputPlaceholder': '[\n  {"host":"www.example.com","limit":"5"},\n  {"host":"de.example.com","limit":"5"}\n]',
  'qa.batch.countHint': '{0} llamada(s) se lanzarán',
  'qa.batch.jsonCountHint': '{0} llamada(s) parseadas del JSON',
  'qa.batch.resultSummary': '{0}/{1} OK · {2} fallo(s) ·',
  'qa.batch.colInput': 'Entrada',
  'qa.batch.colStatus': 'Estado',
  'qa.batch.colResult': 'Resultado',
  'qa.batch.expandRow': 'ver todo',
  'qa.batch.collapseRow': 'ocultar',
  'qa.batch.errorEmpty': 'Ningún ítem para lanzar.',
  'qa.batch.errorJsonArray': 'El JSON debe ser un array (`[...]`).',
  'qa.batch.errorJsonParse': 'JSON inválido: {0}',
  'qp.new': 'Nuevo prompt',
  'qp.empty': 'Sin quick prompts',
  'qp.emptyHint': 'Cree prompts reutilizables con variables {{ticket}}, {{url}}...',
  'qp.name': 'Nombre',
  'qp.namePlaceholder': 'Analizar ticket Jira',
  'qp.prompt': 'Prompt',
  'qp.promptPlaceholder': 'Analiza el ticket {{ticket}} en Jira e identifica...',
  'qp.addVariable': '+ Variable',
  'qp.varName': 'Nombre',
  'qp.varLabel': 'Etiqueta',
  'qp.varPlaceholder': 'Placeholder',
  'qp.save': 'Guardar',
  'qp.saveBlockedHint': 'Campos obligatorios faltantes',
  'qp.bindingsLabel': 'Enlaces (skills · perfiles · directivas)',
  'qp.improveWithAi': 'Mejorar este QP con IA',
  'qp.improveInitialPrompt': 'Audita y mejora el Quick Prompt "{0}" a continuación. Sigue el protocolo: tabla de auditoría, recomendaciones, luego el QP refactorizado completo como bloque JSON seguido de la señal KRONN:QP_IMPROVED.',
  'qp.improveVisibleIntro': '✨ Auditando y mejorando el Quick Prompt "{0}"…',
  'qp.history.openLabel': '🕒 {0} versión(es)',
  'qp.history.openTitle': 'Historial de versiones de "{0}"',
  'qp.history.drawerTitle': 'Historial — {0}',
  'qp.history.empty': 'Aún no hay versiones archivadas. Las versiones se guardan automáticamente en cada edición del QP.',
  'qp.history.current': 'actual',
  'qp.history.launchesTooltip': 'Número de discusiones lanzadas con esta versión del QP.',
  'qp.history.avgTokensTooltip': 'Tokens medios de la primera respuesta del agente.',
  'qp.history.avgDurationTooltip': 'Duración media (wallclock) de la primera respuesta del agente.',
  'qp.history.avgCostTooltip': 'Coste medio en USD de la primera respuesta del agente.',
  'qp.history.showDiff': 'Ver diff v{0} → v{1}',
  'qp.history.hideDiff': 'Ocultar diff',
  'qp.history.diffPrev': 'v{0} (anterior)',
  'qp.history.diffNext': 'v{0} (esta versión)',
  'qp.history.btnTitle': 'Ver historial y stats',
  'qp.history.deleteAria': 'Eliminar la versión {0}',
  'qp.history.deleteConfirm': '¿Eliminar la versión v{0}? Los lanzamientos vinculados a esta versión ya no se atribuirán al QP (las discusiones permanecen intactas).',
  'qp.metrics.chipTooltip': 'Stats de la versión actual (v{0}): media del primer mensaje del agente sobre {1} lanzamiento(s).',
  'qp.launch.missingRequired': 'Campos obligatorios faltantes: {0}',
  'qp.improvedReady': '✨ QP mejorado listo — un clic para desplegar.',
  'qp.deployImproved': 'Desplegar QP mejorado',
  'qp.deployInProgress': 'Desplegando…',
  'qp.deployFailed': 'Despliegue fallido: {0}',
  'qp.deploySuccess': 'QP desplegado en v{0}',
  'qp.deployedAtVersion': '✅ QP desplegado en v{0}',
  'qp.improveCatalogHeader': 'Catálogo disponible (a usar en el refactor)',
  'qp.improveCatalogIntro': 'Aquí está la lista de skills, perfiles y directivas instaladas en esta instancia Kronn. Añádelas a los `skill_ids` / `profile_ids` / `directive_ids` del QP refactorizado cuando sea pertinente — preserva siempre los bindings ya presentes salvo que estén claramente fuera de tema.',
  'qp.improveCatalogSkills': 'Skills',
  'qp.improveCatalogProfiles': 'Perfiles',
  'qp.improveCatalogDirectives': 'Directivas',
  'qp.improveCatalogEmpty': '_Ningún elemento disponible en esta categoría._',
  'qp.fieldName': 'nombre',
  'qp.fieldPrompt': 'prompt',
  'qp.launch': 'Ejecutar',
  'qp.agentBadgeTooltip': 'QP vinculado al agente {0}',
  'qp.compareAgents.button': 'Comparar entre {0} agentes instalados',
  'qp.compareAgents.tooltip': 'Ejecuta este QP en paralelo en los {0} agentes instalados — una discusión por agente, agrupados en batch.',
  'qp.compareAgents.cta': 'Comparar ({0})',
  'qp.compareAgents.selectorLabel': 'Comparar en:',
  'qp.compareAgents.selectAll': 'Todos',
  'qp.compareAgents.selectNone': 'Ninguno',
  'qp.vars': '{0} variable(s)',
  'qp.delete': 'Eliminar',
  'qp.deleteConfirm': '¿Eliminar este quick prompt?',
  'qp.syntaxHint': 'Variables: {{nombre}}. Secciones condicionales: {{#nombre}}texto si completado{{/nombre}}',
  'qp.descriptionLabel': 'Descripción (opcional — ayuda a los workflows batch a entender para qué sirve este prompt)',
  'qp.descriptionPlaceholder': 'Ej: Análisis técnico de un ticket Jira para el scoping (visión técnica, esfuerzo, preguntas abiertas)',
  'qp.variables': 'Variables',
  'qp.varRequired': 'Obligatoria',
  'qp.varDescriptionPlaceholder': '¿Para qué sirve esta variable? (ej: ID del ticket Jira, p. ej. EW-7182)',
  'qp.batch.launch': 'Lanzar en batch',
  'qp.batch.worktree': 'Aislar cada discusión en un worktree git',
  'qp.batch.worktreeHint': 'Cada discusión hija se ejecuta en su propio worktree git con una rama dedicada. Requerido cuando los agentes escriben código en paralelo.',
  'qp.batch.worktreeNoProject': 'Este Quick Prompt no está vinculado a ningún proyecto (repo git). Vincúlalo a un proyecto para habilitar esta opción.',
  'qp.batch.cancel': 'Cancelar',
  'qp.batch.inputLabel': 'Lista de valores para «{0}» (uno por línea O separados por comas / punto y coma)',
  'qp.batch.inputPlaceholder': 'EW-7100, EW-7101, EW-7102\nO un valor por línea',
  'qp.batch.countHint': '{0} discusión(es) se crearán en paralelo (máx 5 simultáneas)',
  'qp.batch.confirm': '¿Lanzar {0} discusiones en paralelo con «{1}»?\n\nCada discusión consume tokens. Esta operación puede costar dinero.',
  'qp.batch.emptyInput': 'Pega al menos un valor (uno por línea).',
  'qp.batch.tooManyItems': 'Demasiadas líneas ({0}). Máximo 50 por batch.',
  'qp.batch.needsVariable': 'Este Quick Prompt no tiene variables — no se puede lanzar en batch. Añade una variable {{clave}} en la plantilla.',
  'qp.batch.failed': 'Fallo al lanzar el batch: {0}',
  'qp.batch.toast.ok': '{0} — terminado ({1} OK)',
  'qp.batch.toast.partial': '{0} — {1} OK, {2} errores',
  'bootstrap.repoReady': 'Repositorio Git creado — listo para validación',
  'bootstrap.repoValidated': 'Repositorio validado. Ahora analiza la arquitectura del proyecto a partir de los documentos proporcionados.',
  'bootstrap.analyzeArchitecture': 'Validar → Analizar arquitectura',
  'bootstrap.architectureReady': 'Arquitectura analizada — lista para validación',
  'bootstrap.architectureValidated': 'Arquitectura validada. Genera el plan del proyecto con épicas, historias y estimaciones.',
  'bootstrap.generatePlan': 'Validar → Generar plan',
  'bootstrap.planReady': 'Plan del proyecto generado — listo para validación',
  'bootstrap.planValidated': 'Plan validado. Crea las issues en el tracker del proyecto.',
  'bootstrap.createIssues': 'Validar → Crear issues',
  'bootstrap.issuesCreated': 'Issues creadas en el tracker',
  'bootstrap.viewProject': 'Ver proyecto',
  'bootstrap.startDev': 'Empezar el desarrollo en la issue #1',
  'bootstrap.devKickoffTitle': 'Inicio de desarrollo — issue #1',
  'bootstrap.devKickoffPrompt': 'Abre la issue #1 en el tracker del proyecto y empieza a trabajar en ella. Lee la descripción, haz preguntas si lo necesitas, luego implementa la solución paso a paso. Usa el MCP tracker para actualizar el estado a medida que avanzas, y haz commits lógicos sobre la marcha.',
  'wf.createWithAI': 'Crear con IA',
  'wf.createWithAIHint': 'Describe tu necesidad en lenguaje natural, la IA te genera el workflow completo. Más rápido para workflows complejos, pero deberías revisar el resultado.',
  'wf.aiArchitectPrompt': 'Quiero crear un nuevo workflow automatizado. Ayúdame a diseñarlo paso a paso.',
  'wf.aiWorkflowReady': 'Workflow diseñado por IA — listo para desplegar',
  // 0.8.3 — bundle CTA. {0}=QPs, {1}=QAs, {2}=Custom APIs.
  'wf.aiBundleReady': 'Bundle diseñado por IA — listo para desplegar ({0} Quick Prompts · {1} Quick APIs · {2} Custom APIs)',
  // 0.8.3 — bundle button. {0}=total extras (QPs+QAs+CustomAPIs).
  'wf.createBundleBtn': 'Crear todo (1 workflow + {0} artefactos)',
  'wf.createThisWorkflow': 'Crear este workflow',
  'wf.workflowCreated': 'Workflow creado con éxito',
  'wf.workflowCreateError': 'Error al crear el workflow',
  'wf.empty': 'Ningún workflow configurado',
  'wf.emptyHint': 'Cree un workflow para automatizar sus tareas',
  'wf.active': 'Activo',
  'wf.inactive': 'Inactivo',
  'wf.trigger': 'Ejecutar',
  'wf.delete': 'Eliminar',
  'wf.selectOne': 'Seleccione un workflow',
  'wf.unknown': 'Desconocido',
  'wf.back': 'Volver',
  'wf.edit': 'Editar',
  'wf.refresh': 'Actualizar',
  'wf.launch': 'Ejecutar',
  'wf.running': 'En ejecución',
  'wf.runDone': 'Run terminado — {0}',
  'wf.runWaiting': 'Esperando tu decisión',
  'wf.stepsExecuted': '{0} steps ejecutados',
  'wf.deleteAll': 'Eliminar todo',
  'wf.noRuns': 'Sin ejecuciones',
  'wf.skipNext': 'Saltar siguiente step',
  'wf.manual': 'Manual',
  'wf.batchStepQpMissing': 'Quick Prompt no encontrado (id: {0}). ¿Fue eliminado?',
  'wf.batchStepItemsLabel': 'Ítems desde:',
  'wf.batchStepWaits': '⏳ Espera a que terminen todas las discusiones',
  'wf.batchStepFireForget': '⚡ Fire-and-forget (no esperar)',
  'wf.batchStepMaxItems': 'Máx {0} ítems',
  'wf.batchStepWorktree': 'Worktree aislado por discusión',
  'wf.batchStepChainLabel': 'Cadena:',
  'wf.runBatchChip': '📋 Ver las {0} conversaciones',
  'wf.runBatchChipHint': 'Saltar a la sidebar Discusiones y abrir el batch creado por este run',
  'wf.cancelRun': 'Detener',
  'wf.cancelRunConfirm': '¿Detener este run y todas sus discusiones hijas en curso?',
  'wf.cancelRunError': 'Error al detener. Revisa la consola para más detalles.',
  'wf.activeRunsTitle': 'Runs activos',
  'wf.activeRunsEmpty': 'No hay runs activos',
  'wf.cancelling': 'Deteniendo…',
  'wf.viewAllWorkflows': 'Ver todos los workflows',
  'wf.elapsed': 'hace {0}',
  // ─── ApiCall step (désagentificación — 0.5.2) ──────────────────────
  'wf.apicall.title': 'Obtener datos',
  'wf.apicall.subtitle': 'Llamada directa a una API — cero tokens consumidos',
  'wf.apicall.pluginPicker': 'Plugin API',
  'wf.apicall.pluginPickerHint': 'Elige una API configurada en este proyecto',
  'wf.apicall.qaPicker': 'Desde un Quick API existente',
  'wf.apicall.qaPickerInline': '— Ninguno (config inline más abajo) —',
  'wf.apicall.qaInheritedFrom': 'Heredado de « {0} »',
  'wf.apicall.qaInheritedHint': 'La llamada hereda la config del Quick API. Haz clic en ✏️ abajo para sobrescribir uno o más campos solo en este step.',
  'wf.apicall.qaDetach': 'Desvincular del Quick API (modo inline)',
  'wf.apicall.qaOverrideToggle': 'Personalizar esta llamada para este step (override)',
  'wf.apicall.qaSummaryEndpoint': 'Llamada:',
  'wf.apicall.qaSummaryExtract': 'Extract:',
  'wf.apicall.endpointPicker': 'Endpoint',
  'wf.apicall.endpointPlaceholder': 'ej.: /repos/owner/repo/issues — haz clic para elegir',
  'wf.apicall.pathParamsTitle': 'Parámetros de ruta',
  'wf.apicall.pathParamsHint': 'Auto-detectados desde el endpoint. Rellena cada token — Kronn los sustituye en la URL al momento de la solicitud.',
  'wf.apicall.pathParamsPlaceholder': 'Valor para {0}',
  'wf.apicall.pathParamsPreview': 'URL resuelta',
  'wf.apicall.methodLabel': 'Método',
  'wf.apicall.queryParams': 'Parámetros (query)',
  'wf.apicall.headers': 'Headers',
  'wf.apicall.body': 'Body JSON',
  'wf.apicall.testBtn': 'Probar la llamada',
  'wf.apicall.testing': 'Probando…',
  'wf.apicall.testHint': 'Envía una solicitud real con las credenciales configuradas',
  'wf.apicall.testVarsTitle': 'Variables a aportar para la prueba',
  'wf.apicall.testVarsHint': 'Tu config contiene variables `{{var}}` sin resolver. Dales un valor de prueba sólo para esta solicitud (la config guardada no se modifica).',
  'wf.apicall.testVarsPlaceholder': 'valor de prueba',
  'wf.apicall.testVarsGo': 'Probar con estos valores',
  'wf.apicall.responseTitle': 'Respuesta JSON',
  'wf.apicall.responseEmpty': 'Ejecuta una prueba para ver la respuesta aquí',
  'wf.apicall.clickToPickHint': '💡 Haz clic en una clave dentro de un array → todos los elementos. En un valor → ese elemento. [N] → iterar.',
  'wf.apicall.suggest.title': 'Sugerencias para esta respuesta',
  'wf.apicall.suggest.iterate': 'Iterar sobre los {0} elementos',
  'wf.apicall.suggest.allField': 'Todos los "{0}" ({1} valores)',
  'wf.apicall.suggest.firstItem': 'El 1.er elemento',
  'wf.apicall.suggest.counter': 'Contador "{0}" = {1}',
  'wf.apicall.extractSection': 'Lo que sale de aquí',
  'wf.apicall.extractSectionHint': 'El siguiente paso recibirá el valor extraído vía {{steps.nombre.data}}',
  'wf.apicall.extractData': 'Dato',
  'wf.apicall.extractStatus': 'Estado',
  'wf.apicall.extractSummary': 'Resumen',
  'wf.apicall.pathLabel': 'Ruta (JSONPath)',
  'wf.apicall.pathPlaceholder': 'ej.: $.issues[*].key',
  'wf.apicall.preview': 'Vista previa',
  'wf.apicall.previewEmpty': 'Sin resultado para esta ruta',
  'wf.apicall.previewType': 'Tipo',
  'wf.apicall.errorInvalidPath': 'Ruta JSONPath inválida',
  'wf.apicall.examples': 'Ejemplos',
  'wf.apicall.advancedToggle': 'Opciones avanzadas (body, método, timeout, reintentos, output var)',
  'wf.apicall.exampleAllIds': 'Todos los IDs',
  'wf.apicall.exampleFirst': 'Primer elemento',
  'wf.apicall.exampleCount': 'Total',
  'wf.apicall.failOnEmpty': 'Marcar como NO_RESULTS si vacío',
  'wf.apicall.failOnEmptyHint': 'Dispara las condiciones Skip/Stop/Goto del paso siguiente',
  'wf.apicall.fallbackLabel': 'Valor por defecto (si vacío)',
  'wf.apicall.nextStepBatchOk': 'El siguiente paso espera un array — Data es un array ✓',
  'wf.apicall.nextStepBatchMismatch': 'El siguiente paso (batch) espera un array, pero Data es {0}',
  'wf.apicall.nextStepBatchWrap': 'Envolver en un array',
  'wf.apicall.tokensSaved': '0 tokens consumidos por este paso',
  'wf.apicall.timeoutLabel': 'Timeout (ms)',
  'wf.apicall.retriesLabel': 'Reintentos (5xx / 429)',
  'wf.apicall.outputVarLabel': 'Nombre de variable',
  'wf.apicall.outputVarHint': 'Disponible en los pasos siguientes vía {{steps.<nombre>.data}}',
  'wf.apicall.testSuccess': 'Llamada exitosa en {0} ms',
  'wf.apicall.testFailed': 'Error',
  'wf.apicall.notSupported': 'Ningún plugin API configurado en este proyecto',
  'wf.apicall.authManagedTitle': 'Auth — gestionada por Kronn',
  'wf.apicall.authManagedHint': 'Estos campos se inyectan en runtime desde las credenciales que registraste en Settings → APIs. No hay que repetirlos aquí.',
  'wf.apicall.authManagedReveal': 'Mostrar el nombre de la variable de entorno',
  'wf.apicall.testNeedsProjectLink': 'Esta configuración del plugin no está vinculada a ningún proyecto — ve a Settings → APIs y marca al menos un proyecto para poder probar.',
  // ── AI helper bubble (0.5.2) ──
  'wf.apicall.helper.trigger': 'Asistente IA',
  'wf.apicall.helper.triggerHint': 'Pide a un agente local que te ayude a elegir el endpoint y la extracción',
  'wf.apicall.helper.triggerDisabled': 'Elige una API primero — sin plugin seleccionado el agente no tiene spec que leer',
  'wf.apicall.helper.pickAgent': '¿Qué agente te ayuda?',
  'wf.apicall.helper.switchAgent': 'Cambiar de agente (reinicia la conversación)',
  'wf.apicall.helper.welcome': '¡Hola! ¿Qué quieres hacer? Elige un atajo o escribe tu pregunta.',
  'wf.apicall.helper.starter.build': 'Ayúdame a construir la llamada',
  'wf.apicall.helper.starter.buildPrompt': 'Ayúdame a construir esta llamada API. Quiero ',
  'wf.apicall.helper.starter.endpoint': 'Sugiere un endpoint',
  'wf.apicall.helper.starter.endpointPrompt': 'Sugiéreme un endpoint para ',
  'wf.apicall.helper.starter.body': 'Ayuda con el body',
  'wf.apicall.helper.starter.bodyPrompt': 'Ayúdame a construir el body JSON para ',
  'wf.apicall.helper.cancel': 'Cancelar',
  'wf.apicall.helper.discTitle': 'Asistente config (efímero)',
  'wf.apicall.helper.bubbleTitle': 'Asistente config',
  'wf.apicall.helper.ephemeral': 'efímero',
  'wf.apicall.helper.minimize': 'Minimizar',
  'wf.apicall.helper.restore': 'Reabrir asistente',
  'wf.apicall.helper.close': 'Cerrar (elimina la conversación)',
  'wf.apicall.helper.thinking': 'El agente piensa…',
  'wf.apicall.helper.inputPlaceholder': 'Describe lo que quieres obtener…',
  'wf.apicall.helper.send': 'Enviar',
  'wf.apicall.helper.stop': 'Stop',
  'wf.apicall.helper.suggestion': 'Sugerencia',
  'wf.apicall.helper.apply': 'Aplicar',
  'wf.apicall.helper.applied': 'Aplicado ✓',
  'wf.apicall.helper.noAgents': 'Ningún agente instalado en este equipo.',
  'wf.apicall.helper.contextHint': 'El contexto (API, endpoint, query, último test) se adjunta a cada mensaje enviado al agente.',
  'wf.apicall.helper.contextNoApi': 'Ninguna API seleccionada',
  'wf.apicall.helper.contextLastErr': 'último test: ❌',
  'wf.apicall.helper.contextLastOk': 'último test: ✓',
  // System prompt strings — drive the agent's reply language. Changing the UI locale
  // changes which language the agent answers in for the API helper conversation.
  'wf.apicall.helper.sys.unknown': '(desconocido)',
  'wf.apicall.helper.sys.noPlugin': '(ningún plugin seleccionado)',
  'wf.apicall.helper.sys.none': '(ninguno)',
  'wf.apicall.helper.sys.default': '(por defecto)',
  'wf.apicall.helper.sys.empty': '(vacío)',
  'wf.apicall.helper.sys.ctxHeader': '### CONTEXTO ACTUAL (instantánea al momento del envío)',
  'wf.apicall.helper.sys.ctxLastFail': '### ÚLTIMO TEST → FALLO',
  'wf.apicall.helper.sys.ctxLastOk': '### ÚLTIMO TEST → ÉXITO (extracto de respuesta)',
  'wf.apicall.helper.sys.userQuestion': '### PREGUNTA DEL USUARIO',
  'wf.apicall.helper.sys.role': `# Rol
Eres un asistente de configuración para un paso "Recuperar datos" (\`StepType::ApiCall\`)
en un workflow Kronn. Este paso hace una llamada HTTP directa (sin tokens LLM consumidos)
y extrae un valor mediante JSONPath, que se redirige al paso siguiente.`,
  'wf.apicall.helper.sys.boundaries': `# Lo que puedes y NO puedes hacer
- ✅ Leer la spec de la API abajo, el estado actual del paso, el resultado del último test.
- ✅ Proponer modificaciones de config (endpoint, method, query, headers, body, extract).
- ❌ NO tienes **NINGUNA** herramienta para llamar a la API tú mismo. Ni Bash, ni MCP, ni fetch.
     Es Kronn (backend Rust) quien hace la llamada cuando el usuario hace clic en "Test the call".
- ❌ NUNCA propongas "verificar en línea" mediante un MCP — no está disponible en esta
     conversación. Si la causa de un fallo se te escapa, redirige a la documentación oficial
     o al dashboard del proveedor.`,
  'wf.apicall.helper.sys.action': `# Tu única acción útil: proponer un \`KRONN:APPLY\`
Cuando sugieres una config, escribe EXACTAMENTE este formato (si no, la UI no te lee):

KRONN:APPLY
\`\`\`json
{ "endpoint": "/path/v4", "query": { "k": "v" }, "extract": "$.data[*]" }
\`\`\`

Campos permitidos: \`endpoint\`, \`method\`, \`query\`, \`headers\`, \`body\`, \`extract\`.
Puedes poner SÓLO UN campo — el resto del paso se preserva.
No más de un bloque \`KRONN:APPLY\` por mensaje (si no, el usuario no sabe cuál elegir).`,
  'wf.apicall.helper.sys.debug': `# Método de debug (aplicar en cuanto un test falle)
1. Lee el bloque \`### ÚLTIMO TEST → FALLO\` — contiene \`HTTP <status> on <method> <url> — <body excerpt>\`.
2. Anota el **status** (4xx vs 5xx → 4xx = tu config es la causa, 5xx = lado servidor).
3. Anota los **query params** en la URL compuesta (la apikey está enmascarada — es normal y ESPERADO).
4. Cruza con los TIPS del plugin abajo + los endpoints permitidos.
5. Propón **UNA** modificación dirigida (un solo KRONN:APPLY). Nada de ráfagas de 3 hipótesis simultáneas.
6. Si el fallo persiste tras 2 intentos, recomienda al usuario verificar en el dashboard
   del proveedor (URL de doc abajo) — no des vueltas en círculos.`,
  'wf.apicall.helper.sys.endpointsHeader': '# Endpoints PERMITIDOS (nunca inventes fuera de esta lista)',
  'wf.apicall.helper.sys.endpointsEmpty': '(ninguno — el usuario aún no ha elegido una API)',
  'wf.apicall.helper.sys.tipsHeader': '# TIPS PLUGIN — {0}',
  'wf.apicall.helper.sys.docsHeader': '# Documentación oficial',
  'wf.apicall.helper.sys.style': `# Estilo
- Español, ≤ 3 líneas por mensaje.
- Sin paja, sin "voy a analizar", sin "esto es lo que sugiero:" — directo al grano.
- La apikey/token mostrados como \`••••••••\` o \`***\` en los logs están ENMASCARADOS SÓLO PARA MOSTRAR.
  El valor real configurado por el usuario SE envía por Kronn. Si te preguntan "¿estás seguro
  de que la clave se envía bien?" — responde SÍ sin reservas, y redirige el diagnóstico a otra
  parte (host, endpoint, scope del token).`,
  'wf.apicall.helper.sys.starter': 'Para empezar, pregunta al usuario qué quiere recuperar.',
  'wf.apicall.helper.sys.authHeader': `### AUTH — gestionada automáticamente por Kronn (NO sugerir)
El backend inyecta estos campos en el momento de la solicitud, con la clave que
el usuario introdujo en Settings → APIs. ⚠️ NUNCA los pongas en una sugerencia
KRONN:APPLY; eso sobreescribiría el valor real con un placeholder
("TU_API_KEY", etc.) y rompería la auth.`,
  'wf.apicall.helper.sys.authQueryItem': '- query param `{0}` (valor sacado del env `{1}`, ya configurado en Kronn)',
  'wf.apicall.helper.sys.authHeaderItem': '- header `{0}` (valor sacado del env `{1}`, ya configurado en Kronn)',
  'wiz.testBatchStep': 'Probar (dry-run)',
  'wiz.testBatchInfo': 'Previsualiza lo que haría este paso batch sin crear nada: lista de ítems, prompt renderizado en el 1er ítem, QP/agente, modo workspace.',
  'wiz.testBatchAlwaysDryRun': 'Siempre dry-run — no se creará ninguna discusión.',
  'wiz.testBatchOk': 'Configuración válida',
  'wiz.testBatchErrors': '{0} error(es) bloqueante(s):',
  'wiz.testBatchItemsCount': 'ítem(s) se lanzarían',
  'wiz.testBatchCappedAt': 'previsualización limitada a los primeros {0}',
  'wiz.testBatchFillsVar': 'rellena {{{0}}}',
  'wiz.testBatchWorkspace': 'Workspace',
  'wiz.testBatchWaits': 'espera la finalización',
  'wiz.testBatchFireForget': 'fire-and-forget',
  'wiz.testBatchItemsSample': 'Ítems extraídos:',
  'wiz.testBatchSamplePromptFor': 'Prompt renderizado para el 1er ítem ({0}):',
  'wiz.testMockPrefilled': 'rellenado automáticamente desde el test de « {0} »',
  'wiz.testMockPrefilledExtract': 'extraído del test de « {0} » (última línea)',
  'wiz.testMockPrefilledFull': 'resultado completo de « {0} »',
  'wiz.testMockShowFull': '↻ Ver el resultado completo',
  'wiz.testMockShowExtract': '↻ Mantener solo la última línea',
  'wiz.testMockUsePrev': 'Usar el resultado de « {0} »',
  'wiz.testBatchWarnings': '{0} advertencia(s):',
  'wiz.testBatchItemToggle': 'Ver el prompt renderizado para este ítem',
  'wiz.testBatchItemPrompt': 'Prompt enviado al agente:',
  'wiz.testBatchItemDryRunBtn': 'Ejecutar dry-run',
  'wiz.testBatchItemDryRunLoading': 'Dry-run en curso…',
  'wiz.testBatchItemDryRunHint': 'Ejecuta el agente en modo solo-lectura sobre este ítem — describe lo que haría sin modificar nada',
  'wiz.testBatchItemDryRunResult': 'Lo que el agente haría:',

  // ── Structured agent questions (0.3.5) ──
  'agentQuestions.hint': 'El agente le hace preguntas. Rellene cada campo y haga clic en Responder — es más rápido que escribir todo en el chat.',
  'agentQuestions.submit': 'Responder',
  'agentQuestions.fillAll': 'Rellena todos los campos para enviar',

  'wf.inProgress': 'en curso...',
  'wf.pending': 'pendiente',
  'wf.live.stepNoOutput': '(este paso no produjo contenido)',
  'wf.live.stepStreamingWaiting': 'El agente está arrancando… (ningún chunk recibido aún)',
  'wf.live.totalElapsedHint': 'Tiempo total transcurrido desde que se lanzó el run.',
  'wf.liveStep.agent': 'El agente está pensando',
  'wf.liveStep.api': 'Llamada HTTP en curso',
  'wf.liveStep.notify': 'Enviando webhook',
  'wf.liveStep.batch': 'Fan-out en curso',
  'wf.liveStep.gate': 'Esperando una decisión humana',
  'wf.liveStep.exec': 'Comando shell en ejecución',
  'wf.runStatusToReview': 'A REVISAR',
  'wf.pausedJustNow': 'ahora mismo',
  'wf.pausedMinutes': 'en pausa desde hace {0} min',
  'wf.pausedHours': 'en pausa desde hace {0}h',
  'wf.pausedHoursMinutes': 'en pausa desde hace {0}h{1}',
  'wf.pausedDays': 'en pausa desde hace {0}d',
  'wf.pausedZeroTokens': '0 tokens consumidos durante la pausa',
  'wf.pausedZeroTokensHint': 'Ninguna llamada IA durante la pausa de un Gate — el contador se para hasta que el operador decida.',
  'wf.tokensTotal': 'tokens (total run)',
  'wf.tokensTotalHint': 'Suma de tokens consumidos por todos los steps de este run desde su inicio. Incluye los agentes; los gates siempre contribuyen 0.',
  'wf.stepTokensSuffix': 'tk',
  'wf.stepTokensHint': 'Tokens consumidos por este step. Gates / Exec / ApiCall / Notify no queman nada — se quedan en 0. Los agentes escalan según largo del prompt + respuesta.',
  'wf.gate.title': 'Se requiere decisión humana',
  'wf.gate.defaultMessage': 'Se requiere aprobación para continuar.',
  'wf.gate.commentPlaceholder': 'Comentario (requerido para «Solicitar cambios»)…',
  'wf.gate.commentRequired': 'Se requiere un comentario al solicitar cambios.',
  'wf.gate.approve': 'Aprobar',
  'wf.gate.requestChanges': 'Solicitar cambios',
  'wf.gate.reject': 'Rechazar',
  'wf.gate.triage.clear': 'Evidentes',
  'wf.gate.triage.decided': 'Decididos (revisar)',
  'wf.gate.triage.mocked': 'Mockeados (reemplazar)',
  'wf.gate.triage.blocked': 'Bloqueados (esperando)',
  'wf.gate.triage.chosen': 'Elegido:',
  'wf.gate.triage.why': 'Razón:',
  'wf.gate.triage.placeholder': 'Marcador:',
  'wf.gate.triage.strategy': 'Estrategia:',
  'wf.gate.triage.revisitWhen': 'Revisar cuando:',
  'wf.gate.triage.neededFrom': 'Necesario de:',
  'wf.gate.triage.workaround': 'Alternativa:',
  'wf.gate.triage.optionsConsidered': '{0} alternativa(s) descartada(s)',
  'wf.gate.triage.filesTouched': '{0} archivo(s) previstos',
  'wf.deleteRun': 'Eliminar este run',
  'wf.deleteWorkflowConfirm': '¿Eliminar este workflow? Acción irreversible — todos los runs y sus discusiones hijas se perderán.',
  'wf.deleteRunConfirm': '¿Eliminar este run? Su worktree y artefactos serán eliminados.',
  'wf.deleteAllRunsConfirm': '¿Eliminar TODOS los runs de este workflow? Irreversible — {0} run(s) + worktrees serán purgados.',
  'wf.runWorktreePath': 'Worktree:',
  'wf.runWorktreeCopy': 'Copiar la ruta',
  'wf.produced.title': '{0} rama(s) preservada(s)',
  'wf.produced.aheadCount': '{0} commit(s) por delante',
  'wf.produced.pushed': 'subido',
  'wf.produced.localOnly': 'solo local',
  'wf.produced.copyCheckout': 'Copiar git checkout',
  'wf.produced.copyDiff': 'Copiar git diff',
  'wf.produced.copyCd': 'Copiar cd',
  'wf.produced.test': 'Probar en worktree',
  'wf.produced.testHint': 'Crea un worktree aislado sobre el commit para probar sin tocar main',
  'wf.produced.creating': 'Creando…',
  'wf.produced.cleanup': 'Limpiar',
  'wf.export': 'Exportar',
  'wf.exportHint': 'Descarga este workflow en JSON (con sus Quick Prompts referenciados). Compartible entre instancias Kronn.',
  'wf.exportDone': 'Workflow «{name}» exportado',
  'wf.import': 'Importar',
  'wf.importHint': 'Importa un workflow exportado desde Kronn (.json). Arrastrar y soltar o selector de archivo.',
  'qp.export': 'Exportar este Quick Prompt',
  'qp.exportDone': 'Quick Prompt «{name}» exportado',
  'qp.import': 'Importar',
  'qp.importHint': 'Importa un Quick Prompt exportado desde Kronn (.json).',
  'imp.workflowTitle': 'Importar un workflow',
  'imp.qpTitle': 'Importar un Quick Prompt',
  'imp.dropOrPick': 'Arrastra un archivo aquí, o elige uno',
  'imp.fileHint': 'Formato esperado: .kronn-workflow.json o .kronn-qp.json',
  'imp.pickFile': 'Elegir un archivo',
  'imp.cancel': 'Cancelar',
  'imp.confirm': 'Importar',
  'imp.pickAnother': '← Elegir otro archivo',
  'imp.previewName': 'Nombre',
  'imp.previewSteps': 'Steps',
  'imp.previewVars': 'Variables',
  'imp.previewBundledQps': 'Quick Prompts incluidos',
  'imp.targetProject': 'Adjuntar al proyecto (opcional)',
  'imp.noProject': 'Ninguno (workflow no vinculado)',
  'imp.errorNotJson': 'Este archivo no es un .json — verifica el formato.',
  'imp.errorReadFailed': 'No se pudo leer el archivo.',
  'imp.errorJsonParse': 'JSON inválido — el archivo está corrupto o incompleto.',
  'imp.errorWrongKind': 'Tipo de export incorrecto: esperado `{expected}`, recibido `{got}`. ¿Has confundido un workflow con un Quick Prompt?',
  'imp.workflowDone': 'Workflow «{name}» importado',
  'imp.qpDone': 'Quick Prompt «{name}» importado',
  'wf.noOutput': '(sin salida)',
  'wf.status': 'Estado',
  'wf.duration': 'Duración',
  'wf.noProject': 'Sin proyecto',

  // ── Workflow wizard ──
  'wiz.modeSimple': 'Simple',
  'wiz.modeAdvanced': 'Avanzado',
  'wiz.helpModeSimple': 'Un solo paso: un agente, una tarea. Perfecto para el 80% de los casos.',
  'wiz.helpModeAdvanced': 'Pipelines multi-paso, condiciones, disparadores programados, batch.',
  'wiz.helpSimpleVsAdvanced': 'Simple = 1 paso, 1 agente, 1 tarea (ej: "genera un informe semanal"). Avanzado = pipelines multi-paso con condiciones (ej: "recupera tickets → batch → notificar"). Siempre puedes cambiar a Avanzado más tarde.',
  'wiz.task': 'Tarea',
  'wiz.agentLabel': 'Agente IA',
  'wiz.helpAgent': 'El modelo IA que ejecutará la tarea. Claude Code suele ser el más capaz; Codex y Gemini son alternativas. Cada agente debe instalarse por separado en tu máquina.',
  'wiz.promptLabel': '¿Qué debe hacer el agente?',
  'wiz.helpPromptSimple': 'Describe la tarea en lenguaje natural, como si hablaras con un compañero. Puedes usar variables como {{issue.title}} si el workflow se activa por una issue del tracker.',
  'wiz.promptSimplePlaceholder': 'Describe la tarea en lenguaje natural...',
  'wiz.starterLabel': 'O empezar desde un ejemplo:',
  'wiz.helpStarters': 'Haz clic en un ejemplo para pre-rellenar el nombre y el prompt. Puedes editarlos libremente antes de guardar.',
  'wiz.starter.codeReview': 'Revisión de código',
  'wiz.starter.codeReviewPrompt': 'Analiza los últimos commits en la rama principal. Para cada archivo modificado, identifica:\n- Problemas de calidad (código duplicado, complejidad excesiva)\n- Fallas de seguridad potenciales\n- Violaciones de convenciones del proyecto\n\nGenera un informe Markdown con los problemas clasificados por severidad (crítico / atención / sugerencia).',
  'wiz.starter.changelog': 'Changelog automático',
  'wiz.starter.changelogPrompt': 'Analiza todos los commits desde el último tag de release. Para cada commit, identifica si es una nueva funcionalidad, corrección, mejora o cambio técnico.\n\nGenera un changelog Markdown estructurado con secciones: Novedades, Correcciones, Mejoras, Técnico.',
  'wiz.starter.techDebt': 'Detección deuda técnica',
  'wiz.starter.techDebtPrompt': 'Escanea el código fuente del proyecto e identifica elementos de deuda técnica:\n- TODO/FIXME/HACK restantes en el código\n- Dependencias obsoletas o con vulnerabilidades conocidas\n- Archivos de más de 500 líneas sin tests\n- Código muerto o imports no utilizados\n\nGenera un informe priorizado con el esfuerzo estimado (S/M/L) para cada elemento.',
  'wiz.starter.testCoverage': 'Auditoría cobertura tests',
  'wiz.starter.testCoveragePrompt': 'Analiza la estructura del proyecto e identifica archivos y funciones críticas sin tests:\n- Endpoints API sin test de integración\n- Funciones utilitarias sin test unitario\n- Componentes UI con interacciones sin test\n\nGenera una lista priorizada de los tests faltantes más importantes, con un esqueleto de test para cada uno.',
  'wiz.starter.docUpdate': 'Actualización documentación',
  'wiz.starter.docUpdatePrompt': 'Compara la documentación existente (README, docs/, comentarios) con el código actual. Identifica:\n- Secciones obsoletas o incorrectas\n- Nuevas funcionalidades no documentadas\n- Instrucciones de instalación o configuración a actualizar\n\nPropone las correcciones para cada archivo.',
  'wiz.starter.securityScan': 'Escaneo seguridad',
  'wiz.starter.securityScanPrompt': 'Realiza una auditoría de seguridad del proyecto:\n- Verifica dependencias para CVE conocidas\n- Busca secretos o tokens hardcodeados en el código\n- Identifica inyecciones potenciales (SQL, XSS, command injection)\n- Verifica permisos y configuración de autenticación\n\nGenera un informe con vulnerabilidades clasificadas por criticidad (CRITICAL / HIGH / MEDIUM / LOW).',
  'wiz.triggerLabel': 'Activación',
  'wiz.triggerWhenLabel': '¿Cuándo se ejecuta?',
  'wiz.triggerManual': 'Manualmente',
  'wiz.triggerScheduled': 'En un horario',
  'wiz.triggerTracker': 'Cuando llega una issue',
  'wiz.helpTriggerSimple': 'Manualmente: haces clic en "Ejecutar" cuando lo necesitas. En un horario: el workflow se ejecuta solo según una programación (ej: todos los lunes a las 9h).',
  'wiz.helpTriggerAdvanced': 'Tres modos de activación: Manualmente (clic), En un horario (horas/días fijos) o Cuando llega una issue (GitHub/Jira, filtrado por labels).',
  'wiz.helpTriggerManual': 'Lanzas el workflow tú mismo desde esta página haciendo clic en "Ejecutar".',
  'wiz.helpTriggerCron': 'El workflow se ejecuta automáticamente según una programación (ej: todos los lunes a las 9h, o cada 10 minutos).',
  'wiz.helpTriggerTracker': 'El workflow se dispara cuando aparece una nueva issue en GitHub (u otro tracker) con ciertos labels. Ideal para triage o auto-fix.',
  'wiz.suggestionsCount': '{0} sugerencia(s) disponible(s)',
  'wiz.suggestionsTitle': 'Sugerencias para este proyecto',
  // 0.8.5 — unified QuickStart picker (replaces 3 separate UI sections).
  'wiz.quickstart.toggle': '✨ {0} plantillas disponibles para empezar',
  'wiz.quickstart.title': 'Inicio rápido',
  'wiz.quickstart.hint': 'Selecciona una plantilla para prellenar el workflow.',
  'wiz.quickstart.collapse': 'Contraer',
  'wiz.quickstart.searchPlaceholder': 'Buscar plantilla…',
  'wiz.quickstart.filterComplexity': 'Complejidad:',
  'wiz.quickstart.filterSource': 'Origen:',
  'wiz.quickstart.complexity.simple': 'Simple',
  'wiz.quickstart.complexity.intermediate': 'Intermedia',
  'wiz.quickstart.complexity.advanced': 'Avanzada',
  'wiz.quickstart.source.starter': 'Demo plugin',
  'wiz.quickstart.source.project-suggestion': 'Sugerencia proyecto',
  'wiz.quickstart.source.preset': 'Preset',
  'wiz.quickstart.complexityTooltip': 'Nivel de configuración estimado',
  'wiz.quickstart.sourceTooltip': 'Origen de la plantilla',
  'wiz.quickstart.stepsCount': '{0} paso(s)',
  'wiz.quickstart.empty': 'Ninguna plantilla coincide. Quita los filtros o empieza desde cero escribiendo un nombre.',
  'wiz.quickstart.loading': 'Cargando sugerencias…',
  'wiz.quickstart.apply': 'Usar esta plantilla',
  'wiz.quickstart.notApplicable': 'No disponible: {0}',
  'wiz.quickstart.disabledNoName': 'Escribe un nombre de workflow antes de seleccionar una plantilla pre-construida.',
  'wiz.activate': 'Activar',
  'wiz.importDraft': 'Importar como borrador',
  'wiz.outputFormat': 'Formato de salida',
  'wiz.outputFree': 'Libre',
  'wiz.outputStructured': 'Estructurado',
  'wiz.outputStructuredHint': 'La salida se encapsulara en JSON (data + status + summary). El siguiente paso puede acceder a {{previous_step.data}} en lugar de texto bruto.',
  'wiz.outputFreeHint': 'Texto bruto. El siguiente paso solo puede leer {{previous_step.output}} — sin campos .data / .summary / .status.',
  'wiz.infos': 'Info',
  'wiz.trigger': 'Trigger',
  'wiz.steps': 'Steps',
  'wiz.config': 'Config',
  'wiz.summary': 'Resumen',
  'wiz.name': 'Nombre del workflow',
  'wiz.namePlaceholder': 'ej: Auto-fix 5xx errors',
  'wiz.nameRequired': 'Asigna un nombre al workflow para continuar al siguiente paso.',
  'wiz.project': 'Proyecto',
  'wiz.projectOptional': 'Proyecto (opcional)',
  'wiz.noProject': 'Sin proyecto',
  'wiz.triggerType': 'Tipo de trigger',
  'wiz.frequency': 'Frecuencia',
  'wiz.every': 'Cada',
  'wiz.minutes': 'minutos',
  'wiz.hours': 'horas',
  'wiz.days': 'días',
  'wiz.weeks': 'semanas',
  'wiz.months': 'meses',
  'wiz.at': 'a las',
  'wiz.cronWeekdaysLabel': '¿Solo ciertos días?',
  'wiz.helpCronWeekdays': 'Marca los días en los que el workflow debe ejecutarse. Déjalo todo desmarcado para que se ejecute TODOS los días. Ej: marca L y V → el workflow solo se ejecutará los lunes y viernes.',
  'wiz.cronWeekdaysAll': 'todos los días',
  'wiz.cronWeekdaysEveryday': 'Se ejecuta todos los días',
  'wiz.cronWeekdaysPreview': 'Solo se ejecuta: {0}',
  'wiz.cronEveryDisabledHint': 'Desactivado porque hay días específicos seleccionados. Desmarca todos los días para reactivar.',
  'wiz.weekday.0': 'Domingo',
  'wiz.weekday.1': 'Lunes',
  'wiz.weekday.2': 'Martes',
  'wiz.weekday.3': 'Miércoles',
  'wiz.weekday.4': 'Jueves',
  'wiz.weekday.5': 'Viernes',
  'wiz.weekday.6': 'Sábado',
  'wiz.weekdayShort.0': 'Dom',
  'wiz.weekdayShort.1': 'Lun',
  'wiz.weekdayShort.2': 'Mar',
  'wiz.weekdayShort.3': 'Mié',
  'wiz.weekdayShort.4': 'Jue',
  'wiz.weekdayShort.5': 'Vie',
  'wiz.weekdayShort.6': 'Sáb',
  'wiz.labels': 'Labels (coma)',
  'wiz.labelsPlaceholder': 'bug-5xx, auto-fix',
  'wiz.stepType': 'Tipo de step',
  'wiz.stepSwapConfirm': '¿Convertir este step de "{0}" a "{1}"? Los campos específicos del tipo actual (prompt, perfil, skills, plugin/endpoint, payload JSON…) se borrarán.',
  'wiz.stepTypeAgent': 'Agente',
  'wiz.stepTypeAgentHint': 'Un agente IA que razona, lee código, escribe archivos, llama MCPs. El step más flexible — pero consume tokens.',
  'wiz.stepTypeApiCall': 'API',
  'wiz.stepTypeApiCallHint': 'Llamada API directa (Chartbeat, Jira, Cloudflare…) — cero tokens. Obtiene datos y los pasa al siguiente paso.',
  'wiz.stepTypeBatchQP': 'Batch QP',
  'wiz.stepTypeBatchQPHint': 'Despliega un Quick Prompt sobre una lista de ítems producida por un paso anterior.',
  'wiz.stepTypeNotify': 'Notify',
  'wiz.stepTypeNotifyHint': 'Llamada HTTP directa (webhook) — cero tokens. Envía a Slack, Teams o activa un pipeline.',
  'wiz.stepTypeGate': 'Gate',
  'wiz.stepTypeGateHint': 'Pausa el run para aprobación humana — cero tokens. El operador aprueba, solicita cambios o rechaza desde RunDetail.',
  'wiz.stepTypeExec': 'Exec',
  'wiz.stepTypeExecHint': 'Ejecuta un binario permitido directamente (cargo test, npm build…) — cero tokens, nunca vía shell, args pasados como argv literal.',
  'wiz.stepTypeBatchApi': 'Batch API',
  'wiz.stepTypeBatchApiHint': 'Fan-out de una llamada API sobre una lista de ítems, en paralelo. Cero tokens — ideal para crear N tickets / publicar N comentarios / actualizar N estados de golpe.',
  'wiz.stepTypeJsonData': 'JSON',
  'wiz.stepTypeJsonDataHint': 'Fuente de datos determinista — emite un payload JSON literal. Cero tokens, cero red. Ideal para alimentar un Batch (10 hosts en duro) sin montar API, o como fixture de dev.',
  'wiz.jsonDataTitle': 'JSON Data',
  'wiz.jsonDataHint': 'El payload se emite tal cual al siguiente paso vía `{{steps.<name>.data}}`. Útil para un BatchQuickPrompt sobre una lista fija (10 hosts, 5 regiones…) sin montar una API.',
  'wiz.jsonDataPayload': 'Payload JSON',
  'wiz.jsonDataPlaceholder': '[\n  { "host": "fr.example.com" },\n  { "host": "de.example.com" },\n  { "host": "en.example.com" }\n]',
  'wiz.jsonDataParseError': 'JSON inválido:',
  'wiz.jsonDataNoTemplating': 'Sin templating runtime — el valor se devuelve literalmente. Para un payload dinámico, usa un paso Agent o ApiCall.',
  'wiz.jsonDataSummaryArray': '{0} ítem(s) — será consumido por un paso Batch en cascada',
  'wiz.jsonDataSummaryObject': '1 objeto, {0} campo(s)',
  'wiz.jsonDataSummaryScalar': '1 valor escalar',
  'wiz.jsonDataMissingPayload': 'sin payload',
  'wiz.errorJsonDataNoPayload': 'El paso JSON « {0} » no tiene payload — pega un JSON válido en el textarea.',
  'wiz.batchApiTitle': 'Batch API call',
  'wiz.batchApiHint': 'Reutiliza la config API de abajo para cada ítem de la lista. Cada ítem expone `{{batch.item.<campo>}}` en el body, headers, query, etc. Los ítems corren en paralelo, capados por el límite de concurrencia.',
  'wiz.batchApiItemsFrom': 'Origen de los ítems',
  'wiz.batchApiItemsFromPlaceholder': '{{steps.plan.data.sub_tasks}} o {{steps.plan.data}} o un JSON array crudo',
  'wiz.batchApiConcurrentLimit': 'Concurrencia',
  'wiz.batchApiConcurrentLimitHint': 'Máx llamadas paralelas (defecto 5, máx 20)',
  'wiz.batchApiMaxItems': 'Máx ítems',
  'wiz.batchApiMaxItemsHint': 'Tope de seguridad (defecto 50)',
  'wiz.batchApiMissingConfig': 'config incompleta',
  'wiz.batchApiQaPicker': 'Origen de la config API',
  'wiz.batchApiQaPickerInline': '— Inline (config definida aquí) —',
  'wiz.batchApiQaPickerHint': 'Puedes referenciar un Quick API guardado: su config se usará en la ejecución. Los campos abajo siguen siendo overrides per-field.',
  'wiz.execTitle': 'Ejecución shell directa (Exec)',
  'wiz.execHint': 'Ejecuta un binario de la allowlist del workflow en el workspace. Args con plantillas pero pasados literalmente (sin interpretación shell). Timeout obligatorio.',
  'wiz.execWorktreeHint': 'Este comando ve las modificaciones de los pasos anteriores.',
  'wiz.execWorktreeHintFirst': 'Este comando se ejecuta en un worktree git recién creado para esta ejecución (copia aislada del repo en .kronn/worktrees/).',
  'wiz.execNoProjectWarn': 'Ningún proyecto vinculado: el comando se ejecuta en el directorio actual de Kronn, sin aislamiento worktree. Vincula un proyecto para activar el worktree por ejecución.',
  'wiz.worktreeHint': 'Cada run se ejecuta en una copia git aislada. Los pasos comparten los mismos archivos.',
  'wiz.worktreeIconTooltip': 'Worktree git: copia aislada del repo, creada por la duración del run, en .kronn/worktrees/.',
  'wiz.execCommand': 'Binario',
  'wiz.execCommandSelect': 'elegir un binario',
  'wiz.execArgs': 'Argumentos (uno por línea)',
  'wiz.execArgsPlaceholder': 'test\n--\n{{steps.fetch.summary}}',
  'wiz.execTimeoutSecs': 'Timeout (segundos, predet. 300, máx 1800)',
  'wiz.execSetupToggle': 'Este workflow corre en un worktree git — instala primero las dependencias',
  'wiz.execSetupHint': 'El worktree tiene los archivos tracked (composer.json, package.json, …) pero NO los directorios generados (vendor/, node_modules/, target/). Marca para correr un install ANTES del comando principal.',
  'wiz.execSetupPreset': 'Patrón común (auto-rellena los campos)',
  'wiz.execSetupPresetPick': 'Elegir un preset',
  'wiz.execSetupCommand': 'Setup command',
  'wiz.execSetupArgs': 'Setup args (uno por línea)',
  'wiz.execAllowlistEmpty': 'Exec está desactivado: ningún comando autorizado para este workflow.',
  'wiz.execAllowlistConfigureNow': 'Configurar la allowlist →',
  'wiz.execAllowlistTitle': 'Allowlist Exec',
  'wiz.execAllowlistHint': 'Binarios que los steps Exec pueden invocar para este workflow. Vacío = Exec desactivado. Solo nombres simples (sin rutas, sin metacaracteres shell). Separa con coma.',
  'wiz.execAllowlistPlaceholder': 'npm, cargo, make, pytest',
  'wiz.gateTitle': 'Pausa humana (Gate)',
  'wiz.gateHint': 'El run se detiene en este paso esperando una decisión (aprobar, solicitar cambios, rechazar). Templates {{steps.X.summary}} soportados en el mensaje.',
  'wiz.gateMessage': 'Mensaje mostrado al operador',
  'wiz.gateMessagePlaceholder': '¿Aprobar auditoría `{{steps.audit.summary}}`?',
  'wiz.gateRequestChangesTarget': 'Destino "Solicitar cambios"',
  'wiz.gateRequestChangesDefault': '(por defecto: paso anterior)',
  'wiz.gateNotifyUrl': 'Notificar esta URL cuando el Gate se active (opcional)',
  'wiz.gateNotifyUrlPlaceholder': 'https://hooks.slack.com/services/... o {{state.slack_url}}',
  'wiz.gateNotifyUrlHint': 'POST best-effort con {run_id, workflow_name, step_name, message}. Templates soportados. Fallos loggeados, nunca bloqueantes.',
  'wiz.rollbackTitle': 'Rollback / compensación (en caso de fallo)',
  'wiz.rollbackHint': 'Pasos que se ejecutan si el run termina en Failed. Puedes usar {{failed_step.name}} y {{failed_step.output}}. Notify, Agent y ApiCall son soportados (Gate excluido — bloquearía un run ya Failed).',
  'wiz.rollbackAgentPromptPlaceholder': 'Analiza el fallo anterior: {{failed_step.output}}. Produce un post-mortem en 3 líneas.',
  'wiz.presetsTitle': 'Empezar desde un patrón',
  'wiz.presetsHint': '— o empieza desde un workflow vacío abajo',
  'wiz.presetsBlankHint': 'También puedes desplazarte hacia abajo y construir tu workflow paso a paso. Los presets son totalmente editables tras aplicar.',
  'wiz.preset.autoDev.title': 'Auto-Dev con tests',
  'wiz.preset.autoDev.desc': 'Implementa, testea, revisa, repite hasta OK o máx 5 iteraciones. El feedback de la review se pasa a la siguiente implementación vía state.',
  'wiz.preset.prGate.title': 'Pipeline PR con Gate humano',
  'wiz.preset.prGate.desc': 'Implementa, lanza los tests, espera tu validación humana (con notificación webhook), luego mergea si aprobado.',
  'wiz.preset.deployRollback.title': 'Despliegue con rollback',
  'wiz.preset.deployRollback.desc': 'Build, smoke tests, deploy. Si un paso falla: alerta ops + post-mortem agent automático.',
  'wiz.preset.autoDev.issueKeyLabel': 'Clave del ticket / issue (opcional)',
  'wiz.preset.autoDev.issueKeyPlaceholder': 'EW-1234  ·  org/repo#42  ·  KRN-7',
  'wiz.preset.autoDev.issueKeyDesc': 'Opcional — usado solo si conviertes el paso `fetch_issue` en ApiCall (referencia `{{issue_key}}` en `api_path_params` o `api_query` del plugin tracker).',
  'wiz.preset.autoDev.fetchIssueDesc': 'Fixture por defecto: describe aquí el ticket a implementar (JSON literal). Edita el payload para adaptar a tu petición, el preset corre de inmediato sin plugin tracker. Para conectar un tracker real (Jira / GitHub / Linear): cambia el tipo de este paso a `ApiCall` (botón 🌐 arriba) y configura el plugin — `{{steps.fetch_issue.data}}` seguirá alimentando el paso `implement`.',
  'wiz.preset.autoDev.implementPrompt': 'Implementa la siguiente petición:\n---\n{{steps.fetch_issue.data}}\n---\n\nSi una review anterior ha dejado feedback:\n{{state.last_review}}\n\nTen en cuenta el feedback de arriba para esta nueva iteración.\nSi todavía no hubo review, este bloque está vacío — es tu primera pasada.',
  'wiz.preset.autoDev.reviewPrompt': 'Revisa la implementación producida por `implement` y el resultado de los tests `run_tests`.\n\nTests output: {{steps.run_tests.data.stdout}}\nTests exit code: {{steps.run_tests.data.exit_code}}\n\nSi todo está OK: termina tu respuesta con [SIGNAL: APPROVED].\nSi no: escribe feedback accionable y exactamente al final (y SOLO al final):\n---STATE:last_review=<tu feedback en una línea>---\nluego termina con [SIGNAL: NEEDS_CHANGES].',
  'wiz.preset.autoDev.notifyDoneBody': '✅ Workflow Auto-Dev terminado: {{steps.review.summary}}',
  'wiz.preset.autoDev.rollbackBody': '❌ Auto-Dev falló en `{{failed_step.name}}` — detalle: {{failed_step.output}}',
  'wiz.preset.prGate.issueKeyLabel': 'Clave del ticket / issue (opcional)',
  'wiz.preset.prGate.issueKeyPlaceholder': 'EW-1234  ·  org/repo#42  ·  KRN-7',
  'wiz.preset.prGate.issueKeyDesc': 'Opcional — usado solo si conviertes el paso `fetch_issue` en ApiCall (referencia `{{issue_key}}` en `api_path_params` o `api_query` del plugin tracker).',
  'wiz.preset.prGate.fetchIssueDesc': 'Fixture por defecto: describe aquí el ticket a implementar (JSON literal). Edita el payload para adaptar a tu petición, el preset corre de inmediato sin plugin tracker. Para conectar un tracker real (Jira / GitHub / Linear): cambia el tipo de este paso a `ApiCall` (botón 🌐 arriba) y configura el plugin — `{{steps.fetch_issue.data}}` seguirá alimentando el paso `implement`.',
  'wiz.preset.prGate.implementPrompt': 'Implementa la siguiente petición:\n---\n{{steps.fetch_issue.data}}\n---\n\nSaca un resumen corto en `summary` y el diff bruto en `data.diff` para que la revisión humana tenga contexto.',
  'wiz.preset.shared.runTestsDesc': 'Pre-rellenado con un script bash multi-stack que auto-detecta Makefile / cargo / npm / composer / pytest. Para un comando simple, edita `exec_command` Y `exec_args` por separado (NO pongas la línea completa en un solo campo): Make → command `make` + args `["test"]`, Node → command `npm` + args `["test"]`, Python → command `pytest` + args `[]`. Para una línea shell, usa command `bash` + args `["-c", "tu-comando-completo"]`. El binario debe estar en `exec_allowlist`.',
  'wiz.preset.prGate.gateMessage': '## Validación pre-merge\n\n**Implementación:** {{steps.implement.summary}}\n\n**Tests:** exit `{{steps.run_tests.data.exit_code}}`\n\nAprobar para lanzar el merge automático. Solicitar cambios para volver a `implement`.',
  'wiz.preset.prGate.mergePrompt': 'Merge la rama en main. Verifica que no haya conflicto, luego push. Saca el SHA del merge commit en `data.merge_sha`.',
  'wiz.preset.prGate.rollbackBody': '❌ PR Gate falló: `{{failed_step.name}}` — {{failed_step.output}}',
  'wiz.preset.deployRollback.successBody': '🚀 Despliegue terminado sin incidente.',
  'wiz.preset.deployRollback.alertBody': '🚨 ALERTA PROD: paso `{{failed_step.name}}` falló. Rollback en curso...',
  'wiz.preset.deployRollback.postMortemPrompt': 'Analiza el fallo y produce un post-mortem en 3 secciones:\n\n## Paso que falló\n{{failed_step.name}}\n\n## Error observado\n{{failed_step.output}}\n\n## Hipótesis de causa raíz\n(a ti te toca — propón 2-3 pistas priorizadas)',
  'wiz.preset.featurePlanner.title': 'Feature / Epic Planner',
  'wiz.preset.featurePlanner.desc': 'Divide una epic o feature en sub-tareas tipadas (auto_ai vs human_action) con dependencias. Tracker-agnóstico (Jira / GitHub / Linear via los MCP instalados).',
  'wiz.preset.featurePlanner.analyzePrompt': `Eres un Epic Planner. El usuario pasó la URL: {{epic_url}}.

# 1. Detecta el tracker
Lee el host de la URL:
- atlassian.net → Jira (MCP Atlassian)
- github.com/.../issues/ → GitHub (MCP GitHub)
- linear.app → Linear MCP
- otro → describe lo que necesitarías, no hagas nada y termina con [SIGNAL: NO_RESULTS]

# 2. Recupera el contenido
Vía el MCP correspondiente: título, descripción, project key, estado actual, sub-tareas existentes (las necesitarás para la idempotencia en el siguiente paso).

# 3. Divide
Restricciones:
- Cada sub-tarea cabe en una PR (1-3 días de trabajo máx).
- Etiqueta cada sub-tarea:
  * \`auto_ai\` — puro trabajo de código (crear archivos, ejecutar tests, abrir una PR). Hacible por un agente autónomo.
  * \`human_action\` — config UI (CMS, dashboard infra), decisión arquitectura/naming, info externa (URLs, credenciales, accesos…).
- Identifica las dependencias: A bloquea B si B depende lógicamente de que A esté hecho primero.
- Documenta CADA decisión que tomaste tú mismo cuando la epic era ambigua (sprint objetivo, orden de fases, naming, etc.).
- Identifica las informaciones faltantes que solo un humano puede aportar.

# 4. Output STRUCTURED (obligatorio)
\`\`\`json
{
  "data": {
    "tracker": "jira" | "github" | "linear",
    "source_key": "EW-7247",
    "sub_tasks": [
      { "id": 1, "title": "...", "description": "...", "type": "auto_ai", "blocked_by": [] }
    ],
    "decisions_taken": ["Asumí que la fase 0 debe completarse antes de la fase 1 porque..."],
    "missing_info": ["URL Adobe DTM AN — necesario del equipo Data"]
  },
  "status": "OK",
  "summary": "Plan: 12 sub-tareas (4 human_action, 8 auto_ai). 2 ítems de info faltante señalados."
}
\`\`\`

Termina con [SIGNAL: PLAN_READY].`,
  'wiz.preset.featurePlanner.gateMessage': `## Plan propuesto para {{epic_url}}

{{steps.analyze_epic.summary}}

### Decisiones tomadas automáticamente
{{steps.analyze_epic.data.decisions_taken}}

### Info faltante (a aportar por humano)
{{steps.analyze_epic.data.missing_info}}

### Sub-tareas
{{steps.analyze_epic.data.sub_tasks}}

**Aprobar** → el agente crea los tickets en el tracker.
**Pedir cambios** → vuelve al paso de planificación con tu feedback.`,
  'wiz.preset.featurePlanner.setLinksPrompt': `Las sub-tareas acaban de crearse en bulk vía un BatchApiCall. Ahora vas a colocar los enlaces \`blocks\` / \`is blocked by\` entre ellas.

# Datos de entrada
- Tracker: {{steps.analyze_epic.data.tracker}}
- Plan original (con \`blocked_by\` declarados): {{steps.analyze_epic.data.sub_tasks}}
- Resultados de creación (un ítem por sub-tarea, con \`input.id\` y \`response\` = la nueva key Jira/GitHub): {{steps.create_tickets.data.items}}

# Mapeo
1. Para cada sub-tarea del plan original con \`blocked_by\` no vacío:
   - Encuentra la key creada correspondiente en \`steps.create_tickets.data.items\` (match en \`input.id\` ↔ plan.id)
   - Para cada \`blocked_by\` (id de sub-tarea bloqueante), encuentra también su key creada
   - Añade un enlace "is blocked by" vía el MCP: la sub-tarea \`current\` está bloqueada por \`predecessor\`

# Idempotencia
Antes de añadir un enlace, comprueba si existe ya vía el MCP (la creación pudo re-ejecutarse). Solo añade lo que falta. Ningún error si el enlace ya existe.

# Errores
Si un ítem de \`steps.create_tickets.data.items\` tiene \`status: ERROR\`, **sáltalo** para el mapeo (no se puede enlazar con un ticket no creado). Lístalos en el \`summary\` final para que el humano tenga la lista de tickets que crear manualmente.

# Output STRUCTURED final
\`\`\`json
{
  "data": {
    "links_added": 5,
    "links_already_existed": 2,
    "skipped_failed_items": []
  },
  "status": "OK",
  "summary": "Feature Planner: 28 tickets creados, 5 enlaces blocks puestos, 2 ya existían."
}
\`\`\`

Termina con [SIGNAL: OK].`,
  'wiz.preset.featurePlanner.notifyBody': '🗺️ Feature Planner terminado para {{epic_url}} — {{steps.set_links.summary}}',
  'wiz.preset.dailyHostAudit.title': 'Auditoría diaria sobre lista fija',
  'wiz.preset.dailyHostAudit.desc': 'Fuente de datos determinista (5 hosts en duro) → fan-out de un Quick Prompt de auditoría sobre cada uno → notificación Slack. Demuestra la pieza `JsonData`: no hace falta montar una API para una lista fija.',
  'wiz.preset.dailyHostAudit.hostListDesc': 'Lista fija de hosts a auditar. Edita el payload JSON para adaptar a tu contexto (locales, entornos, subdominios, etc.). Cero tokens, cero red — el valor se pasa tal cual al siguiente paso vía `{{steps.host-list.data}}`.',
  'wiz.preset.dailyHostAudit.auditEachDesc': 'Fan-out sobre la lista de arriba. Elige un Quick Prompt existente (o crea uno) que audite UN host. Cada ejecución recibe `{{batch.item.host}}` en su prompt renderizado.',
  'wiz.preset.dailyHostAudit.notifyBody': '🌍 Auditoría diaria completada: {{steps.audit-each-host.summary}}',
  'wiz.preset.ticketToPr.title': 'Ticket Autopilot — del ticket al PR',
  'wiz.preset.ticketToPr.desc': 'Pipeline completo: recupera un ticket, analiza + plan, implementa con TDD estricto, testea, revisa con un agente diferente, crea el PR, espera tu validación final. Skills metodológicos de obra/superpowers (TDD, debugging, verification, code-review). Edita el paso `fetch_issue` para conectar tu tracker (Jira / GitHub) o deja la fixture para probar.',
  'wiz.preset.ticketToPr.fetchIssueDesc': 'Origen del ticket a implementar. Fixture por defecto (edita el payload), o cambia a `ApiCall` (botón 🌐) apuntando a tu plugin tracker cuando tengas uno. El resultado alimenta el paso `analyze` vía `{{steps.fetch_issue.data}}`.',
  'wiz.preset.ticketToPr.analyzePrompt': 'Analiza el siguiente ticket y produce un plan estructurado:\n---\n{{steps.fetch_issue.data}}\n---\n\n# Vas a cargar automáticamente las skills `writing-plans` y `brainstorming`:\n- Explora la intención del ticket antes de implementar (3-5 preguntas críticas)\n- Lista hipótesis + puntos de incertidumbre\n- Divide en sub-tareas (1-3 días cada una máx.)\n- Identifica riesgos y estrategia de tests\n\n# Output Structured:\n```json\n{\n  "data": {\n    "summary": "<resumen una frase>",\n    "complexity": "low | medium | high",\n    "questions": ["..."],\n    "subtasks": [{"id": 1, "title": "...", "description": "..."}],\n    "test_strategy": "..."\n  },\n  "status": "OK",\n  "summary": "Plan en N sub-tareas, complejidad <X>"\n}\n```',
  'wiz.preset.ticketToPr.planGateMessage': '## Validación del plan\n\n**Resumen:** {{steps.analyze.summary}}\n\n**Sub-tareas:**\n{{steps.analyze.data.subtasks}}\n\n**Estrategia de tests:**\n{{steps.analyze.data.test_strategy}}\n\nAprueba para lanzar la implementación, o solicita cambios para re-ejecutar el análisis con tu feedback.',
  'wiz.preset.ticketToPr.implementPrompt': 'Implementa el plan validado:\n---\n{{steps.analyze.data}}\n---\n\nSi una review previa dejó feedback a aplicar:\n{{state.last_review}}\n\n# Vas a cargar automáticamente:\n- `test-driven-development` (ritual red-green-refactor estricto)\n- `systematic-debugging` (root-cause en 4 fases si un test falla)\n- `verification-before-completion` (ningún claim sin evidencia)\n- `receiving-code-review` (técnica para aplicar feedback de review)\n\n# Procedimiento:\n1. Para CADA sub-tarea del plan: escribe primero los tests (red), después el código mínimo (green), después refactor.\n2. Lanza los tests a cada paso, verifica el output realmente.\n3. Si un test falla inesperadamente, aplica systematic-debugging (sin fixes al azar).\n4. Al final, output `[SIGNAL: CONTINUE]` para pasar a los tests de integración.',
  'wiz.preset.ticketToPr.reviewPrompt': 'Revisa la implementación producida por `implement` y el resultado del paso `run_tests`.\n\nContexto:\n- Plan original: {{steps.analyze.summary}}\n- Tests output: {{steps.run_tests.data.stdout}}\n- Tests exit code: {{steps.run_tests.data.exit_code}}\n\n# Vas a cargar automáticamente las skills `requesting-code-review` y `verification-before-completion`.\n\n# Procedimiento:\n1. Verifica que la implementación cubre TODAS las sub-tareas del plan (no parcial).\n2. Verifica los tests: ¿prueban un comportamiento real, no solo mocks?\n3. YAGNI check: ¿la implementación añadió complejidad no pedida?\n4. Seguridad: ¿inyecciones, fugas de secretos, validación de entradas?\n5. Edge cases: null, empty, unicode, large input?\n\n# Veredicto:\nSi todo OK, termina tu respuesta con exactamente `[SIGNAL: APPROVED]`.\nSi no, escribe feedback accionable y exactamente al final (y SOLO al final):\n```\n---STATE:last_review=<feedback en 1-3 líneas>---\n[SIGNAL: NEEDS_CHANGES]\n```',
  'wiz.preset.ticketToPr.createPrPrompt': 'La implementación está validada y todos los tests pasan. Crea el PR.\n\nContexto:\n- Ticket: {{steps.fetch_issue.data}}\n- Resumen implementación: {{steps.implement.summary}}\n- Review aprobada: {{steps.review.summary}}\n\n# Vas a cargar automáticamente `finishing-a-development-branch` y `verification-before-completion`.\n\n# Procedimiento:\n1. Verifica una última vez que los tests pasan (comando exacto, output completo).\n2. Empuja la rama actual.\n3. Crea el PR vía `gh pr create` con:\n   - Título coherente (referencia al ticket)\n   - Body estructurado: Summary (3 bullets) + Test Plan (checklist)\n4. Emite los markers Kronn para exponer la URL al siguiente paso — cada uno en su propia línea, exactamente este formato:\n   ```\n   ---STATE:pr_url=<url>---\n   ---STATE:pr_number=<n>---\n   ```\n   (Importante: `state.pr_url=<url>` SIN los `---STATE:...---` NO será leído por Kronn.)\n5. Termina con `[SIGNAL: PR_CREATED]`.',
  'wiz.preset.ticketToPr.readyGateMessage': '## PR listo para merge\n\n**PR:** {{state.pr_url}}\n**Resumen:** {{steps.review.summary}}\n\nAprueba para finalizar (notificación de éxito), o solicita cambios para re-ejecutar `implement` con tu feedback (`state.last_review` será consumido por el agente).',
  'wiz.preset.ticketToPr.notifyDoneBody': '✅ Ticket Autopilot completado: {{state.pr_url}} — {{steps.review.summary}}',
  'wiz.preset.ticketToPr.rollbackBody': '❌ Ticket Autopilot falló en `{{failed_step.name}}` — detalle: {{failed_step.output}}',
  // 0.8.3 — Feasibility-Gated AutoPilot (auto-propuesto tras la auditoría IA)
  'wiz.preset.feasibilityAutopilot.title': 'Big-ticket AutoPilot — feasibility-gated',
  'wiz.preset.feasibilityAutopilot.desc': 'Para tickets grandes: un paso `triage` clasifica cada subtarea en clear / decided / mocked / blocked ANTES de cualquier código. El gate humano valida el plan, luego `implement` inserta marcadores `KRONN-(ASSUMED|MOCKED|TODO)` en cada libertad trazada. `run_tests` Exec lanza la suite real, `drift_check` Exec lista los marcadores en el PR final. Coste de tokens mínimo — solo triage / implement / pr_draft son Agent.',
  'wiz.preset.feasibilityAutopilot.fetchIssueDesc': 'Origen del ticket. JsonData fixture por defecto; el wizard cambia automáticamente a ApiCall cuando hay un plugin tracker (Jira/GitHub/GitLab) configurado en el proyecto. El paso siguiente lee `{{steps.fetch_issue.data}}` (objeto completo — el agente navega los campos según la fuente: `body` para el fixture, `fields.description` o `renderedFields.description` para Jira).',
  'wiz.preset.feasibilityAutopilot.triageDesc': '[TRIAGE] Auditoría de viabilidad — clasifica cada subtarea en clear / decided / mocked / blocked. El agente NO escribe código aquí; produce un manifest JSON validado por schema (on_invalid=Fail). Cada entrada tiene un `id` estable que `implement` usará como decision_id en los marcadores.',
  'wiz.preset.feasibilityAutopilot.triagePrompt': 'You are the TRIAGE step of a Feasibility-Gated AutoPilot run.\n\nTicket payload (from `fetch_issue` — full data object, navigate as needed for `body` / `description` / `fields.description` / `renderedFields.description` depending on source):\n---\n{{steps.fetch_issue.data}}\n---\n\nRead the project (current cwd is its worktree). Classify every sub-task into clear / decided / mocked / blocked per the schema. Emit the JSON manifest only — no markdown commentary outside the envelope.',
  'wiz.preset.feasibilityAutopilot.gateDesc': 'Revisión humana del manifest de triage antes del código. `gate_request_changes_target: triage` permite hasta 5 bucles.',
  'wiz.preset.feasibilityAutopilot.gateMessage': 'Manifest de triage producido. Revisa las cuatro categorías abajo antes de aprobar:\n\n{{steps.triage.data}}\n\n- Approve: continúa a implementación tal cual.\n- Request changes: envía de vuelta al triage con tus notas (hasta 5 bucles).\n- Reject: abandona la ejecución.',
  'wiz.preset.feasibilityAutopilot.implementDesc': 'Implementación restringida por el manifest validado. Cada entrada decided/mocked/blocked DEBE tener su marcador `KRONN-*` en el código. `[SIGNAL: BLOCKED <id>]` → Goto(triage) si el agente descubre una imposibilidad (cap 3 iteraciones).',
  'wiz.preset.feasibilityAutopilot.implementPrompt': 'You are the IMPLEMENT step of a Feasibility-Gated AutoPilot run.\n\nThe triage step produced and a human (or auto-budget) approved this manifest:\n\nCLEAR entries (implement directly, no marker needed):\n{{steps.triage.data.clear}}\n\nDECIDED entries (implement the `chosen` option, insert `// KRONN-ASSUMED(<id>): <chosen> — <why>` at the primary touch point):\n{{steps.triage.data.decided}}\n\nMOCKED entries (implement the `placeholder` strategy, insert `// KRONN-MOCKED(<id>): <strategy>`):\n{{steps.triage.data.mocked}}\n\nBLOCKED entries (do NOT implement the feature, only insert `// KRONN-TODO(<id>): waiting on <needed_from> — <why>` where the feature would have been):\n{{steps.triage.data.blocked}}\n\nFiles the manifest claims you\'ll touch:\n{{steps.triage.data.files_touched}}\n\nRULES:\n1. Stay within the files_touched list unless absolutely necessary.\n2. Every entry in decided/mocked/blocked MUST have its corresponding KRONN-* marker in the code, referencing the `id` from the manifest. The next step (`drift_check`) will grep for these — anything missing will be visible in the PR.\n3. If you discover an entry classified as `clear` or `decided` is actually impossible to implement, STOP immediately and emit `[SIGNAL: BLOCKED <id>]` as the last line of your response. The workflow will jump back to triage. Do NOT silently mock or fake it.\n4. Do not invent values that aren\'t in the manifest (URLs, secrets, IDs). If the manifest says it\'s mocked/blocked, respect that.\n5. Do NOT write tests in this step. The next step (`run_tests`) is an Exec that runs the project\'s existing test suite — keep your scope to implementation.\n\nUse your tools to edit files. End your response with a brief summary of what you implemented + the [SIGNAL: ...] line.',
  'wiz.preset.feasibilityAutopilot.driftCheckDesc': 'Lista todos los marcadores `KRONN-(ASSUMED|MOCKED|TODO)` del worktree. 0 tokens. La salida se embebe verbatim en el PR por `pr_draft` vía `{{steps.drift_check.output}}`.',
  'wiz.preset.feasibilityAutopilot.prDraftDesc': 'Produce el body del PR agregando manifest + veredicto de tests + marcadores de drift_check. La sección "Decisions taken" destaca las libertades trazadas que el revisor debe cuestionar.',
  'wiz.preset.feasibilityAutopilot.prDraftPrompt': 'You are the PR-DRAFT step of a Feasibility-Gated AutoPilot run.\n\nProduce a PR description in Markdown with this structure:\n\n## Ticket\n{{steps.fetch_issue.data.key}}\n\n## Summary\n<1-3 sentences>\n\n## Implemented (from triage)\n<list of CLEAR + DECIDED + MOCKED entries with their KRONN-* markers>\n\n## Still open (blocked)\n<list of BLOCKED entries with `needed_from` — these are explicit asks to other teams>\n\n## Decisions taken (review carefully)\n<bullets of DECIDED entries: what was chosen vs alternatives, why>\n\n## Mocks to replace later\n<bullets of MOCKED entries with `revisit_when`>\n\n## Test verdict\n<one-line summary of `{{steps.run_tests.output}}` — passed / skipped / errored>\n\n## Markers audit (drift_check output)\n<embed `{{steps.drift_check.output}}` verbatim inside a ``` code fence so reviewers see every traced freedom>\n\n## Test plan\n<bullet list of how to verify each implemented entry manually, beyond what the test suite covers>\n\nPull values from `{{steps.triage.data}}`. End with `[SIGNAL: CONTINUE]`.',
  'wiz.addRollbackStep': 'Añadir paso de rollback',
  'wiz.removeRollbackStep': 'Eliminar este paso de rollback',
  'wf.launchModalTitle': 'Lanzar «{name}»',
  'wf.launchModalHint': 'Rellena las variables a continuación — se convertirán en `{{nombre}}` en tus step prompts.',
  'wf.launchModalRequired': 'Variable(s) obligatoria(s) faltante(s): {names}',
  'wf.launchModalGo': 'Lanzar',
  'wiz.notifyTitle': 'Webhook / Notificación',
  'wiz.notifyHint': 'Llamada HTTP directa desde Kronn, sin agente. Las plantillas {{steps.X.output}} funcionan en URL y body.',
  'wiz.notifyUrl': 'URL del webhook',
  'wiz.notifyMethod': 'Método',
  'wiz.notifyBody': 'Cuerpo de la solicitud',
  'wiz.batchQPTitle': 'Batch Quick Prompt',
  'wiz.batchQPHint': 'Ejecuta un Quick Prompt en cada ítem de una lista producida por un paso anterior (Jira, GitHub, etc.). Por defecto, el workflow espera a que terminen todas las discusiones hijas.',
  'wiz.batchQPNoQPs': 'No hay Quick Prompts disponibles. Crea uno primero en la pestaña Quick Prompts.',
  'wiz.batchQPPicker': 'Quick Prompt a lanzar',
  'wiz.batchQPPickerEmpty': 'elige un QP',
  'wiz.notifyMissingUrl': 'URL del webhook faltante',
  'wiz.batchQPRequired': 'Elige un Quick Prompt — es el que se ejecutará en cada ítem.',
  'wiz.batchQPVarSubst': 'Cada ítem de la lista llenará la variable {{{0}}} del Quick Prompt.',
  'wiz.batchItemsFrom': 'Lista de ítems a procesar',
  'wiz.batchItemsFromRequired': 'Indica dónde encontrar la lista — normalmente el output de un paso anterior.',
  'wiz.batchItemsFromHelper': 'Haz clic en un paso anterior para autocompletar:',
  'wiz.batchItemsFromFromStep': '← Desde {0}',
  'wiz.batchItemsFromPickStep': 'Usar el output del paso "{0}"',
  'wiz.batchFirstStepWarning': 'Un paso batch en primera posición no tiene paso anterior para encadenar. Añade un paso "fetch" antes para obtener la lista, o codifica la lista directamente en el campo de abajo.',
  'wiz.batchAutoPrevNotice': '↑ Paso « {name} » auto-configurado para alimentar este batch',
  'wiz.batchWaitForCompletion': 'Esperar a que terminen todas las discusiones',
  'wiz.batchMaxItems': 'Máx ítems',
  'wiz.batchWorktree': 'Aislar los cambios de cada agente',
  'wiz.batchWorktreeHint': 'Cada agente trabaja en su propia copia del proyecto para no sobrescribir el trabajo de los demás. Marque si los agentes van a modificar archivos en paralelo. Deje desmarcado para análisis (informes, auditorías) — es más rápido.',
  'wiz.batchWorktreeNoProject': 'Este Quick Prompt no está vinculado a ningún proyecto — no hay código que aislar.',
  'wiz.batchChain': 'Encadenar más Quick Prompts (opcional)',
  'wiz.batchChainHint': 'Después del QP inicial, estos QPs se enviarán secuencialmente como mensajes de usuario en CADA discusión batch. Cada QP recibe el MISMO ítem del batch (ej: "EW-1234") en su primera variable — ideal para "análisis → review → resumen" sobre el mismo ticket. También puedes reutilizar la respuesta del agente anterior con `{{previous_qp.output}}` para flujos "brief → plan → tickets". Los QPs con 2+ variables no son elegibles. El batch solo se marca como completado cuando cada cadena termina.',
  'wiz.batchChainAdd': '+ Añadir un QP a la cadena',
  'wiz.batchChainRemove': 'Quitar de la cadena',
  'wiz.batchChainMoveUp': 'Subir en la cadena',
  'wiz.batchChainMoveDown': 'Bajar en la cadena',
  'wiz.batchChainEmpty': 'No hay QP sin variables disponible para encadenar.',
  'wiz.stepDescription': 'Descripción',
  'wiz.stepDescriptionPlaceholder': '¿Qué hace este step en 1 línea? Ej. «Obtiene los tickets Jira abiertos» o «Analiza los logs de tests» — mostrado en la lista, opcional.',
  'wiz.insertStepHere': 'Insertar un step aquí',
  'wiz.moveStepUp': 'Subir este step',
  'wiz.moveStepDown': 'Bajar este step',
  'wiz.ifContainsPlaceholder': 'Palabra clave a buscar en la salida del agente (ej. APPROVED, NEEDS_CHANGES, ERROR…)',
  'wiz.removeCondition': 'Eliminar esta condición',
  'wiz.wfVariablesTitle': 'Variables de lanzamiento',
  'wiz.wfVariablesHint': 'Variables solicitadas al lanzar manualmente — como los Quick Prompts. Los valores se convierten en `{{nombre}}` en tus step prompts. Opcional — déjalo vacío si tu workflow obtiene su contexto desde un step previo (ApiCall) o un trigger.',
  'wiz.addVariable': 'Añadir variable',
  'wiz.removeVariable': 'Eliminar esta variable',
  'wiz.undeclaredVarsTitle': 'Variables no declaradas detectadas',
  'wiz.undeclaredVarsHint': 'Estas `{{var}}` no coinciden con ningún step anterior, state, iter, artifact o variable de lanzamiento. Renderizarán vacío en runtime. Decláralas o corrige el nombre.',
  'wiz.undeclaredAddVar': 'Añadir a variables',
  'wiz.undeclaredAddVarHint': 'Añade «{name}» como variable de lanzamiento (solicitada al lanzar manualmente).',
  'wiz.undeclaredUnknownStep': 'Step desconocido — verifica el nombre',
  'wiz.undeclaredFailedOutsideRollback': '`failed_step.*` solo se puede usar en un step de rollback',
  'wiz.pollInterval': 'Intervalo de poll (cron)',
  'wiz.availableVars': 'Variables disponibles',
  'wiz.triggerVars': 'Variables de trigger (Tracker)',
  'wiz.issueTitle': 'Título del issue',
  'wiz.issueBody': 'Contenido del issue',
  'wiz.issueNumber': 'Número (#42)',
  'wiz.issueUrl': 'Enlace al issue',
  'wiz.issueLabels': 'Labels (coma)',
  'wiz.stepChaining': 'Encadenamiento entre steps',
  'wiz.prevOutput': 'Salida del step anterior',
  'wiz.namedOutput': 'Salida de un step por nombre',
  'wiz.iterHint': 'Contador de iteración del step (1, 2, 3…) — útil en bucles Goto',
  'wiz.stateHint': 'Variable de estado durable, los agentes escriben vía ---STATE:clave=valor---',
  'wiz.stateChipsLoopOut': 'Bucle detectado — pasa tu veredicto al step de retorno:',
  'wiz.stateChipsLoopIn': 'Recibe el veredicto de un bucle — lee el contexto anterior:',
  'wiz.stateChipWrite': 'escribir `state.{key}`',
  'wiz.stateChipWriteHint': 'Añade al final del prompt la instrucción de escribir `---STATE:{key}=...---` para pasar tu veredicto al step que reinicia.',
  'wiz.stateChipReadHint': 'Inserta `{{state.{key}}}` en el cursor — el valor escrito por el step anterior del bucle.',
  'wiz.stateInstructionBlock': 'Al final de tu respuesta (y SOLO al final), escribe exactamente:\n---STATE:{key}=<tu veredicto / feedback en una línea>---\n\nEste bloque será leído por el step «{target}» en la próxima iteración del bucle.',
  'wiz.gotoMaxIterPlaceholder': 'N',
  'wiz.gotoMaxIterHint': 'Pasadas N iteraciones de este Goto, el runner sale del bucle y continúa. Vacío = sin límite por-edge (solo aplica el límite global del workflow).',
  'wiz.gotoMaxIterLabel': 'máx',
  'wiz.gotoMaxIterUnit': 'iteraciones',
  'wiz.gotoTargetSelect': 'elige un step',
  'wiz.condActionStop': 'Stop — termina el run',
  'wiz.condActionSkip': 'Skip — salta el siguiente step',
  'wiz.condActionGoto': 'Goto — vuelve a un step anterior',
  'wiz.rollbackFiresFailed': 'Failed (error runtime)',
  'wiz.rollbackSkipsCancelled': 'Cancelled (has clicado «Detener»)',
  'wiz.rollbackSkipsGuard': 'StoppedByGuard (límite alcanzado)',
  'wiz.rollbackSkipsReject': 'Gate Reject (has rechazado)',
  'wiz.availableSignals': 'Señales disponibles (condiciones)',
  'wiz.signalNoResults': 'Nada encontrado o nada que reportar',
  'wiz.signalContinue': 'Continuar normalmente (defecto)',
  'wiz.clickToInsert': 'Clic para insertar',
  'wiz.example': 'Ejemplo: workflow multi-step',
  'wiz.stepName': 'Nombre del step',
  'wiz.advanced': 'Avanzado',
  'wiz.mode': 'Modo',
  'wiz.debateAgents': 'Agentes del debate',
  'wiz.maxRounds': 'Rondas max',
  'wiz.agentSettings': 'Agent settings (opcional)',
  'wiz.model': 'Modelo',
  'wiz.modelPlaceholder': 'ej: o3',
  'wiz.stallTimeout': 'Stall timeout (seg)',
  'wiz.delayAfter': 'Retraso después (seg)',
  'wiz.retry': 'Reintentar',
  'wiz.conditions': 'Condiciones (on_result)',
  'wiz.showMore': 'Ver más',
  'wiz.showLess': 'Reducir',
  'wiz.cronSwitchSimple': 'Modo simple',
  'wiz.testStep': 'Probar',
  'wiz.testInfo': 'Prueba este paso de forma aislada. En modo solo lectura, el agente describe lo que haría sin hacer cambios (sin comentarios Jira, sin commits, sin escritura). Desmarca para ejecutar realmente.',
  'wiz.testMockInput': 'Datos del paso anterior (mock)',
  'wiz.testMockPlaceholder': 'Pegue el JSON o texto que el paso anterior habría producido...',
  'wiz.testDryRun': 'Solo lectura (dry-run)',
  'wiz.testRun': 'Ejecutar test',
  'wiz.testRunning': 'Ejecutando...',
  'wiz.testCancel': 'Cancelar',
  'wiz.testLogConnecting': 'Conectando al agente...',
  'wiz.testLogStarted': 'Paso "{0}" iniciado — el agente está trabajando...',
  'wiz.testLogDone': 'Terminado — estado: {0}',
  'wiz.ifContains': 'si contiene',
  'wiz.noResultsPlaceholder': 'NO_RESULTS (obligatorio)',
  'wiz.noResultsStop': 'Sin resultados → Stop',
  'wiz.addStep': 'Agregar un step',
  'wiz.errorNoName': 'Nombre del workflow requerido',
  'wiz.errorNoPrompt': 'Prompt faltante para "{0}"',
  'wiz.agentQpPicker': 'Desde un Quick Prompt existente',
  'wiz.agentQpPickerInline': '— Ninguno (prompt inline más abajo) —',
  'wiz.agentQpInheritedFrom': 'Heredado de « {0} »',
  'wiz.agentQpInheritedHint': 'Los `prompt_template`, `tier` y `skill_ids` del QP se cargan en runtime. Las `{{var}}` se resuelven contra el contexto del workflow (variables de lanzamiento / state / previous_step…).',
  'wiz.agentQpOverridePlaceholder': 'Override del prompt — déjalo vacío para mantener el del Quick Prompt. Rellena para sobrescribir solo en este step.',
  'wiz.qrefOverrideActive': 'override activo',
  'wiz.qrefOverrideActiveHint': 'Has rellenado un override abajo. Sobrescribe el valor correspondiente del QP/QA solo en este step.',
  'wiz.qrefPreview': 'Prompt:',
  'wiz.qrefVars': 'Variables:',
  'wiz.qrefEmpty': '(prompt vacío)',
  'wiz.qrefOverridePromptToggle': 'Personalizar el prompt para este step (override)',
  'wiz.errorBatchNoQP': 'Quick Prompt no seleccionado para "{0}"',
  'wiz.errorApiNoPlugin': 'Plugin API no seleccionado para "{0}"',
  'wiz.errorApiNoEndpoint': 'Endpoint faltante para "{0}"',
  'wiz.errorNotifyNoUrl': 'URL del webhook faltante para "{0}"',
  'wiz.errorGateNoMessage': 'Mensaje de aprobación faltante para "{0}"',
  'wiz.errorExecNoCommand': 'Comando shell faltante para "{0}"',
  'wiz.errorBatchNoItemsFrom': 'Lista de ítems faltante para "{0}" (ej: {{steps.step1.data.tickets}})',
  'wiz.errorNoCondition': 'Condición vacía en "{0}" (condición #{1})',
  'wiz.security': 'Seguridad',
  'wiz.sandbox': 'Sandbox (Docker)',
  'wiz.requireApproval': 'Aprobación requerida',
  'wiz.maxFiles': 'Max archivos modificados',
  'wiz.maxLines': 'Max líneas modificadas',
  'wiz.concurrency': 'Límite de concurrencia (ejecuciones simultáneas)',
  'wiz.hooks': 'Hooks de workspace (opcional)',
  'wiz.hooksHint': 'Comandos shell ejecutados en cada fase del worktree git',
  'wiz.hookAfterCreate': 'Después de creación',
  'wiz.hookBeforeRun': 'Antes de ejecución',
  'wiz.hookAfterRun': 'Después de ejecución',
  'wiz.hookBeforeRemove': 'Antes de eliminación',
  'wiz.previous': 'Anterior',
  'wiz.next': 'Siguiente',
  'wiz.create': 'Crear',
  'wiz.saveError': 'Error al guardar. Revisa la config de tus pasos.',
  'wiz.save': 'Guardar',

  // ── Skills (WHAT) ──
  'skills.title': 'Skills',
  'skills.active': 'Skills activos',
  'skills.add': 'Agregar skill',
  'skills.remove': 'Quitar',
  'skills.builtin': 'Integrado',
  'skills.custom': 'Personalizado',
  'skills.language': 'Lenguaje',
  'skills.domain': 'Dominio',
  'skills.business': 'Negocio',
  'skills.noSkills': 'Ningún skill seleccionado',
  'skills.selectSkills': 'Seleccionar skills...',
  'skills.createCustom': 'Crear skill personalizado',
  'skills.name': 'Nombre',
  'skills.icon': 'Icono',
  'skills.category': 'Categoría',
  'skills.description': 'Descripción corta',
  'skills.descriptionPlaceholder': 'Lo que aporta este skill en una frase',
  'skills.content': 'Contenido (system prompt)',
  'skills.deleteConfirm': 'Eliminar este skill?',
  'skills.editCustom': 'Editar este skill',
  'skills.saveChanges': 'Guardar cambios',
  'skills.projectDefaults': 'Skills por defecto',
  'skills.projectDefaultsHint': 'Pre-seleccionados para discusiones y workflows de este proyecto',
  'skills.searchProjects': 'Buscar proyectos…',

  // ── Profiles (WHO) ──
  'profiles.title': 'Perfiles de agente',
  'profiles.select': 'Perfil de agente',
  'profiles.none': 'Sin perfil',
  'profiles.builtin': 'Integrado',
  'profiles.clickToEditName': 'Haz clic para editar el nombre',
  'profiles.custom': 'Personalizado',
  'profiles.technical': 'Técnico',
  'profiles.business': 'Negocio',
  'profiles.meta': 'Meta',
  'profiles.createCustom': 'Crear perfil personalizado',
  'profiles.name': 'Nombre',
  'profiles.personaName': 'Nombre',
  'profiles.role': 'Rol',
  'profiles.avatar': 'Avatar',
  'profiles.color': 'Color',
  'profiles.category': 'Categoría',
  'profiles.persona': 'Persona (system prompt)',
  'profiles.defaultEngine': 'Agente por defecto',
  'profiles.deleteConfirm': 'Eliminar este perfil?',
  'profiles.projectDefault': 'Perfil por defecto del proyecto',
  'profiles.projectDefaultHint': 'Pre-seleccionado para nuevas discusiones de este proyecto',
  'profiles.searchProjects': 'Buscar proyectos…',

  // ── Directives (HOW) ──
  'directives.title': 'Directivas',
  'directives.active': 'Directivas activas',
  'directives.add': 'Agregar directiva',
  'directives.remove': 'Quitar',
  'directives.builtin': 'Integrado',
  'directives.custom': 'Personalizado',
  'directives.source': 'Fuente',
  'skills.source': 'Fuente',
  'directives.output': 'Salida',
  'directives.language': 'Idioma',
  'directives.noDirectives': 'Ninguna directiva seleccionada',
  'directives.selectDirectives': 'Seleccionar directivas...',
  'directives.createCustom': 'Crear directiva personalizada',
  'directives.name': 'Nombre',
  'directives.icon': 'Icono',
  'directives.category': 'Categoría',
  'directives.description': 'Descripción corta',
  'directives.descriptionPlaceholder': 'Lo que cambia esta directiva en el comportamiento',
  'directives.content': 'Contenido (system prompt)',
  'directives.conflicts': 'Conflictos',
  'directives.deleteConfirm': 'Eliminar esta directiva?',
  'directives.conflictWarning': 'Conflicto detectado con:',

  // ── Setup Wizard ──
  'setup.step.agents': 'Agentes',
  'setup.step.repos': 'Repositorios',
  'setup.step.done': 'Listo',
  'setup.subtitle': 'Entra en la red. Controla tus agentes.',
  'setup.aiAgents': 'Agentes IA',
  'setup.refresh': 'Actualizar',
  'setup.agentsDetected': '{0} agente{1} detectado{2} en tu sistema.',
  'setup.noAgentDetected': 'Ningún agente detectado — instala al menos uno.',
  'setup.install': 'Instalar',
  'setup.enabled': 'Activado',
  'setup.disabled': 'Desactivado',
  'setup.continue': 'Continuar',
  'setup.skip': 'Saltar este paso',
  'setup.back': 'Atrás',
  'setup.detectedRepos': 'Repositorios detectados',
  'setup.rescan': 'Re-escanear',
  'setup.scanningRepos': 'Escaneando repositorios git...',
  'setup.reposFound': '{0} repositorio{1} git encontrado{2}.',
  'setup.hiddenRepos': '+ {0} oculto{1}',
  'setup.noRepoFound': 'Ningún repositorio git encontrado. Indique una ruta para escanear.',
  'setup.addPath': 'Agregar otra ruta',
  'setup.scan': 'Escanear',
  'setup.hide': 'Ocultar',
  'setup.show': 'Mostrar',
  'setup.hiddenReposToggle': '{0} los {1} repositorio{2} oculto{3}',
  'setup.hidden': 'oculto',
  'setup.configDone': 'Configuración completa',
  'setup.summary': '{0} agente{1}, {2} repositorio{3} detectado{4}.',
  'setup.goToDashboard': 'Ir al dashboard',
  'setup.preparingDashboard': 'Preparando el dashboard...',
  'setup.completingStep1': 'Guardando la configuración...',
  'setup.completingStep2': 'Registrando proyectos detectados...',
  'setup.completingStep3': 'Inicializando el dashboard...',
  'setup.detecting': 'Detectando agentes...',
  'setup.detectingHint': 'Analizando su sistema (PATH, WSL, npm)... esto puede tardar unos segundos.',
  'setup.detectionFailed': 'Detección fallida',
  'setup.installFailed': 'Instalación fallida',
  'setup.scanFailed': 'Escaneo fallido',

  // ── Settings: Voice ──
  'settings.voice': 'Voz',
  'settings.sttLabel': 'Speech-to-Text (Whisper)',
  'settings.sttDesc': '100% local via Whisper WASM. El modelo se descarga en el primer uso y se guarda en el navegador.',
  'settings.ttsLabel': 'Text-to-Speech (Piper)',
  'settings.ttsDesc': '100% local via Piper WASM. Activable con el botón de volumen en las discusiones. El modelo se descarga en el primer uso (~50 MB) y se guarda en el navegador.',
  'settings.stallTimeout': 'Timeout de inactividad del agente',
  'settings.stallTimeoutHint': 'Duración máxima sin salida antes de interrumpir el agente. Aumente si sus agentes ejecutan comandos largos (tests, builds).',
  'settings.stallTimeoutWarning': 'Un timeout alto da más tiempo a los agentes para ejecutar operaciones largas, lo que puede aumentar el consumo de tokens. Usar para builds y suites de tests grandes.',
  'settings.debugSection': 'Debug',
  'settings.debugMode': 'Modo debug',
  'settings.debugModeHint': 'Activa logs detallados del backend (detección de agentes, escaneo de proyectos, resolución de rutas del host). Útil para diagnosticar problemas macOS / multi-plataforma. Para forzar puntualmente sin persistir: ./kronn start --debug',
  'settings.debugModeRestart': 'El nuevo nivel de log se aplica en el próximo reinicio del backend. Usa ./kronn restart para reiniciar.',
  'settings.debugLogsTitle': 'Logs backend (en vivo)',
  'settings.debugLogsEmpty': 'Ningún log capturado todavía. Interactúa con la app para generar events.',
  'settings.debugLogsHint': 'Fuente: ringbuffer en RAM alimentado por cada event tracing. Sin archivo en disco. Usa Ctrl+F en el visor para buscar.',
  'settings.debugLogsRefresh': 'Actualizar',
  'settings.debugLogsStartFollow': 'Seguir',
  'settings.debugLogsStopFollow': 'Pausar',
  'settings.debugLogsCopy': 'Copiar',
  'settings.debugLogsClear': 'Vaciar',
  'settings.debugBufferedCount': '{0} / {1} líneas',
  'settings.debugReportCta': 'Reportar un bug en GitHub',
  'settings.debugReportHint': 'Abre una nueva pestaña con los logs precargados (secretos ocultados). Revisa antes de enviar.',
  'settings.debugReportIssueList': 'Ver issues existentes',
  'settings.debugLiveLabel': 'LIVE',
  'common.on': 'Activado',
  'common.off': 'Desactivado',

  // ── Discussions: Voice & misc ──
  'disc.recording': 'Grabando...',
  'disc.transcribing': 'Transcribiendo...',
  'disc.resumeListening': 'Reanudando la escucha...',
  'disc.cancelVoice': 'Cancelar',
  'disc.sendVoice': 'Enviar',
  'disc.stopRecording': 'Detener',
  'disc.micStop': 'Detener',
  'disc.micDictate': 'Dictar',
  'disc.draftRestored': 'Borrador restaurado · escrito {0}',
  'disc.draftDismiss': 'Descartar borrador',
  'disc.voiceModeOn': 'Modo conversación por voz',
  'disc.voiceModeOff': 'Desactivar modo conversación',
  'disc.logs': 'Logs',
  'disc.editTimeout': 'Modificar timeout',
  'disc.ttsDisable': 'Desactivar lectura vocal',
  'disc.ttsEnable': 'Activar lectura vocal',

  // ── Common ──
  'common.cancel': 'Cancelar',
  'common.dismiss': 'Cerrar',
  'common.save': 'Guardar',
  'common.delete': 'Eliminar',
  'common.close': 'Cerrar',
  'common.add': 'Añadir',
  'common.copy': 'Copiar',
  'common.loading': 'Cargando...',
  'common.seeMore': 'Ver más',
  'common.seeLess': 'Ver menos',

  // Workflow execution guards (0.7.0)
  'wf.guards.title': 'Límites de ejecución',
  'wf.guards.description': 'Red de seguridad contra bucles, runs sin fin y presupuestos imprevistos. Deja vacío para usar los valores por defecto.',
  'wf.guards.summaryDefaults': 'Defaults: {0} min · {1} llamadas IA · {2} revisitas/step',
  'wf.guards.summaryActive': 'Límites personalizados',
  'wf.guards.timeoutLabel': 'Duración máx',
  'wf.guards.timeoutUnit': '(minutos)',
  'wf.guards.timeoutHint': 'Por defecto {0} min — tiempo total transcurrido del run.',
  'wf.guards.maxLlmCallsLabel': 'Llamadas IA máx',
  'wf.guards.maxLlmCallsHint': 'Por defecto {0} — Agent = 1, BatchQP = 1, ApiCall/Notify = 0.',
  'wf.guards.loopDetectionLabel': 'Detección de bucle',
  'wf.guards.loopDetectionHint': 'Por defecto {0} — máximo de revisitas del mismo step (Goto).',
  'wf.guards.stoppedBy.title': 'Detenido por límite',
  'wf.guards.stoppedBy.timeout': 'Duración máx alcanzada ({0} s)',
  'wf.guards.stoppedBy.maxLlmCalls': 'Demasiadas llamadas IA ({0} de {1})',
  'wf.guards.stoppedBy.loopDetection': 'Bucle detectado en step "{0}" ({1} veces)',

  // ── Stale-stream watchdog (TD-20260504) ──
  'discussions.streamRecovered': 'Conexión con el agente perdida — último estado recargado',

  // ── Migration ai/ → docs/ banner ──
  'migration.title': 'Convención `ai/` heredada detectada',
  'migration.desc': 'Este proyecto aún usa `ai/index.md`. Migra hacia la convención `docs/AGENTS.md` (Kronn 0.7.1+) — `git mv` preserva el historial, las referencias internas se reescriben.',
  'migration.symlink': 'Crear un symlink `ai → docs` para retro-compatibilidad',
  'migration.cta': 'Migrar a docs/',
  'migration.ctaPending': 'Migrando…',
  'migration.ctaDone': 'Migración completada',
  'migration.inProgress': 'Moviendo archivos, reescribiendo referencias…',
  'migration.successInline': '{0} archivos movidos — `docs/AGENTS.md` listo',
  'migration.successToast': 'Migración exitosa ({0} archivos movidos)',
  'migration.alreadyToast': 'Proyecto ya migrado',
  'migration.failedToast': 'Migración fallida para {0}',
  'migration.failedGeneric': 'Migración fallida — revisa los logs del servidor',

  // ── User context (cross-project agent prompts) ──
  'userContext.title': 'Mis contextos',
  'userContext.subtitle': 'Notas markdown inyectadas en cada agente, sin importar el proyecto ni el CLI. Almacenadas en ~/.kronn/user-context/.',
  'userContext.newNamePlaceholder': 'nombre-de-archivo (añadirá .md)',
  'userContext.newNameAria': 'Nombre del nuevo archivo de contexto',
  'userContext.add': 'Añadir',
  'userContext.deleteConfirm': '¿Eliminar el archivo «{0}»?',
  'userContext.deleteAria': 'Eliminar {0}',
  'userContext.delete': 'Eliminar',
  'userContext.loading': 'Cargando…',
  'userContext.empty': 'Sin archivos. Añade uno para compartir notas con todos tus agentes.',
  'userContext.save': 'Guardar',
  'userContext.editingAria': 'Editando {0}',
};

/** Exposed for tests (key-parity check) and dev tooling. Do not mutate. */
export const dictionaries: Record<UILocale, TranslationDict> = { fr, en, es };

/** Get a translated string with optional {0}, {1}... interpolation */
export function t(locale: UILocale, key: string, ...args: (string | number)[]): string {
  const dict = dictionaries[locale] ?? dictionaries.fr;
  let str = dict[key] ?? dictionaries.fr[key] ?? key;
  for (let i = 0; i < args.length; i++) {
    str = str.replace(`{${i}}`, String(args[i]));
  }
  return str;
}
