@imenam/mcp-feature-analyzer 1.0.18 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +6 -5
  2. package/dist/shared/review-state.d.ts +6 -0
  3. package/dist/shared/review-state.js +11 -2
  4. package/dist/shared/schemas/analysis.schema.d.ts +31 -2
  5. package/dist/shared/schemas/analysis.schema.js +5 -1
  6. package/dist/shared/schemas/api.schema.d.ts +13 -0
  7. package/dist/shared/schemas/api.schema.js +6 -0
  8. package/dist/shared/schemas/ipc.schema.d.ts +1 -1
  9. package/dist/shared/schemas/ipc.schema.js +1 -0
  10. package/dist/src/core/migrate.d.ts +5 -0
  11. package/dist/src/core/migrate.js +38 -1
  12. package/dist/src/core/review-actions.d.ts +16 -3
  13. package/dist/src/core/review-actions.js +25 -0
  14. package/dist/src/gui-worker.js +7 -1
  15. package/dist/src/index.js +13 -6
  16. package/dist/src/tools/format.d.ts +3 -2
  17. package/dist/src/tools/format.js +8 -3
  18. package/dist/src/tools/get-review-feedback.js +10 -8
  19. package/dist/src/tools/list-analyses.d.ts +2 -1
  20. package/dist/src/tools/list-analyses.js +7 -6
  21. package/dist/src/tools/mark-feedback-processed.d.ts +2 -3
  22. package/dist/src/tools/mark-feedback-processed.js +4 -17
  23. package/gui/dist/assets/{angular-html-khSGtcDV.js → angular-html-DjjSiqQZ.js} +1 -1
  24. package/gui/dist/assets/{angular-ts-CQZskKbG.js → angular-ts-BTRcXEj8.js} +1 -1
  25. package/gui/dist/assets/{apl-Dk2A1KW8.js → apl-By3131Sn.js} +1 -1
  26. package/gui/dist/assets/{astro-CDNLxpIR.js → astro-GOx0I6tH.js} +1 -1
  27. package/gui/dist/assets/{blade-HVRxvZlI.js → blade-SxDzr2ab.js} +1 -1
  28. package/gui/dist/assets/{c-BAogwQrx.js → c-rXSNR5kl.js} +1 -1
  29. package/gui/dist/assets/{chapel-CXJ1GKiJ.js → chapel-nBfsanOz.js} +1 -1
  30. package/gui/dist/assets/{cobol-CUpuiRry.js → cobol-BiZycENC.js} +1 -1
  31. package/gui/dist/assets/{coffee-Bvuk4CPF.js → coffee-BlljDtvu.js} +1 -1
  32. package/gui/dist/assets/{cpp-DrhGCibo.js → cpp-B_sYZ92I.js} +1 -1
  33. package/gui/dist/assets/{crystal-CoGIF_h4.js → crystal-Cg0a9qqm.js} +1 -1
  34. package/gui/dist/assets/{css-CUXMpymc.js → css-LHykI9SM.js} +1 -1
  35. package/gui/dist/assets/{edge-MTcLzslY.js → edge-5wEkHYNr.js} +1 -1
  36. package/gui/dist/assets/{elixir-DpYpNuu6.js → elixir-B2Ez6C_y.js} +1 -1
  37. package/gui/dist/assets/{elm-CAdLQ8j2.js → elm-CnwUpgFN.js} +1 -1
  38. package/gui/dist/assets/{erb-wGT1uBBo.js → erb-9ibbHo7M.js} +1 -1
  39. package/gui/dist/assets/{git-rebase-D7SHvGwZ.js → git-rebase-DffAuf0G.js} +1 -1
  40. package/gui/dist/assets/{glimmer-js-DEF7JSd6.js → glimmer-js-VAknUS9x.js} +1 -1
  41. package/gui/dist/assets/{glimmer-ts-CM94Mbx8.js → glimmer-ts-DcrYlRqt.js} +1 -1
  42. package/gui/dist/assets/{glsl-DvB1v13j.js → glsl-CYGBzxMW.js} +1 -1
  43. package/gui/dist/assets/{graphql-CpBeqKe2.js → graphql-Bv55IETC.js} +1 -1
  44. package/gui/dist/assets/{hack-CBlJ1Su7.js → hack-D8V-_7lb.js} +1 -1
  45. package/gui/dist/assets/{haml-Db0uDFny.js → haml-Bp-WEz3w.js} +1 -1
  46. package/gui/dist/assets/{handlebars-B9jQcMgj.js → handlebars-B5v0_vx8.js} +1 -1
  47. package/gui/dist/assets/{html-DTSojrQm.js → html-BudjcI9P.js} +1 -1
  48. package/gui/dist/assets/{html-derivative-7jGgl4AX.js → html-derivative-BVPFhHTi.js} +1 -1
  49. package/gui/dist/assets/{http-DJbcEzJc.js → http-BNWhTfh4.js} +1 -1
  50. package/gui/dist/assets/{hurl-BjBdeErH.js → hurl-BQXVMX2w.js} +1 -1
  51. package/gui/dist/assets/index-DVca12tD.css +1 -0
  52. package/gui/dist/assets/{index-CR3WhYFz.js → index-TuSnqzFd.js} +17 -17
  53. package/gui/dist/assets/{java-Dn64Xe0Z.js → java-U-bszQtz.js} +1 -1
  54. package/gui/dist/assets/{javascript-BbkSu_9c.js → javascript-BOp_I2j_.js} +1 -1
  55. package/gui/dist/assets/{jinja-DqC5t5sF.js → jinja-CJ8_uK8s.js} +1 -1
  56. package/gui/dist/assets/{jison-DpuZbYSp.js → jison-CsIefIrr.js} +1 -1
  57. package/gui/dist/assets/{json-B2a7n5JV.js → json-BwmgqzNJ.js} +1 -1
  58. package/gui/dist/assets/{jsx-ClgcRv-g.js → jsx-BLTVsDXL.js} +1 -1
  59. package/gui/dist/assets/{julia-Do1-bHs6.js → julia-BEeV8R5w.js} +1 -1
  60. package/gui/dist/assets/{just-DHt2GVD3.js → just-BwQDH9rp.js} +1 -1
  61. package/gui/dist/assets/{latex-B_f2UeRI.js → latex-BJn4gKgg.js} +1 -1
  62. package/gui/dist/assets/{liquid-Ds2eU_MP.js → liquid-B5SMgGD6.js} +1 -1
  63. package/gui/dist/assets/{lua-S7tZuNGt.js → lua-CVTScK8G.js} +1 -1
  64. package/gui/dist/assets/{marko-udM4lCVd.js → marko-BneKfb4h.js} +1 -1
  65. package/gui/dist/assets/{mdc-Cp5CPiQh.js → mdc-DHkkf3Ef.js} +1 -1
  66. package/gui/dist/assets/{nginx-fGikazrY.js → nginx-DxGETPm7.js} +1 -1
  67. package/gui/dist/assets/{nim-ByS27m8u.js → nim-DV7N6lGB.js} +1 -1
  68. package/gui/dist/assets/{org-CWef6EjL.js → org-D9idAuxR.js} +1 -1
  69. package/gui/dist/assets/{perl-B3b8PFq6.js → perl-itV17GnJ.js} +1 -1
  70. package/gui/dist/assets/{php-BgIqtqOt.js → php-CCAkcfa5.js} +1 -1
  71. package/gui/dist/assets/{pug-BRImkfAG.js → pug-C5resLq0.js} +1 -1
  72. package/gui/dist/assets/{qml-CzXZbZNQ.js → qml-BkHobuTC.js} +1 -1
  73. package/gui/dist/assets/{r-Nteo2x9j.js → r-BqWX-dEx.js} +1 -1
  74. package/gui/dist/assets/{razor-D_Afw34M.js → razor-BpA2ENLe.js} +1 -1
  75. package/gui/dist/assets/{regexp-CKIOMRkq.js → regexp-C-XnPPLY.js} +1 -1
  76. package/gui/dist/assets/{rst-BKqSLPk8.js → rst-CUTbSAax.js} +1 -1
  77. package/gui/dist/assets/{ruby-e08qQ71e.js → ruby-D7bW5EZD.js} +1 -1
  78. package/gui/dist/assets/{sas-CBP-Z7IW.js → sas-w8fOFl4u.js} +1 -1
  79. package/gui/dist/assets/{scss-D_FoTbGY.js → scss-BNjT1ImJ.js} +1 -1
  80. package/gui/dist/assets/{shellscript-Ck8Pcu_n.js → shellscript-CMWcepnG.js} +1 -1
  81. package/gui/dist/assets/{shellsession-ClnG1wCa.js → shellsession-D5YeFBMB.js} +1 -1
  82. package/gui/dist/assets/{soy-SRwHcO2G.js → soy-CaNGX-mM.js} +1 -1
  83. package/gui/dist/assets/{sql-CjUCW4z6.js → sql-DxGfvOCg.js} +1 -1
  84. package/gui/dist/assets/{stata-DbpO8t8N.js → stata-Bp4oSjfv.js} +1 -1
  85. package/gui/dist/assets/{surrealql-BvdjWgRW.js → surrealql-C8Utj_WH.js} +1 -1
  86. package/gui/dist/assets/{svelte-Bce_bVba.js → svelte-BFHeACD2.js} +1 -1
  87. package/gui/dist/assets/{templ-D1Wmfx4s.js → templ-CxVazkuY.js} +1 -1
  88. package/gui/dist/assets/{tex-DAUoYbei.js → tex-CSoJ8Afo.js} +1 -1
  89. package/gui/dist/assets/{ts-tags-DeHdW_lm.js → ts-tags-BUTYhDKV.js} +1 -1
  90. package/gui/dist/assets/{tsx-BtUXi9Pr.js → tsx-BvqayBnu.js} +1 -1
  91. package/gui/dist/assets/{twig-NY4Ref0L.js → twig-DWygjTq9.js} +1 -1
  92. package/gui/dist/assets/{typescript-BRVtVFtH.js → typescript-C7DqIINO.js} +1 -1
  93. package/gui/dist/assets/{typst-CEx4fnEq.js → typst-Dp1YSMkC.js} +1 -1
  94. package/gui/dist/assets/{vue-CzSBuAS4.js → vue-C6O_bBVG.js} +1 -1
  95. package/gui/dist/assets/{vue-html-3GkraVg2.js → vue-html-CMX7hzAZ.js} +1 -1
  96. package/gui/dist/assets/{vue-vine-un5mayXW.js → vue-vine-DnhOPozF.js} +1 -1
  97. package/gui/dist/assets/{xml-DYFbmBqP.js → xml-03xiog6c.js} +1 -1
  98. package/gui/dist/assets/{xsl-CVaXn7rF.js → xsl-Di6xLqaY.js} +1 -1
  99. package/gui/dist/assets/{yaml-CCwQ0s68.js → yaml-BfGI9DCW.js} +1 -1
  100. package/gui/dist/index.html +2 -2
  101. package/package.json +1 -1
  102. package/gui/dist/assets/index-Bj1iisUG.css +0 -1
package/README.md CHANGED
@@ -136,7 +136,7 @@ Quatorze outils. Les noms, descriptions et textes de résultat sont en anglais ;
136
136
 
137
137
  | Outil | Effet |
138
138
  |---|---|
139
- | `list_analyses` | Point d'entrée : les analyses regroupées par projet, chacune avec ses refs, ses constats ouverts par gravité, ses fichiers revus, son nombre de schémas et l'état de sa revue ; une revue soumise est « pending processing » tant que l'agent ne l'a pas marquée traitée, « processed at … » ensuite, avec la note du traitement. `project` limite la liste à un projet (un nom inconnu liste les projets existants) ; `review_state` à un état de revue (`not_started`, `in_progress`, `files_reviewed`, `submitted`), aux revues soumises restant à traiter (`to_process`) ou déjà traitées (`processed`). Invite l'agent à reprendre l'analyse existante d'une feature plutôt qu'à en créer une autre. Signale aussi les fichiers d'analyse illisibles. |
139
+ | `list_analyses` | Point d'entrée : les analyses regroupées par projet, chacune avec ses refs, ses constats ouverts par gravité, ses fichiers revus, son nombre de schémas et l'état de sa revue ; une revue soumise est « pending processing » tant que l'agent ne l'a pas marquée traitée, « processed at … » ensuite, avec la note du traitement ; une revue approuvée est « nothing to process ». `project` limite la liste à un projet (un nom inconnu liste les projets existants) ; `review_state` à un état de revue (`not_started`, `in_progress`, `files_reviewed`, `submitted`), aux revues soumises restant à traiter (`to_process`) ou qui n'attendent plus l'agent (`processed` : marquées traitées, ou approuvées). Invite l'agent à reprendre l'analyse existante d'une feature plutôt qu'à en créer une autre. Signale aussi les fichiers d'analyse illisibles. |
140
140
  | `create_analysis` | Calcule et fige le diff : `repo_path` (racine absolue du dépôt), `project` (projet ou feature, dossier de rangement, requis), `title`, `mode` (`branch` ou `working_tree`), `base` (requis en `branch`), `head` (en `branch`, défaut `HEAD`), `request_text` et `request_source` (la demande initiale : ticket, prompt). Renvoie l'identifiant et la liste des fichiers. |
141
141
  | `get_analysis` | Vue complète : projet, demande, vue d'ensemble, résumé, fichiers (revus ou non), constats avec statut et emplacement, remarques du relecteur, schémas (signalés quand leur tracé a des croisements), état de la revue. |
142
142
  | `update_analysis` | Change `title`, `project`, la vue d'ensemble (`objective`, `approach`, `attention_points`), `summary` (les changements fonctionnels de la feature, une puce par changement, remplace toute la liste) ou la demande initiale (`request_text`, `request_source` ; `null` efface). Les champs absents sont conservés. |
@@ -197,11 +197,11 @@ Chaque nœud a un `id`, un `label`, une forme `shape` (`box` par défaut, `pill`
197
197
  | Outil | Effet |
198
198
  |---|---|
199
199
  | `get_review_feedback` | Une fois la revue soumise : décision (`approve`, `request_changes`, `reject`), état du traitement (à traiter, ou traité avec sa date et sa note), constats et remarques retenus avec leur détail, et le prompt généré, recopié tel quel. Après une nouvelle soumission, il liste aussi ce qui a changé depuis la version soumise précédente (décision, constats et remarques ajoutés, modifiés ou retirés) et rappelle comment celle-ci avait été traitée. Avant la soumission, ou pendant que le relecteur modifie une revue soumise : l'avancement seul (fichiers revus, constats ignorés, remarques). Lecture seule. |
200
- | `mark_feedback_processed` | Marque traité le retour soumis, avec la date et une `note` facultative (« corrigé par 12785d6e3 et 1ff5baaad », « réponses données en conversation »). Sert quand `refresh_analysis` ne convient pas : analyse d'un commit précis corrigée dans d'autres commits, remarques auxquelles l'agent a répondu dans la conversation. Le retour reste lisible avec `get_review_feedback`. Refusé tant que la revue n'est pas soumise ; un nouveau marquage remplace le précédent. |
200
+ | `mark_feedback_processed` | Marque traité le retour soumis, avec la date et une `note` facultative (« corrigé par 12785d6e3 et 1ff5baaad », « réponses données en conversation »). Sert quand `refresh_analysis` ne convient pas : analyse d'un commit précis corrigée dans d'autres commits, remarques auxquelles l'agent a répondu dans la conversation. Le retour reste lisible avec `get_review_feedback`. Refusé tant que la revue n'est pas soumise ; un nouveau marquage remplace le précédent. Le relecteur peut aussi marquer un retour traité depuis l'accueil de la GUI ; le traitement enregistre son auteur, et `get_review_feedback` l'indique (« by the agent » ou « by the reviewer »). |
201
201
 
202
202
  Le retour se lit **avant** `refresh_analysis`, qui le remplace par une revue vierge : le recalcul vaut traitement, puisqu'il relance la revue.
203
203
 
204
- Une revue soumise est **à traiter** jusqu'à `mark_feedback_processed` ou `refresh_analysis`. Dès que le relecteur la modifie (remarque ajoutée, modifiée ou supprimée, constat ignoré ou réactivé), elle repasse en attente, qu'elle ait été traitée ou non : `get_review_feedback` n'en donne plus que l'avancement, et l'agent n'agit pas sur un retour en cours de modification. La version soumise est conservée, avec son traitement s'il a eu lieu ; à la nouvelle soumission, la revue est de nouveau à traiter et `get_review_feedback` montre ce qui a changé. Changer la sélection ou la décision se fait en soumettant à nouveau, avec le même effet.
204
+ Une revue soumise est **à traiter** jusqu'à `mark_feedback_processed` ou `refresh_analysis`, sauf une revue approuvée, qui n'a rien à traiter dès sa soumission, qu'elle retienne des points ou non. Dès que le relecteur la modifie (remarque ajoutée, modifiée ou supprimée, constat ignoré ou réactivé), elle repasse en attente, qu'elle ait été traitée ou non : `get_review_feedback` n'en donne plus que l'avancement, et l'agent n'agit pas sur un retour en cours de modification. La version soumise est conservée, avec son traitement s'il a eu lieu ; à la nouvelle soumission, la revue est de nouveau à traiter et `get_review_feedback` montre ce qui a changé. Changer la sélection ou la décision se fait en soumettant à nouveau, avec le même effet.
205
205
 
206
206
  ### GUI
207
207
 
@@ -242,7 +242,7 @@ Thème sombre. La barre supérieure porte la marque (un clic ramène à l'accuei
242
242
 
243
243
  ### Accueil (`#/`)
244
244
 
245
- Les analyses sont rangées par projet, dans des sections repliables. L'en-tête d'un projet compte ses analyses par état de revue. Chaque analyse est une carte séparée des autres. Son titre s'affiche en entier sur toute la largeur et passe à la ligne s'il le faut. Dessous, sur une seconde ligne : ses refs, la date de sa dernière modification, la progression des fichiers revus, ses constats ouverts par gravité (exigences manquantes comprises, ce que dit l'infobulle) et son état de revue : « Non commencée », « En cours », « Fichiers revus » ou « Soumise », suivi de la décision, puis « Traitée » une fois que l'agent a marqué le retour traité (date et note en infobulle) ; la pastille disparaît dès que le relecteur modifie la revue, qui repasse en cours. Le filtre « À relire » (par défaut) masque les revues soumises, « Toutes » les montre. L'accueil n'ouvre aucune analyse d'office ; sans analyse, il explique que l'agent les crée avec `create_analysis`.
245
+ Les analyses sont rangées par projet, dans des sections repliables. L'en-tête d'un projet compte ses analyses par état de revue. Chaque analyse est une carte séparée des autres. Son titre s'affiche en entier sur toute la largeur et passe à la ligne s'il le faut. Dessous, sur une seconde ligne : ses refs, la date de sa dernière modification, la progression des fichiers revus, ses constats ouverts par gravité (exigences manquantes comprises, ce que dit l'infobulle) et son état de revue : « Non commencée », « En cours », « Fichiers revus » ou « Soumise », suivi de la décision, puis « Traitée » une fois que l'agent a marqué le retour traité (date et note en infobulle) ; la pastille disparaît dès que le relecteur modifie la revue, qui repasse en cours. Quand le retour est marqué traité, une ligne sous les références donne la note, l'auteur du marquage (l'agent, ou le relecteur) et la date. Dans le filtre « À traiter par l'agent », chaque carte porte à droite du titre un bouton « Marquer comme traité » : il ouvre une petite fenêtre avec une note facultative (Entrée confirme, Échap referme), et la revue passe dans « Traitées par l'agent », comme avec `mark_feedback_processed`. Le reste de la carte mène à l'analyse. Une colonne à gauche filtre la liste, chaque filtre avec son icône et son nombre d'analyses : « À relire » (par défaut, revues pas encore soumises), « À traiter par l'agent » (retour soumis que l'agent n'a pas encore traité), « Traitées par l'agent » (retour traité, corrections à vérifier ; ce filtre ressort en sarcelle tant qu'il n'est pas vide), « Approuvées », et « Toutes ». Les quatre premiers se partagent les analyses sans se recouvrir. Sous 900 px de large, les filtres passent au-dessus de la liste. L'accueil n'ouvre aucune analyse d'office ; sans analyse, il explique que l'agent les crée avec `create_analysis`.
246
246
 
247
247
  ### Écran Feature (`#/<id>`)
248
248
 
@@ -259,7 +259,8 @@ L'ajustement à la fenêtre ne descend jamais sous l'échelle **0,85** : en dess
259
259
 
260
260
  ### Écran Revue (`#/<id>/review/<chemin>`)
261
261
 
262
- - **Panneau des fichiers** — progression, fichiers groupés par dossier, état revu, pastilles des constats ouverts par gravité et, en bout de ligne, la lettre du statut git à la manière de VS Code : `M` modifié, `A` ajouté, `D` supprimé, `R` renommé, `U` non suivi (libellé complet en infobulle). Un nom trop long est tronqué au milieu (`reminder-sch…uler.ts`), l'extension reste visible et le chemin complet est en infobulle. Chaque pastille est un lien : elle ouvre le fichier sur son premier constat ouvert de cette gravité (`?line=N`) ; son infobulle le dit (« 3 constats mineurs ouverts — aller au premier »). Sa largeur se règle en glissant son bord droit (entre 200 et 600 px ; flèches gauche et droite quand la poignée a le focus, double-clic pour revenir à 280 px) et se conserve dans le navigateur d'un rechargement à l'autre. Le bouton en tête de l'en-tête du fichier masque ou réaffiche le panneau, pour toute la session : le diff s'élargit vers la gauche, la colonne de droite garde sa largeur. Il reste affiché quand le chemin demandé ne fait pas partie de l'analyse.
262
+ - **Progression** — dans la barre supérieure, à gauche de « Terminer la revue » : « 3/7 revus » et une barre étroite, visibles quel que soit le défilement et même panneau des fichiers masqué.
263
+ - **Panneau des fichiers** — nombre de fichiers, fichiers groupés par dossier, état revu, pastilles des constats ouverts par gravité et, en bout de ligne, la lettre du statut git à la manière de VS Code : `M` modifié, `A` ajouté, `D` supprimé, `R` renommé, `U` non suivi (libellé complet en infobulle). Un nom trop long est tronqué au milieu (`reminder-sch…uler.ts`), l'extension reste visible et le chemin complet est en infobulle. Chaque pastille est un lien : elle ouvre le fichier sur son premier constat ouvert de cette gravité (`?line=N`) ; son infobulle le dit (« 3 constats mineurs ouverts — aller au premier »). Sa largeur se règle en glissant son bord droit (entre 200 et 600 px ; flèches gauche et droite quand la poignée a le focus, double-clic pour revenir à 280 px) et se conserve dans le navigateur d'un rechargement à l'autre. Le bouton en tête de l'en-tête du fichier masque ou réaffiche le panneau, pour toute la session : le diff s'élargit vers la gauche, la colonne de droite garde sa largeur. Il reste affiché quand le chemin demandé ne fait pas partie de l'analyse.
263
264
  - **En-tête du fichier** — bascule du panneau des fichiers, chemin, `+`/`−`, statut, fichier précédent et suivant, « Remarque sur le fichier », « Marquer comme revu ».
264
265
  - **Diff** — code coloré selon le langage du fichier, déduit de son extension ou de son nom (grammaires de [Shiki](https://shiki.style), chargées à la demande) : le côté nouveau à partir du contenu complet du fichier quand le snapshot le conserve, sinon bloc par bloc ; les lignes supprimées bloc par bloc ; chaque correctif proposé séparément ; les lignes barrées par un correctif restent sans couleur. Un fichier dont le langage n'est pas reconnu s'affiche sans coloration ; une grammaire qui ne se charge pas est signalée dans la barre du diff. Numéros du côté nouveau et repère vertical à la couleur de la gravité le long des lignes visées par un constat. La barre du diff porte trois réglages, communs à tous les fichiers et conservés dans le navigateur d'un rechargement à l'autre : chacun est un bouton compact qui affiche sa valeur courante, et un clic ouvre la liste de ses choix, chacun avec son icône et une description d'une ligne (un clic dehors, Échap ou un défilement la referme). « Afficher » choisit entre « Modifications » (les blocs du diff git, entourés de leurs lignes de contexte) et « Fichier entier » (tout le fichier en un seul bloc : les lignes inchangées, tirées du contenu conservé, s'intercalent entre les modifications, ce qui donne à lire en entier une fonction modifiée en son milieu ; les deux autres sélecteurs s'y appliquent de la même façon, un correctif dont les lignes chevauchaient deux blocs s'y affiche, et « Copier une demande d'explication » porte alors sur tout le fichier). En « Fichier entier », une mini-carte de 22 px longe le bord droit du diff et reste à l'écran pendant le défilement : toute sa hauteur représente tout le fichier affiché. Sa piste de gauche place les lignes ajoutées (vert), supprimées (rouge), supprimées et ajoutées en vis-à-vis en vue « Côte à côte » (moitié rouge, moitié verte) ou seulement réindentées (violet) ; sa piste de droite porte un point par constat, à la couleur de sa gravité (atténué s'il n'est plus ouvert), et un point par ligne portant une remarque. Un cadre bleuté suit la portion visible. Le survol d'un repère en donne la description (« 12 lignes ajoutées · lignes 120 à 131 », « Constat 3 · Majeur » et son titre) ; un clic ou un glissé sur la carte fait défiler le diff jusqu'à l'endroit visé. « Fichier entier » est inactif pour un fichier dont le contenu n'est pas conservé ; la liste en donne la raison à la place de sa description. « Code » choisit entre « Unifié » (une seule colonne : lignes supprimées sur fond rouge au-dessus des lignes ajoutées sur fond vert), « Côte à côte » (ancien code à gauche avec ses numéros du côté ancien, nouveau code à droite : lignes de contexte des deux côtés, lignes supprimées et ajoutées appariées ligne à ligne, case hachurée en face d'une ligne sans vis-à-vis ; remarques, repères et lignes proposées par un correctif sont du côté droit, le bandeau d'un correctif sur toute la largeur) et « Nouveau code » (le code du côté nouveau seul, dans les mêmes blocs : les lignes supprimées ne sont pas affichées et les lignes ajoutées portent un fin filet vert à la place de leur fond et de leur marque ; une explication de code supprimé s'aligne alors sur la ligne voisine du bloc). « Correctifs » choisit entre « Sans correctif », « Avant / après » et « Code corrigé ». Le correctif proposé s'affiche à l'endroit où il s'applique : bandeau « Correctif proposé par l'agent — remplace les lignes X à Y » avec le numéro du constat et « Masquer le correctif », lignes visées barrées sur fond ambre, lignes proposées juste en dessous sur fond violet (marque « › », sans numéro). Le correctif est affiché d'office pour un constat ouvert ; masqué, ou pour un constat sans correctif, une pastille numérotée marque la première ligne visée. Un filet sarcelle longe les lignes décrites par une explication. En « Modifications », quand le contenu du fichier est conservé, l'en-tête de chaque bloc porte un réglage « −5 · N lignes · +5 » : « +5 » ajoute cinq lignes de contexte au-dessus et cinq en dessous du bloc, « −5 » les retire, et le compteur donne le nombre de lignes de contexte au-dessus de la première modification. Les lignes ajoutées s'insèrent sous l'en-tête et après le bloc, si bien que le réglage ne bouge pas à l'écran ; elles se distinguent par un fond plus sombre et un filet gris (« Contexte ajouté » dans la légende). Les lignes entre deux blocs vont d'abord au bloc du dessus ; quand plus aucune ligne masquée ne les sépare, les deux blocs n'en forment plus qu'un, sous un seul en-tête dont « +5 » et « −5 » s'appliquent à chacun des blocs réunis. « +5 » s'éteint quand le bloc atteint les bords du fichier. Le contexte choisi est propre à chaque fichier et dure le temps de la session. L'en-tête de chaque bloc porte aussi « Copier une demande d'explication » (icône du robot) : le bouton copie un prompt à coller dans la conversation de l'agent, qui situe le bloc (analyse, dépôt et refs, fichier, lignes des deux côtés, code tel que figé) et lui demande de l'expliquer en enregistrant ses explications avec `add_explanations`, ou `update_explanation` si une explication couvre déjà ces lignes. Deux correctifs dont les lignes se recouvrent ne s'affichent pas ensemble. Le fond ambre des lignes visées se distingue du rouge des lignes `−` supprimées par la feature. Une légende suit le diff, limitée aux fonds que la vue et le mode affichent : « Ajouté par la feature » (fond ou filet selon la vue), « Supprimé par la feature » (Unifié et Côte à côte), « Lignes remplacées par le correctif » (Avant / après), « Correctif proposé » ou « Correctif appliqué », « Code décrit par une explication ». Quand une ligne supprimée et la ligne ajoutée qui la remplace se ressemblent (la moitié de leurs mots au moins en commun), seules leurs parties modifiées sont surlignées, en rouge sur l'ancienne ligne et en vert sur la nouvelle, sans fond sur le reste de la ligne ; dans chaque suite de lignes supprimées puis ajoutées, la i-ème ligne supprimée est comparée à la i-ème ligne ajoutée, mot à mot. Une ligne sans correspondante proche garde son fond entier (« Partie modifiée de la ligne » dans la légende). Un clic sur un numéro de ligne ouvre la saisie d'une remarque sur cette ligne. Les exigences manquantes sans emplacement s'affichent en bandeau au-dessus du diff de chaque fichier ; le bandeau « N exigences manquantes » se replie, et reste replié pour la session.
265
266
  - **Affichage des correctifs** — le sélecteur « Correctifs », au-dessus du diff, propose trois modes, conservés d'un fichier à l'autre et d'un rechargement à l'autre :
@@ -1,4 +1,10 @@
1
1
  import type { Analysis, Review, ReviewProgress, SubmissionRecord } from "./schemas/analysis.schema.js";
2
+ /**
3
+ * Vrai quand le retour d'une revue attend l'agent : revue soumise, non marquée
4
+ * traitée, et dont la décision n'est pas une approbation. Une revue approuvée
5
+ * vaut traitée dès sa soumission.
6
+ */
7
+ export declare function needsProcessing(review: Pick<Review, "state" | "decision" | "processed">): boolean;
2
8
  /**
3
9
  * Avancement de la revue d'une analyse :
4
10
  * - `submitted` : revue soumise, avec sa décision et son traitement par l'agent ;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Vrai quand le retour d'une revue attend l'agent : revue soumise, non marquée
3
+ * traitée, et dont la décision n'est pas une approbation. Une revue approuvée
4
+ * vaut traitée dès sa soumission.
5
+ */
6
+ export function needsProcessing(review) {
7
+ return review.state === "submitted" && review.decision !== "approve" && review.processed === null;
8
+ }
1
9
  /**
2
10
  * Avancement de la revue d'une analyse :
3
11
  * - `submitted` : revue soumise, avec sa décision et son traitement par l'agent ;
@@ -10,10 +18,11 @@ export function reviewProgress(analysis) {
10
18
  const totalFiles = analysis.files.length;
11
19
  const reviewedFiles = analysis.files.filter((file) => file.reviewed).length;
12
20
  if (analysis.review.state === "submitted") {
13
- return { state: "submitted", reviewedFiles, totalFiles, decision: analysis.review.decision, processed: analysis.review.processed };
21
+ const { decision, processed } = analysis.review;
22
+ return { state: "submitted", reviewedFiles, totalFiles, decision, processed, toProcess: needsProcessing(analysis.review) };
14
23
  }
15
24
  const state = reviewedFiles === 0 ? "not_started" : reviewedFiles === totalFiles ? "files_reviewed" : "in_progress";
16
- return { state, reviewedFiles, totalFiles, decision: null, processed: null };
25
+ return { state, reviewedFiles, totalFiles, decision: null, processed: null, toProcess: false };
17
26
  }
18
27
  /** Copie autonome de la revue soumise de `analysis` : décision, date, traitement, points retenus et points écartés. */
19
28
  export function recordSubmission(analysis) {
@@ -191,10 +191,17 @@ export declare const DiagramSchema: z.ZodObject<{
191
191
  }, z.core.$strip>>;
192
192
  updatedAt: z.ZodString;
193
193
  }, z.core.$strip>;
194
- /** Traitement d'un retour par l'agent : date et note facultative (commits, réponses données…). */
194
+ /**
195
+ * Traitement d'un retour : date, note facultative (commits, réponses données…) et
196
+ * auteur du marquage — l'agent, ou le relecteur depuis la GUI.
197
+ */
195
198
  export declare const ProcessingSchema: z.ZodObject<{
196
199
  at: z.ZodString;
197
200
  note: z.ZodNullable<z.ZodString>;
201
+ by: z.ZodEnum<{
202
+ agent: "agent";
203
+ user: "user";
204
+ }>;
198
205
  }, z.core.$strip>;
199
206
  /**
200
207
  * Dernière version soumise d'une revue, conservée quand le relecteur la modifie
@@ -211,6 +218,10 @@ export declare const SubmissionRecordSchema: z.ZodObject<{
211
218
  processed: z.ZodNullable<z.ZodObject<{
212
219
  at: z.ZodString;
213
220
  note: z.ZodNullable<z.ZodString>;
221
+ by: z.ZodEnum<{
222
+ agent: "agent";
223
+ user: "user";
224
+ }>;
214
225
  }, z.core.$strip>>;
215
226
  findings: z.ZodArray<z.ZodObject<{
216
227
  id: z.ZodString;
@@ -244,6 +255,10 @@ export declare const ReviewSchema: z.ZodObject<{
244
255
  processed: z.ZodNullable<z.ZodObject<{
245
256
  at: z.ZodString;
246
257
  note: z.ZodNullable<z.ZodString>;
258
+ by: z.ZodEnum<{
259
+ agent: "agent";
260
+ user: "user";
261
+ }>;
247
262
  }, z.core.$strip>>;
248
263
  previousSubmission: z.ZodNullable<z.ZodObject<{
249
264
  decision: z.ZodEnum<{
@@ -255,6 +270,10 @@ export declare const ReviewSchema: z.ZodObject<{
255
270
  processed: z.ZodNullable<z.ZodObject<{
256
271
  at: z.ZodString;
257
272
  note: z.ZodNullable<z.ZodString>;
273
+ by: z.ZodEnum<{
274
+ agent: "agent";
275
+ user: "user";
276
+ }>;
258
277
  }, z.core.$strip>>;
259
278
  findings: z.ZodArray<z.ZodObject<{
260
279
  id: z.ZodString;
@@ -440,6 +459,10 @@ export declare const AnalysisSchema: z.ZodObject<{
440
459
  processed: z.ZodNullable<z.ZodObject<{
441
460
  at: z.ZodString;
442
461
  note: z.ZodNullable<z.ZodString>;
462
+ by: z.ZodEnum<{
463
+ agent: "agent";
464
+ user: "user";
465
+ }>;
443
466
  }, z.core.$strip>>;
444
467
  previousSubmission: z.ZodNullable<z.ZodObject<{
445
468
  decision: z.ZodEnum<{
@@ -451,6 +474,10 @@ export declare const AnalysisSchema: z.ZodObject<{
451
474
  processed: z.ZodNullable<z.ZodObject<{
452
475
  at: z.ZodString;
453
476
  note: z.ZodNullable<z.ZodString>;
477
+ by: z.ZodEnum<{
478
+ agent: "agent";
479
+ user: "user";
480
+ }>;
454
481
  }, z.core.$strip>>;
455
482
  findings: z.ZodArray<z.ZodObject<{
456
483
  id: z.ZodString;
@@ -516,8 +543,10 @@ export interface ReviewProgress {
516
543
  totalFiles: number;
517
544
  /** Décision de la revue soumise ; null tant qu'elle ne l'est pas. */
518
545
  decision: ReviewDecision | null;
519
- /** Traitement de la revue soumise par l'agent ; null tant qu'il est à faire ou que la revue n'est pas soumise. */
546
+ /** Traitement de la revue soumise par l'agent ; null tant qu'il ne l'a pas marquée ou que la revue n'est pas soumise. */
520
547
  processed: Processing | null;
548
+ /** Revue soumise dont le retour attend l'agent (voir `needsProcessing`). */
549
+ toProcess: boolean;
521
550
  }
522
551
  /** Vue légère d'une analyse, pour les listes. */
523
552
  export interface AnalysisSummary {
@@ -122,10 +122,14 @@ export const DiagramSchema = z.object({
122
122
  links: z.array(DiagramLinkSchema),
123
123
  updatedAt: IsoDateSchema,
124
124
  });
125
- /** Traitement d'un retour par l'agent : date et note facultative (commits, réponses données…). */
125
+ /**
126
+ * Traitement d'un retour : date, note facultative (commits, réponses données…) et
127
+ * auteur du marquage — l'agent, ou le relecteur depuis la GUI.
128
+ */
126
129
  export const ProcessingSchema = z.object({
127
130
  at: IsoDateSchema,
128
131
  note: z.string().min(1).nullable(),
132
+ by: z.enum(EDITORS),
129
133
  });
130
134
  /**
131
135
  * Dernière version soumise d'une revue, conservée quand le relecteur la modifie
@@ -40,7 +40,13 @@ export declare const SubmitReviewBodySchema: z.ZodObject<{
40
40
  noteIds: z.ZodArray<z.ZodString>;
41
41
  baseRevision: z.ZodNumber;
42
42
  }, z.core.$strict>;
43
+ /** Marquage du retour soumis comme traité par le relecteur, avec une note facultative. */
44
+ export declare const MarkProcessedBodySchema: z.ZodObject<{
45
+ note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
46
+ baseRevision: z.ZodNumber;
47
+ }, z.core.$strict>;
43
48
  export type SetFileReviewedBody = z.infer<typeof SetFileReviewedBodySchema>;
49
+ export type MarkProcessedBody = z.infer<typeof MarkProcessedBodySchema>;
44
50
  export type SetFindingStatusBody = z.infer<typeof SetFindingStatusBodySchema>;
45
51
  export type AddNoteBody = z.infer<typeof AddNoteBodySchema>;
46
52
  export type UpdateNoteBody = z.infer<typeof UpdateNoteBodySchema>;
@@ -108,3 +114,10 @@ export declare const SubmitReviewRequestSchema: z.ZodObject<{
108
114
  baseRevision: z.ZodNumber;
109
115
  }, z.core.$strict>;
110
116
  }, z.core.$strict>;
117
+ export declare const MarkProcessedRequestSchema: z.ZodObject<{
118
+ analysisId: z.ZodString;
119
+ body: z.ZodObject<{
120
+ note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
121
+ baseRevision: z.ZodNumber;
122
+ }, z.core.$strict>;
123
+ }, z.core.$strict>;
@@ -39,6 +39,11 @@ export const SubmitReviewBodySchema = z.strictObject({
39
39
  noteIds: z.array(z.string().min(1)),
40
40
  baseRevision: BaseRevisionSchema,
41
41
  });
42
+ /** Marquage du retour soumis comme traité par le relecteur, avec une note facultative. */
43
+ export const MarkProcessedBodySchema = z.strictObject({
44
+ note: z.string().trim().min(1, "the note is empty").nullable().optional(),
45
+ baseRevision: BaseRevisionSchema,
46
+ });
42
47
  /** Données IPC des requêtes de la GUI qui visent une analyse. */
43
48
  export const AnalysisRequestSchema = z.strictObject({ analysisId: z.string().min(1) });
44
49
  export const SetFileReviewedRequestSchema = AnalysisRequestSchema.extend({ body: SetFileReviewedBodySchema });
@@ -56,3 +61,4 @@ export const DeleteNoteRequestSchema = AnalysisRequestSchema.extend({
56
61
  body: DeleteNoteBodySchema,
57
62
  });
58
63
  export const SubmitReviewRequestSchema = AnalysisRequestSchema.extend({ body: SubmitReviewBodySchema });
64
+ export const MarkProcessedRequestSchema = AnalysisRequestSchema.extend({ body: MarkProcessedBodySchema });
@@ -40,5 +40,5 @@ export type ChangesResponse = {
40
40
  events: IPCMessage[];
41
41
  };
42
42
  /** Requêtes du GUI worker au maître ; chacune reçoit une réponse de même `correlationId`. */
43
- export declare const GUI_REQUEST_TYPES: readonly ["GET_CONFIG", "LIST_ANALYSES", "GET_ANALYSIS", "GET_DIFF", "SET_FILE_REVIEWED", "SET_FINDING_STATUS", "ADD_NOTE", "UPDATE_NOTE", "DELETE_NOTE", "SUBMIT_REVIEW"];
43
+ export declare const GUI_REQUEST_TYPES: readonly ["GET_CONFIG", "LIST_ANALYSES", "GET_ANALYSIS", "GET_DIFF", "SET_FILE_REVIEWED", "SET_FINDING_STATUS", "ADD_NOTE", "UPDATE_NOTE", "DELETE_NOTE", "SUBMIT_REVIEW", "MARK_PROCESSED"];
44
44
  export type GuiRequestType = (typeof GUI_REQUEST_TYPES)[number];
@@ -28,4 +28,5 @@ export const GUI_REQUEST_TYPES = [
28
28
  "UPDATE_NOTE",
29
29
  "DELETE_NOTE",
30
30
  "SUBMIT_REVIEW",
31
+ "MARK_PROCESSED",
31
32
  ];
@@ -21,6 +21,11 @@ export declare const addNoteFindingIds: MigrationStep;
21
21
  * avant ce format est à traiter, sans version précédente.
22
22
  */
23
23
  export declare const addReviewProcessing: MigrationStep;
24
+ /**
25
+ * Ajoute `by: "agent"` aux traitements enregistrés sans auteur, dans la revue et
26
+ * dans sa version soumise précédente : seul l'agent pouvait alors les poser.
27
+ */
28
+ export declare const addProcessingAuthor: MigrationStep;
24
29
  /** Étapes appliquées aux analyses (`analyses/<id>.json`), dans l'ordre. */
25
30
  export declare const ANALYSIS_MIGRATIONS: readonly MigrationStep[];
26
31
  /** Étapes appliquées aux snapshots de diff (`diffs/<id>.json`), dans l'ordre. */
@@ -49,8 +49,45 @@ export const addReviewProcessing = (doc) => {
49
49
  return null;
50
50
  return { ...doc, review: { ...raw, processed: raw.processed ?? null, previousSubmission: raw.previousSubmission ?? null } };
51
51
  };
52
+ /** `processed` augmenté de `by: "agent"` quand il n'a pas d'auteur ; null quand il n'y a rien à changer. */
53
+ function withProcessingAuthor(processed) {
54
+ if (!processed || typeof processed !== "object" || Array.isArray(processed))
55
+ return null;
56
+ const raw = processed;
57
+ return raw.by !== undefined ? null : { ...raw, by: "agent" };
58
+ }
59
+ /**
60
+ * Ajoute `by: "agent"` aux traitements enregistrés sans auteur, dans la revue et
61
+ * dans sa version soumise précédente : seul l'agent pouvait alors les poser.
62
+ */
63
+ export const addProcessingAuthor = (doc) => {
64
+ const review = doc.review;
65
+ if (!review || typeof review !== "object" || Array.isArray(review))
66
+ return null;
67
+ const raw = review;
68
+ const processed = withProcessingAuthor(raw.processed);
69
+ const previous = raw.previousSubmission;
70
+ const previousProcessed = previous && typeof previous === "object" ? withProcessingAuthor(previous.processed) : null;
71
+ if (processed === null && previousProcessed === null)
72
+ return null;
73
+ return {
74
+ ...doc,
75
+ review: {
76
+ ...raw,
77
+ ...(processed === null ? {} : { processed }),
78
+ ...(previousProcessed === null ? {} : { previousSubmission: { ...previous, processed: previousProcessed } }),
79
+ },
80
+ };
81
+ };
52
82
  /** Étapes appliquées aux analyses (`analyses/<id>.json`), dans l'ordre. */
53
- export const ANALYSIS_MIGRATIONS = [addProject, addOverview, addExplanations, addNoteFindingIds, addReviewProcessing];
83
+ export const ANALYSIS_MIGRATIONS = [
84
+ addProject,
85
+ addOverview,
86
+ addExplanations,
87
+ addNoteFindingIds,
88
+ addReviewProcessing,
89
+ addProcessingAuthor,
90
+ ];
54
91
  /** Étapes appliquées aux snapshots de diff (`diffs/<id>.json`), dans l'ordre. */
55
92
  export const SNAPSHOT_MIGRATIONS = [];
56
93
  /** Applique `steps` à la suite. Renvoie null si aucune étape n'a modifié le document. */
@@ -1,6 +1,6 @@
1
- import type { Analysis } from "../../shared/schemas/analysis.schema.js";
2
- import type { AddNoteBody, DeleteNoteBody, SetFileReviewedBody, SetFindingStatusBody, SubmitReviewBody, UpdateNoteBody } from "../../shared/schemas/api.schema.js";
3
- import type { AnalysisStore } from "./analysis-store.js";
1
+ import type { Analysis, Editor } from "../../shared/schemas/analysis.schema.js";
2
+ import type { AddNoteBody, DeleteNoteBody, MarkProcessedBody, SetFileReviewedBody, SetFindingStatusBody, SubmitReviewBody, UpdateNoteBody } from "../../shared/schemas/api.schema.js";
3
+ import type { AnalysisStore, MutateOptions } from "./analysis-store.js";
4
4
  /**
5
5
  * Modifications faites par le relecteur depuis la GUI. Chacune passe par
6
6
  * `store.mutate(id, "user", …, { baseRevision })` et renvoie l'analyse écrite.
@@ -25,6 +25,19 @@ export declare function addNote(store: AnalysisStore, analysisId: string, body:
25
25
  export declare function updateNote(store: AnalysisStore, analysisId: string, noteId: string, body: UpdateNoteBody): Analysis;
26
26
  /** Supprime une remarque et la retire de la sélection de la revue. */
27
27
  export declare function deleteNote(store: AnalysisStore, analysisId: string, noteId: string, body: DeleteNoteBody): Analysis;
28
+ /** Analyse marquée traitée, et date du marquage qu'elle remplace s'il y en avait un. */
29
+ export interface MarkProcessedResult {
30
+ analysis: Analysis;
31
+ replaced: string | null;
32
+ }
33
+ /**
34
+ * Marque traité le retour soumis d'une analyse : date, note facultative et
35
+ * auteur (`agent` par l'outil MCP, `user` depuis la GUI). Refusé tant que la
36
+ * revue n'est pas soumise ; un nouveau marquage remplace le précédent.
37
+ */
38
+ export declare function markFeedbackProcessed(store: AnalysisStore, analysisId: string, editor: Editor, note: string | null, options?: MutateOptions): MarkProcessedResult;
39
+ /** Marque traité, depuis la GUI, le retour soumis d'une analyse. */
40
+ export declare function markProcessedByReviewer(store: AnalysisStore, analysisId: string, body: MarkProcessedBody): Analysis;
28
41
  /**
29
42
  * Soumet la revue : décision, points retenus et prompt calculé ici. La revue
30
43
  * soumise est à traiter par l'agent. Une nouvelle soumission remplace la
@@ -86,6 +86,31 @@ export function deleteNote(store, analysisId, noteId, body) {
86
86
  draft.review.selectedNoteIds = draft.review.selectedNoteIds.filter((id) => id !== noteId);
87
87
  }, { baseRevision: body.baseRevision });
88
88
  }
89
+ /**
90
+ * Marque traité le retour soumis d'une analyse : date, note facultative et
91
+ * auteur (`agent` par l'outil MCP, `user` depuis la GUI). Refusé tant que la
92
+ * revue n'est pas soumise ; un nouveau marquage remplace le précédent.
93
+ */
94
+ export function markFeedbackProcessed(store, analysisId, editor, note, options = {}) {
95
+ let replaced = null;
96
+ const analysis = store.mutate(analysisId, editor, (draft) => {
97
+ const review = draft.review;
98
+ if (review.state !== "submitted") {
99
+ const reopened = review.previousSubmission;
100
+ throw new AnalysisError(reopened
101
+ ? `The review of analysis "${analysisId}" is not submitted: the reviewer reopened the feedback submitted at ${reopened.submittedAt} and is changing it. ` +
102
+ `Wait until it is submitted again, then read it with get_review_feedback.`
103
+ : `The review of analysis "${analysisId}" is not submitted yet: there is no feedback to mark processed.`);
104
+ }
105
+ replaced = review.processed?.at ?? null;
106
+ review.processed = { at: new Date().toISOString(), note, by: editor };
107
+ }, options);
108
+ return { analysis, replaced };
109
+ }
110
+ /** Marque traité, depuis la GUI, le retour soumis d'une analyse. */
111
+ export function markProcessedByReviewer(store, analysisId, body) {
112
+ return markFeedbackProcessed(store, analysisId, "user", body.note ?? null, { baseRevision: body.baseRevision }).analysis;
113
+ }
89
114
  function assertKnownIds(label, ids, known) {
90
115
  const knownSet = new Set(known);
91
116
  const unknown = ids.filter((id) => !knownSet.has(id));
@@ -7,7 +7,7 @@ import fs from "fs";
7
7
  import { fileURLToPath } from "url";
8
8
  import { randomUUID } from "crypto";
9
9
  import { IPCMessageSchema, PUSHED_EVENT_TYPES } from "../shared/schemas/ipc.schema.js";
10
- import { AddNoteBodySchema, DeleteNoteBodySchema, SetFileReviewedBodySchema, SetFindingStatusBodySchema, SubmitReviewBodySchema, UpdateNoteBodySchema, } from "../shared/schemas/api.schema.js";
10
+ import { AddNoteBodySchema, DeleteNoteBodySchema, MarkProcessedBodySchema, SetFileReviewedBodySchema, SetFindingStatusBodySchema, SubmitReviewBodySchema, UpdateNoteBodySchema, } from "../shared/schemas/api.schema.js";
11
11
  import { resolveLogDir } from "./core/log-dir.js";
12
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
13
  function findProjectRoot(startDir) {
@@ -276,6 +276,12 @@ app.post("/api/analyses/:id/review", async (c) => {
276
276
  return read.response;
277
277
  return forward(c, "SUBMIT_REVIEW", { analysisId: c.req.param("id"), body: read.body });
278
278
  });
279
+ app.post("/api/analyses/:id/review/processed", async (c) => {
280
+ const read = await readBody(c, MarkProcessedBodySchema);
281
+ if ("response" in read)
282
+ return read.response;
283
+ return forward(c, "MARK_PROCESSED", { analysisId: c.req.param("id"), body: read.body });
284
+ });
279
285
  /** Route d'API inconnue : 404 explicite plutôt que la page de la SPA. */
280
286
  app.all("/api/*", (c) => c.json({ error: `Unknown API route: ${c.req.method} ${c.req.path}` }, 404));
281
287
  // Fichiers statiques de la SPA
package/dist/src/index.js CHANGED
@@ -16,9 +16,9 @@ import { DEFAULT_HEAD_REF, localRepo } from "./core/git.js";
16
16
  import { agentRepo } from "./core/agent-repo.js";
17
17
  import { EXEC_ARG, handleAgentExec } from "./core/gateway-exec.js";
18
18
  import { resolveRole } from "./core/role.js";
19
- import { addNote, deleteNote, setFileReviewed, setFindingStatus, submitReview, updateNote } from "./core/review-actions.js";
19
+ import { addNote, deleteNote, markProcessedByReviewer, setFileReviewed, setFindingStatus, submitReview, updateNote, } from "./core/review-actions.js";
20
20
  import { GUI_REQUEST_TYPES, IPCMessageSchema } from "../shared/schemas/ipc.schema.js";
21
- import { AddNoteRequestSchema, AnalysisRequestSchema, DeleteNoteRequestSchema, SetFileReviewedRequestSchema, SetFindingStatusRequestSchema, SubmitReviewRequestSchema, UpdateNoteRequestSchema, } from "../shared/schemas/api.schema.js";
21
+ import { AddNoteRequestSchema, AnalysisRequestSchema, DeleteNoteRequestSchema, SetFileReviewedRequestSchema, SetFindingStatusRequestSchema, SubmitReviewRequestSchema, MarkProcessedRequestSchema, UpdateNoteRequestSchema, } from "../shared/schemas/api.schema.js";
22
22
  import { ANALYSIS_MODES, DIAGRAM_KINDS, EXPLANATION_SIDES, FINDING_KINDS, NODE_SHAPES, NODE_STATUSES, SEVERITIES, } from "../shared/schemas/analysis.schema.js";
23
23
  import { errorResult, textResult } from "./tools/types.js";
24
24
  import { createAnalysis } from "./tools/create-analysis.js";
@@ -104,7 +104,7 @@ Workflow:
104
104
  5. add_findings for every problem, including requirement_gap findings for requested behaviour that is missing.
105
105
  6. set_diagram when a picture helps (impacted layers, process flow, concept tree).
106
106
  7. Tell the user to review the analysis in the GUI (the link is in the tool results).
107
- 8. get_review_feedback to read the decision, the selected points and the prompt. list_analyses with review_state "to_process" lists the submitted reviews you have not handled yet.
107
+ 8. get_review_feedback to read the decision, the selected points and the prompt. list_analyses with review_state "to_process" lists the submitted reviews you have not handled yet; an approved review has nothing to process.
108
108
  9. Fix the code, then refresh_analysis to recompute the diff for a new review round; re-anchor or rewrite the outdated findings and explanations. When the diff must stay as it is (an analysis of one commit, fixes in other commits, answers given in the conversation), call mark_feedback_processed instead, with a note saying how the feedback was handled.
109
109
 
110
110
  Explanation rules:
@@ -223,6 +223,12 @@ function handleGuiRequest(msg) {
223
223
  afterUserMutation(analysis.id);
224
224
  return { analysis };
225
225
  }
226
+ case "MARK_PROCESSED": {
227
+ const request = parseRequest(MarkProcessedRequestSchema, msg.data);
228
+ const analysis = markProcessedByReviewer(store, request.analysisId, request.body);
229
+ afterUserMutation(analysis.id);
230
+ return { analysis };
231
+ }
226
232
  default:
227
233
  throw new Error(`GUI request ${msg.type} has no handler.`);
228
234
  }
@@ -352,7 +358,8 @@ const TOOLS = [
352
358
  {
353
359
  name: "list_analyses",
354
360
  description: "List the analyses grouped by project, with their refs, open findings by severity, reviewed files, diagram count and review state " +
355
- "(not started, in progress, all files reviewed, submitted; a submitted review is pending processing until mark_feedback_processed or refresh_analysis). " +
361
+ "(not started, in progress, all files reviewed, submitted; a submitted review is pending processing until mark_feedback_processed or refresh_analysis, " +
362
+ "except an approved one, which has nothing to process). " +
356
363
  "Start here: when the project already has an analysis of the feature, " +
357
364
  "continue it (update_analysis to rewrite the overview and summary, refresh_analysis after changing the code) instead of creating a duplicate; " +
358
365
  "create a new one with create_analysis for another feature or another diff.",
@@ -363,8 +370,8 @@ const TOOLS = [
363
370
  review_state: {
364
371
  type: "string",
365
372
  enum: [...REVIEW_STATE_FILTERS],
366
- description: "Only list the analyses in this review state. to_process = submitted reviews not marked processed yet: the feedback you still have to handle; " +
367
- "processed = submitted reviews marked processed.",
373
+ description: "Only list the analyses in this review state. to_process = submitted reviews, not approved, not marked processed yet: the feedback you still have to handle; " +
374
+ "processed = submitted reviews marked processed or approved.",
368
375
  },
369
376
  },
370
377
  },
@@ -24,8 +24,9 @@ export declare function indentBlock(text: string, indent: string): string;
24
24
  /** Nom anglais d'un état de revue : `not started`, `in progress`… */
25
25
  export declare function progressStateLabel(state: ReviewProgressState): string;
26
26
  /**
27
- * `in progress (2/7 files reviewed)`, `submitted, decision approve, pending processing (7/7 files reviewed)`
28
- * ou `submitted, decision approve, processed at <date> (7/7 files reviewed)`.
27
+ * `in progress (2/7 files reviewed)`, `submitted, decision request_changes, pending processing (7/7 files reviewed)`,
28
+ * `submitted, decision request_changes, processed at <date> (7/7 files reviewed)`
29
+ * ou `submitted, decision approve, nothing to process (7/7 files reviewed)`.
29
30
  */
30
31
  export declare function progressLabel(progress: ReviewProgress): string;
31
32
  /** Bloc « Overview » : objectif, approche et points d'attention ; une ligne d'invitation quand il n'y en a pas. */
@@ -74,11 +74,16 @@ export function progressStateLabel(state) {
74
74
  return REVIEW_PROGRESS_TEXT[state];
75
75
  }
76
76
  /**
77
- * `in progress (2/7 files reviewed)`, `submitted, decision approve, pending processing (7/7 files reviewed)`
78
- * ou `submitted, decision approve, processed at <date> (7/7 files reviewed)`.
77
+ * `in progress (2/7 files reviewed)`, `submitted, decision request_changes, pending processing (7/7 files reviewed)`,
78
+ * `submitted, decision request_changes, processed at <date> (7/7 files reviewed)`
79
+ * ou `submitted, decision approve, nothing to process (7/7 files reviewed)`.
79
80
  */
80
81
  export function progressLabel(progress) {
81
- const processing = progress.processed === null ? "pending processing" : `processed at ${progress.processed.at}`;
82
+ const processing = progress.toProcess
83
+ ? "pending processing"
84
+ : progress.processed === null
85
+ ? "nothing to process"
86
+ : `processed at ${progress.processed.at}${progress.processed.by === "user" ? " by the reviewer" : ""}`;
82
87
  const decision = progress.state === "submitted" ? `, decision ${progress.decision}, ${processing}` : "";
83
88
  return `${REVIEW_PROGRESS_TEXT[progress.state]}${decision} (${progress.reviewedFiles}/${progress.totalFiles} files reviewed)`;
84
89
  }
@@ -1,16 +1,18 @@
1
- import { hasNoChanges, recordSubmission, reviewProgress, submissionChanges } from "../../shared/review-state.js";
1
+ import { hasNoChanges, needsProcessing, recordSubmission, reviewProgress, submissionChanges } from "../../shared/review-state.js";
2
2
  import { sortBySeverity } from "../../shared/severity.js";
3
3
  import { findingLine, indentBlock, noteLocationLabel, overviewLines, progressLabel } from "./format.js";
4
4
  import { rejectUnknownFields, requireString, textResult } from "./types.js";
5
- /** `processed at <date> (note: …)` ou `not processed`. */
6
- function processingLabel(processed) {
7
- if (processed === null)
8
- return "not processed";
9
- return `processed at ${processed.at}${processed.note === null ? "" : ` (note: ${processed.note})`}`;
5
+ /** `processed at <date> by the agent (note: …)`, `nothing to process (approved)` ou `not processed`. */
6
+ function processingLabel(decision, processed) {
7
+ if (processed !== null) {
8
+ const by = processed.by === "user" ? "the reviewer" : "the agent";
9
+ return `processed at ${processed.at} by ${by}${processed.note === null ? "" : ` (note: ${processed.note})`}`;
10
+ }
11
+ return decision === "approve" ? "nothing to process (approved)" : "not processed";
10
12
  }
11
13
  /** `submitted at <date>, decision <d>, processed at <date>…` : rappel d'une version soumise. */
12
14
  function submissionLabel(record) {
13
- return `submitted at ${record.submittedAt}, decision ${record.decision}, ${processingLabel(record.processed)}`;
15
+ return `submitted at ${record.submittedAt}, decision ${record.decision}, ${processingLabel(record.decision, record.processed)}`;
14
16
  }
15
17
  function recordedNoteLine(note) {
16
18
  return `${note.id} — ${noteLocationLabel(note)}\n${indentBlock(note.text, " ")}`;
@@ -81,7 +83,7 @@ export function getReviewFeedback(store, args) {
81
83
  const lines = [
82
84
  `Review of "${analysis.title}" (${analysis.id}, project "${analysis.project}") submitted at ${review.submittedAt}.`,
83
85
  `Decision: ${review.decision}`,
84
- `Processing: ${review.processed === null ? "to process (call mark_feedback_processed once handled)" : processingLabel(review.processed)}`,
86
+ `Processing: ${needsProcessing(review) ? "to process (call mark_feedback_processed once handled)" : processingLabel(review.decision, review.processed)}`,
85
87
  ];
86
88
  if (review.previousSubmission)
87
89
  lines.push("", ...changeLines(review.previousSubmission, recordSubmission(analysis)));
@@ -2,7 +2,8 @@ import type { AnalysisStore } from "../core/analysis-store.js";
2
2
  import { type Args, type ToolResult } from "./types.js";
3
3
  /**
4
4
  * Filtre `review_state` : un état d'avancement, ou parmi les revues soumises
5
- * celles qui restent à traiter (`to_process`) ou déjà traitées (`processed`).
5
+ * celles qui restent à traiter (`to_process`) ou n'attendent plus l'agent
6
+ * (`processed` : marquées traitées, ou approuvées).
6
7
  */
7
8
  export declare const REVIEW_STATE_FILTERS: readonly ["not_started", "in_progress", "files_reviewed", "submitted", "to_process", "processed"];
8
9
  export type ReviewStateFilter = (typeof REVIEW_STATE_FILTERS)[number];
@@ -3,15 +3,16 @@ import { progressLabel, progressStateLabel, refsLabel, severityCountsLabel } fro
3
3
  import { optionalEnum, optionalString, rejectUnknownFields, textResult } from "./types.js";
4
4
  /**
5
5
  * Filtre `review_state` : un état d'avancement, ou parmi les revues soumises
6
- * celles qui restent à traiter (`to_process`) ou déjà traitées (`processed`).
6
+ * celles qui restent à traiter (`to_process`) ou n'attendent plus l'agent
7
+ * (`processed` : marquées traitées, ou approuvées).
7
8
  */
8
9
  export const REVIEW_STATE_FILTERS = [...REVIEW_PROGRESS_STATES, "to_process", "processed"];
9
10
  function matchesReviewState(summary, filter) {
10
- const { state, processed } = summary.progress;
11
+ const { state, toProcess } = summary.progress;
11
12
  if (filter === "to_process")
12
- return state === "submitted" && processed === null;
13
+ return toProcess;
13
14
  if (filter === "processed")
14
- return state === "submitted" && processed !== null;
15
+ return state === "submitted" && !toProcess;
15
16
  return state === filter;
16
17
  }
17
18
  /** Analyses regroupées par projet, les projets triés par leur analyse la plus récente. */
@@ -34,8 +35,8 @@ function projectCounts(analyses) {
34
35
  return `${analyses.length} analysis(es): ${counts.join(", ")}`;
35
36
  }
36
37
  function analysisLines(summary) {
37
- const { state, processed } = summary.progress;
38
- const feedback = state !== "submitted" ? "" : processed === null ? " — read it with get_review_feedback" : "";
38
+ const { processed, toProcess } = summary.progress;
39
+ const feedback = toProcess ? " — read it with get_review_feedback" : "";
39
40
  const lines = [
40
41
  `- ${summary.id} — "${summary.title}"`,
41
42
  ` ${refsLabel(summary)} | files reviewed ${summary.reviewedCount}/${summary.fileCount} | ` +
@@ -1,8 +1,7 @@
1
1
  import type { AnalysisStore } from "../core/analysis-store.js";
2
2
  import { type Args, type MutationResult } from "./types.js";
3
3
  /**
4
- * Marque traité le retour soumis d'une analyse, avec la date et une note
5
- * facultative. Le retour reste lisible avec get_review_feedback. Refusé tant
6
- * que la revue n'est pas soumise ; un nouveau marquage remplace le précédent.
4
+ * Marque traité, au nom de l'agent, le retour soumis d'une analyse, avec la date
5
+ * et une note facultative. Le retour reste lisible avec get_review_feedback.
7
6
  */
8
7
  export declare function markFeedbackProcessed(store: AnalysisStore, args: Args): MutationResult;