@imenam/mcp-organizer 0.0.0-stage → 1.0.2

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 (165) hide show
  1. package/README.md +548 -3
  2. package/dist/shared/logic/first-account.d.ts +13 -0
  3. package/dist/shared/logic/first-account.js +15 -0
  4. package/dist/shared/logic/labels.d.ts +5 -0
  5. package/dist/shared/logic/labels.js +17 -0
  6. package/dist/shared/logic/positions.d.ts +9 -0
  7. package/dist/shared/logic/positions.js +19 -0
  8. package/dist/shared/logic/spaces.d.ts +13 -0
  9. package/dist/shared/logic/spaces.js +48 -0
  10. package/dist/shared/logic/test-summary.d.ts +28 -0
  11. package/dist/shared/logic/test-summary.js +35 -0
  12. package/dist/shared/schemas/agent-api.schema.d.ts +269 -0
  13. package/dist/shared/schemas/agent-api.schema.js +97 -0
  14. package/dist/shared/schemas/agent-tools.schema.d.ts +818 -0
  15. package/dist/shared/schemas/agent-tools.schema.js +198 -0
  16. package/dist/shared/schemas/api.schema.d.ts +249 -0
  17. package/dist/shared/schemas/api.schema.js +116 -0
  18. package/dist/shared/schemas/entities.schema.d.ts +472 -0
  19. package/dist/shared/schemas/entities.schema.js +262 -0
  20. package/dist/shared/schemas/enums.schema.d.ts +135 -0
  21. package/dist/shared/schemas/enums.schema.js +58 -0
  22. package/dist/shared/schemas/gui-api.schema.d.ts +53 -0
  23. package/dist/shared/schemas/gui-api.schema.js +20 -0
  24. package/dist/shared/schemas/ideas-api.schema.d.ts +81 -0
  25. package/dist/shared/schemas/ideas-api.schema.js +40 -0
  26. package/dist/shared/schemas/notifications-api.schema.d.ts +87 -0
  27. package/dist/shared/schemas/notifications-api.schema.js +58 -0
  28. package/dist/shared/schemas/tasks-api.schema.d.ts +675 -0
  29. package/dist/shared/schemas/tasks-api.schema.js +225 -0
  30. package/dist/shared/schemas/tokens-api.schema.d.ts +106 -0
  31. package/dist/shared/schemas/tokens-api.schema.js +42 -0
  32. package/dist/shared/schemas/transcription-api.schema.d.ts +15 -0
  33. package/dist/shared/schemas/transcription-api.schema.js +12 -0
  34. package/dist/src/agent-api/checklist.d.ts +3 -0
  35. package/dist/src/agent-api/checklist.js +127 -0
  36. package/dist/src/agent-api/ideas.d.ts +3 -0
  37. package/dist/src/agent-api/ideas.js +93 -0
  38. package/dist/src/agent-api/notifications.d.ts +16 -0
  39. package/dist/src/agent-api/notifications.js +91 -0
  40. package/dist/src/agent-api/requests.d.ts +3 -0
  41. package/dist/src/agent-api/requests.js +60 -0
  42. package/dist/src/agent-api/router.d.ts +3 -0
  43. package/dist/src/agent-api/router.js +41 -0
  44. package/dist/src/agent-api/tasks.d.ts +2 -0
  45. package/dist/src/agent-api/tasks.js +290 -0
  46. package/dist/src/agent-api/types.d.ts +41 -0
  47. package/dist/src/agent-api/types.js +14 -0
  48. package/dist/src/cli/backup.d.ts +12 -0
  49. package/dist/src/cli/backup.js +126 -0
  50. package/dist/src/cli/prompt.d.ts +7 -0
  51. package/dist/src/cli/prompt.js +40 -0
  52. package/dist/src/cli/set-user.d.ts +2 -0
  53. package/dist/src/cli/set-user.js +114 -0
  54. package/dist/src/cli/setup-mcp.d.ts +10 -0
  55. package/dist/src/cli/setup-mcp.js +110 -0
  56. package/dist/src/cli/setup.d.ts +39 -0
  57. package/dist/src/cli/setup.js +626 -0
  58. package/dist/src/cli/watch.d.ts +4 -0
  59. package/dist/src/cli/watch.js +62 -0
  60. package/dist/src/core/accounts.d.ts +129 -0
  61. package/dist/src/core/accounts.js +284 -0
  62. package/dist/src/core/agent-tokens.d.ts +31 -0
  63. package/dist/src/core/agent-tokens.js +120 -0
  64. package/dist/src/core/agent.d.ts +71 -0
  65. package/dist/src/core/agent.js +358 -0
  66. package/dist/src/core/attachments.d.ts +21 -0
  67. package/dist/src/core/attachments.js +44 -0
  68. package/dist/src/core/data-dir.d.ts +5 -0
  69. package/dist/src/core/data-dir.js +11 -0
  70. package/dist/src/core/db.d.ts +13 -0
  71. package/dist/src/core/db.js +38 -0
  72. package/dist/src/core/env-file.d.ts +11 -0
  73. package/dist/src/core/env-file.js +36 -0
  74. package/dist/src/core/errors.d.ts +24 -0
  75. package/dist/src/core/errors.js +42 -0
  76. package/dist/src/core/history.d.ts +34 -0
  77. package/dist/src/core/history.js +279 -0
  78. package/dist/src/core/ideas.d.ts +16 -0
  79. package/dist/src/core/ideas.js +100 -0
  80. package/dist/src/core/ids.d.ts +2 -0
  81. package/dist/src/core/ids.js +5 -0
  82. package/dist/src/core/log-dir.d.ts +2 -0
  83. package/dist/src/core/log-dir.js +9 -0
  84. package/dist/src/core/logger.d.ts +14 -0
  85. package/dist/src/core/logger.js +37 -0
  86. package/dist/src/core/mail.d.ts +43 -0
  87. package/dist/src/core/mail.js +44 -0
  88. package/dist/src/core/mcp-entry.d.ts +20 -0
  89. package/dist/src/core/mcp-entry.js +15 -0
  90. package/dist/src/core/migrations.d.ts +18 -0
  91. package/dist/src/core/migrations.js +404 -0
  92. package/dist/src/core/native-sqlite.d.ts +2 -0
  93. package/dist/src/core/native-sqlite.js +23 -0
  94. package/dist/src/core/notifications.d.ts +46 -0
  95. package/dist/src/core/notifications.js +173 -0
  96. package/dist/src/core/project-root.d.ts +2 -0
  97. package/dist/src/core/project-root.js +13 -0
  98. package/dist/src/core/requests.d.ts +50 -0
  99. package/dist/src/core/requests.js +240 -0
  100. package/dist/src/core/snapshots.d.ts +32 -0
  101. package/dist/src/core/snapshots.js +35 -0
  102. package/dist/src/core/spaces.d.ts +66 -0
  103. package/dist/src/core/spaces.js +252 -0
  104. package/dist/src/core/store.d.ts +577 -0
  105. package/dist/src/core/store.js +2119 -0
  106. package/dist/src/core/task-history.d.ts +7 -0
  107. package/dist/src/core/task-history.js +41 -0
  108. package/dist/src/core/tasks.d.ts +58 -0
  109. package/dist/src/core/tasks.js +389 -0
  110. package/dist/src/core/test-tasks.d.ts +19 -0
  111. package/dist/src/core/test-tasks.js +135 -0
  112. package/dist/src/core/transcription.d.ts +45 -0
  113. package/dist/src/core/transcription.js +92 -0
  114. package/dist/src/core/watchers.d.ts +30 -0
  115. package/dist/src/core/watchers.js +50 -0
  116. package/dist/src/deploy/env-render.d.ts +26 -0
  117. package/dist/src/deploy/env-render.js +98 -0
  118. package/dist/src/deploy/templates.d.ts +38 -0
  119. package/dist/src/deploy/templates.js +130 -0
  120. package/dist/src/gui-api.d.ts +37 -0
  121. package/dist/src/gui-api.js +383 -0
  122. package/dist/src/index.d.ts +2 -0
  123. package/dist/src/index.js +110 -0
  124. package/dist/src/mcp/client.d.ts +38 -0
  125. package/dist/src/mcp/client.js +106 -0
  126. package/dist/src/mcp/notifications.d.ts +13 -0
  127. package/dist/src/mcp/notifications.js +54 -0
  128. package/dist/src/mcp/start.d.ts +14 -0
  129. package/dist/src/mcp/start.js +123 -0
  130. package/dist/src/mcp/tools.d.ts +46 -0
  131. package/dist/src/mcp/tools.js +348 -0
  132. package/dist/src/mcp/watch-handoff.d.ts +35 -0
  133. package/dist/src/mcp/watch-handoff.js +91 -0
  134. package/dist/src/server/app.d.ts +25 -0
  135. package/dist/src/server/app.js +305 -0
  136. package/dist/src/server/config.d.ts +23 -0
  137. package/dist/src/server/config.js +75 -0
  138. package/dist/src/server/limits.d.ts +2 -0
  139. package/dist/src/server/limits.js +4 -0
  140. package/dist/src/server/login-limiter.d.ts +36 -0
  141. package/dist/src/server/login-limiter.js +81 -0
  142. package/dist/src/server/start.d.ts +5 -0
  143. package/dist/src/server/start.js +88 -0
  144. package/dist/src/version.d.ts +1 -0
  145. package/dist/src/version.js +16 -0
  146. package/gui/dist/assets/index-BVN0agCc.css +1 -0
  147. package/gui/dist/assets/index-DI1_kr81.js +70 -0
  148. package/gui/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  149. package/gui/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  150. package/gui/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  151. package/gui/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  152. package/gui/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  153. package/gui/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  154. package/gui/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  155. package/gui/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  156. package/gui/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  157. package/gui/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  158. package/gui/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  159. package/gui/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  160. package/gui/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  161. package/gui/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  162. package/gui/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  163. package/gui/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  164. package/gui/dist/index.html +13 -0
  165. package/package.json +66 -4
package/README.md CHANGED
@@ -1,3 +1,548 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # mcp-organizer
2
+
3
+ Gestionnaire de tâches multi-utilisateur piloté par un agent. Les membres organisent leurs tâches dans des espaces personnels et communs, sur un site web ; les demandes qu'ils adressent à l'IA (idées, remarques, reviews de test, messages) sont déposées dans une file d'attente qu'un agent, connecté en MCP avec un jeton, traite quand on le lui demande. Chacune de ses actions est journalisée et annulable.
4
+
5
+ Le serveur n'appelle aucun modèle de langage : l'intelligence vient de l'agent connecté.
6
+
7
+ Un seul paquet, deux modes :
8
+
9
+ | Commande | Mode |
10
+ |---|---|
11
+ | `mcp-organizer --server` | **Site** : processus web permanent, derrière un reverse proxy (nginx). Il détient la base, sert l'interface, son API et l'API des agents (`/api/agent/*`). |
12
+ | `mcp-organizer` | **MCP** (stdio) : serveur MCP de l'agent, sans base ni logique métier. Configuré par `ORGANIZER_URL` et `ORGANIZER_TOKEN`, il relaie chaque appel d'outil à l'API des agents du site. Il peut tourner sur n'importe quelle machine, notamment dans une gateway de mcp-gateway-manager. |
13
+
14
+ Autres commandes : `--setup` (installation du site : `.env`, unité systemd, bloc nginx, premier compte), `--set-user` (compte), `--backup` (sauvegarde de la base et des pièces jointes), `--claude-setup-mcp` (entrée `.mcp.json` du mode MCP), `--watch` (veilleur de notifications de l'agent, lancé par son outil `Monitor`), `--version`, `--help`. Chaque mode et chaque commande chargent leur code à la demande.
15
+
16
+ Prérequis : Node.js 20 ou plus récent.
17
+
18
+ ## Installer le site
19
+
20
+ Le site s'installe par clonage du dépôt, sur la machine qui l'héberge (Linux avec systemd et nginx pour un site exposé) :
21
+
22
+ ```bash
23
+ git clone <dépôt> /opt/mcp-organizer && cd /opt/mcp-organizer
24
+ npm ci
25
+ npm run build
26
+ npm run setup
27
+ ```
28
+
29
+ Les scripts `npm run setup`, `npm run server`, `npm run set-user` et `npm run backup` lancent `node dist/src/index.js` avec l'option du même nom ; leurs arguments se passent après `--` (`npm run setup -- --help`).
30
+
31
+ `--setup` pose les questions, refuse une réponse invalide en disant pourquoi, puis :
32
+
33
+ 1. écrit le fichier `.env` du site (`<package>/.env`, ou le fichier de `--dotenv=<chemin>`), avec les droits 600 sous Linux ;
34
+ 2. écrit dans le dossier de sortie (`<package>/deploy`, ou `--output-dir=<chemin>`) l'unité systemd `mcp-organizer.service` et le bloc nginx `mcp-organizer.nginx.conf`, remplis avec les réponses ;
35
+ 3. crée le premier compte (voir « Premier compte ») ;
36
+ 4. affiche les commandes qui restent à lancer en root.
37
+
38
+ Il n'écrit aucun fichier système : copier l'unité et le bloc, recharger systemd et nginx restent des gestes de l'administrateur.
39
+
40
+ | Question | Option | Défaut |
41
+ |---|---|---|
42
+ | URL publique | `--public-url=<url>` | aucun. En `https://` ; `http://` n'est admis que pour un essai local (`localhost`, `127.0.0.1`). Son chemin éventuel devient `ORGANIZER_BASE_PATH`. |
43
+ | Port d'écoute | `--port=<n>` | aucun (4800 proposé) |
44
+ | Adresse d'écoute | `--host=<adresse>` | `127.0.0.1` |
45
+ | Adresse du client lue dans `X-Forwarded-For` | `--trust-proxy=oui\|non` | aucun (oui proposé : nginx écrit l'en-tête). Refusé avec une adresse d'écoute hors de la boucle locale (voir « Sécurité »). |
46
+ | Répertoire de données | `--data-dir=<chemin>` | `<package>/.organizer-data` ; écrit en chemin absolu |
47
+ | Clé Groq (dictée) | `ORGANIZER_SETUP_GROQ_API_KEY`, ou `--no-groq` | dictée désactivée. Avec une clé, le setup écrit `ORGANIZER_TRANSCRIBE_MODEL=whisper-large-v3-turbo`. |
48
+ | URL SMTP | `ORGANIZER_SETUP_SMTP_URL`, ou `--no-smtp` | e-mail désactivé |
49
+ | Expéditeur des e-mails | `--mail-from=<adresse>` | aucun, requis avec SMTP |
50
+ | Premier compte | `--admin-email=<adresse>`, `--admin-name=<nom>`, `ORGANIZER_SETUP_ADMIN_PASSWORD` | requis tant qu'aucun compte n'existe |
51
+
52
+ Options de l'unité systemd : `--install-dir=<chemin>` (racine du dépôt, par défaut celle où tourne le setup), `--service-user=<compte>` (par défaut `mcp-organizer`), `--node=<chemin>` (par défaut le Node qui exécute le setup). `npm run setup -- --help` les liste toutes.
53
+
54
+ **Mode non interactif** : avec `--non-interactive`, ou quand l'entrée n'est pas un terminal, rien n'est demandé. Chaque valeur vient de son option, ou de la variable `ORGANIZER_SETUP_<NOM>` correspondante (`--public-url` → `ORGANIZER_SETUP_PUBLIC_URL`), sinon du `.env` existant, sinon du défaut du tableau ; une valeur requise absente arrête le setup en la nommant. Les secrets (clé Groq, URL SMTP, mot de passe) ne se passent jamais en argument : saisis sans écho en mode interactif, ils viennent sinon de `ORGANIZER_SETUP_GROQ_API_KEY`, `ORGANIZER_SETUP_SMTP_URL` et `ORGANIZER_SETUP_ADMIN_PASSWORD`. Une option de secret (`--admin-password=…`, `--groq-api-key=…`, `--smtp-url=…`) est refusée.
55
+
56
+ ```bash
57
+ ORGANIZER_SETUP_ADMIN_PASSWORD='…' ORGANIZER_SETUP_SMTP_URL='smtps://…' \
58
+ npm run setup -- --non-interactive \
59
+ --public-url=https://organizer.example.com --port=4800 --trust-proxy=oui \
60
+ --data-dir=/var/lib/mcp-organizer --no-groq --mail-from='Organizer <organizer@example.com>' \
61
+ --admin-email=admin@example.com --admin-name='Prénom Nom'
62
+ ```
63
+
64
+ Le setup refuse, sans rien écrire, une option inconnue ou répétée, une valeur invalide, et les combinaisons incohérentes : `--trust-proxy=oui` avec une adresse d'écoute qui n'est pas la boucle locale (`localhost`, `127.x.x.x`, `::1`), `--no-groq` avec une clé Groq, `--no-smtp` avec `--mail-from`, `--mail-from` sans serveur SMTP, `--admin-name` ou un mot de passe sans compte désigné alors que des comptes existent. Il fait relire la configuration obtenue par la validation du site avant d'écrire.
65
+
66
+ **Relancer le setup** reconfigure une installation : les valeurs du `.env` existant sont proposées par défaut (et conservées en mode non interactif ; Entrée conserve un secret, « - » le retire). Le fichier est fusionné : commentaires et variables que le setup ne gère pas restent tels quels, chaque variable gérée est réécrite à sa place, et `ORGANIZER_ADMIN_EMAIL` / `ORGANIZER_ADMIN_NAME` / `ORGANIZER_ADMIN_PASSWORD` sont retirées (le compte vit en base). Quand des comptes existent, le compte est facultatif.
67
+
68
+ **Fichiers produits** :
69
+
70
+ - `mcp-organizer.service` : `node <dépôt>/dist/src/index.js --server --dotenv=<.env>`, sous le compte dédié, redémarrage sur échec, durcissement (`NoNewPrivileges`, `ProtectSystem=strict` avec écriture limitée au répertoire de données, `ProtectHome=read-only`, `PrivateTmp`, `PrivateDevices`, aucune capacité sauf `CAP_NET_BIND_SERVICE` pour un port inférieur à 1024, familles d'adresses limitées), journaux dans journald.
71
+ - `mcp-organizer.nginx.conf` : redirection du port 80 vers HTTPS, `listen 443 ssl http2` (et `[::]:443`) avec les chemins de certificat Let's Encrypt, `Strict-Transport-Security`, proxy vers `HOST:PORT` (la boucle locale quand le site écoute sur toutes les interfaces) avec `Host`, `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto` et `X-Forwarded-Host`, `client_max_body_size 26m` (pièces jointes de 20 Mo et dictée de 25 Mo, enveloppe du formulaire comprise, comme la limite du site), `/api/` jamais mis en cache (`Cache-Control: no-store`). Publié sous un chemin (`https://exemple.com/organizer`), le bloc retire le préfixe avant de joindre le site et redirige `/organizer` vers `/organizer/`.
72
+
73
+ **Version de nginx** : 1.9.5 au minimum. Le bloc active HTTP/2 par `listen … ssl http2`, compris par toutes ces versions, dont celles de Debian 12 (1.22) et d'Ubuntu 24.04 (1.24). À partir de nginx 1.25.1, `nginx -t` signale cette forme comme dépréciée (simple avertissement) ; la directive `http2 on;` qui la remplace n'existe pas avant 1.25.1.
74
+
75
+ **Étapes suivantes**, affichées par le setup avec les chemins de l'installation :
76
+
77
+ ```bash
78
+ useradd --system --no-create-home --shell /usr/sbin/nologin mcp-organizer
79
+ chown -R mcp-organizer:mcp-organizer /var/lib/mcp-organizer
80
+ chown root:mcp-organizer /opt/mcp-organizer/.env && chmod 640 /opt/mcp-organizer/.env
81
+ cp deploy/mcp-organizer.service /etc/systemd/system/
82
+ systemctl daemon-reload && systemctl enable --now mcp-organizer
83
+ certbot certonly --nginx -d organizer.example.com
84
+ cp deploy/mcp-organizer.nginx.conf /etc/nginx/sites-available/mcp-organizer
85
+ ln -s /etc/nginx/sites-available/mcp-organizer /etc/nginx/sites-enabled/mcp-organizer
86
+ nginx -t && systemctl reload nginx
87
+ ```
88
+
89
+ **Lancement à la main** (essai local, ou sans systemd) : le site écoute sur `HOST:PORT` et sert l'interface (`gui/dist`) et l'API (`/api/*`) dans un seul processus Hono. Il s'arrête proprement sur `SIGINT` / `SIGTERM`.
90
+
91
+ ```bash
92
+ npm run server # lit <package>/.env
93
+ npm run server -- --dotenv=/etc/mcp-organizer.env
94
+ ```
95
+
96
+ Il refuse de démarrer, avec un message qui dit quoi corriger, quand `PORT` ou `ORGANIZER_PUBLIC_URL` manque ou est invalide, quand `ORGANIZER_BASE_PATH` ne correspond pas au chemin de l'URL publique, quand `ORGANIZER_TRUST_PROXY` vaut autre chose que `1`, quand le port est déjà pris, ou quand le premier compte demandé par `ORGANIZER_ADMIN_*` est incomplet.
97
+
98
+ ## Premier compte
99
+
100
+ Les comptes se créent sur invitation uniquement : il n'y a pas d'inscription libre. Le premier compte se crée de l'une de trois façons :
101
+
102
+ - **Setup** : `--setup` le crée à l'installation, avec le même code que `--set-user`. L'adresse vient de `--admin-email`, le nom de `--admin-name`, le mot de passe d'une saisie sans écho avec confirmation, ou de `ORGANIZER_SETUP_ADMIN_PASSWORD` en mode non interactif. Tant qu'aucun compte n'existe, ce compte est requis ; ensuite, il est facultatif, et une adresse existante voit son mot de passe remplacé. Le mot de passe n'est écrit dans aucun fichier.
103
+ - **Commande** :
104
+
105
+ ```bash
106
+ npm run set-user -- [email] [--name="Prénom Nom"]
107
+ ```
108
+
109
+ Crée un compte actif avec son espace personnel, ou remplace le mot de passe d'un compte existant, qui redevient actif (ses sessions ouvertes sont fermées) ; les invitations d'un compte encore invité restent en attente dans ses « Invitations reçues ». Le mot de passe est saisi sans écho, avec confirmation, et ne se passe jamais en argument. L'adresse vient de l'argument, sinon de `ORGANIZER_ADMIN_EMAIL`, sinon d'une question. Le nom affiché vient de `--name`, sinon de `ORGANIZER_ADMIN_NAME`, sinon d'une question ; il est obligatoire pour créer un compte, et facultatif pour un compte qui en a déjà un (il le remplace). En mode non interactif (`--non-interactive`, ou entrée qui n'est pas un terminal), le mot de passe vient de `ORGANIZER_ADMIN_PASSWORD`, et une adresse ou un nom manquant fait échouer la commande avec la marche à suivre. La commande lit le même `.env` que le site (`--dotenv` compris) et écrit dans son répertoire de données.
110
+ - **Variables d'environnement** : au démarrage du site, si la table des comptes est vide et que `ORGANIZER_ADMIN_EMAIL` et `ORGANIZER_ADMIN_PASSWORD` sont définies, le compte est créé (actif, avec son espace personnel) avec le nom `ORGANIZER_ADMIN_NAME`. Sans ce nom, le site refuse de démarrer et dit quelle variable définir : aucun nom n'est déduit de l'adresse. Les variables peuvent ensuite être retirées : le compte vit en base.
111
+
112
+ Tant qu'aucun compte n'existe, le site refuse toute connexion et indique comment en créer un : la commande d'une installation par clonage (`node dist/src/index.js --set-user <email> --name="Prénom Nom"`, dans le dossier du site), celle du paquet npm (`npx -y @imenam/mcp-organizer --set-user …`, qui exige les modules natifs construits, voir « Via une gateway de mcp-gateway-manager ») et les variables `ORGANIZER_ADMIN_*`. Chaque utilisateur change ensuite son nom affiché et son mot de passe dans les Paramètres.
113
+
114
+ ## Connecter un agent (mode MCP)
115
+
116
+ Un agent se connecte au site avec un **jeton d'agent** (voir « Jetons d'agent ») : un utilisateur le crée dans Paramètres > Agents, pour un espace dont il est membre. L'agent voit alors les demandes de tous les membres de cet espace et agit avec les droits du créateur du jeton.
117
+
118
+ Le mode MCP est un serveur stdio léger : il n'ouvre aucune base, ne charge aucun module natif (`better-sqlite3`, `argon2`) et n'a aucune logique métier. Chaque outil est un appel HTTPS à l'API des agents du site, avec le jeton dans `Authorization: Bearer`. Une instance correspond à un jeton, donc à un utilisateur et un espace.
119
+
120
+ | Variable | Rôle |
121
+ |---|---|
122
+ | `ORGANIZER_URL` | URL publique du site (`ORGANIZER_PUBLIC_URL` du site), par exemple `https://organizer.example.com`. **Obligatoire.** |
123
+ | `ORGANIZER_TOKEN` | Jeton d'agent `org_…`. **Obligatoire.** |
124
+ | `MCP_LOG_DIR` | Facultatif : répertoire où écrire `server.log` en plus de stderr. |
125
+
126
+ Le mode MCP tient sa configuration de son seul environnement, fourni par l'agent (bloc `env` de `.mcp.json`) ou par la gateway : il ne lit aucun fichier `.env`, pas même celui du paquet, sauf celui que désigne explicitement `--dotenv=<fichier>`.
127
+
128
+ Sans `ORGANIZER_URL` ou `ORGANIZER_TOKEN`, ou avec une URL invalide, le mode MCP refuse de démarrer et dit quoi renseigner. `ORGANIZER_URL` doit être en `https://` ; `http://` n'est admis que pour un site de la machine locale (`localhost`, `127.0.0.1`, `::1`), le jeton circulant sinon en clair. Le mode MCP ne suit jamais une redirection : si le site en répond une, l'outil échoue en nommant l'adresse visée (« … answered with a redirect (HTTP 301) to … Use the final https URL of the site … ») sans y envoyer le jeton ; indiquez alors l'URL finale du site, son `ORGANIZER_PUBLIC_URL`. Un refus du site (jeton absent, inconnu, révoqué ou expiré, propriétaire qui n'est plus membre, demande hors de l'espace…) et un site injoignable reviennent à l'agent comme une erreur d'outil (`isError: true`) avec un message utile.
129
+
130
+ ### Directement, dans un projet
131
+
132
+ À la création d'un jeton, la GUI affiche l'entrée `.mcp.json` à copier. La commande suivante l'écrit aussi, dans le dossier du projet où l'agent travaille :
133
+
134
+ ```bash
135
+ npx -y @imenam/mcp-organizer --claude-setup-mcp [--force] --url=https://organizer.example.com --token=org_…
136
+ ```
137
+
138
+ Elle écrit l'entrée `mcp-organizer` de `<dossier>/.mcp.json`, ou l'ajoute à un fichier existant en conservant les autres serveurs. Elle échoue sans rien écrire quand une option est inconnue, quand `--url` ou `--token` manque ou est invalide (URL http(s) sans paramètres ; jeton commençant par `org_`), quand `.mcp.json` est illisible, n'est pas du JSON, n'est pas un objet ou a un `mcpServers` qui n'est pas un objet, ou quand l'entrée existe déjà sans `--force`.
139
+
140
+ ```json
141
+ {
142
+ "mcpServers": {
143
+ "mcp-organizer": {
144
+ "command": "npx",
145
+ "args": ["-y", "@imenam/mcp-organizer"],
146
+ "env": {
147
+ "ORGANIZER_URL": "https://organizer.example.com",
148
+ "ORGANIZER_TOKEN": "org_…"
149
+ }
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ Le jeton est un secret : `.mcp.json` ne se partage pas et ne se versionne pas. Il se révoque depuis Paramètres > Agents.
156
+
157
+ ### Via une gateway de mcp-gateway-manager
158
+
159
+ Dans une gateway, déclarez un serveur MCP stdio de commande `npx`, d'arguments `-y @imenam/mcp-organizer`, avec `ORGANIZER_URL` et `ORGANIZER_TOKEN` dans son environnement, le jeton en variable secrète. Dans l'éditeur JSON d'une gateway du dashboard :
160
+
161
+ ```json
162
+ {
163
+ "mcpServers": {
164
+ "organizer-refonte": {
165
+ "command": "npx",
166
+ "args": ["-y", "@imenam/mcp-organizer"],
167
+ "route": "/organizer-refonte",
168
+ "env": {
169
+ "ORGANIZER_URL": "https://organizer.example.com",
170
+ "ORGANIZER_TOKEN": { "secret": true, "value": "org_…" }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ Dans le `mcp-servers.json` d'une gateway `mcp-http-gateway` lancée seule, la route s'écrit `"MCP_SERVER_ROUTE": "/organizer-refonte"` dans `env`, et le jeton en clair. Les outils sont alors appelés en `POST /organizer-refonte/tools/<outil>` (corps JSON : les arguments de l'outil), ou par le relais `mcp-http-gateway --mcp` (`call_tool` avec `route`, `tool_name` et `arguments`) ; les erreurs du site reviennent telles quelles, avec `isError: true`.
178
+
179
+ - **Un jeton par déclaration** : chaque déclaration correspond à un jeton, donc à un espace. Pour exposer plusieurs espaces, déclarez une instance par jeton, chacune sous sa route.
180
+ - **Modules natifs** : `npx` (comme `npm install`) installe tout le paquet, y compris `better-sqlite3` et `argon2`, qui ne servent qu'au site. npm 12 bloque leurs scripts d'installation (avertissement `install-scripts … not covered by allowScripts`), car le champ `allowScripts` du paquet ne vaut que pour l'installation par clonage. Le mode MCP ne charge jamais ces modules : il fonctionne sans rien approuver. Le site, lui, s'installe par clonage (voir « Installer le site »). Lancés depuis une installation npm dont les scripts ont été bloqués, `--server`, `--set-user`, `--setup` et `--backup` s'arrêtent avant toute écriture avec un message qui l'explique (« better-sqlite3 n'a pas de module natif compilé ») et donne les deux issues : installer par clonage, ou lancer `npm install-scripts approve better-sqlite3` puis `npm rebuild better-sqlite3` dans cette installation.
181
+ - **Instructions** : la gateway relaie les outils mais pas le champ `instructions` du serveur. Les descriptions des outils et le résultat de `claim_request`, qui rappelle comment traiter chaque type de demande, suffisent à l'agent pour suivre la boucle (voir « Boucle de travail de l'agent »).
182
+
183
+ ### Les outils
184
+
185
+ Les outils MCP sont destinés à l'agent ; leurs schémas JSON sont écrits à la main et leurs descriptions disent quand les utiliser. Ils travaillent tous sur l'espace du jeton. Les outils d'écriture exigent `request_id`, une demande `processing` prise par ce même jeton. Une tâche se désigne par sa clé (`ORG-41`) ou son identifiant, un membre par son identifiant ou son adresse e-mail.
186
+
187
+ | Outil | Route de l'API des agents | Rôle |
188
+ |---|---|---|
189
+ | `list_requests` | `GET requests` | Demandes de l'espace par statut (`pending` par défaut, ou `all`) : en attente de la plus ancienne à la plus récente, les autres statuts de la plus récente à la plus ancienne ; avec auteur, type, tâche de contexte et, en cours, si ce jeton les a prises |
190
+ | `claim_request` | `POST requests/:id/claim` | Prend une demande pour ce jeton (atomique) et renvoie son contexte : texte, auteur, tâche ou idée de contexte (et, pour une review, les résultats du testeur point par point), arbre de l'espace, membres, idées récentes |
191
+ | `get_context` | `GET context` | Espace du jeton : nom, préfixe, membres, arbre des tâches, idées récentes |
192
+ | `get_task` | `GET tasks/:ref` | Détail d'une tâche : champs, parents, assignés, tags, description, checklist ou points de vérification (avec leurs identifiants), sous-tâches, commentaires récents, pièces jointes |
193
+ | `search_tasks` | `GET tasks?query=…` | Recherche par mots (titre, description, clé) et filtres (statut, priorité, type, assigné) |
194
+ | `create_task` | `POST tasks` | Tâche, sous-tâche ou sous-sous-tâche (`parent`), standard ou test |
195
+ | `update_task` | `PATCH tasks/:ref` | Titre, description, statut, priorité, type, parent, position dans la colonne (`after_task`), tâche vérifiée par un test (`tested_task_id`, clé ou identifiant ; `null` la retire) |
196
+ | `set_assignees` | `PUT tasks/:ref/assignees` | Remplace les assignés (testeurs d'un test) ; les nouveaux assignés sont notifiés |
197
+ | `set_tags` | `PUT tasks/:ref/tags` | Remplace les tags (créés au besoin) |
198
+ | `edit_checklist` | `POST tasks/:ref/checklist` | Ajoute, renomme, coche ou supprime des éléments de checklist ; ajoute, renomme ou supprime des points de vérification d'un test (tout ou rien) |
199
+ | `add_comment` | `POST tasks/:ref/comments` | Commentaire de l'agent ; assignés et commentateurs précédents notifiés |
200
+ | `create_idea` / `update_idea` | `POST ideas`, `PATCH ideas/:id` | Idée dans la boîte à idées ; reformulation, archivage |
201
+ | `promote_idea` | `POST ideas/:id/promote` | Crée une tâche à partir d'une idée et marque l'idée promue |
202
+ | `complete_request` | `POST requests/:id/complete` | Termine la demande avec la réponse à l'auteur |
203
+ | `fail_request` | `POST requests/:id/fail` | Termine la demande en échec avec la raison |
204
+ | `enable_notifications` | `GET notifications` | Exécuté par le mode MCP lui-même : dépose le relais du veilleur et rend l'appel `Monitor` à faire (voir « Notifications de l'agent ») |
205
+
206
+ Chaque écriture enregistre une action dans le lot de la demande (avec le jeton), crée une version de l'entité (avec le jeton), alimente le journal des changements et les notifications. Une erreur dit à l'agent quoi faire ensuite (« call claim_request on it first », « Call search_tasks to find it »…).
207
+
208
+ ### Boucle de travail de l'agent
209
+
210
+ L'agent traite la file quand l'utilisateur le lui demande, ou quand son veilleur lui annonce une demande (voir « Notifications de l'agent ») ; les `instructions` du serveur présentent les concepts (espace, tâche, demande, lot, portée), les notifications et la boucle :
211
+
212
+ 1. `list_requests` ;
213
+ 2. `claim_request` sur la plus ancienne (`get_context` montre l'espace à tout moment) ;
214
+ 3. lectures avec `get_task` et `search_tasks` ;
215
+ 4. actions avec les outils d'écriture, en passant toujours le `request_id` ;
216
+ 5. `complete_request` avec une réponse courte dans la langue de l'auteur (ou `fail_request`), puis retour à l'étape 1 jusqu'à ce que la file soit vide.
217
+
218
+ Règles de traitement rappelées à l'agent : une **idée** se rattache à une tâche ou sous-tâche existante (sous-tâche, élément de checklist, commentaire), devient une nouvelle tâche si elle est actionnable et assez prioritaire, ou va dans la boîte à idées ; une **review de test** se réinjecte dans les tâches existantes (statut, checklist, commentaires, priorités) ou donne une tâche par problème ; une **remarque** ou un **message** appelle toute action utile ; une **promotion d'idée** se fait avec `promote_idea`.
219
+
220
+ **Portée** : une demande vise l'espace où elle est écrite (l'espace ouvert dans l'interface) ; une review de test vise donc l'espace de sa tâche, une promotion d'idée l'espace de l'idée. Un jeton couvre un espace : son agent voit et traite les demandes de tous les membres de cet espace, avec les droits du propriétaire du jeton, et seulement là. Une demande prise par un jeton n'est traitée que par lui ; si l'agent s'arrête en cours de route, un membre la « Libère » depuis l'interface. Le jeton est revérifié à chaque appel ; quand son propriétaire quitte l'espace ou en est retiré, ses jetons sur cet espace sont révoqués et refusés dès l'appel suivant.
221
+
222
+ ### Notifications de l'agent
223
+
224
+ L'agent peut être prévenu en temps réel des demandes qui passent en attente, sur le modèle de mcp-messenger. Le site ne pousse rien : un veilleur, sur la machine de l'agent, l'interroge en attente longue, et chaque ligne qu'il écrit réveille l'agent.
225
+
226
+ 1. En début de session, l'agent appelle `enable_notifications`. Le mode MCP lit sur le site le jeton, son espace, le nombre de demandes en attente et l'état du veilleur (`GET notifications`), écrit le **relais** puis rend l'appel `Monitor` exact : commande `npx -y @imenam/mcp-organizer --watch <id du jeton>`, `timeout_ms` 1 800 000.
227
+ 2. L'agent lance cette commande avec `Monitor`. Le veilleur est un processus du shell de la session, pas un enfant du mode MCP : il ne reçoit pas le bloc `env` du `.mcp.json`. L'identifiant du jeton, passé en argument, est public (ce n'est pas sa valeur) : il sert seulement à retrouver le relais.
228
+ 3. Le **relais** est un fichier `~/.mcp-organizer/watch/<sha256(id du jeton)>.json` en 0600, qui porte l'URL du site et le jeton ; la valeur du jeton n'apparaît donc jamais dans la conversation. Il est écrit par `enable_notifications`, jamais au démarrage, et supprimé à l'arrêt du mode MCP qui l'a écrit en dernier ; un relais de plus de 7 jours est purgé. `ORGANIZER_STATE_DIR` déplace le répertoire (`<ORGANIZER_STATE_DIR>/watch`), pour les tests. Sans relais, le veilleur refuse de démarrer et dit d'appeler `enable_notifications`.
229
+ 4. Le veilleur appelle `GET requests/wait?watcher=true` en boucle : chaque appel reste ouvert jusqu'à 50 s et répond dès qu'une demande de l'espace passe en attente (création, ou libération par un membre). Le curseur rendu, repassé en `since`, rattrape les demandes arrivées entre deux appels. Une panne réseau ou du site est réessayée avec un délai de 1 s à 30 s.
230
+ 5. Pour chaque demande, une ligne sur stdout : `[organizer] idea · Bob Martin — "Un mode sombre" · ORG-4 (<id de la demande>)` (type, auteur, extrait, tâche de contexte, identifiant). L'agent se réveille et la traite par la boucle de travail, en commençant par `claim_request`. Les demandes déjà en attente quand le veilleur démarre ne sont pas annoncées : `enable_notifications` en donne le nombre, `list_requests` les liste.
231
+ 6. Un refus du site (jeton révoqué, expiré, propriétaire retiré…) arrête le veilleur après une dernière ligne `[organizer] notifications stopped: <raison>`.
232
+
233
+ L'hôte arrête un `Monitor` au bout de 30 minutes au plus : l'agent relance alors le même appel `Monitor`, sans rappeler `enable_notifications`.
234
+
235
+ **Présence** : le site tient en mémoire, par jeton, les attentes en cours de son veilleur, avec un délai de grâce de 2 minutes entre deux attentes. `enable_notifications` répond « already live » quand un veilleur est déjà armé (en armer un second doublerait chaque notification), et la page Agent de l'interface affiche « à l'écoute » à côté du jeton.
236
+
237
+ ### API des agents
238
+
239
+ `/api/agent/*` sert le mode MCP ; n'importe quel client HTTP peut l'appeler avec un jeton.
240
+
241
+ - **Authentification** : `Authorization: Bearer org_…`, revérifiée à chaque requête (jeton non révoqué, non expiré, propriétaire actif et toujours membre de l'espace) ; le dernier usage du jeton est mis à jour. Les cookies sont ignorés et le contrôle d'origine des modifications ne s'applique pas : sans cookie, une page tierce ne peut rien faire au nom d'un utilisateur.
242
+ - **Codes** : 401 pour un jeton absent, inconnu, révoqué (y compris au départ de son propriétaire) ou expiré (avec `WWW-Authenticate: Bearer`) ; 403 pour un propriétaire inactif ou qui n'est plus membre, une tâche ou une idée d'un autre espace, ou une demande prise par un autre jeton ; 404 pour une demande, une tâche ou une route inconnue ; 409 pour un état qui interdit l'action (demande en attente ou close) ; 400 pour des paramètres invalides ; 500 pour une erreur interne, avec un message générique et une référence (voir « Sécurité »). Le corps d'une erreur est `{ "error": "<message qui dit quoi faire>" }`.
243
+ - **Réponses** : `{ "text": "…", "data": { … } }`. `text` est le résultat rédigé pour l'agent (celui que renvoie l'outil) ; `data` porte les mêmes informations structurées, validées par les schémas zod de `shared/schemas/agent-tools.schema.ts`, partagés par le site et le mode MCP.
244
+ - **Attente longue** : `GET requests/wait?timeout_seconds=<1-50>&since=<curseur>&watcher=true` rend les demandes de l'espace passées en attente depuis `since`, sinon attend la prochaine, au plus `timeout_seconds` (30 par défaut) ; `data` porte les demandes, le curseur suivant et `timedOut`. `watcher=true` compte l'attente dans la présence du veilleur du jeton. `GET notifications` donne le jeton, son espace, le nombre de demandes en attente et l'état du veilleur.
245
+ - **Paramètres** : les noms des arguments des outils (`request_id`, `after_task`…) ; dans le chemin pour la tâche (`:ref`), l'idée et la demande, dans la requête pour un `GET`, dans le corps JSON sinon.
246
+
247
+ ## Configuration
248
+
249
+ Le setup écrit cette configuration ; elle se modifie aussi à la main. Le site et ses commandes (`--set-user`, `--backup`) lisent au démarrage le fichier `<package>/.env` s'il existe, dont les valeurs priment sur l'environnement du processus. L'option `--dotenv=<chemin>` désigne un autre fichier, qui doit alors exister ; les tests l'utilisent pour ne jamais lire la configuration du développeur. Une variable vide est traitée comme absente. Le mode MCP, lui, ne lit pas `<package>/.env` (voir « Connecter un agent »).
250
+
251
+ | Variable | Rôle |
252
+ |---|---|
253
+ | `PORT` | Port d'écoute. **Obligatoire.** |
254
+ | `HOST` | Adresse d'écoute. Par défaut `127.0.0.1` : le site n'est joignable que par le reverse proxy de la machine. |
255
+ | `ORGANIZER_PUBLIC_URL` | URL que les utilisateurs ouvrent, par exemple `https://organizer.example.com`. **Obligatoire.** Son origine est la seule admise pour une modification ; en `https://`, le cookie de session porte `Secure` ; elle sert de base aux liens des e-mails. |
256
+ | `ORGANIZER_BASE_PATH` | Chemin sous lequel le reverse proxy publie le site, par exemple `/organizer` (le proxy retire ce préfixe). Il fixe le `<base href>` de l'interface et le `Path` du cookie, et doit être le chemin de `ORGANIZER_PUBLIC_URL`. Sans lui, le site est publié à la racine. |
257
+ | `ORGANIZER_TRUST_PROXY` | `1` : l'adresse du client est la dernière entrée de `X-Forwarded-For`, écrite par le reverse proxy. Vide : l'en-tête est ignoré et l'adresse est celle de la connexion. Seulement avec un `HOST` de la boucle locale (voir « Sécurité »). |
258
+ | `MCP_ORGANIZER_DATA_DIR` | Répertoire de données ; à défaut `MCP_DATA_DIR`, puis `<package>/.organizer-data`. Il contient `organizer.db`, `attachments/` et `logs/`. |
259
+ | `MCP_LOG_DIR` | Répertoire des journaux ; à défaut `<données>/logs`. Les journaux vont aussi sur stderr. |
260
+ | `GROQ_API_KEY` | Clé de l'API Groq, pour la dictée vocale. |
261
+ | `ORGANIZER_TRANSCRIBE_MODEL` | Modèle de transcription Groq (`whisper-large-v3-turbo`). |
262
+ | `SMTP_URL` | Serveur d'envoi des e-mails : `smtp://utilisateur:motdepasse@hote:587` ou `smtps://…`. |
263
+ | `MAIL_FROM` | Expéditeur des e-mails, par exemple `Organizer <organizer@example.com>`. |
264
+ | `ORGANIZER_ADMIN_EMAIL`, `ORGANIZER_ADMIN_NAME`, `ORGANIZER_ADMIN_PASSWORD` | Premier compte, créé au démarrage si aucun compte n'existe (voir « Premier compte »). |
265
+
266
+ Exemple de `.env` :
267
+
268
+ ```ini
269
+ PORT=4800
270
+ ORGANIZER_PUBLIC_URL=https://organizer.example.com
271
+ ORGANIZER_TRUST_PROXY=1
272
+ MCP_ORGANIZER_DATA_DIR=/var/lib/mcp-organizer
273
+ GROQ_API_KEY=gsk_…
274
+ ORGANIZER_TRANSCRIBE_MODEL=whisper-large-v3-turbo
275
+ SMTP_URL=smtps://organizer:motdepasse@smtp.example.com:465
276
+ MAIL_FROM=Organizer <organizer@example.com>
277
+ ```
278
+
279
+ Sans `GROQ_API_KEY` ou sans `ORGANIZER_TRANSCRIBE_MODEL`, la dictée est désactivée : le journal le signale au démarrage, `GET /api/settings` renvoie `transcription.enabled: false` avec la raison, et le bouton micro de l'interface est désactivé avec cette raison en infobulle. Aucun modèle n'est choisi par défaut.
280
+
281
+ Sans `SMTP_URL` ou sans `MAIL_FROM`, l'envoi d'e-mail est désactivé : le journal le signale au démarrage, chaque invitation renvoie `emailSent: false` avec la raison (l'interface l'affiche à côté du lien à transmettre), et la page Paramètres le dit sous les notifications. Le lien affiché à l'invitant est formé par l'interface à partir de l'adresse de la page ouverte.
282
+
283
+ `ORGANIZER_TEST_GROQ_BASE_URL` est **réservée aux tests** : elle remplace l'URL de base de l'API Groq (`https://api.groq.com/openai/v1`) par celle d'un serveur Groq simulé. Elle ne fait pas partie de la configuration d'une installation.
284
+
285
+ ### Derrière nginx
286
+
287
+ Le bloc produit par `--setup` (voir « Installer le site ») est la référence. Un bloc écrit à la main doit transmettre `X-Forwarded-For` et `X-Forwarded-Proto` (avec `ORGANIZER_TRUST_PROXY=1`), accepter des corps de 26 Mo (`client_max_body_size 26m`) et ne pas mettre `/api/` en cache. Pour publier sous un chemin : `location /organizer/ { proxy_pass http://127.0.0.1:4800/; … }`, avec `ORGANIZER_BASE_PATH=/organizer` et `ORGANIZER_PUBLIC_URL=https://exemple.com/organizer`. Le site pose lui-même ses en-têtes de sécurité (voir « Sécurité ») ; nginx apporte HTTPS et `Strict-Transport-Security`.
288
+
289
+ ## Sécurité
290
+
291
+ Le site est fait pour être exposé derrière un reverse proxy HTTPS.
292
+
293
+ - **Écoute** : `127.0.0.1` par défaut ; seul le reverse proxy de la machine le joint.
294
+ - **Origine des modifications** : toute requête `POST`, `PUT`, `PATCH` ou `DELETE` sur `/api/*` exige `Sec-Fetch-Site: same-origin` ou un en-tête `Origin` égal à l'origine de `ORGANIZER_PUBLIC_URL` ; sinon elle reçoit un 403 avant d'atteindre l'API. Une page d'un autre site ne peut donc rien modifier avec la session d'un utilisateur. L'API des agents (`/api/agent/*`) en est exemptée : elle ignore les cookies et n'accepte qu'un jeton dans `Authorization`.
295
+ - **Connexion** : les tentatives de `POST /api/auth/login` sont comptées sur une fenêtre glissante de 15 minutes, par adresse IP (20 au plus) et par couple (adresse IP, adresse e-mail) (5 au plus). Une tentative est comptée dès son arrivée, avant la vérification du mot de passe, et décomptée si la connexion réussit : des tentatives envoyées en parallèle ne dépassent pas la limite. Au-delà, la tentative reçoit un 429 « Trop de tentatives de connexion. Réessayez dans N minutes. » avec `Retry-After`, même avec le bon mot de passe. Le compteur par couple empêche un tiers de bloquer un compte depuis sa propre adresse : son titulaire reste admis depuis la sienne.
296
+ - **Adresse du client** : celle de la connexion ; avec `ORGANIZER_TRUST_PROXY=1` seulement, la dernière entrée de `X-Forwarded-For`, celle qu'écrit le reverse proxy (un client peut ajouter des entrées en tête, pas en fin). Cela suppose que le site ne soit joignable que par le proxy : un site qui écoute sur une adresse publique (`HOST=0.0.0.0`…) reçoit aussi des requêtes directes dont le client écrit lui-même `X-Forwarded-For` ; il pourrait ainsi choisir l'adresse vue par le site et contourner la limitation des connexions. `--setup` refuse donc `--trust-proxy=oui` avec une adresse d'écoute hors de la boucle locale.
297
+ - **Erreurs internes** : une erreur inattendue (fichier manquant, base indisponible…) reçoit un 500 au message générique, avec une référence courte (« Erreur interne du site (référence 1a2b3c4d)… », en anglais pour l'API des agents) ; le détail (message, chemins, pile d'appels) ne va que dans le journal du site, sous cette référence.
298
+ - **En-têtes** sur toutes les réponses : `Content-Security-Policy` (`default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; font-src 'self' data:; media-src 'self' blob:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'`), `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy: same-origin`, `Cross-Origin-Opener-Policy` et `Cross-Origin-Resource-Policy: same-origin`, `Permissions-Policy` (micro pour le site seul, ni caméra ni géolocalisation). Les téléchargements de pièces jointes portent `Content-Disposition: attachment` et `Cache-Control: private, no-store`.
299
+ - **Mots de passe** : 8 caractères minimum, hachés en argon2id avec des paramètres fixés (19 Mio, 2 passes, 1 fil). Une adresse inconnue est vérifiée contre un hash factice, pour un temps de réponse identique. Le changement de mot de passe exige l'ancien et ferme les autres sessions du compte.
300
+ - **Sessions** : cookie `organizer_session` opaque (32 octets aléatoires), `HttpOnly`, `SameSite=Lax`, `Path` égal au chemin de base (`ORGANIZER_BASE_PATH`, sinon `/`), `Secure` quand `ORGANIZER_PUBLIC_URL` est en HTTPS. La base ne stocke que son hash sha256. Expiration après 14 jours sans activité ; fermeture à la déconnexion, quand le compte n'est plus actif et quand le mot de passe change.
301
+ - **Liens d'accès** : jeton de 32 octets, stocké haché, valable 7 jours, à usage unique. Un lien d'invitation est rattaché à son invitation et n'accepte qu'elle ; il ne fonctionne plus une fois le compte activé.
302
+ - **Agent** : aucune action hors de l'espace de son jeton, jamais dans l'espace personnel d'un autre utilisateur, et seulement pour une demande prise par ce jeton.
303
+ - **Jetons d'agent** : 32 octets aléatoires, stockés hachés (sha256), montrés une seule fois ; limités à un espace, révocables, éventuellement expirants, revérifiés à chaque usage (compte actif, appartenance à l'espace), révoqués quand leur propriétaire quitte l'espace ou en est retiré.
304
+ - **Mode MCP** : `ORGANIZER_URL` en `https://` hors de la machine locale, aucune redirection suivie : le jeton ne part que vers l'URL configurée.
305
+ - **Secrets** : la clé Groq et l'URL SMTP ne sont jamais renvoyées à l'interface.
306
+
307
+ ## Exploitation
308
+
309
+ ### Mise à jour
310
+
311
+ ```bash
312
+ cd /opt/mcp-organizer
313
+ npm run backup -- /var/backups/mcp-organizer/avant-maj.db
314
+ git pull
315
+ npm ci
316
+ npm run build
317
+ systemctl restart mcp-organizer
318
+ ```
319
+
320
+ Les migrations de la base s'appliquent au démarrage du site. Une nouvelle variable de configuration se pose en relançant `--setup`, qui conserve les valeurs en place.
321
+
322
+ ### Sauvegarde
323
+
324
+ ```bash
325
+ npm run backup -- /var/backups/mcp-organizer/organizer-2026-10-18.db [--force]
326
+ ```
327
+
328
+ La commande lit le même `.env` que le site (`--dotenv` compris) pour trouver le répertoire de données, et s'exécute site en service : la base est copiée par l'API de sauvegarde de SQLite, qui produit un instantané cohérent pendant les écritures. Le résultat est un fichier SQLite autonome (sans fichier `-wal`), vérifié par `integrity_check`. Les pièces jointes que cet instantané référence sont ensuite copiées dans le dossier `<fichier>.attachments/`, à côté, sous leur nom de stockage ; les fichiers plus récents que l'instantané n'y figurent pas.
329
+
330
+ Elle refuse une destination existante (le fichier ou son dossier `.attachments`), sauf avec `--force` qui les remplace. Une pièce jointe référencée mais introuvable fait échouer la sauvegarde, qui ne laisse alors aucun fichier. Hors Windows, le fichier de la base est créé avec les droits 600 et le dossier des pièces jointes avec les droits 700, dès leur création : la sauvegarde contient les empreintes des mots de passe et des jetons, et n'est lisible que par son propriétaire, y compris pendant son écriture.
331
+
332
+ **Restauration**, site arrêté :
333
+
334
+ ```bash
335
+ systemctl stop mcp-organizer
336
+ cp organizer-2026-10-18.db /var/lib/mcp-organizer/organizer.db
337
+ rm -f /var/lib/mcp-organizer/organizer.db-wal /var/lib/mcp-organizer/organizer.db-shm
338
+ rm -rf /var/lib/mcp-organizer/attachments
339
+ cp -r organizer-2026-10-18.db.attachments /var/lib/mcp-organizer/attachments
340
+ chown -R mcp-organizer:mcp-organizer /var/lib/mcp-organizer
341
+ systemctl start mcp-organizer
342
+ ```
343
+
344
+ Le `.env` n'est pas dans la sauvegarde : il contient les secrets (clé Groq, URL SMTP) et se conserve à part.
345
+
346
+ ## Développement
347
+
348
+ ```bash
349
+ npm install
350
+ npx playwright install chromium # une fois
351
+ npm run build # compile vers dist/, construit gui/dist
352
+ npm test # tests node --test sur dist/test/*.test.js
353
+ npm run test:e2e # parcours Playwright (Chromium), après npm run build
354
+ npm start # lance le mode MCP compilé (npm start -- --server pour le site)
355
+ ```
356
+
357
+ Interface seule, en développement : `cd gui && npm run dev` (les appels `/api/*` exigent le site) ; `npm run lint` (dans `gui/`) lance oxlint. `package.json` et `gui/package.json` se modifient uniquement par des commandes npm ; la version est portée par `npm run release`.
358
+
359
+ ### Architecture
360
+
361
+ #### Deux modes, une base
362
+
363
+ - Le **site** (`src/server/`) est un seul processus Hono (`@hono/node-server`) : `start.ts` lit la configuration (`config.ts`), ouvre la base, crée le premier compte et écoute ; `app.ts` pose les protections (en-têtes, contrôle d'origine, limitation des connexions, taille des corps), lit les formulaires multipart, appelle le routeur de l'API (`src/gui-api.ts`, où les droits sont vérifiés) et sert `gui/dist` en injectant `<base href>`.
364
+ - L'**API des agents** (`src/agent-api/`) est montée dans le même serveur, sous `/api/agent/*` : `router.ts` résout le jeton puis appelle la route ; les routes (`requests.ts`, `tasks.ts`, `checklist.ts`, `ideas.ts`) portent la logique des outils, avec `src/core/agent.ts` (demande prise par le jeton, résolution des tâches et membres de l'espace, contexte, résumés des actions).
365
+ - Le **mode MCP** (`src/mcp/`) dialogue avec le client MCP sur stdio (stdout est réservé au JSON-RPC, les journaux vont sur stderr) : `tools.ts` définit les outils (schémas JSON, descriptions) et leur route, `client.ts` appelle le site avec `fetch`, `start.ts` lit `ORGANIZER_URL` et `ORGANIZER_TOKEN` et relaie chaque appel. Il n'importe ni la base ni les modules natifs.
366
+ - `src/index.ts` lit la ligne de commande, charge le `.env` et importe le mode demandé.
367
+
368
+ #### Persistance SQLite partagée
369
+
370
+ `better-sqlite3`, mode WAL, clés étrangères actives, attente des verrous de 5 s. La base `<données>/organizer.db` est la seule source de vérité, détenue par le site ; les commandes comme `--set-user` l'ouvrent sur le même répertoire de données, et chaque processus voit les écritures des autres. La prise d'une demande est une mise à jour conditionnelle dans une transaction exclusive : un seul jeton l'obtient, et la demande lui est réservée (`claimedByTokenId`).
371
+
372
+ Le schéma est versionné par `PRAGMA user_version` (`src/core/migrations.ts`) ; les migrations s'appliquent au démarrage dans des transactions exclusives, une seule fois même si deux processus démarrent ensemble. Une base plus récente que le code est refusée.
373
+
374
+ Toute écriture passe par `store.mutate(actor, fn)` (`src/core/store.ts`) : l'acteur est un utilisateur ou l'agent (un jeton, pour une demande `processing` prise par ce jeton, avec l'identité du propriétaire du jeton) ; `fn` s'exécute dans une transaction exclusive ; à sa sortie les invariants sont vérifiés sur l'état final (profondeur des tâches, membres des espaces, assignés membres, espace cible des demandes, actions de l'agent limitées à son espace cible…), puis les versions et le journal sont écrits ; une erreur annule tout.
375
+
376
+ #### Journal des changements
377
+
378
+ Chaque mutation ajoute à la table `changes` une ligne par entité touchée (`seq` croissant, espace, utilisateur concerné, tâche concernée, auteur). `GET /api/changes?since=<seq>` renvoie les lignes des espaces de l'utilisateur et de ses notifications : c'est ce que l'interface interroge toutes les 3 s. Le journal étant en base, il couvre les écritures de tous les processus.
379
+
380
+ #### Historique
381
+
382
+ - **Versions** : chaque modification d'une tâche ou d'une idée en crée un instantané (champs, assignés, tags) avec son auteur. Le menu ⋯ d'une tâche ouvre son historique et restaure une version (nouvelle version au nom de l'utilisateur).
383
+ - **Annuler une action de l'agent** : l'état courant de l'entité doit être celui que l'action a laissé ; sinon le refus nomme l'auteur de la modification (« La tâche ORG-41 a été modifiée depuis par Julie Laurent… »), sans fusion devinée. Une création est supprimée, une suppression recréée, une modification ramenée à l'état d'avant, position dans la colonne comprise (un réordonnancement s'annule ; si la position d'alors est occupée par une autre tâche de la même colonne, l'annulation est refusée plutôt que marquée sans effet).
384
+ - **Annuler un lot** : toutes les actions non annulées, de la plus récente à la plus ancienne, dans une seule transaction : un refus annule tout.
385
+
386
+ ### Arborescence
387
+
388
+ ```
389
+ src/index.ts ligne de commande : aide, version, .env, choix du mode ou de la commande
390
+ src/server/ mode site : configuration, serveur Hono, protections, limitation des connexions
391
+ src/mcp/ mode MCP (stdio) : instructions, définitions des outils, client HTTP du site,
392
+ enable_notifications (notifications.ts) et relais du veilleur (watch-handoff.ts)
393
+ src/agent-api/ routes /api/agent/* : logique des outils de l'agent, authentifiée par jeton
394
+ src/gui-api.ts routes /api/* de l'interface
395
+ src/cli/ --setup (setup.ts), --set-user (set-user.ts), --backup (backup.ts),
396
+ --claude-setup-mcp (setup-mcp.ts), --watch (watch.ts), saisies au terminal (prompt.ts)
397
+ src/deploy/ fichiers écrits par --setup : .env fusionné (env-render.ts), unité systemd et bloc nginx (templates.ts)
398
+ src/core/ base SQLite, migrations, store, comptes, espaces, tâches, tests, pièces jointes,
399
+ notifications, e-mail, dictée, file de l'agent, jetons d'agent, historique, .env, journaux, entrée .mcp.json
400
+ shared/schemas/ schémas zod communs au serveur et à l'interface
401
+ shared/logic/ logique pure commune (préfixes, initiales, rangs fractionnaires, bilan d'un test, création du premier compte)
402
+ gui/ SPA Vite + React 19 + zustand, routage par hash
403
+ test/ tests node --test (test/launch.ts : lancement hermétique du site et du mode MCP)
404
+ e2e/ parcours Playwright sur le site réel ; captures dans e2e/screenshots/
405
+ ```
406
+
407
+ ### Tests
408
+
409
+ Les tests ne lisent jamais la configuration du développeur : `test/launch.ts`, partagé par les tests node et les parcours e2e, lance chaque processus avec `--dotenv=<fichier vide du répertoire temporaire>` et vide toutes les variables de configuration héritées du shell. Le site de test est lancé en `--server` sur un port libre de `127.0.0.1`, avec `ORGANIZER_PUBLIC_URL=http://127.0.0.1:<port>` ; les outils sont appelés par un client MCP réel sur le mode MCP, relié à ce site par un jeton créé par l'API de l'interface (`ORGANIZER_URL`, `ORGANIZER_TOKEN`). Un test vérifie, sur une copie du package dont le `.env` détourne le répertoire de données, que ce `.env` n'atteint pas un processus lancé par les tests (et qu'il l'atteindrait sans `--dotenv`).
410
+
411
+ **Tests node** : migrations, invariants, versions, journal des changements, connexions concurrentes ; comptes (amorçage avec nom obligatoire, `--set-user` et `--name`, connexion, sessions, liens d'accès, invitations, droits, e-mail) ; profil ; jetons d'agent (affichage unique, configuration du mode MCP, hash seul en base, révocation, expiration, perte d'appartenance, espace hors de portée) ; `--claude-setup-mcp` (entrée du mode MCP, `--url` et `--token` obligatoires, fusion, `--force`, options inconnues ou invalides, fichier illisible laissé intact) ; `--setup` (`.env`, unité systemd et bloc nginx écrits, premier compte créé, `.env` existant fusionné, publication sous un chemin, valeurs incohérentes ou manquantes refusées sans rien écrire, valeurs dotenv citées, site démarré depuis le `.env` écrit) ; `--backup` (copie de la base d'un site en service et de ses pièces jointes, restaurable ; destination existante refusée sans `--force` ; pièce jointe manquante qui ne laisse aucun fichier ; répertoire sans base refusé) ; `.env` et `--dotenv` ; tâches, tâches de test, pièces jointes, notifications, boîte à idées, dictée avec un serveur Groq simulé, historique et annulation ; chaque outil appelé par un client MCP du SDK sur le mode MCP relié à un site de test, jeton inconnu, révoqué ou expiré, site injoignable, espace hors portée, demande prise par un autre jeton, deux jetons de deux membres sur le même espace commun (prise atomique), mode MCP sans `ORGANIZER_URL` / `ORGANIZER_TOKEN`, mode MCP qui ne charge aucun module natif (crochet de résolution qui les bloque), `ORGANIZER_URL` en http refusée hors de la machine locale, redirection du site refusée sans y envoyer le jeton, `.env` du paquet ignoré par le mode MCP (lu avec `--dotenv`) ; commandes du site arrêtées avec la marche à suivre quand better-sqlite3 n'a pas de module natif (crochet de résolution qui simule l'échec) ; `list_requests` sur plus de 100 demandes closes (les plus récentes d'abord) ; jetons révoqués au départ ou au retrait de leur propriétaire, et migration qui révoque ceux des anciens membres ; erreurs internes au message générique ; sauvegarde privée pendant son écriture (droits observés hors Windows, test ignoré sous Windows) ; `--setup` qui refuse `--trust-proxy=oui` avec une écoute hors de la boucle locale, bloc nginx en `listen 443 ssl http2` ; notifications de l'agent (présence et délai de grâce du veilleur, ligne d'une demande, `GET notifications`, attente longue réveillée par une demande créée ou libérée, curseur qui rattrape une demande arrivée entre deux attentes, expiration, jeton inconnu, durée hors limites ; `enable_notifications` puis veilleur `--watch` réel qui écrit une ligne par nouvelle demande, sans le jeton dans la conversation, « already live » une fois armé, arrêt sur jeton révoqué, refus de démarrer sans relais) ; API des agents en HTTP direct (401 et 403 explicites, cookies ignorés, écriture sans `Origin`, réponses validées par les schémas partagés) ; site : `--help` (qui cite `--setup` et `--backup`) et `--version`, refus de connexion sans compte avec les commandes d'une installation par clonage et du paquet npm, démarrage refusé sans `ORGANIZER_PUBLIC_URL` ou `PORT` ou avec un chemin de base incohérent, SPA et `<base href>`, cookie (`Path`, `Secure` en https), formulaires multipart et 413 ; protections : modification refusée sans origine ou d'une origine étrangère, en-têtes de sécurité, limitation des connexions par couple (IP, e-mail) et par IP, rafale de tentatives parallèles limitée, compte non bloqué par un tiers, `X-Forwarded-For` ignoré sans `ORGANIZER_TRUST_PROXY` et pris en compte avec.
412
+
413
+ **Parcours e2e** (site compilé en `--server`, port libre, répertoire de données temporaire, premier compte créé par `ORGANIZER_ADMIN_*`). Chaque page est surveillée : une exception ou une erreur de console, dont toute violation de la CSP, fait échouer le parcours.
414
+
415
+ - écran de connexion sans compte : commandes `node dist/src/index.js --set-user …` et `npx -y @imenam/mcp-organizer --set-user …` ;
416
+ - connexion refusée puis réussie, création d'un espace commun, invitation, activation du compte invité par le lien dans un second navigateur ; invitation d'un second espace rejointe depuis « Invitations reçues », ancien lien refusé ;
417
+ - tâches, sous-tâches et sous-sous-tâches, glisser-déposer entre colonnes et dans une colonne (persisté), assignation notifiée chez l'assignée ;
418
+ - tâche de test à deux testeurs, résultats, review soumise qui crée une demande traitée par l'agent ; testeuse désassignée puis retirée, résultats conservés en lecture seule ;
419
+ - idée saisie dans l'interface → demande en attente → traitement par un client MCP réel sur le mode MCP, avec un jeton créé par l'API de l'interface (tâche créée, statut et priorité changés) → réponse, actions, jeton qui a traité la demande et dernier passage de l'agent visibles → annulation d'une action puis du lot, tâche retirée du tableau de bord ; demande restée en cours libérée depuis la page Agent puis reprise ; jeton dont le veilleur attend affiché « à l'écoute », réveillé par une demande ;
420
+ - portée : les agents de Julie (un jeton sur son espace personnel, un sur l'espace commun) ne voient que les demandes de leur espace et ne lisent ni ne modifient l'espace personnel de Matthieu, sur lequel elle ne peut pas créer de jeton ;
421
+ - panneau assistant, « Modifiée par l'agent · Annuler » et restauration d'une version ;
422
+ - boîte à idées : promotion manuelle et promotion confiée à l'agent ;
423
+ - commentaires notifiés aux assignés et aux commentateurs précédents, préférence désactivée ;
424
+ - deux utilisateurs, deux agents : Matthieu et Julie créent chacun leur jeton dans Paramètres > Agents et y relient un client MCP réel sur le mode MCP ; les deux agents de l'espace commun voient les demandes des deux membres ; l'agent de Matthieu traite l'idée de Julie, qui annule ses actions depuis la page Agent ; le jeton personnel de Julie est refusé hors de son espace ; le jeton de Julie, révoqué pendant qu'elle traite une demande, est refusé à l'appel suivant avec un message qui dit quoi faire ; la demande restée en cours est libérée depuis la page Agent et reprise par l'agent de Matthieu ;
425
+ - Paramètres : création d'un jeton montré une seule fois, configuration du mode MCP copiée, jeton absent de la page ensuite, révocation, nom affiché et mot de passe ;
426
+ - dictée : faux micro de Chromium et serveur Groq simulé.
427
+
428
+ Les attentes portent sur l'état de la page ou de l'API, jamais sur un délai fixe. Les captures (1440 px de large, 900 px de haut sauf les détails de tâche, à la hauteur de leur maquette ; données proches des maquettes) sont écrites dans `e2e/screenshots/` : `01-tableau-de-bord`, `02-detail-tache-standard`, `03-detail-tache-de-test`, `04-saisir-une-idee`, `05-panneau-assistant`, `06-agent`, `06-agent-deux-agents`, `08-idees`, `09-menu-espaces`, `09-membres`, `09-invitations-recues`, `10-connexion`, `10-connexion-aucun-compte`, `11-parametres`, `11-parametres-jeton-cree`, `11-parametres-agents`, `12-notifications`, et quelques états intermédiaires.
429
+
430
+ ## Fonctionnement
431
+
432
+ ### L'interface
433
+
434
+ Thème sombre, épuré, une seule action principale par écran. L'interface se rafraîchit en interrogeant `GET /api/changes` toutes les 3 s, sans connexion permanente, et ne recharge que les données touchées ; les écritures de l'agent, faites par l'API des agents, apparaissent ainsi.
435
+
436
+ | Écran | Contenu |
437
+ |---|---|
438
+ | **Connexion** | Carte unique : e-mail, mot de passe, « Se connecter ». |
439
+ | **Activation** (`#/activation/<jeton>`) | Lien d'invitation : nom affiché, mot de passe, confirmation ; l'activation ouvre la session. |
440
+ | **Coquille** | Barre latérale : sélecteur d'espace (menu : espaces, « Invitations reçues » quand il y en a, signalées par un point sur le sélecteur, « Membres », « Nouvel espace commun »), Tableau de bord, Idées, Agent avec le nombre de demandes en attente, utilisateur en bas (Paramètres, Déconnexion). En-tête : titre, recherche, cloche, bouton principal « Saisir une idée ». Bulle de l'assistant en bas à droite. |
441
+ | **Tableau de bord** | Kanban à quatre colonnes figées (À faire, En cours, En revue, Terminé) avec glisser-déposer (statut et ordre, souris ou clavier), « Ajouter une tâche » en bas de colonne, onglet « Liste », menu « Filtrer » (type, priorité, assigné, tag). Une carte montre le titre, la priorité si elle est Haute ou Critique, l'icône d'un test et ses KO, l'avancement des sous-tâches et les avatars ; une tâche terminée se réduit à son titre. |
442
+ | **Détail d'une tâche** (`#/task/<id>`) | Une colonne : fil d'Ariane, titre, ligne statut · priorité · assignés · menu ⋯ (tags, type, tâche testée, historique, suppression) ; « Modifiée par l'agent il y a … · Annuler » ; description markdown ; pièces jointes ; checklist ; Kanban des sous-tâches (avec le nombre de sous-sous-tâches) ; commentaires. |
443
+ | **Détail d'un test** | Même gabarit avec « Test », la ligne « Teste ORG-41 … », la barre de bilan et « 9/12 vérifiés · 2 KO », un onglet par testeur, puis un onglet en lecture seule par ancien testeur (désassigné ou parti de l'espace), un résultat par point (OK, KO, Bloqué, À vérifier, avec commentaire), « Ma review » avec dictée et « Soumettre la review », reviews des autres testeurs repliées avec l'état de leur demande. |
444
+ | **Saisir une idée** | Modale : texte, micro, « Envoyer » ; « L'agent la classera à son prochain passage ». |
445
+ | **Assistant** | Panneau latéral : contexte « À propos de ORG-41 » (tâche ouverte), fil des demandes de l'utilisateur et des réponses de l'agent, statut, « Modifier · Annuler » en attente, « Libérer » en cours, nom du jeton qui traite ou a traité la demande (son propriétaire en infobulle), « N actions · Annuler ». |
446
+ | **Agent** (`#/agent`) | État des agents de l'espace, rechargé toutes les 30 s : chaque jeton actif qui le couvre, son propriétaire et son dernier passage (point allumé s'il date de moins de 10 min), ou « à l'écoute » quand son veilleur de notifications est armé, ou « Aucun agent sur cet espace · Ajouter un agent » (lien vers Paramètres) ; « Filtrer » ; demandes En attente (crayon et croix au survol), En cours (jeton qui l'a prise, « Libérer »), Traitées (jeton qui l'a traitée ; elles se déplient : réponse, actions avec « Annuler », « Tout annuler »), Annulées repliées. |
447
+ | **Idées** (`#/idees`) | Liste avec ajout rapide, idées classées par l'agent, « Promouvoir » ; tiroir « Promouvoir en tâche » : « Laisser l'agent la formater », ou « la remplir moi-même » (titre, type, priorité). |
448
+ | **Membres** | Modale : invitation par adresse, membres et rôles, invitations en attente (renvoyer, révoquer, lien à copier quand l'e-mail ne part pas), renommer ou supprimer l'espace. |
449
+ | **Invitations reçues** | Modale ouverte depuis le menu du sélecteur d'espace : pour chaque espace qui invite l'utilisateur, l'auteur de l'invitation, « Refuser » et « Rejoindre » (qui ouvre l'espace). |
450
+ | **Notifications** | Menu sous la cloche : non lues d'abord, phrase courte, extrait du commentaire, « Tout marquer comme lu » ; un clic ouvre la tâche. |
451
+ | **Paramètres** (`#/parametres`) | Profil (nom affiché, mot de passe avec l'ancien) ; Agents (jetons : nom, espace, dernier usage, état ; « Créer un jeton », « Révoquer ») ; Notifications (assignation / commentaire × application / e-mail, avec la raison quand les e-mails ne partent pas). |
452
+
453
+ « Créer un jeton » demande un nom, un espace parmi ceux de l'utilisateur et une expiration facultative (7 jours, 30 jours, 90 jours, 1 an), puis affiche une seule fois le jeton et « Copier la configuration », qui copie l'entrée `.mcp.json` du mode MCP prête à coller (voir « Jetons d'agent »). Le presse-papiers exige HTTPS ou localhost ; sinon la page dit pourquoi la copie échoue.
454
+
455
+ ### Espaces, membres et droits
456
+
457
+ - Chaque compte actif a un **espace personnel** (« Personnel »), créé à son activation, dont il est le seul membre.
458
+ - Un **espace commun** a un propriétaire et des membres. Son préfixe de tâches (`RSV` dans `RSV-12`) est dérivé de son nom et reste modifiable à la création.
459
+ - **Inviter** une adresse (propriétaire) : un compte actif devient membre immédiatement ; une adresse inconnue reçoit un compte `invited` et un lien d'activation à usage unique, valable 7 jours, envoyé par e-mail et affiché à l'invitant. Un compte `invited` peut être invité par plusieurs espaces : chaque invitation a son propre lien, et inviter l'adresse depuis un autre espace ne révoque pas le lien déjà envoyé.
460
+ - **Un lien n'accepte que son invitation** : il active le compte (nom affiché, mot de passe, espace personnel) et fait rejoindre l'espace qui l'a émis, lui seul. Les autres invitations restent en attente : une fois connecté, l'utilisateur les accepte ou les refuse dans « Invitations reçues ». Une fois le compte actif, ses autres liens d'activation ne fonctionnent plus et le disent.
461
+ - Une invitation se **renvoie** (nouvelle échéance ; nouveau lien, qui remplace celui de cette invitation seulement, tant que le compte n'est pas activé ; pour un compte déjà actif, l'invitation l'attend dans « Invitations reçues ») ou se **révoque** (son lien ne fonctionne plus). Une invitation expirée n'apparaît plus dans « Invitations reçues » tant qu'elle n'est pas renvoyée.
462
+ - Le propriétaire renomme, supprime l'espace et retire des membres ; un membre peut quitter l'espace. Un membre encore assigné à une tâche doit en être désassigné d'abord ; ses résultats et reviews de test restent en historique, en lecture seule.
463
+
464
+ | Action | Qui |
465
+ |---|---|
466
+ | Voir un espace, ses membres et ses invitations ; lire et modifier ses tâches, idées, commentaires | Membres de l'espace |
467
+ | Modifier ou supprimer un commentaire | Son auteur |
468
+ | Saisir ses résultats et sa review sur une tâche de test | Ses testeurs (assignés) |
469
+ | Inviter, révoquer, retirer un membre, renommer, supprimer l'espace | Propriétaire |
470
+ | Accepter ou refuser une invitation | Son destinataire, connecté |
471
+ | Voir une demande, la modifier ou l'annuler en attente, la libérer quand l'agent l'a prise | Membres de l'espace où elle est écrite |
472
+ | Voir les actions de l'agent et les annuler | Membres de l'espace de la demande : tous dans un espace commun, le propriétaire seul pour son espace personnel |
473
+ | Agent | Droits du propriétaire du jeton, limités à l'espace du jeton, pour une demande prise par ce jeton |
474
+
475
+ Un non-membre reçoit la même réponse qu'un espace ou une tâche inexistants (404) ; un membre qui tente une action réservée au propriétaire reçoit un refus (403). Les droits sont vérifiés dans la transaction de l'écriture : un membre retiré par un autre processus entre-temps est refusé.
476
+
477
+ ### Tâches
478
+
479
+ - Une **tâche** porte un numéro propre à l'espace (`ORG-41`, jamais réutilisé), un titre, une description markdown, un statut, une priorité (Basse, Moyenne, Haute, Critique), un type (standard ou test), des assignés (membres de l'espace) et des tags (créés au premier usage).
480
+ - **Sous-tâches** sur deux niveaux : tâche > sous-tâche > sous-sous-tâche, dans le même espace, sans cycle.
481
+ - **Ordre** : chaque colonne (espace, parent, statut) est ordonnée par un rang fractionnaire, pris au milieu des deux voisines ; la colonne est renumérotée quand leur écart devient trop faible. La checklist et les points de vérification suivent la même règle.
482
+ - **Pièces jointes** : 20 Mo au plus, stockées sous `<données>/attachments/<id>` ; supprimer une pièce jointe, sa tâche ou son espace efface le fichier.
483
+ - **Tâches de test** : des points de vérification à la place d'une checklist ; chaque testeur saisit son propre résultat par point (`ok`, `ko`, `blocked`, `untested`) et un commentaire. Le bilan d'un point est KO si un testeur a trouvé un KO, sinon Bloqué, sinon OK, sinon à vérifier. Un test peut désigner la tâche qu'il vérifie. La review se rédige en brouillon puis se soumet : elle est figée et crée une demande `test_review` qui vise l'espace de la tâche.
484
+
485
+ ### Demandes
486
+
487
+ - **Types** : `idea` (« Saisir une idée »), `message` (panneau assistant, avec la tâche ouverte pour contexte), `remark`, `test_review` et `idea_promotion`.
488
+ - **Cycle** : `pending`, puis `processing` quand l'agent la prend, puis `done` avec sa réponse ou `failed` avec la raison. Une demande en attente se modifie ou s'annule (`cancelled`). Une demande restée `processing` (agent arrêté en cours de route) se **libère** : un membre de son espace la repasse `pending`, et un agent pourra la reprendre ; les actions déjà faites restent dans son lot. Rien ne la libère automatiquement.
489
+ - **Lot** : chaque écriture de l'agent pour une demande est une action numérotée, avec l'état de l'entité avant et après et un résumé (« Statut En cours → En revue », « Assigné à Julie Laurent »).
490
+
491
+ ### Notifications
492
+
493
+ - **Assignation** : un utilisateur ajouté aux assignés d'une tâche est notifié, sauf s'il s'assigne lui-même.
494
+ - **Commentaire** : notifie les assignés de la tâche et ceux qui l'ont déjà commentée, sauf l'auteur.
495
+ - L'auteur d'une notification est un utilisateur ou l'agent (avec la demande pour laquelle il agit).
496
+ - **Préférences** (toutes activées par défaut) : assignation et commentaire, chacun dans l'application et par e-mail. L'e-mail part du site après la validation de la transaction, y compris pour les actions de l'agent (le mode MCP n'envoie rien), avec un lien vers la tâche ; son issue (`emailedAt`, ou `emailError` avec la raison) est enregistrée.
497
+
498
+ ### Boîte à idées
499
+
500
+ - Idées manuelles (ajout rapide) et idées classées par l'agent (`create_idea`).
501
+ - **Promotion manuelle** : titre, type et priorité ; la tâche est créée dans « À faire » avec l'idée pour description, et l'idée sort de la liste active.
502
+ - **Promotion confiée à l'agent** : une demande `idea_promotion` ; une idée a au plus une promotion ouverte, pendant laquelle la promotion manuelle est refusée.
503
+ - Une idée s'archive et se désarchive ; on ne promeut ni une idée archivée, ni une idée déjà promue.
504
+
505
+ ### Jetons d'agent
506
+
507
+ - Un **jeton** se crée dans Paramètres > Agents. Il couvre **exactement un espace** : l'espace personnel de son créateur ou un espace commun dont il est membre.
508
+ - Il vaut `org_` suivi de 32 octets aléatoires en base64url. Il n'est affiché qu'une fois, à sa création ; la base n'en garde que le hash sha256 et le début (`org_` et 8 caractères), qui sert à le reconnaître.
509
+ - La création renvoie aussi l'entrée `.mcp.json` du mode MCP : `npx -y @imenam/mcp-organizer` avec `ORGANIZER_URL` (l'URL publique du site) et `ORGANIZER_TOKEN` (le jeton).
510
+ - Un jeton est vérifié à chaque usage : il ne doit être ni révoqué ni expiré, son propriétaire doit avoir un compte actif et être toujours membre de l'espace couvert. Chaque usage accepté met à jour son « dernier usage ».
511
+ - État affiché : actif ; expiré ; **révoqué**, par son propriétaire ou quand celui-ci quitte l'espace ou en est retiré (dans la même transaction que le départ : une nouvelle invitation ne le fait pas revivre, il faut créer un nouveau jeton) ; inactif quand le compte de son propriétaire n'est plus actif. Seul son propriétaire voit, révoque un jeton ; la révocation est immédiate et définitive.
512
+ - Les membres d'un espace voient, sur la page Agent, le nom et le propriétaire des jetons actifs qui le couvrent et leur dernier passage, ainsi que le jeton qui a pris ou traité chaque demande ; jamais leur valeur.
513
+
514
+ ### Dictée vocale
515
+
516
+ Le navigateur enregistre le micro en webm (MediaRecorder) et envoie l'enregistrement à `POST /api/transcribe` (25 Mo au plus). Le site appelle `POST https://api.groq.com/openai/v1/audio/transcriptions` (`Authorization: Bearer <GROQ_API_KEY>`, multipart `file`, `model`, `language=fr`, `response_format=json`) et renvoie le texte, inséré au curseur. Une erreur de Groq remonte telle quelle, avec son code et son message ; un 401 de Groq ne ferme pas la session.
517
+
518
+ ### API de l'interface
519
+
520
+ | Route | Rôle |
521
+ |---|---|
522
+ | `GET /api/health` | État du serveur et `accountsExist` |
523
+ | `POST /api/auth/login`, `POST /api/auth/logout`, `GET /api/auth/me` | Connexion (limitée, voir « Sécurité »), déconnexion, utilisateur connecté |
524
+ | `GET` / `POST /api/access-links/:token` | Validité d'un lien ; activation (nom affiché, mot de passe) |
525
+ | `PATCH /api/profile`, `POST /api/profile/password` | Nom affiché ; mot de passe `{ currentPassword, newPassword }` (les autres sessions sont fermées) |
526
+ | `GET` / `POST /api/spaces`, `PATCH` / `DELETE /api/spaces/:id` | Espaces ; création d'un espace commun ; renommage, suppression |
527
+ | `GET /api/spaces/:id/members`, `DELETE …/members/:userId`, `POST …/leave` | Membres et invitations ; retrait ; départ |
528
+ | `POST /api/spaces/:id/invitations`, `POST …/invitations/:id/resend`, `DELETE …/invitations/:id` | Invitation, renvoi (`link` nul pour un compte déjà actif), révocation |
529
+ | `GET /api/invitations`, `POST /api/invitations/:id/accept`, `POST /api/invitations/:id/decline` | Invitations reçues par l'utilisateur connecté ; acceptation ; refus |
530
+ | `GET` / `POST /api/spaces/:id/tasks` | Arbre des tâches (filtres `type`, `priority`, `assignee`, `tag`) ; création |
531
+ | `GET` / `PATCH` / `DELETE /api/tasks/:id`, `POST /api/tasks/:id/move` | Détail, modification, suppression ; déplacement Kanban `{ status, afterId }` |
532
+ | `PUT /api/tasks/:id/assignees`, `PUT /api/tasks/:id/tags` | Assignés ; tags |
533
+ | `POST /api/tasks/:id/checklist`, `PATCH` / `DELETE /api/checklist/:id`, `POST /api/checklist/:id/move` | Checklist |
534
+ | `POST /api/tasks/:id/checkpoints`, `PATCH` / `DELETE /api/checkpoints/:id`, `POST …/move`, `PUT …/result` | Points de vérification et résultats |
535
+ | `PUT /api/tasks/:id/review`, `POST /api/tasks/:id/review/submit` | Brouillon de review ; soumission |
536
+ | `POST /api/tasks/:id/comments`, `PATCH` / `DELETE /api/comments/:id` | Commentaires |
537
+ | `POST /api/tasks/:id/attachments` (multipart, champ `file`), `GET` / `DELETE /api/attachments/:id` | Pièces jointes |
538
+ | `GET` / `POST /api/spaces/:id/ideas`, `PATCH /api/ideas/:id`, `POST /api/ideas/:id/promote`, `POST /api/ideas/:id/delegate` | Idées, archivage, promotion manuelle, promotion confiée à l'agent |
539
+ | `GET` / `POST /api/spaces/:id/requests`, `GET` / `PATCH /api/requests/:id`, `POST /api/requests/:id/cancel`, `POST /api/requests/:id/release` | File de l'agent (`pendingCount` compte toutes les demandes en attente, au-delà des 200 renvoyées ; `agents` liste les jetons actifs de l'espace avec leur dernier usage ; chaque demande porte `agent`, le jeton qui l'a prise) ; dépôt, détail avec les actions du lot, modification et annulation en attente, libération d'une demande en cours |
540
+ | `POST /api/agent-actions/:id/undo`, `POST /api/requests/:id/undo` | Annulation d'une action ; d'un lot |
541
+ | `GET /api/tasks/:id/versions`, `POST /api/tasks/:id/versions/:version/restore` | Versions d'une tâche ; restauration |
542
+ | `GET /api/notifications`, `GET …/unread-count`, `POST …/:id/read`, `POST …/read-all` | Notifications |
543
+ | `GET` / `PUT /api/settings` | Préférences de notification ; disponibilité de l'e-mail et de la dictée |
544
+ | `GET` / `POST /api/tokens`, `POST /api/tokens/:id/revoke` | Jetons d'agent de l'utilisateur ; création `{ name, spaceId, expiresInDays }` (renvoie une seule fois `value` et `mcpJson`, l’entrée `.mcp.json` du mode MCP) ; révocation |
545
+ | `POST /api/transcribe` (multipart, champ `file`) | Dictée |
546
+ | `GET /api/changes?since=<seq>` | Changements des espaces de l'utilisateur et de ses notifications |
547
+
548
+ Toutes les routes exigent une session, sauf la santé, la connexion, la déconnexion et les liens d'accès. Un segment de chemin mal encodé (`%` invalide) reçoit un 400 ; un corps de plus de 25 Mo, un 413.