Lot en un seul commit: les quatre tickets partagent les mêmes fichiers
(événements domaine/DTO, ProjectPluginsPanel, useProjectPlugins) — une
séparation par hunks aurait risqué des états intermédiaires non
compilables (convention lot des batches #310-#313, #315 et #325-#326).
#328 — section dédiée plugins dans Paramètres du projet:
- frontend: ProjectPluginsPanel intégré à ProjectSettingsPanel
(Panneaux > Paramètres du projet) — activation/désactivation par
plugin avec état actif/inactif explicite par projet, distinct de
l'assignation agents/skills/tools
- domaine: événement ProjectPluginSettingsChanged publié par
SetProjectPluginEnabled, MutateAgentPluginSkill et
MutateAgentPluginTool après persistance de .ideai/project-plugins.json
(application + backend + DTO camelCase, test dto.rs)
#329 — les menus plugin qui échouent ne sont plus muets:
- usePluginMenus: dispatcher strict (runCommandStrict) pour les
commandes réellement enregistrées au lieu du dispatcher laxiste qui
journalisait et avalait les rejets — rendait les échecs d'ouverture
de fenêtres natifs invisibles pour l'hôte de menus
- le no-op SDK historique (commande déclarée sans handler) est préservé
- échec remonté à l'hôte via commandError/clearCommandError (journal
console + état exposé); test window_usecases adapté à un plugin
installé
- le correctif côté SDK/GitPlugin vit dans le projet GitPlugin (son
commit/merge local y est fait, hors périmètre de ce dépôt)
#330 — refresh automatique des pages:
- frontend/domain: typage des événements pluginEnabled,
pluginDisabled (restartRequired), pluginUninstalled
(restartRequired), pluginLoadFailed et projectPluginSettingsChanged
- useProjectPlugins et PluginRuntimeProvider: souscription
onDomainEvent — refresh sur changement catalogue global ou sur
projectPluginSettingsChanged du projet courant; gardes
révision/projet contre les réponses obsolètes
- useProjectAgents: refresh auto quand un agent est ajouté au projet;
ProjectsView et AgentsPanel suivent (erreur de rafraîchissement
affichée avec bouton Réessayer)
#331 — droplist agents des cellules:
- SmallDropdown: liste bg-overlay + bordure border-strong (plus de
fond blanc hors thème), hover/focus bg-raised, sélection
bg-primary/15, disabled text-faint opacity-60, role=combobox
- LayoutGrid: sélecteur d'agent des cellules migré du <select> natif
vers SmallDropdown (mêmes libellés Terminal/visible
ailleurs/arrière-plan)
QA verte réelle sur les quatre lots (tests ciblés + exécution).
sdk/IdeaSDK: contenu modifié non committable ici (pointeur inchangé).
Aucun push.
Symptôme: après changement de projet puis retour, une cellule en CLI
custom retombait en TUI native et un nouveau switch CLI custom repartait
à neuf (aucune conversation reprenable) car le mode préféré de la
cellule n'était pas persisté dans l'arbre de layout.
- domaine frontend: type PreferredView (tui | chat) et champ
preferredView optionnel sur LeafCell; opération de layout
setCellPreferredView persistant la surface préférée (tui = défaut,
champ omis pour garder les arbres existants compacts)
- LayoutGrid: le mode de présentation d'une cellule agent dérive de
preferredView persisté; le cache localStorage du mode custom n'est
plus qu'un cache de migration secondaire; remount par clé
projectId:layoutId:nodeId à chaque changement de projet pour éviter
tout état résiduel inter-projets; la conversation/engine session/
session/agentWasRunning de la cellule sont préservés par les
bascules chat <-> TUI
- useLayout: setCellPreferredView exposé au view model
- tests: layout_usecases (persistance chat puis retour TUI en
conservant session/conversation/engine/running), layout.test
(setCellPreferredView chat persiste, tui omet le champ),
LayoutGrid.chat et cellControlsLayering alignés
QA verte sur fix/batch-322-323-session-restore-prompt-history. Aucun push.
La cellule expose un bouton Redémarrer l'agent valable pour les trois
modes de présentation (headless, TUI native, CLI custom), sans jamais
prendre la présentation pour vérité runtime.
- Application: use case RestartCellAgentRuntime validant le binding
exact cellule/runtime via les snapshots live (RestartRuntimeNotFound,
ConflictAgentVisibleElsewhere, AgentNotBoundToCell) puis déléguant au
scheduler self-restart existant (schedule_cell); CellAgentPresentation
purement informationnelle.
- Backend/app-tauri/web-server: commande restart_cell_agent sur contrat
DTO identique Tauri/HTTP, stop borné par timeout typé
(RestartStopTimeout), relance préservant routage TUI natif, géométrie
de cellule (node/rows/cols) et conversation; évenements domaine
enrichis.
- Frontend: bouton dans la barre de cellule avec confirmation,
overlay d'état Redémarrage…/échec (libellés par code d'erreur, Fermer/
Réessayer), gardes d'identité de cellule et de restarts externes,
gateway/mock/http adapters et ports alignés.
- Tests: dto_agents, workstate_actions, contrats web-server,
LayoutGrid.cellControlsLayering, adapters agent/mock/http.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#305: nouvel agrégat durable ScheduledProjectTask dans domain (scheduled_task.rs, ids, ports ScheduledProjectTaskStore), source de vérité <app_data_dir>/scheduled-project-tasks/<projectId>.json, schedules bornés V1 interval/daily/weekly avec fuseau, cycle À approuver/Active/Suspendue avec approbation obligatoire des tâches demandées par agent; use cases application/scheduled_task (list/create/update/delete/run_now/reconcile/fire) réutilisant BackgroundTask pour les runs avec distinction planifié/manuel; store infrastructure dédié + scheduler reconcile au boot sans rattrapage en rafale, cwd confiné au projet; 7 tools MCP idea_scheduled_task_* (list/read lecture seule, create/update/delete/run_now/set_enabled write-actions soumises aux permissions natives); commandes Tauri + routes HTTP aux contrats DTO identiques (camelCase nextRunAtMs/lastRunAtMs/lastTaskId/createdBy/updatedBy); frontend: gateways Tauri/HTTP/mock + types ports/domain, surface projet 'Tâches planifiées' (liste utilisateur+agent unifiée, création/édition avec récapitulatif avant activation, approbation/refus, exécuter maintenant, suspendre/reprendre, suppression), catalogage surface web (webSurfaceCatalog/viewPlacement/ViewPanelBody/WebWorkspace); docs/scheduled-project-tasks.md et sdk docs/services.md (047c38b: pas un service plugin V1, pas d'émulation via cron système).
#314: correction de 'Conversation liée — transcript indisponible' après reboot: restauration du transcript lié côté application (orchestrator service, lifecycle, session_limit, background sink, terminal registry) et côté frontend (adapters agent/http frames/stream/requestResponse, workStateNormalization, CustomAgentChatView avec état et action utile), tests dto_chat/agent_lifecycle/structured_registry_d1/background_completion_sink étendus.
QA verte rapportée par QA sur feature/305-scheduled-project-tasks-mcp, sans bloquant, pour #305 et #314.
Hors périmètre worktree: tickets non liés, attachments/, scripts/; aucun push.
- #310: horodatage de tous les messages et événements de conversation visibles — composant partagé EntryTimestamp, timestamps propagés dans le transcript chat, les annonces inter-agent et le viewer de conversation, normalisation côté domain/adapters (conversation_log, paginate).
- #311: stop d'agent robuste pour PTY non monté, sessions headless et CLI custom — lifecycle/orchestrator durcis, ponts chat app-tauri/web-server alignés, arrêt fiable sans session orpheline.
- #312: préférence globale d'interface terminal par défaut — nouveau store settings (domain/application/infrastructure + tests), DTO et commandes backend, adapter frontend userSettings, panneau TerminalSettings dans les paramètres globaux.
- #313: Paramètres du projet — contexte de projet éditable et couleur de thème par projet (ProjectSettingsPanel + projectTheme), teinte des onglets projet, persistance via DTO projet.
QA verte (GO QA sur feature/batch-310-311-312-313-chat-stop-prefs-theme) — vérifications Git: git diff --cached --check OK; staging borné à crates/ et frontend/ (74 fichiers, +2105/-531), commit unique de lot conforme à la convention des lots (précédents batch-151/158/159, batch-210/211/212) car les fichiers partagés (dto.rs, commands.rs, CustomAgentChatView) entrelacent les 4 tickets.
fichiers .ideai/* (agents/test.md, chat-drafts.json, system-permissions.json, tickets/counter.json, tickets/index.json, tickets/308-313/), attachments/ et scripts/ hors périmètre et préservés en worktree ; aucun push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cause: l'utilisateur ne pouvait pas retirer un projet du registre IdeA (projects.json) ; la seule issue laissait l'entrée fantôme dans la liste des projets, sans capacité générique de retrait.
domain: port ProjectStore::remove_project(id) -> Option<Project> avec implémentation par défaut « not supported » pour ne pas casser les stores spécialisés.
infrastructure: FsProjectStore implémente remove_project — retire l'entrée du registre persisté et la renvoie, None si id inconnu, ordre relatif du registre préservé ; test store dédié.
application: use case RemoveProjectFromRegistry (remove.rs) — NotFound explicite si id absent, retourne le projet retiré + registre rafraîchi.
backend/app-tauri/web-server: DTO RemoveProjectRequestDto/RemoveProjectResponseDto (removed, projects, restore_state, was_open), adapter partagé remove_project_for_adapter réutilisé par la commande Tauri remove_project et la route HTTP — même contrat desktop/web ; remove_project_and_deactivate désactive le projet s'il était ouvert, purge l'entrée open-projects restore correspondante et renvoie was_open/restore_state ; test adapter vérifiant le contrat de bout en bout (entrée retirée, restore purgé, root intact).
frontend: DTO + ProjectGateway.removeProject (HTTP + mock), ProjectsView expose un menu d'actions projet (ProjectActionsMenu) avec entrée Retirer, RemoveProjectDialog — confirmation explicite précisant que le dossier disque n'est pas supprimé, invalidation/rechargement de la liste des projets après succès (useProjects), tests ProjectsView/projects/adapters/httpInvoker/services.
QA verte: cargo targeted tests/check OK (domain, application project_usecases, infrastructure project_store, backend, app-tauri, web-server), npm targeted/typecheck/build OK ; suite npm test complète rouge non bloquante hors #308 (échecs préexistants listProjectSettings/web). Vérifications Git: git diff --check OK, cargo fmt --all -- --check OK.
fichiers .ideai/tickets/308/, tickets/counter.json, tickets/index.json, attachments/ et scripts/ hors périmètre et préservés en worktree (sync store traité séparément) ; aucun push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Attachments: projection canonique ChatPromptAttachmentDto (référence [file n]/[image n] par compteur de type, ordre d'import) insérée dans le prompt modèle et transportée par ReplyChunk::UserPrompt (attachmentRefs) ; rendu compact côté CustomAgentChatView.
- Resize: la zone chat (LeafView/LayoutGrid) est redimensionnable, taille actuelle conservée par défaut, préférence locale persistée.
- Tests: commands (projection des refs), dto_chat, chat_bridge, CustomAgentChatView, LayoutGrid.chat.
- Codex: les items agent_message sont désormais tapés en live comme progress (Message/Delta) dès item.completed, au lieu d'être bufferisés dans pending_final jusqu'à l'événement tool/MCP suivant.
- Corrélation stable progressId (codex:item:<id>) ajoutée sur ReplyProgress, propagée jusqu'au DTO (ReplyProgressDto.progressId) et au frontend ; le ProgressSettler et la vue procèdent par upsert running -> done au lieu d'empiler des lignes dupliquées.
- Tests: chat_bridge (settler par progressId), dto_chat, session codex, CustomAgentChatView (upsert par progressId).
Cancel transitif, idempotent et ownership-aware sur le graphe parent→enfants des background tasks:
- domain/background_task + ports: modèle parent/enfant (parent_task_id, rejet du self-parent), index children_by_parent, traversal des descendants en largeur.
- application/background + infrastructure/store: cascade cancel (annule les descendants running/open, laisse les terminaux), idempotente face aux double-cancel et ordres de terminaison non déterministes; respect de l'ownership (parent_task_link).
- orchestrator/service: stop agent → cascade cancel des tasks lancées par cet agent (relatesTo #196); cancel task parent → cascade de ses sous-tasks (dependsOn #199). Refactor unifiant le chemin de cancel, sans sémantique #196/#199 regreffée (tests hérités verts).
- frontend: projection parentTaskId (workstate normalization/domain/services).
Tests QA verts: domain background_task, application background_task_cancel (cascades_background_task_subtree, cascades_to_running_child, cascades_to_open_descendants_and_leaves_terminal_child, parent_task_link_roundtrips_and_rejects_self_parent), orchestrator_service, workstate_actions, workstate.test.tsx.
Transforme l'annulation d'une background task en contrat immédiat, observable et non-best-effort:
- domain/background_task: états Cancelling/Cancelled/CancelFailed et transitions idempotentes; un kill PTY qui échoue est surfacé en CancelFailed plutôt qu'en succès silencieux.
- infrastructure/runner + store: cancel effectif de la task ciblée, reconcile marque Cancelling même sans live handle, et départage CancelFailed des tâches pending au boot.
- application/background + workstate + orchestrator/wake: chemin de cancel initié depuis le workstate, projection synchronisée de l'état (pas d'attente d'un cycle ambigu).
- frontend: projection workstate de l'état cancelled/cancelling/cancelFailed (cancellingRow/cancelFailedRow/cancelButtons), normalization et services.
Tests QA verts: background_task_cancel, background_task_runner cancel_wins_even_if_output_is_still_available, background_task_store reconcile_marks_cancelling_without_live_handle_cancel_failed_and_pending, workstate.test.tsx.
Solidifie le cycle stop/restart d'un agent visible en cellule:
- terminal/registry: kill effectif de la TUI native et de la CLI custom, arrêt idempotent du live runtime (PTY + structuré), distinction nette « agent stoppé » vs « pas de session ».
- workstate + lifecycle: projection d'un état runtime explicite « stopped » (AgentRuntimeState: running/stopping/stopped/failedStart), réconcilié avec le live state; traitement des courses « agent not found ».
- relance froide via freshStart: repart de zéro sans réattacher une session morte et recharge la configuration courante de l'agent (y compris changement de modèle).
- contrat projeté côté adapters frontend (ports/domain/normalization/mock) pour la consommation UI future (#197/#198), sans surface UI ici.
Tests QA verts: workstate_actions, orchestrator_service stop_agent, agent_lifecycle (reopen/fresh_start), workstate stopped runtime state, vitest adapter stop_live_agent.
Le collage d'une image (ex. capture d'écran) échouait en environnement desktop : le presse-papier image n'est souvent pas exposé comme un File synchrone dans clipboardData.files, mais uniquement via l'Async Clipboard API. On ajoute donc un repli sur navigator.clipboard.read() (MIME image/*) quand l'événement de collage ne fournit aucun fichier. Le preventDefault() n'est émis que lorsqu'on a effectivement lu des fichiers issus de l'événement, afin de ne pas bloquer le collage texte quand le repli async ne remonte rien.
- refactor clipboardFiles(event) -> dataTransferFiles(DataTransfer) réutilisable
- dedupeKey robuste pour les images sans nom (mime + taille + contenu base64)
- ReplyChunk::UserPrompt porte désormais les labels d'attachments (DTO + accounting reply_chunk_bytes, skip_serializing_if Vec::is_empty)
- tests ciblés frontend (+219) et backend (chat_bridge/dto/attachments) verts
QA : tests ciblés verts. Réserve : environnement headless, pas de validation manuelle desktop — validation AppImage à confirmer en suivi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Permet à un plugin de contribuer des commandes slash adossées à une callback,
transitant par le registry/contrat unifié (#162) :
- sdk/IdeaSDK: manifeste contributes.slashCommands (déclaratif) — pointer bumpé.
- domain: source Plugin étendue + effet PluginCallback (la commande ne
s'exécute pas directement : elle renvoie une référence à la callback du plugin).
- application: registration des commandes plugin dans le registry + exécution
renvoyant l'effet PluginCallback ; métadonnées UI suffisantes pour l'autocomplete.
- backend + app-tauri: DTO + commandes Tauri listant/invoquant les commandes plugin.
- frontend (contrat): types adapters/domain/ports/mock pour pluginCallback et
l'invocation avec arguments.
L'exécution effective de la callback côté runtime UI est livrée par #166.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
À la saisie d'un « / » en début de draft (sans espace), ouverture d'un menu
contextuel listant les commandes matchant le préfixe, via le contrat unifié
listSlashCommands — aucune liste codée en dur côté UI. Rafinement incrémental
des suggestions à la frappe, navigation clavier + sélection, exécution par
executeSlashCommand.
- adapters/agent: listSlashCommands(prefix?) + executeSlashCommand (invoke Tauri).
- domain + ports: types SlashCommand / ExecuteSlashCommandResult.
- mock: données de test.
- CustomAgentChatView: détection de préfixe (/ sans espace), fetch avec séquence
anti-retard, gestion ouverture/active index, navigation clavier, fermeture
quand le préfixe cesse d'être éligible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Étend le flux de création de layout backend (DTO, usecases, store) et le
frontend (sélecteur, adaptateurs, LayoutTabs/LayoutGrid) pour permettre
d'ouvrir un layout déclaré par un plugin installé (ex. Android Health),
sans passer par le message bloquant "extension backend pas encore livrée".
Ticket #141 — QA vert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Plugins can now declare activationScope ("app" | "project") in their
manifest; loader/runtime honor it to defer activation of project-scoped
plugins until a project is focused instead of activating everything at
app bootstrap. Bumps sdk/IdeaSDK to the commit that adds the field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le contexte projet d'un agent s'affichait "[object Object]" et se
vidait à la réouverture du panneau : read_agent_context renvoie déjà
{content} côté backend, mais les gateways front traitaient la réponse
comme une string brute. Les deux gateways (Tauri + HTTP) unwrap
maintenant .content ; type AgentContextDocument ajouté au domaine.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
État d'intégration confiné à la branche batch. Les tickets #119 (skills →
capacités agent découvrables), #122 (override permissions par défaut), #131
(effort par agent/presets) et #132 (outil MCP d'édition du contexte projet)
sont verts en périmètre. Le sprint plugins multi-fichiers ESM / persistance
plugin-owned (#135/#136/#139) est co-implémenté dans les MÊMES fichiers de
câblage (frontend/src/ports/index.ts, backend/src/lib.rs, domain/ports.rs,
backend/dto.rs), inséparable sans staging interactif (indisponible ici).
Commit unique volontaire : préserve l'état vert QA sans découpe hunk risquée.
NON mergé vers develop tant que #137 (QA e2e plugins) n'est pas vert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- capability manifeste tooling supportée backend + transport runtime catalog
- ctx.services publique côté SDK/runtime, gated par tooling
- surface honnête: workspace, terminal, et tasks en observation/contrôle uniquement
- docs/exemple/tests mis à jour
- QA PASS sur feature/sdk-plugin-tooling-build-debug-surface
- plugin.ts: normalizeReview() garantit issues/installable définis même si backend omet ces champs
- mock/index.ts: MockPluginGateway.uninstall() retourne removalOutcome pour cohérence avec le domaine
- domain/index.ts: PluginUninstallResult ajoute removalOutcome optionnel
- Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stockage flat côté ticket + métadonnées d'attachments, lecture exposée côté
backend/MCP, et UI minimale de liste/ajout dans TicketDetail. Traverse le
domaine (Issue, ports), l'application (usecases + assistant de ticket), les
adaptateurs infra/MCP (issues store, orchestrateur), les DTO backend/web-
server/app-tauri, et le frontend (domain/ports/adapters/hooks/UI).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
createdBy était déjà présent dans le frontmatter des tickets mais ni exposé
ni exploitable côté UI. Ajout du DTO (domain/ports/mock), affichage dans
TicketDetail, colonne/filtre créateur dans TicketsPanel, et persistance du
filtre. ticketActor.ts introduit pour porter la logique de filtrage côté
frontend, sans changement backend nécessaire (lot frontend pur).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ajout du catalogue enrichi pour les modèles Codex et Claude avec:
- Compatibilité estimée avec la version CLI locale détectée
- Source d'origine (catalogue/Provider) pour chaque entrée
- Support du catalogue Provider API externe
- Matrice de compatibilité embarquée dans l'application
Frontend:
- UI de configuration des modèles avec affichage des états de compatibilité
- Suggestions dynamiques avec badges de compatibilité
- Messages d'aide contextuels (compatible/unknown/likelyTooRecent)
- Alertes non-bloquantes pour les modèles trop récents
- Gestion des échecs de catalogue avec saisie manuelle conservée
Backend:
- Ports CliVersionReader, ProviderModelCatalogue, CompatibilityMatrixSource
- Implémentations: ProcessCliVersionReader, HttpProviderModelCatalogue, EmbeddedCompatibilityMatrix
- Enrichissement des DTOs avec compatibility, cli_version, warnings
- Tests unitaires complets pour le resolver de catalogue
Le picker de provider OpenCode du first-run wizard consomme le
catalogue dynamique exposé par le backend et propose une option
« provider personnalisé » avec un formulaire de saisie libre
(id + clé API) quand le provider souhaité n'y figure pas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ajoute l'UI de configuration des providers OpenCode cloud dans le wizard
first-run (sélection, édition, sauvegarde, suppression), le domaine et
les ports associés, ainsi que les adapters HTTP/mock correspondants.
Build + 952 tests verts, comportements clés vérifiés en exécution réelle,
y compris un test de couverture ajouté pour le parcours d'édition.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lots F1-F4 : runtime de chargement/registre plugin, extension des menus
existants, panneau de gestion des plugins, types de layout custom
(sélecteur, fallback, cellule dédiée) branchés sur le port plugin.
Suite npm typecheck/test verte (947/947).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Étend ServerExposureSettings avec autoStart (défaut false, mock aligné sur le
backend). useDeployment expose setAutoStart, qui persiste immédiatement le
draft sans jamais appeler start() ni toucher le comportement de stop(), et ne
met à jour l'état local qu'après confirmation de la sauvegarde (même piste de
validation que start/save). DeploymentSettings ajoute la case à cocher sous
le panneau Serveur, et des actions « Modifier le port »/« Réessayer » quand le
statut échoue avec le code PORT_IN_USE stabilisé côté backend.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Écoute l'event Tauri `app-exit-work-guard` (émis par le backend quand la
fermeture de la fenêtre main est interceptée) et affiche une popup modale
"Du travail est encore en cours" avec le résumé pluralisé exact du carnet
#83, le détail agents/tâches capé à 5 lignes, et les deux actions Annuler
(focus par défaut, no-op local) / Quitter quand même (danger, appelle
confirm_app_exit). Pas d'option "ne plus demander".
- domain/ports/adapters (Tauri listen+invoke, HTTP desktop-only stub, mock
avec helpers de test) : onAppExitWorkGuard/confirmAppExit sur
SystemGateway, suivant le patron déjà utilisé pour focused-project et les
domain events.
- AppExitConfirmDialog : mounted une fois près de la racine (App.tsx), à
côté d'AnnouncementsProvider — role="alertdialog", focus trap, Échap =
Annuler, pas de fermeture au clic extérieur, ne se referme jamais
automatiquement (un event pendant l'ouverture rafraîchit juste le résumé).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>