fix: fix some ui displays and features miss implemented
This commit is contained in:
19
.ideai/agents.json
Normal file
19
.ideai/agents.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 1,
|
||||
"agents": [
|
||||
{
|
||||
"agentId": "a6ced819-b893-4213-b003-9e9dc79b9641",
|
||||
"name": "Main",
|
||||
"mdPath": "agents/main.md",
|
||||
"profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4",
|
||||
"synchronized": false
|
||||
},
|
||||
{
|
||||
"agentId": "dce19c75-9669-4e45-b8de-9950025157da",
|
||||
"name": "Architect",
|
||||
"mdPath": "agents/architect.md",
|
||||
"profileId": "664cc20c-47b8-53ad-9351-dce3c09c0de4",
|
||||
"synchronized": false
|
||||
}
|
||||
]
|
||||
}
|
||||
601
.ideai/agents/architect.md
Normal file
601
.ideai/agents/architect.md
Normal file
@ -0,0 +1,601 @@
|
||||
# IdeA — Cartographie d'Architecture
|
||||
|
||||
> Document de référence produit par l'**Agent Architecture**.
|
||||
> Fait autorité sur les frontières, ports, adapters, modules et conventions.
|
||||
> Toute feature DOIT être validée contre ce document avant développement.
|
||||
> Architecture **Hexagonale (Ports & Adapters)** + **SOLID**, stricte.
|
||||
>
|
||||
> Stack non négociable : Tauri v2 (shell) · Rust (cœur hexagonal) · TypeScript + React (UI) · xterm.js + portable-pty (terminaux) · git2/libgit2 · russh/ssh2 · wsl.exe.
|
||||
|
||||
---
|
||||
|
||||
## 1. Principes : SOLID + Hexagonal, appliqués concrètement
|
||||
|
||||
### 1.1 Règle de dépendance (la seule qui compte)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Le sens des dépendances │
|
||||
│ │
|
||||
Présentation ─► Application ─► Domaine ◄─ Infrastructure │
|
||||
(React/Tauri) (use cases) (pur) (adapters) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Le Domaine ne dépend de RIEN** : ni Tauri, ni tokio, ni git2, ni portable-pty, ni serde (le moins possible — voir §1.4). Il ne contient que des entités, value objects, règles métier et **traits = ports**.
|
||||
- **L'Application** dépend du Domaine. Elle orchestre les use cases en parlant **uniquement aux ports** (traits), jamais aux adapters concrets.
|
||||
- **L'Infrastructure** dépend du Domaine et de l'Application (elle implémente les ports). Elle contient tous les détails techniques (PTY, FS, git, SSH, WSL, stores).
|
||||
- **La Présentation** (Tauri commands + React) dépend de l'Application. Les commandes Tauri sont des **adapters entrants (driving adapters)** ; les impl de ports sont des **adapters sortants (driven adapters)**.
|
||||
|
||||
Aucune flèche ne pointe **vers** la présentation ou l'infrastructure. L'inversion de dépendance (le **D** de SOLID) est matérialisée par les traits définis dans le domaine et implémentés dehors.
|
||||
|
||||
### 1.2 SOLID, point par point, traduit IdeA
|
||||
|
||||
| Principe | Application concrète |
|
||||
|---|---|
|
||||
| **S** — Single Responsibility | Un use case = une intention métier (`LaunchAgent`, `SyncAgentWithTemplate`). Un adapter = une techno (`Git2Repository` ne fait que du git). Le `LayoutNode` ne gère que la topologie, pas le rendu. |
|
||||
| **O** — Open/Closed | Ajouter une IA = ajouter un **profil déclaratif** (donnée), pas du code. Ajouter un mode distant = nouvel adapter `RemoteHost` sans toucher aux use cases. Ajouter une stratégie d'injection de contexte = nouvelle variante d'enum + handler, use case inchangé. |
|
||||
| **L** — Liskov | Tout `RemoteHost` (local, SSH, WSL) est substituable : un use case marche identiquement quelle que soit l'impl. Les contrats (pré/postconditions) des ports sont documentés et respectés par chaque adapter. |
|
||||
| **I** — Interface Segregation | Ports **fins et ciblés** : `ProcessSpawner`, `FileSystem`, `PtyPort` séparés plutôt qu'un `System` fourre-tout. Un use case ne reçoit que les ports qu'il consomme. |
|
||||
| **D** — Dependency Inversion | Domaine définit les traits ; infra les implémente ; l'application reçoit des `Arc<dyn Port>` par **injection** (composition root dans la couche Tauri). |
|
||||
|
||||
### 1.3 Hexagonal côté Frontend (React aussi)
|
||||
|
||||
L'hexagonal ne s'arrête pas à Rust. Côté React on applique le même découpage :
|
||||
|
||||
- **Domaine UI / modèles de vue** : types TS purs (miroir des DTO), logique de présentation pure (ex. calcul de tailles de cellules d'un `LayoutNode`), testable sans React ni Tauri.
|
||||
- **Ports UI** : interfaces TS (`AgentGateway`, `TerminalGateway`, `ProjectGateway`, `LayoutGateway`, `GitGateway`, `RemoteGateway`) décrivant **ce dont l'UI a besoin**, indépendamment du transport.
|
||||
- **Adapters UI** : implémentation des ports via `@tauri-apps/api` (`invoke` pour commands, `listen` pour events). Remplaçables par des **mocks** en test/Storybook.
|
||||
- **Présentation** : composants React, hooks, state (Zustand/Redux) qui consomment les ports UI, jamais `invoke()` en direct.
|
||||
|
||||
Bénéfice : le frontend est testable et développable sans backend (adapters mock), et la frontière IPC est centralisée en un seul endroit.
|
||||
|
||||
### 1.4 Domaine pur vs adapters — règle pratique Rust
|
||||
|
||||
- Le crate `domain` est **`#![no_std]`-friendly d'esprit** (pas imposé), sans dépendance I/O. Tolérance pragmatique : `serde` est autorisé **uniquement** pour dériver la (dé)sérialisation des entités persistées (manifeste, layout, profils), car c'est une contrainte métier de format, pas un détail technique d'I/O. Les **traits/ports** y vivent. Pas de `tokio`, pas de `std::process`, pas de `std::fs`.
|
||||
- Tout ce qui touche le monde réel (`std::fs`, `Command`, sockets, libgit2, PTY) vit **exclusivement** dans `infrastructure`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Découpage en couches & frontière Rust ↔ Tauri ↔ React
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ PRÉSENTATION (Frontend) — TypeScript + React + xterm.js │
|
||||
│ features/* · ui-ports (gateways) · tauri-adapters (invoke/listen) │
|
||||
└───────────────────────────────┬───────────────────────────────────────┘
|
||||
│ IPC Tauri (commands ⇄ events, JSON)
|
||||
┌───────────────────────────────▼───────────────────────────────────────┐
|
||||
│ PRÉSENTATION (Backend) — crate `app-tauri` (DRIVING ADAPTER) │
|
||||
│ #[tauri::command] handlers · event emitters · COMPOSITION ROOT (DI) │
|
||||
│ PTY byte-stream bridge ⇄ xterm.js │
|
||||
└───────────────────────────────┬───────────────────────────────────────┘
|
||||
│ appels de use cases (Arc<UseCase>)
|
||||
┌───────────────────────────────▼───────────────────────────────────────┐
|
||||
│ APPLICATION — crate `application` │
|
||||
│ Use cases / services · DTOs · orchestration · transactions métier │
|
||||
│ Dépend UNIQUEMENT des ports (traits) du domaine │
|
||||
└───────────────────────────────┬───────────────────────────────────────┘
|
||||
│ implémente / consomme
|
||||
┌───────────────────────────────▼───────────────────────────────────────┐
|
||||
│ DOMAINE — crate `domain` (PUR, sans I/O) │
|
||||
│ Entities · Value Objects · Invariants · PORTS (traits) · DomainEvents │
|
||||
└───────────────────────────────▲───────────────────────────────────────┘
|
||||
│ implémentent les ports (DRIVEN ADAPTERS)
|
||||
┌───────────────────────────────┴───────────────────────────────────────┐
|
||||
│ INFRASTRUCTURE — crate `infrastructure` │
|
||||
│ portable-pty · git2 · russh/ssh2 · wsl.exe · fs local · md/json store │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Frontière IPC Tauri — deux directions
|
||||
|
||||
- **Commands (Frontend → Backend, request/response)** : `invoke("create_project", {...})`. Le handler `#[tauri::command]` désérialise le DTO, appelle le use case, renvoie un `Result<DTO, ErrorDTO>`. **Stateless** côté forme : tout l'état vit dans des services managés via `tauri::State`.
|
||||
- **Events (Backend → Frontend, push)** : flux PTY (octets/base64), changements de statut d'agent, fin de processus, progrès git, drift de template détecté. Émis via `app_handle.emit(...)` / channels Tauri. L'`EventBus` domaine est relayé vers ces events Tauri par un adapter dans `app-tauri`.
|
||||
|
||||
> **Décision** : le flux PTY haute fréquence passe par des **Tauri Channels** (`tauri::ipc::Channel`) plutôt que des events globaux, pour la perf et l'isolement par session terminal.
|
||||
|
||||
---
|
||||
|
||||
## 3. Modèle de domaine
|
||||
|
||||
### 3.1 Vue d'ensemble (relations)
|
||||
|
||||
```
|
||||
Workspace 1───* Window 1───* Tab 1───1 Project
|
||||
│ │
|
||||
│ 1 ├──* Agent ─────? AgentTemplate (origine)
|
||||
│ │ │ 1
|
||||
│ 1 │ └──1 AgentProfile (runtime IA, par réf id)
|
||||
LayoutTree ├──1 GitRepository
|
||||
(LayoutNode récursif) ├──1 RemoteHost (Local | Ssh | Wsl)
|
||||
│ feuilles └──1 AgentManifest (.ideai/agents.json)
|
||||
▼
|
||||
TerminalSession 1───? Agent (si lancé par un agent)
|
||||
```
|
||||
|
||||
### 3.2 Entités & Value Objects (avec invariants)
|
||||
|
||||
**`ProjectId`, `AgentId`, `TemplateId`, `ProfileId`, `SessionId`, `WindowId`, `TabId`, `NodeId`** — VO `newtype(Uuid)` ou string typée. Invariant : non vide, immuable.
|
||||
|
||||
**`Project`** (entité, racine d'agrégat projet)
|
||||
- Champs : `id`, `name`, `root: ProjectPath`, `remote: RemoteRef`, `created_at`.
|
||||
- Invariants : `root` doit être un chemin **absolu et valide pour son `RemoteRef`** ; deux projets ne peuvent partager le même `(remote, root)`.
|
||||
|
||||
**`ProjectPath`** (VO) — chemin absolu normalisé, conscient de la plateforme cible (POSIX vs Windows vs WSL `/mnt/...`).
|
||||
|
||||
**`Agent`** (entité)
|
||||
- Champs : `id`, `name`, `context: AgentContextRef` (chemin du `.md` dans `.ideai/`), `profile_id: ProfileId`, `origin: AgentOrigin` (`Scratch` | `FromTemplate { template_id, synced_version }`), `synchronized: bool`.
|
||||
- Invariants : `synchronized == true` ⇒ `origin == FromTemplate{..}` (on ne peut pas synchroniser un agent créé from scratch). `context` doit exister à l'activation. `profile_id` doit référencer un `AgentProfile` connu.
|
||||
|
||||
**`AgentTemplate`** (entité, store global)
|
||||
- Champs : `id`, `name`, `content_md: MarkdownDoc`, `version: TemplateVersion`, `default_profile_id`.
|
||||
- Invariants : `version` **monotone croissante** ; toute modification du `content_md` ⇒ `version + 1` (voir §8).
|
||||
|
||||
**`AgentProfile`** (entité de config runtime IA — le port `AgentRuntime` est paramétré par elle)
|
||||
- Champs : `id`, `name`, `command: String`, `args: Vec<String>`, `context_injection: ContextInjection`, `detect: Option<String>`, `cwd_template: String` (ex. `"{projectRoot}"`).
|
||||
- Invariants : `command` non vide ; cohérence de `ContextInjection` (voir VO ci-dessous).
|
||||
|
||||
**`ContextInjection`** (VO, enum — cœur du moteur IA flexible)
|
||||
```
|
||||
ContextInjection =
|
||||
| ConventionFile { target: String } // ex. "CLAUDE.md" / "AGENTS.md" / "GEMINI.md"
|
||||
| Flag { flag: String } // ex. "--context-file {path}" ou "-f"
|
||||
| Stdin // pipe du contenu md sur stdin
|
||||
| Env { var: String } // ex. "AGENT_CONTEXT_FILE"
|
||||
```
|
||||
- Invariants : `ConventionFile.target` est un nom de fichier relatif (pas de `..`, pas absolu) ; `Env.var` est un identifiant d'env valide ; `Flag.flag` non vide.
|
||||
|
||||
**`TerminalSession`** (entité)
|
||||
- Champs : `id`, `node_id` (cellule du layout qui l'héberge), `cwd: ProjectPath`, `kind: SessionKind` (`Plain` | `Agent { agent_id }`), `pty_size: PtySize { rows, cols }`, `status` (`Starting|Running|Exited{code}`).
|
||||
- Invariants : une cellule (feuille de layout) héberge **au plus une** `TerminalSession` active. `pty_size.rows>0 && cols>0`.
|
||||
|
||||
**`LayoutNode` / `LayoutTree`** (VO récursif — voir §7 pour le détail complet)
|
||||
- Invariants : poids relatifs strictement positifs ; somme normalisable ; pas de fusion qui chevauche deux conteneurs distincts ; un `Leaf` référence 0 ou 1 `SessionId`.
|
||||
|
||||
**`RemoteHost`** (VO de stratégie de localisation — abstrait Local/SSH/WSL)
|
||||
```
|
||||
RemoteRef =
|
||||
| Local
|
||||
| Ssh { host, port, user, auth: SshAuth, remote_root }
|
||||
| Wsl { distro: String }
|
||||
```
|
||||
- Invariants : `Ssh.port` ∈ 1..=65535 ; `Wsl.distro` non vide ; pour `Ssh`/`Wsl`, les chemins projet sont interprétés côté distant.
|
||||
|
||||
**`GitRepository`** (entité)
|
||||
- Champs : `project_id`, `root`, `current_branch`, `is_dirty`.
|
||||
- Invariants : `root` contient (ou contiendra après init) un `.git`. État dérivé, rafraîchi via le port.
|
||||
|
||||
**`AgentManifest`** (entité — image en mémoire de `.ideai/agents.json`)
|
||||
- Champs : `entries: Vec<ManifestEntry { agent_id, md_path, template_id?, synchronized, synced_template_version? }>`.
|
||||
- Invariants : `synchronized ⇒ template_id.is_some() && synced_template_version.is_some()` ; `md_path` unique ; cohérence avec les `Agent` chargés.
|
||||
|
||||
**`Workspace` / `Window` / `Tab`** (entités de présentation persistée)
|
||||
- `Workspace` = ensemble des fenêtres d'une session utilisateur.
|
||||
- `Window` = fenêtre OS ; possède un `LayoutTree` **par onglet actif** et une liste de `Tab`.
|
||||
- `Tab` = onglet ⇔ **un `Project`** (1:1).
|
||||
- Invariants : un `Project` ouvert apparaît dans **exactement un** `Tab` à la fois (le drag déplace, ne duplique pas) ; un `Window` a ≥ 1 `Tab` ou est fermée.
|
||||
|
||||
**`DomainEvent`** (enum) — `ProjectCreated`, `AgentLaunched`, `AgentExited`, `TemplateUpdated`, `AgentDriftDetected`, `LayoutChanged`, `RemoteConnected`, `GitStateChanged`, `PtyOutput{session_id, bytes}` (ce dernier souvent court-circuité vers un Channel).
|
||||
|
||||
---
|
||||
|
||||
## 4. Ports (traits du domaine)
|
||||
|
||||
> Signatures **conceptuelles** (Rust idiomatique, `async` via `async_trait` ou retours `Future` ; erreurs typées par port). « Consommé par » = use cases. « Implémenté par » = adapters de §5.
|
||||
|
||||
### `AgentRuntime`
|
||||
- **Rôle** : lancer/piloter la CLI d'une IA selon un `AgentProfile`, en gérant l'injection du contexte `.md`.
|
||||
- **Signature** :
|
||||
```rust
|
||||
trait AgentRuntime {
|
||||
fn detect(&self, profile: &AgentProfile) -> Result<bool, RuntimeError>;
|
||||
fn prepare_invocation(&self, profile: &AgentProfile, ctx: &PreparedContext, cwd: &ProjectPath)
|
||||
-> Result<SpawnSpec, RuntimeError>; // commande + args + plan d'injection (fichier/flag/stdin/env)
|
||||
}
|
||||
```
|
||||
- **Consommé par** : `LaunchAgent`, `DetectProfilesUseCase` (first-run).
|
||||
- **Implémenté par** : `CliAgentRuntime` (un seul adapter générique piloté par le profil déclaratif — c'est l'**Open/Closed**). La diversité des IA = données, pas code.
|
||||
|
||||
### `PtyPort` (alias domaine de `TerminalSessionPort`)
|
||||
- **Rôle** : ouvrir un pseudo-terminal, lire/écrire, redimensionner, tuer.
|
||||
- **Signature** :
|
||||
```rust
|
||||
trait PtyPort {
|
||||
async fn spawn(&self, spec: SpawnSpec, size: PtySize) -> Result<PtyHandle, PtyError>;
|
||||
fn write(&self, h: &PtyHandle, data: &[u8]) -> Result<(), PtyError>;
|
||||
fn resize(&self, h: &PtyHandle, size: PtySize) -> Result<(), PtyError>;
|
||||
fn subscribe_output(&self, h: &PtyHandle) -> OutputStream; // flux d'octets
|
||||
async fn kill(&self, h: &PtyHandle) -> Result<ExitStatus, PtyError>;
|
||||
}
|
||||
```
|
||||
- **Consommé par** : `OpenTerminal`, `LaunchAgent`, `CloseTerminal`.
|
||||
- **Implémenté par** : `PortablePtyAdapter` (local), `SshPtyAdapter` (PTY distant via russh exec/shell), `WslPtyAdapter` (PTY via `wsl.exe`). Sélection par stratégie `RemoteRef` (Liskov).
|
||||
|
||||
### `RemoteHost`
|
||||
- **Rôle** : abstraction de la **localisation d'exécution** (local / SSH / WSL) : exécuter une commande, ouvrir un PTY, accéder au FS, dans le bon contexte.
|
||||
- **Signature** :
|
||||
```rust
|
||||
trait RemoteHost {
|
||||
fn kind(&self) -> RemoteKind;
|
||||
async fn connect(&self) -> Result<(), RemoteError>;
|
||||
fn file_system(&self) -> Arc<dyn FileSystem>;
|
||||
fn process_spawner(&self) -> Arc<dyn ProcessSpawner>;
|
||||
fn pty(&self) -> Arc<dyn PtyPort>;
|
||||
}
|
||||
```
|
||||
- **Consommé par** : tous les use cases qui touchent un projet (résolvent leurs ports via le `RemoteHost` du projet → **transparence local/distant**).
|
||||
- **Implémenté par** : `LocalHost`, `SshHost` (russh/ssh2), `WslHost` (wsl.exe). C'est la **stratégie** qui unifie les 3 modes.
|
||||
|
||||
### `ProcessSpawner`
|
||||
- **Rôle** : lancer un process **non interactif** et récupérer sortie/exit (ex. `detect`, commandes git hors libgit2, scripts).
|
||||
- **Signature** : `async fn run(&self, spec: SpawnSpec) -> Result<Output, ProcessError>;`
|
||||
- **Consommé par** : `DetectProfilesUseCase`, services divers.
|
||||
- **Implémenté par** : `LocalProcessSpawner`, `SshProcessSpawner`, `WslProcessSpawner`.
|
||||
|
||||
### `FileSystem`
|
||||
- **Rôle** : lecture/écriture/listing/symlink, neutre vis-à-vis de la localisation.
|
||||
- **Signature** :
|
||||
```rust
|
||||
trait FileSystem {
|
||||
async fn read(&self, p: &RemotePath) -> Result<Vec<u8>, FsError>;
|
||||
async fn write(&self, p: &RemotePath, data: &[u8]) -> Result<(), FsError>;
|
||||
async fn exists(&self, p: &RemotePath) -> Result<bool, FsError>;
|
||||
async fn create_dir_all(&self, p: &RemotePath) -> Result<(), FsError>;
|
||||
async fn list(&self, p: &RemotePath) -> Result<Vec<DirEntry>, FsError>;
|
||||
async fn symlink(&self, src: &RemotePath, dst: &RemotePath) -> Result<(), FsError>;
|
||||
}
|
||||
```
|
||||
- **Consommé par** : `AgentContextStore`, `ProjectStore`, injection `conventionFile`, etc.
|
||||
- **Implémenté par** : `LocalFileSystem` (std::fs/tokio::fs), `SshFileSystem` (SFTP), `WslFileSystem` (via `wsl.exe` ou chemins `\\wsl$`).
|
||||
|
||||
### `TemplateStore`
|
||||
- **Rôle** : CRUD des `AgentTemplate` dans le store global IDE + versioning.
|
||||
- **Signature** : `list / get / save / delete / bump_version`.
|
||||
- **Consommé par** : `CreateTemplate`, `UpdateTemplate`, `CreateAgentFromTemplate`, `SyncAgentWithTemplate`.
|
||||
- **Implémenté par** : `FsTemplateStore` (md + index json dans le dossier de données app).
|
||||
|
||||
### `ProjectStore`
|
||||
- **Rôle** : persistance de la liste des projets connus, workspaces, windows, tabs, layouts.
|
||||
- **Signature** : `list_projects / load_project / save_project / save_workspace / load_workspace`.
|
||||
- **Consommé par** : `CreateProject`, `OpenProject`, persistance fenêtres/onglets/layout.
|
||||
- **Implémenté par** : `FsProjectStore` (json dans données app pour le registre ; layout par projet dans `.ideai/`).
|
||||
|
||||
### `AgentContextStore`
|
||||
- **Rôle** : lire/écrire les `.md` d'agents **et** le manifeste `.ideai/agents.json` (au sein du projet, via le `FileSystem` du `RemoteHost`).
|
||||
- **Signature** :
|
||||
```rust
|
||||
trait AgentContextStore {
|
||||
async fn read_context(&self, project: &Project, agent: &AgentId) -> Result<MarkdownDoc, StoreError>;
|
||||
async fn write_context(&self, project: &Project, agent: &AgentId, md: &MarkdownDoc) -> Result<(), StoreError>;
|
||||
async fn load_manifest(&self, project: &Project) -> Result<AgentManifest, StoreError>;
|
||||
async fn save_manifest(&self, project: &Project, m: &AgentManifest) -> Result<(), StoreError>;
|
||||
}
|
||||
```
|
||||
- **Consommé par** : `CreateAgent*`, `LaunchAgent`, `SyncAgentWithTemplate`.
|
||||
- **Implémenté par** : `IdeaiContextStore` (compose `FileSystem`, écrit `.ideai/`).
|
||||
|
||||
### `GitRepository`
|
||||
- **Rôle** : opérations git du projet.
|
||||
- **Signature** : `status / stage / unstage / commit / branches / checkout / current_branch / diff / log / pull / push / clone / init`.
|
||||
- **Consommé par** : use cases Git.
|
||||
- **Implémenté par** : `Git2Repository` (libgit2, local) ; sur SSH/WSL, `RemoteGitRepository` délègue à git CLI via `ProcessSpawner` quand libgit2 ne peut pas atteindre le FS distant (point ouvert §13).
|
||||
|
||||
### `EventBus`
|
||||
- **Rôle** : publier/souscrire les `DomainEvent` (découple émetteurs et présentation).
|
||||
- **Signature** : `fn publish(&self, e: DomainEvent); fn subscribe(&self) -> EventStream;`
|
||||
- **Consommé par** : tous use cases (publient) ; l'adapter Tauri (souscrit → relaye en events/channels IPC).
|
||||
- **Implémenté par** : `TokioBroadcastEventBus` (in-process), relayé par `TauriEventRelay`.
|
||||
|
||||
### `Clock` & `IdGenerator` (ports utilitaires — testabilité)
|
||||
- **Rôle** : éliminer le non-déterminisme (`now()`, `uuid`) du domaine/application.
|
||||
- **Implémenté par** : `SystemClock` / `UuidGenerator` (prod), `FixedClock` / `SeqIdGenerator` (tests).
|
||||
|
||||
---
|
||||
|
||||
## 5. Adapters (impl concrètes par port)
|
||||
|
||||
| Port | Adapter(s) | Techno | Notes |
|
||||
|---|---|---|---|
|
||||
| `AgentRuntime` | `CliAgentRuntime` | piloté par `AgentProfile` | Construit `SpawnSpec` + plan d'injection. Un seul adapter, N profils. |
|
||||
| `PtyPort` | `PortablePtyAdapter` | portable-pty | Local. Stream octets → Channel Tauri. |
|
||||
| | `SshPtyAdapter` | russh (channel shell/exec + pty req) | Distant SSH. |
|
||||
| | `WslPtyAdapter` | `wsl.exe -d <distro>` + portable-pty | PTY dans la distro. |
|
||||
| `RemoteHost` | `LocalHost` / `SshHost` / `WslHost` | — / russh,ssh2 / wsl.exe | Stratégie ; fabrique FS/Spawner/PTY adaptés. |
|
||||
| `ProcessSpawner` | `LocalProcessSpawner` | std/tokio `Command` | |
|
||||
| | `SshProcessSpawner` | russh exec | |
|
||||
| | `WslProcessSpawner` | `wsl.exe` | |
|
||||
| `FileSystem` | `LocalFileSystem` | tokio::fs | |
|
||||
| | `SshFileSystem` | SFTP (ssh2/russh-sftp) | |
|
||||
| | `WslFileSystem` | `\\wsl$\` / `wsl.exe cat`… | |
|
||||
| `TemplateStore` | `FsTemplateStore` | tokio::fs + serde_json | Dossier données app. |
|
||||
| `ProjectStore` | `FsProjectStore` | tokio::fs + serde_json | Registre projets + workspace. |
|
||||
| `AgentContextStore` | `IdeaiContextStore` | compose `FileSystem` | Écrit `.ideai/`. |
|
||||
| `GitRepository` | `Git2Repository` | git2 | Local. |
|
||||
| | `RemoteGitRepository` | git CLI via `ProcessSpawner` | SSH/WSL fallback. |
|
||||
| `EventBus` | `TokioBroadcastEventBus` (+ `TauriEventRelay`) | tokio::broadcast | Relais vers IPC. |
|
||||
| `Clock`/`IdGenerator` | `SystemClock`/`UuidGenerator` | std/uuid | Mocks en test. |
|
||||
|
||||
**Adapters entrants (driving)** : handlers `#[tauri::command]` (frontend → app) + `TauriEventRelay` (app → frontend). Côté UI : `tauri-adapters` implémentant les gateways TS.
|
||||
|
||||
---
|
||||
|
||||
## 6. Use cases / services applicatifs
|
||||
|
||||
> Chaque use case : un struct `XxxUseCase` portant ses ports en `Arc<dyn Port>`, une méthode `execute(input: XxxInput) -> Result<XxxOutput, AppError>`. **Single Responsibility**. Aucune dépendance à Tauri.
|
||||
|
||||
| Use case | Rôle | Ports consommés |
|
||||
|---|---|---|
|
||||
| `CreateProject` | Crée un projet (project root), init `.ideai/`, registre. | `ProjectStore`, `FileSystem`, `IdGenerator`, `EventBus` |
|
||||
| `OpenProject` | Charge projet, manifeste, layout, résout `RemoteHost`. | `ProjectStore`, `AgentContextStore`, `RemoteHost` |
|
||||
| `CloseProject` / `CloseTab` | Persiste l'état, libère PTYs. | `ProjectStore`, `PtyPort`, `EventBus` |
|
||||
| `DetectProfiles` (first-run) | Teste `detect` de chaque profil candidat. | `AgentRuntime`, `ProcessSpawner` |
|
||||
| `ConfigureProfiles` | Enregistre profils choisis/édités/custom. | `TemplateStore`/profile store, `FileSystem` |
|
||||
| `CreateAgentFromScratch` | Crée agent + `.md`, met à jour manifeste. | `AgentContextStore`, `IdGenerator` |
|
||||
| `CreateAgentFromTemplate` | Copie le `content_md` du template → agent ; lie origine + version + `synchronized`. | `TemplateStore`, `AgentContextStore` |
|
||||
| `UpdateTemplate` | Modifie un template, **bump version**, signale drift aux agents liés. | `TemplateStore`, `EventBus` |
|
||||
| `DetectAgentDrift` | Compare `synced_template_version` vs `template.version`. | `TemplateStore`, `AgentContextStore` |
|
||||
| `SyncAgentWithTemplate` | Applique la MAJ template→agent si `synchronized`. | `TemplateStore`, `AgentContextStore`, `EventBus` |
|
||||
| `LaunchAgent` | Résout profil+contexte, prépare injection, ouvre cellule PTY au bon `cwd`, spawn CLI. | `AgentRuntime`, `AgentContextStore`, `RemoteHost`→`PtyPort`/`FileSystem`, `EventBus` |
|
||||
| `OpenTerminal` | Ouvre un PTY simple dans une cellule. | `RemoteHost`→`PtyPort`, `EventBus` |
|
||||
| `WriteToTerminal` / `ResizeTerminal` / `CloseTerminal` | I/O PTY. | `PtyPort` |
|
||||
| `MutateLayout` (split/merge/resize/move) | Applique une opération sur le `LayoutTree` (logique **pure** dans le domaine, persistée ici). | `ProjectStore` (persistance) |
|
||||
| `ConnectRemote` (SSH/WSL) | Établit la connexion, valide l'accès au root. | `RemoteHost`, `FileSystem` |
|
||||
| `MoveTabToNewWindow` | Détache un onglet → nouvelle fenêtre (réaffectation `WindowId`). | `ProjectStore`, `EventBus` |
|
||||
| Use cases Git | `GitStatus`, `GitCommit`, `GitCheckout`, `GitPush`, … | `GitRepository`, `EventBus` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Modèle de layout terminal (grille tableur récursive + fusion)
|
||||
|
||||
### 7.1 Structure de données
|
||||
|
||||
La grille « type tableur, lignes/colonnes imbriquées indépendamment + fusion » est modélisée par un **arbre de splits récursif** où chaque conteneur définit son propre découpage. La **fusion** est obtenue nativement : fusionner = ne pas subdiviser une zone (un `Leaf` couvre plusieurs « cellules visuelles » d'un parent voisin). Pour le cas Excel pur (fusion arbitraire chevauchant la grille), on superpose un modèle **GridContainer** avec spans.
|
||||
|
||||
```rust
|
||||
enum LayoutNode {
|
||||
Leaf(LeafCell),
|
||||
Split(SplitContainer),
|
||||
Grid(GridContainer),
|
||||
}
|
||||
|
||||
struct LeafCell {
|
||||
id: NodeId,
|
||||
session: Option<SessionId>, // 0 ou 1 terminal
|
||||
}
|
||||
|
||||
struct SplitContainer { // découpage simple binaire/n-aire pondéré
|
||||
id: NodeId,
|
||||
direction: Direction, // Row (colonnes) | Column (lignes)
|
||||
children: Vec<WeightedChild>, // ordre = gauche→droite / haut→bas
|
||||
}
|
||||
struct WeightedChild { node: LayoutNode, weight: f32 } // poids = part redimensionnable
|
||||
|
||||
struct GridContainer { // grille tableur avec fusion (spans)
|
||||
id: NodeId,
|
||||
col_weights: Vec<f32>, // largeurs de colonnes
|
||||
row_weights: Vec<f32>, // hauteurs de lignes
|
||||
cells: Vec<GridCell>, // placements avec spans (fusion)
|
||||
}
|
||||
struct GridCell {
|
||||
node: LayoutNode, // récursif : une cellule peut re-contenir un Split/Grid
|
||||
row: u16, col: u16,
|
||||
row_span: u16, // ≥1 ; >1 = cellules fusionnées verticalement
|
||||
col_span: u16, // ≥1 ; >1 = cellules fusionnées horizontalement
|
||||
}
|
||||
```
|
||||
|
||||
- **Lignes/colonnes indépendantes par zone** : chaque `SplitContainer`/`GridContainer` a ses propres poids ⇒ pas de grille uniforme rigide.
|
||||
- **Imbrication** : un enfant peut être un nouveau `Split`/`Grid` ⇒ « N colonnes dans une ligne, M lignes dans une colonne » de façon arbitraire.
|
||||
- **Fusion** : `row_span`/`col_span` dans `GridContainer` (modèle tableur fidèle) **ou** simplement un `Leaf` plus grand via `SplitContainer` (cas courant). Le domaine supporte les deux ; l'UI choisit la représentation selon l'interaction.
|
||||
|
||||
### 7.2 Invariants (validés dans le domaine, testables sans I/O)
|
||||
|
||||
- Tous les `weight > 0`. Les poids sont **relatifs** (l'UI normalise pour le rendu).
|
||||
- Dans un `GridContainer` : aucune superposition de spans ; toute la surface couverte ; `row+row_span ≤ rows`, `col+col_span ≤ cols`.
|
||||
- Un `SessionId` n'apparaît que dans **un seul** `Leaf`.
|
||||
- Les opérations `split`, `merge`, `resize`, `move` sont des **fonctions pures** `LayoutTree -> Result<LayoutTree, LayoutError>` (immutabilité ⇒ testabilité, undo/redo facile).
|
||||
|
||||
### 7.3 Sérialisation & persistance
|
||||
|
||||
- Sérialisé en **JSON** (serde, `tag`/`content` pour l'enum) → `.ideai/layout.json` (par projet, donc voyage avec le projet, y compris distant).
|
||||
- Le `Workspace`/`Window`/`Tab` (organisation des fenêtres OS) est persisté côté **store global IDE** (machine-local, pas dans le projet) car lié à l'écran de l'utilisateur, pas au code.
|
||||
|
||||
---
|
||||
|
||||
## 8. Synchronisation template → agents
|
||||
|
||||
### 8.1 Versioning
|
||||
|
||||
- `AgentTemplate.version: u64` monotone. **`UpdateTemplate` incrémente** la version à chaque changement de `content_md`. Un hash du contenu (`content_hash`) est aussi stocké pour détecter les éditions hors-app.
|
||||
- Chaque `ManifestEntry` d'agent lié garde `synced_template_version` = version du template **au dernier sync réussi**.
|
||||
|
||||
### 8.2 Détection de drift
|
||||
|
||||
```
|
||||
drift(agent) =
|
||||
agent.synchronized
|
||||
&& agent.origin == FromTemplate{ template_id, .. }
|
||||
&& template_store.get(template_id).version > entry.synced_template_version
|
||||
```
|
||||
`DetectAgentDrift` est lancé à `OpenProject` et après chaque `UpdateTemplate` ; émet `AgentDriftDetected { agent_id, from, to }` → badge UI.
|
||||
|
||||
### 8.3 Application de la MAJ (`SyncAgentWithTemplate`)
|
||||
|
||||
```
|
||||
1. Charger template (version courante) + manifeste projet.
|
||||
2. Pour chaque agent ciblé avec synchronized==true :
|
||||
a. Stratégie de MAJ = REMPLACEMENT du .md par content_md du template
|
||||
(le contexte d'un agent synchronisé est "possédé" par le template).
|
||||
→ Variante future : merge 3-way si l'agent a un bloc local marqué.
|
||||
b. write_context(agent, template.content_md)
|
||||
c. entry.synced_template_version = template.version
|
||||
3. save_manifest. publish(AgentSynced{..}).
|
||||
```
|
||||
|
||||
### 8.4 Agents non synchronisés
|
||||
|
||||
- `synchronized == false` : ne reçoivent **jamais** de MAJ auto. Ils gardent leur `.md` libre. On peut afficher « une nouvelle version du template existe » (info) mais aucune écriture n'a lieu sans action explicite (qui basculerait `synchronized` ou ferait un sync ponctuel one-shot).
|
||||
- Agents `Scratch` : aucun lien template, hors périmètre de sync.
|
||||
|
||||
---
|
||||
|
||||
## 9. Stockage & arborescence des fichiers
|
||||
|
||||
### 9.1 Dans le projet — `.ideai/` (voyage avec le code, versionnable)
|
||||
|
||||
```
|
||||
<project_root>/
|
||||
├── .ideai/
|
||||
│ ├── agents.json # AgentManifest (mapping md ↔ template ↔ sync ↔ version)
|
||||
│ ├── layout.json # LayoutTree de l'onglet (sérialisé)
|
||||
│ ├── project.json # méta projet local (nom, profil par défaut, remote ref)
|
||||
│ └── agents/
|
||||
│ ├── reviewer.md # contexte d'un agent de projet
|
||||
│ ├── backend-dev.md
|
||||
│ └── ...
|
||||
└── (CLAUDE.md / AGENTS.md / GEMINI.md générés/symlinkés à l'activation si conventionFile)
|
||||
```
|
||||
|
||||
**Schéma `agents.json`** :
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"agents": [
|
||||
{
|
||||
"id": "a3f1...",
|
||||
"name": "Backend Dev",
|
||||
"md": "agents/backend-dev.md",
|
||||
"profileId": "claude-code",
|
||||
"origin": { "type": "fromTemplate", "templateId": "tpl-backend", "syncedTemplateVersion": 4 },
|
||||
"synchronized": true
|
||||
},
|
||||
{
|
||||
"id": "b7c2...",
|
||||
"name": "Ad-hoc",
|
||||
"md": "agents/adhoc.md",
|
||||
"profileId": "codex-cli",
|
||||
"origin": { "type": "scratch" },
|
||||
"synchronized": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 Store global IDE (données app, hors projet, machine-local)
|
||||
|
||||
Emplacement résolu via Tauri path API (`AppData`/`~/.local/share/IdeA`/`~/Library/Application Support/IdeA`).
|
||||
|
||||
```
|
||||
<app_data_dir>/IdeA/
|
||||
├── profiles.json # AgentProfile[] configurés (first-run + custom + édités)
|
||||
├── settings.json # préférences IDE
|
||||
├── workspace.json # Workspace/Window/Tab + quel projet dans quel onglet (machine-local)
|
||||
└── templates/
|
||||
├── index.json # [{id, name, version, contentHash, defaultProfileId}]
|
||||
└── md/
|
||||
├── tpl-backend.md
|
||||
├── tpl-reviewer.md
|
||||
└── ...
|
||||
```
|
||||
|
||||
**Schéma `profiles.json` (item)** : exactement le profil déclaratif de CONTEXT.md §9 (`id, name, command, args, contextInjection{strategy,target/flag/var}, detect, cwd`).
|
||||
|
||||
**Formats** : contextes & templates en **Markdown** ; tout le reste en **JSON** (serde). Pas de base de données : fichiers plats, simples, diffables, portables (AppImage friendly).
|
||||
|
||||
---
|
||||
|
||||
## 10. Arborescence du repo
|
||||
|
||||
### 10.1 Décision : workspace Cargo **multi-crate**
|
||||
|
||||
**Multi-crate** retenu (vs mono-crate) pour **forcer** la règle de dépendance à la compilation : le crate `domain` ne peut littéralement pas dépendre de `infrastructure` si ce n'est pas dans son `Cargo.toml`. C'est la garantie mécanique de l'hexagonal (mieux qu'une convention). Coût : un peu de cérémonie de workspace — acceptable et même souhaitable ici vu le découpage en lots/agents (§12).
|
||||
|
||||
```
|
||||
IdeA/
|
||||
├── Cargo.toml # [workspace] members
|
||||
├── ARCHITECTURE.md
|
||||
├── CONTEXT.md
|
||||
├── crates/
|
||||
│ ├── domain/ # PUR : entities, VO, ports (traits), domain events, layout logic
|
||||
│ │ └── src/{project,agent,template,profile,terminal,layout,remote,git,ports,events}.rs
|
||||
│ ├── application/ # use cases, DTOs, AppError ; dépend de domain
|
||||
│ │ └── src/{project,agent,template,terminal,layout,remote,git}/
|
||||
│ ├── infrastructure/ # adapters ; dépend de domain (+ application pour DTO si besoin)
|
||||
│ │ └── src/{pty,fs,process,remote,git,store,runtime,eventbus}/
|
||||
│ └── app-tauri/ # binaire Tauri : commands, events, COMPOSITION ROOT (DI)
|
||||
│ ├── src/{commands,events,state,main.rs}
|
||||
│ ├── tauri.conf.json
|
||||
│ ├── build.rs
|
||||
│ └── icons/, bundle (NSIS + AppImage)
|
||||
├── frontend/ # TypeScript + React (Vite)
|
||||
│ ├── package.json, vite.config.ts, index.html
|
||||
│ └── src/
|
||||
│ ├── domain/ # types & logique de vue purs (miroir DTO, calc layout)
|
||||
│ ├── ports/ # gateways TS (interfaces) : AgentGateway, TerminalGateway, ...
|
||||
│ ├── adapters/ # impl gateways via @tauri-apps/api (invoke/listen/Channel)
|
||||
│ │ └── mock/ # impl mock pour dev/test/storybook
|
||||
│ ├── features/ # par feature : projects, agents, templates, terminals, layout, git, remote, first-run
|
||||
│ │ └── <feature>/{components,hooks,store,index.ts}
|
||||
│ ├── shared/ # ui kit, xterm wrapper, design system
|
||||
│ └── app/ # bootstrap, routing, providers (DI des adapters)
|
||||
└── docs/ # ADRs, schémas
|
||||
```
|
||||
|
||||
`app-tauri` = **seul** endroit qui connaît tous les crates : il instancie les adapters concrets et injecte dans les use cases (composition root). Personne d'autre ne fait de `new ConcreteAdapter`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Stratégie de tests
|
||||
|
||||
| Couche | Type de test | Comment / où |
|
||||
|---|---|---|
|
||||
| `domain` | **Unitaires purs** (sans I/O, sans async) | `#[cfg(test)] mod tests` par module. Invariants d'entités, opérations de layout (split/merge/resize), détection de drift, validation `ContextInjection`. Déterministe via `FixedClock`/`SeqIdGenerator`. |
|
||||
| `application` | **Unitaires avec ports mockés** | Chaque use case testé avec des **mocks de ports** (`mockall` ou fakes manuels). Ex. `LaunchAgent` vérifie qu'il appelle `prepare_invocation` puis `pty.spawn` avec le bon `cwd` et plan d'injection. **Aucun vrai PTY/FS/git.** |
|
||||
| `infrastructure` | **Tests d'intégration ciblés** | Par adapter : `LocalFileSystem` sur tmpdir, `Git2Repository` sur repo temporaire, `PortablePtyAdapter` lance `echo`. SSH/WSL : tests `#[ignore]` gated derrière feature/env (CI conditionnelle). |
|
||||
| `app-tauri` | Tests des commands (mapping DTO ↔ use case) | Wiring testé avec use cases réels + adapters in-memory. |
|
||||
| Frontend `domain`/`ports` | **Vitest** (unitaires purs) | Logique de vue, calc tailles cellules, réducteurs de state. |
|
||||
| Frontend `features` | **React Testing Library** + **gateways mock** | Composants testés avec adapters mock ⇒ **sans backend**. |
|
||||
| E2E (plus tard) | Playwright / `tauri-driver` | Smoke tests des parcours clés. |
|
||||
|
||||
**Clé de testabilité** : grâce aux **ports**, le domaine et l'application se testent **100 % sans I/O**. C'est l'argument central de l'hexagonal et le socle du cycle dev↔test (chaque agent dev appairé à un agent test, cf. CONTEXT §3). Règle d'or : une feature n'est verte que quand `cargo test -p <crate>` et `vitest` passent.
|
||||
|
||||
---
|
||||
|
||||
## 12. Découpage en lots/features livrables
|
||||
|
||||
> Chaque lot = périmètre autonome, validable par le cycle dev/test, confiable à **un binôme (agent dev + agent test)**. Ordonnés par dépendance.
|
||||
|
||||
| # | Lot | Contenu | Crates/zones |
|
||||
|---|---|---|---|
|
||||
| L0 | **Socle domaine & ports** | Entities, VO, **tous les traits ports**, domain events, `AppError`. Aucun adapter. | `domain` (+ ports utilitaires) |
|
||||
| L1 | **Composition root & IPC** | `app-tauri` : DI, registre de commands/events, bridge PTY↔Channel, gateways TS + adapters Tauri + mocks. | `app-tauri`, `frontend/ports`+`adapters` |
|
||||
| L2 | **Projets & stockage** | `CreateProject`/`OpenProject`/`CloseProject`, `FsProjectStore`, `LocalFileSystem`, init `.ideai/`. UI projets/onglets. | `application/project`, `infrastructure/{fs,store}`, `frontend/features/projects` |
|
||||
| L3 | **Terminaux & PTY (local)** | `PtyPort` + `PortablePtyAdapter`, use cases terminal, wrapper xterm.js, flux Channel. | `infrastructure/pty`, `application/terminal`, `frontend/features/terminals` |
|
||||
| L4 | **Layout tableur** | Logique pure `LayoutTree` (déjà en L0 partiellement), `MutateLayout`, persistance `layout.json`, UI grille redimensionnable + fusion. | `domain/layout`, `application/layout`, `frontend/features/layout` |
|
||||
| L5 | **Profils IA & runtime** | `AgentProfile`, `CliAgentRuntime`, `DetectProfiles`, first-run wizard, `profiles.json`. | `infrastructure/runtime`, `application/agent`, `frontend/features/first-run` |
|
||||
| L6 | **Agents & contextes** | `AgentContextStore`/`IdeaiContextStore`, CRUD agents, `LaunchAgent` (injection + spawn + cellule). | `application/agent`, `infrastructure/store`, `frontend/features/agents` |
|
||||
| L7 | **Templates & synchro** | `TemplateStore`, versioning, `DetectAgentDrift`, `SyncAgentWithTemplate`. UI templates + badges drift. | `application/template`, `infrastructure/store`, `frontend/features/templates` |
|
||||
| L8 | **Git** | `GitRepository`/`Git2Repository`, use cases git, UI git. | `infrastructure/git`, `application/git`, `frontend/features/git` |
|
||||
| L9 | **Remote (SSH + WSL)** | `RemoteHost` stratégie, `SshHost`/`WslHost`, adapters FS/PTY/Spawner distants, `RemoteGitRepository`. UI connexion. | `infrastructure/remote`, `application/remote`, `frontend/features/remote` |
|
||||
| L10 | **Fenêtres & multi-window** | `Workspace`/`Window`/`Tab`, `MoveTabToNewWindow`, drag d'onglet → nouvelle fenêtre OS Tauri. | `application`, `app-tauri`, `frontend/app` |
|
||||
| L11 | **Packaging & livraison** | Tauri bundle : NSIS `setup.exe`, **AppImage** multi-distro, CI Linux+Windows. | `app-tauri`, CI |
|
||||
|
||||
---
|
||||
|
||||
## 13. Risques techniques & points ouverts (spikes)
|
||||
|
||||
1. **PTY cross-platform** : portable-pty + xterm.js OK sur les 3 OS, mais signaux/resize/exit codes diffèrent (Windows ConPTY). **Spike** L3.
|
||||
2. **AppImage multi-distro** : libgit2/openssl/glibc liés dynamiquement → risque de non-portabilité. **Spike** : vendoring statique (`git2` features, `rustls` pour russh au lieu d'OpenSSL), test sur ≥3 distros (Ubuntu/Fedora/Arch). L11.
|
||||
3. **Drag d'onglet entre fenêtres Tauri** : Tauri v2 multi-webview/multi-window + DnD natif inter-fenêtres est délicat (le DnD HTML ne traverse pas les fenêtres OS). **Spike** : protocole « detach » (créer une `WebviewWindow`, transférer l'état via store + event, fermer l'onglet source). L10.
|
||||
4. **Git sur FS distant** : libgit2 ne lit pas un FS SSH/WSL directement. Décision : **fallback git CLI** (`RemoteGitRepository`) côté distant via `ProcessSpawner`. À valider (perf, parsing). L9.
|
||||
5. **Synchro temps réel UI ↔ PTY** : volume d'octets élevé ; backpressure des Channels Tauri, throttling/coalescing côté front. **Spike** L3.
|
||||
6. **Injection `conventionFile`** : symlink vs copie du `.md` vers `CLAUDE.md`/`AGENTS.md` ; conflits si fichier existant, .gitignore, droits Windows (symlinks). À cadrer L6.
|
||||
7. **SSH auth** : agent/clé/mot de passe/known_hosts ; choix russh (rustls) vs ssh2 (libssh2/OpenSSL — impacte point 2). Décision à figer début L9.
|
||||
8. **WSL chemins** : conversion `/mnt/c/...` ↔ `\\wsl$\...`, distros multiples, perf I/O cross-boundary. Spike L9.
|
||||
9. **Détection d'édition hors-app** des `.md`/templates (content hash) et résolution de conflit lors du sync. L7.
|
||||
|
||||
---
|
||||
|
||||
*Document maintenu par l'Agent Architecture — base du jalon « cadrage architecture » avant tout code applicatif.*
|
||||
187
.ideai/agents/main.md
Normal file
187
.ideai/agents/main.md
Normal file
@ -0,0 +1,187 @@
|
||||
# IdeA — Contexte & Méthode de travail
|
||||
|
||||
> Ce document définit **mon rôle**, **la méthode de développement** et **la vision produit** du projet IdeA.
|
||||
> Il fait autorité sur la façon dont le projet est piloté. Toute évolution de méthode doit être répercutée ici.
|
||||
|
||||
---
|
||||
|
||||
## 1. Mon rôle : chef d'orchestre, pas développeur
|
||||
|
||||
Je **n'écris pas de code moi-même**. Mon rôle est de **piloter des agents** qui réalisent le travail.
|
||||
Je suis responsable de :
|
||||
|
||||
- Découper le travail en tâches claires et autonomes.
|
||||
- Attribuer chaque tâche aux bons agents.
|
||||
- Garantir que le cycle de développement/test est respecté.
|
||||
- Faire respecter les principes d'architecture (SOLID, Hexagonal).
|
||||
- Maintenir la cohérence globale du projet et de ce document.
|
||||
- Arbitrer et valider avant toute action irréversible ou sortante.
|
||||
|
||||
---
|
||||
|
||||
## 2. Les agents
|
||||
|
||||
### 2.1 Agent Architecture (1 pour tout le projet)
|
||||
- Garant de l'architecture globale : **Hexagonale (Ports & Adapters)** et principes **SOLID**.
|
||||
- Définit les frontières (domaine / application / infrastructure), les ports, les contrats.
|
||||
- Valide que chaque nouvelle feature respecte la structure avant son développement.
|
||||
- Tient à jour la cartographie d'architecture et les conventions.
|
||||
|
||||
### 2.2 Agents de Développement
|
||||
- Écrivent le code des features.
|
||||
- Respectent strictement l'architecture définie par l'agent Architecture.
|
||||
- Code **propre, structuré, stable**.
|
||||
- Reçoivent les rapports d'erreurs des agents de test et corrigent.
|
||||
|
||||
### 2.3 Agents de Test
|
||||
- **Chaque agent de développement est appairé avec un agent de test dédié.**
|
||||
- Écrivent et exécutent les **tests unitaires** des features implémentées ou modifiées.
|
||||
- Produisent un **rapport d'erreurs** clair quand un test échoue.
|
||||
- Re-testent après chaque correction.
|
||||
|
||||
---
|
||||
|
||||
## 3. Le cycle de développement (boucle obligatoire)
|
||||
|
||||
Pour **chaque** feature implémentée ou modifiée :
|
||||
|
||||
```
|
||||
1. Agent Architecture → valide le découpage et les contrats (ports/interfaces)
|
||||
2. Agent Développement → écrit le code
|
||||
3. Agent Test → écrit les tests unitaires + les exécute
|
||||
4a. Tests OK → feature validée, on passe à la suite
|
||||
4b. Tests KO → rapport d'erreurs → retour à l'agent Développement
|
||||
→ correction → retour à l'étape 3 (boucle jusqu'au vert)
|
||||
```
|
||||
|
||||
**Règle d'or :** aucune feature n'est considérée terminée tant que ses tests ne passent pas.
|
||||
Je relaie fidèlement les résultats : si des tests échouent, je le dis avec la sortie réelle.
|
||||
|
||||
---
|
||||
|
||||
## 4. Principes de code
|
||||
|
||||
- **SOLID** appliqué au maximum.
|
||||
- **Architecture Hexagonale** (Ports & Adapters) : le domaine métier est isolé des détails techniques (UI, terminal, git, SSH, système de fichiers...).
|
||||
- Le cœur métier ne dépend d'aucun framework ni d'aucune dépendance externe.
|
||||
- Tests unitaires systématiques ; couverture des features critiques.
|
||||
- Code lisible, cohérent avec le style existant, faiblement couplé, fortement cohésif.
|
||||
|
||||
---
|
||||
|
||||
## 5. Vision produit : IdeA
|
||||
|
||||
**IdeA est un IDE next-gen 100 % IA.** On n'y code pas : **on gère des IA.**
|
||||
|
||||
### Fonctionnalités clés
|
||||
- **Multi-projets en parallèle** : un **onglet par projet**.
|
||||
- **Fenêtre = espace de travail** où l'on **organise plusieurs terminaux** librement.
|
||||
- **Agents par projet** : chaque projet a ses propres agents.
|
||||
- **Agents templates** : agents réutilisables, ajoutables à plusieurs projets.
|
||||
- **Création d'agents** : depuis zéro ou à partir d'un template.
|
||||
- **Synchronisation template → agents** : option « garder l'agent à jour ».
|
||||
Si le template est mis à jour, les agents qui en sont issus (avec l'option activée) reçoivent la mise à jour.
|
||||
- **Contextes d'agents stockés en `.md`** (toujours).
|
||||
- **Création de projet** = définition de son **project root**.
|
||||
|
||||
### Intégrations
|
||||
- **Git** intégré.
|
||||
- **Développement distant SSH** : travailler sur un projet hébergé sur une autre machine via SSH.
|
||||
- **Développement WSL** : travailler sur une WSL depuis Windows.
|
||||
|
||||
### Plateformes & livraison
|
||||
- Cible : **macOS, Linux, Windows**.
|
||||
- Première phase de compilation : **Linux et Windows**.
|
||||
- Livraison :
|
||||
- **Windows** : `setup.exe`.
|
||||
- **Linux** : **AppImage** (doit fonctionner sur les différentes distributions).
|
||||
|
||||
---
|
||||
|
||||
## 6. Stack technique (validée)
|
||||
|
||||
- **Shell applicatif** : **Tauri v2** (binaires légers, performants, multi-OS, AppImage + installeur `setup.exe`/NSIS Windows natifs).
|
||||
- **Cœur / backend** : **Rust** — stabilité, performance, et expression idiomatique du domaine hexagonal (ports = traits, adapters = implémentations).
|
||||
- **Frontend / UI** : **TypeScript + React**.
|
||||
- **Terminaux** : **xterm.js** (rendu) + **portable-pty** (PTY côté Rust).
|
||||
- **Git** : **libgit2** via `git2` (Rust).
|
||||
- **SSH** : `russh` / `ssh2` (Rust).
|
||||
- **WSL** : invocation de `wsl.exe` depuis le backend.
|
||||
|
||||
## 7. Layout des terminaux (exigence produit)
|
||||
|
||||
Disposition en **grille redimensionnable de type tableur (Excel)** :
|
||||
|
||||
- Splits redimensionnables horizontaux **et** verticaux.
|
||||
- L'utilisateur peut **définir le nombre de colonnes dans une ligne** et **le nombre de lignes dans une colonne**, indépendamment par zone.
|
||||
- Possibilité de **fusionner des cellules** (ex. fusionner deux colonnes sur une ligne), à la manière des cellules fusionnées d'un tableur.
|
||||
- Chaque cellule de la grille héberge un terminal.
|
||||
- → Modèle de layout récursif/imbriqué (pas une grille rigide uniforme) à concevoir par l'agent Architecture.
|
||||
|
||||
## 8. Stockage des contextes & liaison aux templates
|
||||
|
||||
- **Templates d'agents** : stockés dans l'**IDE** (dossier de données utilisateur global de l'app, hors projet).
|
||||
- **Agents de projet** : leurs `.md` sont stockés dans un dossier **`.ideai/`** à la racine du project root.
|
||||
*(Nom choisi pour éviter toute collision avec le `.idea` de JetBrains.)*
|
||||
- **Manifeste de liaison** dans `.ideai/` (ex. `.ideai/agents.json`) qui mappe pour chaque agent de projet :
|
||||
- le `.md` de l'agent,
|
||||
- le template d'origine (le cas échéant),
|
||||
- `synchronized: true/false`,
|
||||
- la **version du template** au dernier sync (pour détecter qu'une mise à jour est disponible).
|
||||
- **Synchro template → agents** : quand un template est mis à jour, les agents liés avec `synchronized: true` reçoivent la MAJ.
|
||||
|
||||
## 9. Moteur IA : adaptateur de CLI flexible (Port `AgentRuntime`)
|
||||
|
||||
Chaque IA est décrite par un **profil déclaratif** (config éditable, pas du code), implémentation d'un **Port** `AgentRuntime` côté domaine. Deux variables clés par IA :
|
||||
|
||||
1. **Commande de lancement** + arguments (ex. `claude`, `codex`, `gemini`, `aider`).
|
||||
2. **Stratégie d'injection du contexte `.md`** :
|
||||
- `conventionFile` : écrire/symlink le `.md` vers le fichier attendu par la CLI (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`…).
|
||||
- `flag` : passer le chemin via un argument.
|
||||
- `stdin` : piper le contenu.
|
||||
- `env` : passer via variable d'environnement.
|
||||
|
||||
Exemple de profil :
|
||||
```json
|
||||
{
|
||||
"id": "claude-code",
|
||||
"name": "Claude Code",
|
||||
"command": "claude",
|
||||
"args": [],
|
||||
"contextInjection": { "strategy": "conventionFile", "target": "CLAUDE.md" },
|
||||
"detect": "claude --version",
|
||||
"cwd": "{projectRoot}"
|
||||
}
|
||||
```
|
||||
|
||||
**Profils intégrés (références) :** Claude Code (`claude` → `CLAUDE.md`), OpenAI Codex CLI (`codex` → `AGENTS.md`), Gemini CLI (`gemini` → `GEMINI.md`), Aider (`aider` → args/message).
|
||||
|
||||
**Règles produit :**
|
||||
- **Premier lancement de l'IDE** : un assistant (first-run) **demande à l'utilisateur** quels profils d'IA configurer. On ne présume rien par défaut.
|
||||
- Les commandes des profils sont **pré-remplies mais éditables**.
|
||||
- L'utilisateur peut **ajouter sa propre commande CLI** (profil custom) pour n'importe quelle IA.
|
||||
|
||||
**Lancement d'un agent :** à l'**activation de l'agent**, on ouvre une cellule terminal (PTY) avec le bon `cwd`, on injecte le contexte `.md`, et on **auto-lance** la CLI du profil.
|
||||
|
||||
## 10. Fenêtres & onglets
|
||||
|
||||
- **Par défaut : un onglet par projet** (comme les IDE classiques).
|
||||
- **Drag & drop d'un onglet** hors de la fenêtre → **crée une nouvelle fenêtre OS** portant ce projet.
|
||||
- **Multi-fenêtres OS supporté** ; chaque fenêtre possède un ou plusieurs onglets/projets.
|
||||
|
||||
## 11. Feuille de route
|
||||
|
||||
1. **Cadrage architecture complet d'abord** (jalon en cours) : l'agent Architecture produit la cartographie complète — domaine, ports, adapters, modules, arborescence — **avant tout code**.
|
||||
2. Puis MVP incrémental selon le cycle dev/test de la section 3.
|
||||
|
||||
## 12. Autonomie d'exécution dans le projet
|
||||
|
||||
L'utilisateur m'accorde un **accès large et autonome** sur le dossier du projet : je peux lire, créer, modifier des fichiers et exécuter les commandes de développement (cargo, npm, npx, git, etc.) **sans demander confirmation à chaque fois**.
|
||||
|
||||
- Concrètement, ces autorisations sont matérialisées dans `.claude/settings.local.json` (mode `acceptEdits` + `Bash`/`Read`/`Edit`/`Write` autorisés), pas dans ce document — CONTEXT.md ne fait que **documenter l'intention**.
|
||||
- **Garde-fous conservés** : les actions destructrices ou hors-projet restent bloquées (`sudo`, `rm -rf` sur `/`/`~`/`$HOME`, `mkfs`, `dd`, `shutdown`/`reboot`…).
|
||||
- L'esprit du rôle (§1) ne change pas : je reste **chef d'orchestre**. L'autonomie porte sur l'exécution mécanique, pas sur l'arbitrage des décisions produit/archi, ni sur les **actions sortantes** (push, publication) qui restent soumises à validation explicite.
|
||||
|
||||
---
|
||||
|
||||
*Dernière mise à jour : 2026-06-05*
|
||||
54
.ideai/layouts.json
Normal file
54
.ideai/layouts.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": 1,
|
||||
"activeId": "9188db80-8535-4786-a20b-3c9a36b222e3",
|
||||
"layouts": [
|
||||
{
|
||||
"id": "9188db80-8535-4786-a20b-3c9a36b222e3",
|
||||
"name": "Default",
|
||||
"kind": "terminal",
|
||||
"tree": {
|
||||
"root": {
|
||||
"type": "split",
|
||||
"node": {
|
||||
"id": "8aca2f93-1a9b-4693-9bba-9a01e130a48c",
|
||||
"direction": "row",
|
||||
"children": [
|
||||
{
|
||||
"node": {
|
||||
"type": "leaf",
|
||||
"node": {
|
||||
"id": "d8a86eb1-cd4d-4937-b900-4989da7c868d",
|
||||
"agent": "a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
}
|
||||
},
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"node": {
|
||||
"type": "leaf",
|
||||
"node": {
|
||||
"id": "6c5be5e7-a54b-468c-a2e2-8ec853629d5e"
|
||||
}
|
||||
},
|
||||
"weight": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "40ea4fa9-25b3-410b-9d3e-6350834b421b",
|
||||
"name": "Git Graph",
|
||||
"kind": "gitGraph",
|
||||
"tree": {
|
||||
"root": {
|
||||
"type": "leaf",
|
||||
"node": {
|
||||
"id": "c840bfdd-3330-46a3-b727-0799f6853e72"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
9
.ideai/project.json
Normal file
9
.ideai/project.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": 1,
|
||||
"id": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023",
|
||||
"name": "IdeA",
|
||||
"remote": {
|
||||
"kind": "local"
|
||||
},
|
||||
"createdAt": 1780702317785
|
||||
}
|
||||
Reference in New Issue
Block a user