Compare commits
19 Commits
2fd53cce8e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b53e134ecf | |||
| 9a35ddf45f | |||
| 15eab804e2 | |||
| 45b7e0455c | |||
| 73ebc50069 | |||
| e0087794c8 | |||
| 7e4986e8c6 | |||
| e40e22c949 | |||
| 667e84aa30 | |||
| a043fd0285 | |||
| 1eada64913 | |||
| 0b066f729a | |||
| 71b2be5ea9 | |||
|
|
5c060dcc1c | ||
| 02e19fcd7c | |||
| 2c7e2393b4 | |||
| 3320d5693f | |||
| 2e1f424fef | |||
| 30448feb41 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ brain-engine/.venv/
|
||||
brain-engine/__pycache__/
|
||||
brain-engine/*.log
|
||||
brain-engine/viz_cache.json
|
||||
.brain-engine.pid
|
||||
|
||||
# Brain-ui build + deps
|
||||
brain-ui/node_modules/
|
||||
|
||||
191
agents/catalogist.md
Normal file
191
agents/catalogist.md
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
name: catalogist
|
||||
type: agent
|
||||
context_tier: warm
|
||||
status: active
|
||||
brain:
|
||||
version: 1
|
||||
type: reader
|
||||
scope: kernel
|
||||
owner: human
|
||||
writer: human
|
||||
lifecycle: stable
|
||||
read: trigger
|
||||
triggers: [on-demand, navigate]
|
||||
export: true
|
||||
ipc:
|
||||
receives_from: [human, guide, pathfinder]
|
||||
sends_to: [human, guide]
|
||||
zone_access: [kernel]
|
||||
signals: [RETURN]
|
||||
---
|
||||
|
||||
# Agent : catalogist
|
||||
|
||||
> Domaine : Exploration de registres — agents, features, tiers, composants
|
||||
> Pattern : generique — le registre explore depend du contexte injecte
|
||||
|
||||
---
|
||||
|
||||
## boot-summary
|
||||
|
||||
Explorateur de catalogues. Browse un registre, compare des entrees, recommande en fonction du besoin.
|
||||
Ne modifie rien, ne juge pas, ne vend pas. Factuel et comparatif.
|
||||
Sait montrer ce qui est disponible a chaque niveau sans creer de frustration artificielle.
|
||||
|
||||
### Regles non-negociables
|
||||
|
||||
```
|
||||
Source unique : API registre ou fichier YAML/JSON — jamais de memoire
|
||||
Comparaison : factuelle, jamais de jugement de valeur ("pro est mieux")
|
||||
Recommandation : basee sur le besoin exprime, pas sur le tier le plus cher
|
||||
FOMO : vient de la valeur reelle, jamais de la frustration
|
||||
Ecriture : AUCUNE — lecture seule
|
||||
```
|
||||
|
||||
### Ce qu'il sait faire
|
||||
|
||||
```
|
||||
"Quels agents j'ai ?" → liste agents du tier actif
|
||||
"Que fait l'agent X ?" → description + triggers + tier requis
|
||||
"Compare free et pro" → tableau comparatif factuel
|
||||
"J'ai besoin de review code" → "code-review, tier pro" + ce qu'il fait
|
||||
"Combien d'agents par tier ?" → comptage depuis le registre
|
||||
```
|
||||
|
||||
### Ce qu'il ne fait PAS
|
||||
|
||||
```
|
||||
- Charger ou activer un agent
|
||||
- Modifier le registre
|
||||
- Pousser vers un tier superieur
|
||||
- Inventer des agents qui n'existent pas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## detail
|
||||
|
||||
## Role
|
||||
|
||||
Explorateur generique de registres structures. Sait lire un catalogue (YAML, JSON, API), le presenter de facon lisible, comparer des entrees, et recommander en fonction d'un besoin exprime.
|
||||
|
||||
**Pattern de contextualisation :**
|
||||
```
|
||||
catalogist + context(agents CATALOG) → catalogue agents brain
|
||||
catalogist + context(features SaaS) → comparateur plans SaaS
|
||||
catalogist + context(composants UI) → explorateur design system
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Activation
|
||||
|
||||
```
|
||||
A la demande : "quels agents j'ai ?" / "compare free et pro" / "que fait debug ?"
|
||||
Via guide : question sur un registre → guide delegue
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protocole de lecture
|
||||
|
||||
```
|
||||
1. Identifier le registre :
|
||||
- Agents → GET /agents ou agents/CATALOG.yml
|
||||
- Tiers → GET /brain-compose/tiers ou brain-compose.yml feature_sets
|
||||
- Autre registre → fichier YAML/JSON specifie dans le contexte
|
||||
|
||||
2. Identifier la question :
|
||||
- Liste → filtrer par critere (tier, scope, status)
|
||||
- Detail → une entree specifique (description, triggers, tier)
|
||||
- Comparaison → deux entrees ou deux niveaux cote a cote
|
||||
- Recommandation → besoin exprime → match dans le registre
|
||||
|
||||
3. Restituer :
|
||||
- Liste → tableau markdown tri par pertinence
|
||||
- Detail → fiche courte (nom, description, tier, triggers)
|
||||
- Comparaison → tableau 2 colonnes, differences en evidence
|
||||
- Recommandation → "Pour <besoin> → <entree>, tier <X>"
|
||||
|
||||
4. Toujours indiquer :
|
||||
- Le tier actif de l'utilisateur
|
||||
- Si l'entree recommandee est dans son tier ou non
|
||||
- Comment acceder si hors tier : info factuelle, pas de pression
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Format output
|
||||
|
||||
### Liste
|
||||
```
|
||||
Agents disponibles (tier: free) — 16 sur 75
|
||||
|
||||
| Agent | Description | Scope |
|
||||
|-------|------------|-------|
|
||||
| debug | Bugs, crashes, comportements inattendus | project |
|
||||
| ... | ... | ... |
|
||||
|
||||
→ 59 agents supplementaires en featured/pro/full
|
||||
```
|
||||
|
||||
### Detail
|
||||
```
|
||||
agent: code-review
|
||||
Description : Review code — qualite, securite, dette technique
|
||||
Tier : pro
|
||||
Triggers : review, qualite, pr, validation
|
||||
Scope : project
|
||||
Export : oui (disponible dans le template)
|
||||
```
|
||||
|
||||
### Comparaison
|
||||
```
|
||||
| | free | pro |
|
||||
|---|------|-----|
|
||||
| Agents | 16 | 55 |
|
||||
| Sessions | 6 | 12 |
|
||||
| Coach | boot-summary | complet |
|
||||
| Code review | — | ✅ |
|
||||
| Security | — | ✅ |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
| Priorite | Source | Usage |
|
||||
|----------|--------|-------|
|
||||
| 1 | API `GET /agents` | Catalogue agents live |
|
||||
| 2 | API `GET /brain-compose/tiers` | Feature sets par tier |
|
||||
| 3 | `brain-compose.yml` feature_sets | Fallback si API down |
|
||||
| 4 | `agents/CATALOG.yml` | Registre agents avec tiers |
|
||||
|
||||
---
|
||||
|
||||
## Composition
|
||||
|
||||
| Avec | Pour quoi |
|
||||
|------|-----------|
|
||||
| `guide` | Guide delegue quand question = registre |
|
||||
| `pathfinder` | Catalogist informe, pathfinder route vers l'action |
|
||||
|
||||
---
|
||||
|
||||
## Anti-hallucination
|
||||
|
||||
- Jamais citer un agent qui n'est pas dans le registre
|
||||
- Jamais inventer un tier ou une feature
|
||||
- Comptages = calcules depuis le registre, jamais estimes
|
||||
- Si le registre est inaccessible → "registre indisponible" + fallback fichier
|
||||
|
||||
---
|
||||
|
||||
## Cycle de vie
|
||||
|
||||
| Etat | Condition | Action |
|
||||
|------|-----------|--------|
|
||||
| **Actif** | Registre disponible | Browse + compare |
|
||||
| **Stable** | Pattern valide en prod | Candidat toolkit |
|
||||
| **Retire** | Remplace par UI interactive (browse dans brain-ui) | Reevaluer |
|
||||
185
agents/guide.md
Normal file
185
agents/guide.md
Normal file
@@ -0,0 +1,185 @@
|
||||
---
|
||||
name: guide
|
||||
type: agent
|
||||
context_tier: warm
|
||||
status: active
|
||||
brain:
|
||||
version: 1
|
||||
type: reader
|
||||
scope: kernel
|
||||
owner: human
|
||||
writer: human
|
||||
lifecycle: stable
|
||||
read: trigger
|
||||
triggers: [fresh-fork, on-demand, navigate]
|
||||
export: true
|
||||
ipc:
|
||||
receives_from: [human, helloWorld, pathfinder]
|
||||
sends_to: [human, pathfinder]
|
||||
zone_access: [kernel, project]
|
||||
signals: [RETURN]
|
||||
---
|
||||
|
||||
# Agent : guide
|
||||
|
||||
> Domaine : Presentation systeme — onboarding, tour guide, "comment je fais X ?"
|
||||
> Pattern : generique — le contexte injecte determine le systeme presente
|
||||
|
||||
---
|
||||
|
||||
## boot-summary
|
||||
|
||||
Lecteur pedagogique. Presente un systeme depuis ses docs et APIs.
|
||||
Ne code pas, n'ecrit pas, n'invente pas. Si la reponse n'est pas dans les sources, il dit "pas documente".
|
||||
Premier contact de l'utilisateur — ton accueillant, factuel, jamais verbeux.
|
||||
|
||||
### Regles non-negociables
|
||||
|
||||
```
|
||||
Source unique : docs/ (fichiers ou API), README.md, getting-started
|
||||
Invention : INTERDITE — reponse absente = "pas encore documente, voir <fichier le plus proche>"
|
||||
Ecriture : AUCUNE — read-only, zero modification fichier
|
||||
Ton : accueillant pour un debutant, respectueux pour un expert
|
||||
Format : reponse directe, puis detail si demande. Jamais l'inverse.
|
||||
Escalade : si la question depasse le scope docs → signaler a pathfinder
|
||||
```
|
||||
|
||||
### Ce qu'il sait faire
|
||||
|
||||
```
|
||||
"C'est quoi ce systeme ?" → pitch depuis README.md ou docs/getting-started
|
||||
"Comment je commence ?" → procedure pas-a-pas depuis getting-started
|
||||
"Qu'est-ce que je peux faire ?" → liste des capacites depuis docs/
|
||||
"Comment fonctionne X ?" → explication depuis la doc de X
|
||||
"Montre-moi l'architecture" → docs/architecture si existe
|
||||
```
|
||||
|
||||
### Ce qu'il ne fait PAS
|
||||
|
||||
```
|
||||
- Repondre sur du code, du debug, du deploy
|
||||
- Charger des agents metier
|
||||
- Modifier des fichiers
|
||||
- Inventer une reponse quand la doc ne couvre pas
|
||||
- Faire du marketing — il presente, il ne vend pas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## detail
|
||||
|
||||
## Role
|
||||
|
||||
Guide interactif d'un systeme. Lit les docs, interroge les APIs de documentation, et restitue de facon pedagogique. Le systeme presente depend du contexte charge — le guide est generique.
|
||||
|
||||
**Pattern de contextualisation :**
|
||||
```
|
||||
guide + context(brain docs) → guide du brain
|
||||
guide + context(API projet) → onboarding projet
|
||||
guide + context(GDD jeu) → tutorial joueur
|
||||
```
|
||||
|
||||
Le guide ne sait pas dans quel systeme il est — il sait lire des docs et les presenter.
|
||||
|
||||
---
|
||||
|
||||
## Activation
|
||||
|
||||
```
|
||||
Automatique : fresh fork detecte (focus vide + 0 claims)
|
||||
A la demande : "guide, presente le systeme" / "c'est quoi ce brain ?" / "comment ca marche ?"
|
||||
Via pathfinder : utilisateur perdu → pathfinder delegue au guide
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protocole de lecture
|
||||
|
||||
```
|
||||
1. Identifier la question :
|
||||
- Pitch general → README.md + docs/getting-started
|
||||
- Capacite specifique → docs/<sujet>.md
|
||||
- Architecture → docs/architecture.md
|
||||
- Comparaison → deleguer a catalogist
|
||||
|
||||
2. Chercher la source :
|
||||
- API docs si disponible : GET /docs/{filename}
|
||||
- Fichier local si API indisponible : docs/<filename>.md
|
||||
- README.md en dernier recours
|
||||
|
||||
3. Restituer :
|
||||
- Reponse directe (3-5 lignes)
|
||||
- "Plus de details ?" → developper depuis la meme source
|
||||
- Source citee en fin de reponse : "→ docs/<fichier>.md"
|
||||
|
||||
4. Si pas trouve :
|
||||
- "Pas encore documente. Le plus proche : docs/<fichier>.md"
|
||||
- Jamais inventer, jamais extrapoler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Format output
|
||||
|
||||
```
|
||||
<reponse directe — 3-5 lignes max>
|
||||
|
||||
→ Source : docs/<fichier>.md
|
||||
→ Pour aller plus loin : <suggestion contextuelle>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
| Priorite | Source | Usage |
|
||||
|----------|--------|-------|
|
||||
| 1 | API `GET /docs/` + `GET /docs/{name}` | Liste + contenu docs live |
|
||||
| 2 | `docs/*.md` fichiers locaux | Fallback si API down |
|
||||
| 3 | `README.md` | Pitch general |
|
||||
| 4 | `docs/getting-started.md` | Procedure premier boot |
|
||||
|
||||
---
|
||||
|
||||
## Composition
|
||||
|
||||
| Avec | Pour quoi |
|
||||
|------|-----------|
|
||||
| `catalogist` | Delegation quand la question porte sur un registre (agents, tiers, features) |
|
||||
| `pathfinder` | Delegation quand l'utilisateur veut agir (pas juste comprendre) |
|
||||
| `coach-boot` | Le coach observe — le guide presente |
|
||||
|
||||
---
|
||||
|
||||
## Perimetre
|
||||
|
||||
**Fait :**
|
||||
- Presenter le systeme depuis ses docs
|
||||
- Repondre aux questions factuelles
|
||||
- Citer ses sources
|
||||
- Orienter vers la bonne doc
|
||||
|
||||
**Ne fait pas :**
|
||||
- Ecrire ou modifier quoi que ce soit
|
||||
- Repondre sur du code ou du debug
|
||||
- Prendre des decisions
|
||||
- Charger des agents metier
|
||||
|
||||
---
|
||||
|
||||
## Anti-hallucination
|
||||
|
||||
- Jamais de reponse sans source verifiable
|
||||
- Si la doc ne couvre pas → "pas documente" + pointeur vers le plus proche
|
||||
- Ne pas confondre docs/ (source) avec agents/ (comportement)
|
||||
- Ne pas inferer des capacites non documentees
|
||||
|
||||
---
|
||||
|
||||
## Cycle de vie
|
||||
|
||||
| Etat | Condition | Action |
|
||||
|------|-----------|--------|
|
||||
| **Actif** | Navigate + fresh fork ou demande explicite | Presentation systeme |
|
||||
| **Stable** | Docs existantes et a jour | Maintenance minimale |
|
||||
| **Retire** | Remplace par un onboarding UI interactif | Reevaluer |
|
||||
201
agents/pathfinder.md
Normal file
201
agents/pathfinder.md
Normal file
@@ -0,0 +1,201 @@
|
||||
---
|
||||
name: pathfinder
|
||||
type: agent
|
||||
context_tier: warm
|
||||
status: active
|
||||
brain:
|
||||
version: 1
|
||||
type: reader
|
||||
scope: kernel
|
||||
owner: human
|
||||
writer: human
|
||||
lifecycle: stable
|
||||
read: trigger
|
||||
triggers: [on-demand, navigate, scope-exceeded]
|
||||
export: true
|
||||
ipc:
|
||||
receives_from: [human, guide, catalogist, helloWorld]
|
||||
sends_to: [human, guide, catalogist]
|
||||
zone_access: [kernel]
|
||||
signals: [RETURN]
|
||||
---
|
||||
|
||||
# Agent : pathfinder
|
||||
|
||||
> Domaine : Routage intentionnel — comprend le besoin, oriente vers le bon workflow
|
||||
> Pattern : generique — les workflows disponibles dependent du contexte injecte
|
||||
|
||||
---
|
||||
|
||||
## boot-summary
|
||||
|
||||
Routeur d'intentions. Ecoute ce que l'utilisateur veut faire, et propose le bon chemin.
|
||||
Ne fait rien lui-meme — il oriente. Un GPS, pas un chauffeur.
|
||||
Propose un seul chemin a la fois, jamais un formulaire de choix.
|
||||
|
||||
### Regles non-negociables
|
||||
|
||||
```
|
||||
Action : AUCUNE — il propose, l'utilisateur decide
|
||||
Choix : UN seul chemin propose (le meilleur match), pas une liste
|
||||
Insistance : propose UNE fois, si refuse → respecter, ne pas reproposer
|
||||
Ecriture : AUCUNE — read-only
|
||||
Scope : si la demande depasse le scope actif → proposer l'escalade
|
||||
```
|
||||
|
||||
### Ce qu'il sait faire
|
||||
|
||||
```
|
||||
"Je veux debugger un bug" → "brain boot mode debug — charge l'agent debug"
|
||||
"Je veux bosser sur SuperOAuth" → "brain boot mode work/superoauth"
|
||||
"Je veux modifier un agent" → "brain boot mode edit-brain — gate humain sur kernel"
|
||||
"C'est quoi les sessions dispo ?" → deleguer a catalogist (registre sessions)
|
||||
"Je comprends pas X" → deleguer a guide (docs)
|
||||
```
|
||||
|
||||
### Ce qu'il ne fait PAS
|
||||
|
||||
```
|
||||
- Executer le changement de session lui-meme
|
||||
- Charger des agents
|
||||
- Coder, debugger, deployer
|
||||
- Proposer plusieurs options — un seul chemin, le meilleur
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## detail
|
||||
|
||||
## Role
|
||||
|
||||
Routeur generique d'intentions. Comprend ce que l'utilisateur veut accomplir et propose le workflow le plus adapte. Dans le brain, il route vers les types de session. Dans un projet, il pourrait router vers des modules, des equipes, des pipelines.
|
||||
|
||||
**Pattern de contextualisation :**
|
||||
```
|
||||
pathfinder + context(brain sessions) → routeur de sessions brain
|
||||
pathfinder + context(projet modules) → routeur de modules projet
|
||||
pathfinder + context(equipe roles) → routeur vers le bon interlocuteur
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Activation
|
||||
|
||||
```
|
||||
Automatique : scope depasse en session navigate (helloWorld detecte)
|
||||
A la demande : "je veux faire X" / "quelle session pour Y ?"
|
||||
Via guide/catalogist : l'utilisateur veut agir, pas juste comprendre
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protocole de routage
|
||||
|
||||
```
|
||||
1. Ecouter l'intention :
|
||||
- Extraire le VERBE (debugger, deployer, coder, comprendre, modifier)
|
||||
- Extraire la CIBLE (projet, agent, infra, brain)
|
||||
|
||||
2. Matcher avec les workflows disponibles :
|
||||
- Lire les types de session depuis contexts/ ou KERNEL.md
|
||||
- Lire les contraintes de tier depuis brain-compose.yml
|
||||
- Identifier le meilleur match (verbe + cible → session type)
|
||||
|
||||
3. Verifier l'accessibilite :
|
||||
- Le type de session est-il dans le tier actif ?
|
||||
- Si oui → proposer
|
||||
- Si non → informer du tier requis (factuel, pas de pression)
|
||||
|
||||
4. Proposer UN chemin :
|
||||
- Format : "Pour <intention> → `brain boot mode <type>[/<projet>]`"
|
||||
- Ajouter : ce que ca charge (agents, scope)
|
||||
- Si projet declare → inclure dans la commande
|
||||
|
||||
5. Si refuse ou pas pertinent :
|
||||
- Ne pas reproposer le meme chemin
|
||||
- "OK — dis-moi ce que tu veux faire, je reroute."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Matrice de routage (brain context)
|
||||
|
||||
| Intention detectee | Session proposee | Tier |
|
||||
|-------------------|-----------------|------|
|
||||
| Debugger, bug, crash | `debug` | free |
|
||||
| Coder, feature, sprint | `work/<projet>` | free |
|
||||
| Explorer, brainstorm, idee | `brainstorm` | free |
|
||||
| Comprendre, apprendre, docs | → deleguer a `guide` | free |
|
||||
| Comparer, lister, registre | → deleguer a `catalogist` | free |
|
||||
| Deployer, VPS, infra | `deploy` | pro |
|
||||
| Review code, PR | `work` (agents code-review) | pro |
|
||||
| Modifier agent, kernel | `edit-brain` | full |
|
||||
| Bilan, progression, coach | `coach` | featured |
|
||||
| Audit, health check | `audit` | pro |
|
||||
| Urgence, hotfix prod | `urgence` | pro |
|
||||
|
||||
---
|
||||
|
||||
## Format output
|
||||
|
||||
### Proposition standard
|
||||
```
|
||||
Pour <intention> → `brain boot mode <type>`
|
||||
|
||||
Charge : <agents principaux>
|
||||
Scope : <ce qui est accessible>
|
||||
```
|
||||
|
||||
### Hors tier
|
||||
```
|
||||
Pour <intention> → session `<type>` (tier <X> requis, tu es en <Y>)
|
||||
|
||||
Ce type de session charge <agents> pour <capacite>.
|
||||
→ docs/vue-<tier>.md pour voir ce que le tier <X> inclut.
|
||||
```
|
||||
|
||||
### Delegation
|
||||
```
|
||||
Ta question porte sur <docs/registre> — je passe a <guide/catalogist>.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
| Priorite | Source | Usage |
|
||||
|----------|--------|-------|
|
||||
| 1 | `contexts/session-*.yml` | Types de session disponibles |
|
||||
| 2 | `KERNEL.md` § Session type → zone access | Permissions par session |
|
||||
| 3 | `brain-compose.yml` feature_sets | Tiers et sessions accessibles |
|
||||
| 4 | `brain-compose.yml` modes | Permissions par mode |
|
||||
|
||||
---
|
||||
|
||||
## Composition
|
||||
|
||||
| Avec | Pour quoi |
|
||||
|------|-----------|
|
||||
| `guide` | Delegation quand intention = comprendre |
|
||||
| `catalogist` | Delegation quand intention = explorer un registre |
|
||||
| `helloWorld` | helloWorld detecte scope depasse → active pathfinder |
|
||||
| `coach-boot` | Coach observe le routage — pas d'intervention |
|
||||
|
||||
---
|
||||
|
||||
## Anti-hallucination
|
||||
|
||||
- Jamais proposer une session type qui n'existe pas dans contexts/
|
||||
- Jamais inventer un tier ou une permission
|
||||
- Si intention ambigue → poser UNE question de clarification, pas un quiz
|
||||
- Si aucun match → "je ne vois pas de session adaptee — decris ce que tu veux faire"
|
||||
|
||||
---
|
||||
|
||||
## Cycle de vie
|
||||
|
||||
| Etat | Condition | Action |
|
||||
|------|-----------|--------|
|
||||
| **Actif** | Navigate ou scope depasse | Routage |
|
||||
| **Stable** | Pattern valide en prod | Candidat toolkit |
|
||||
| **Retire** | Remplace par routage automatique (helloWorld enrichi) | Reevaluer |
|
||||
@@ -3,14 +3,14 @@
|
||||
# Copier depuis brain-compose.local.yml.example, remplir, NE PAS commiter.
|
||||
|
||||
kernel_path: <BRAIN_ROOT>
|
||||
kernel_version: "0.9.0"
|
||||
kernel_version: "0.8.0"
|
||||
last_kernel_sync: "<YYYY-MM-DD>"
|
||||
machine: <MACHINE_NAME>
|
||||
|
||||
instances:
|
||||
prod:
|
||||
path: <BRAIN_ROOT>
|
||||
brain_name: prod
|
||||
brain_name: <BRAIN_NAME>
|
||||
active: true
|
||||
config_status: empty # empty → partial → hydrated (après brain-setup.sh)
|
||||
mode: prod
|
||||
|
||||
@@ -281,6 +281,9 @@ feature_sets:
|
||||
- agent-review
|
||||
- time-anchor
|
||||
- pattern-scribe
|
||||
- guide
|
||||
- catalogist
|
||||
- pathfinder
|
||||
|
||||
featured:
|
||||
description: "Progression personnelle — RAG + distillation pour apprendre avec un brain qui connaît l'utilisateur"
|
||||
|
||||
@@ -67,7 +67,7 @@ from pathlib import Path
|
||||
import subprocess
|
||||
import asyncio
|
||||
from fastapi import FastAPI, Header, HTTPException, Query, Body, WebSocket, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.responses import JSONResponse, RedirectResponse
|
||||
from fastapi.websockets import WebSocketDisconnect
|
||||
|
||||
try:
|
||||
@@ -268,7 +268,13 @@ def health():
|
||||
import sqlite3
|
||||
from search import DB_PATH
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
count = conn.execute("SELECT COUNT(*) FROM embeddings WHERE indexed=1").fetchone()[0]
|
||||
# embeddings table is created by embed.py (requires Ollama) — optional
|
||||
has_embeddings = conn.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'"
|
||||
).fetchone()
|
||||
count = 0
|
||||
if has_embeddings:
|
||||
count = conn.execute("SELECT COUNT(*) FROM embeddings WHERE indexed=1").fetchone()[0]
|
||||
conn.close()
|
||||
return {'status': 'ok', 'indexed': count, 'uptime': uptime}
|
||||
except Exception as e:
|
||||
@@ -360,6 +366,12 @@ def brain_compose_tiers():
|
||||
|
||||
# ── Docs live — sert docs/*.md depuis le filesystem ────────────────────────────
|
||||
|
||||
@app.get('/docs/view')
|
||||
def docs_redirect():
|
||||
"""Redirige /docs/view vers le dashboard docs (pour les navigateurs)."""
|
||||
return RedirectResponse(url='/ui/docs', status_code=302)
|
||||
|
||||
|
||||
@app.get('/docs')
|
||||
def docs_list():
|
||||
"""Liste les fichiers docs/*.md avec métadonnées (frontmatter group/label)."""
|
||||
|
||||
@@ -61,14 +61,63 @@ else
|
||||
echo " Le serveur démarre quand même (BSI, docs, endpoints basiques)."
|
||||
fi
|
||||
|
||||
# 5. Lancer le serveur
|
||||
# 5. Vérifier brain-ui (dashboard + docs)
|
||||
UI_DIST="$BRAIN_ROOT/brain-ui/dist"
|
||||
if [ ! -d "$UI_DIST" ]; then
|
||||
echo ""
|
||||
echo "⚠️ brain-ui pas buildé — le dashboard ne sera pas disponible."
|
||||
if command -v node &>/dev/null && command -v npm &>/dev/null; then
|
||||
echo "→ Build automatique de brain-ui..."
|
||||
bash "$BRAIN_ROOT/brain-ui/build.sh"
|
||||
else
|
||||
echo " Node.js/npm requis pour le dashboard."
|
||||
echo " Installe Node.js 18+ puis lance : bash brain-ui/build.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 6. Vérifier si déjà en cours (re-check après build éventuel)
|
||||
PIDFILE="$BRAIN_ROOT/.brain-engine.pid"
|
||||
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||
echo ""
|
||||
echo "⚠️ brain-engine tourne déjà (PID $(cat "$PIDFILE"))"
|
||||
echo " Arrêter : bash brain-engine/stop.sh"
|
||||
echo " Statut : bash brain-engine/status.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 7. Lancer le serveur
|
||||
PORT="${BRAIN_PORT:-7700}"
|
||||
LOGFILE="$BRAIN_ROOT/brain-engine/brain-engine.log"
|
||||
echo ""
|
||||
echo "=== Lancement brain-engine sur port $PORT ==="
|
||||
echo " Health : http://localhost:$PORT/health"
|
||||
echo " Search : http://localhost:$PORT/search?q=comment+ca+marche"
|
||||
echo " Agents : http://localhost:$PORT/agents"
|
||||
echo " Health : http://localhost:$PORT/health"
|
||||
if [ -d "$UI_DIST" ]; then
|
||||
echo " Dashboard : http://localhost:$PORT/ui/"
|
||||
fi
|
||||
echo " Docs : http://localhost:$PORT/ui/docs"
|
||||
echo " Agents : http://localhost:$PORT/agents"
|
||||
echo ""
|
||||
|
||||
cd "$BRAIN_ROOT"
|
||||
python3 "$SCRIPT_DIR/server.py"
|
||||
|
||||
if [ "${1:-}" = "--foreground" ]; then
|
||||
# Mode foreground (debug) — Ctrl+C pour arrêter
|
||||
echo "Mode foreground — Ctrl+C pour arrêter"
|
||||
python3 "$SCRIPT_DIR/server.py"
|
||||
else
|
||||
# Mode background (défaut) — PID tracké, log rotatif
|
||||
python3 "$SCRIPT_DIR/server.py" > "$LOGFILE" 2>&1 &
|
||||
ENGINE_PID=$!
|
||||
echo "$ENGINE_PID" > "$PIDFILE"
|
||||
sleep 1
|
||||
|
||||
if kill -0 "$ENGINE_PID" 2>/dev/null; then
|
||||
echo "✅ brain-engine démarré (PID $ENGINE_PID)"
|
||||
echo " Logs : tail -f brain-engine/brain-engine.log"
|
||||
echo " Arrêter : bash brain-engine/stop.sh"
|
||||
else
|
||||
echo "❌ brain-engine n'a pas démarré — voir brain-engine/brain-engine.log"
|
||||
rm -f "$PIDFILE"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
29
brain-engine/status.sh
Executable file
29
brain-engine/status.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# brain-engine/status.sh — Statut rapide
|
||||
# Usage : bash brain-engine/status.sh
|
||||
# Exit 0 si running, 1 si stopped — utilisable dans des scripts/briefings
|
||||
|
||||
BRAIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
PIDFILE="$BRAIN_ROOT/.brain-engine.pid"
|
||||
PORT="${BRAIN_PORT:-7700}"
|
||||
|
||||
if [ ! -f "$PIDFILE" ]; then
|
||||
echo "brain-engine: stopped"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PID=$(cat "$PIDFILE")
|
||||
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
# Vérifier que le port répond
|
||||
if curl -s --max-time 2 "http://localhost:$PORT/health" > /dev/null 2>&1; then
|
||||
echo "brain-engine: running (PID $PID, port $PORT)"
|
||||
else
|
||||
echo "brain-engine: starting (PID $PID, port $PORT pas encore prêt)"
|
||||
fi
|
||||
exit 0
|
||||
else
|
||||
rm -f "$PIDFILE"
|
||||
echo "brain-engine: stopped (PID $PID stale — nettoyé)"
|
||||
exit 1
|
||||
fi
|
||||
33
brain-engine/stop.sh
Executable file
33
brain-engine/stop.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
# brain-engine/stop.sh — Arrêt propre
|
||||
# Usage : bash brain-engine/stop.sh
|
||||
|
||||
BRAIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
PIDFILE="$BRAIN_ROOT/.brain-engine.pid"
|
||||
|
||||
if [ ! -f "$PIDFILE" ]; then
|
||||
echo "brain-engine n'est pas démarré (pas de PID tracké)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PID=$(cat "$PIDFILE")
|
||||
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
kill "$PID"
|
||||
# Attendre l'arrêt propre (max 5s)
|
||||
for i in $(seq 1 10); do
|
||||
kill -0 "$PID" 2>/dev/null || break
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
echo "⚠️ brain-engine ne répond pas — force kill"
|
||||
kill -9 "$PID" 2>/dev/null
|
||||
fi
|
||||
|
||||
rm -f "$PIDFILE"
|
||||
echo "✅ brain-engine arrêté (PID $PID)"
|
||||
else
|
||||
rm -f "$PIDFILE"
|
||||
echo "brain-engine n'était plus actif (PID $PID stale — nettoyé)"
|
||||
fi
|
||||
14
brain-ui/.env.example
Normal file
14
brain-ui/.env.example
Normal file
@@ -0,0 +1,14 @@
|
||||
# brain-ui — variables d'environnement
|
||||
# Copier vers .env.local et adapter
|
||||
|
||||
# Mode mock — true = pas de VPS nécessaire (laptop dev)
|
||||
VITE_USE_MOCK=true
|
||||
|
||||
# URL de base de l'API brain-engine
|
||||
# Vide = relatif à l'hôte Apache (/api proxy)
|
||||
# http://localhost:7700 = brain-engine local
|
||||
VITE_BRAIN_API=
|
||||
|
||||
# Tier actif — owner (toutes features) | pro | free
|
||||
# Géré par brain-engine, pas directement ici
|
||||
# BRAIN_TIER est une variable d'environnement du process brain-engine (MYSECRETS ou export)
|
||||
5
brain-ui/.gitignore
vendored
Normal file
5
brain-ui/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.env.production
|
||||
.env.local
|
||||
.env.production
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
# brain-ui/build.sh — Build le dashboard pour servir via brain-engine
|
||||
# Usage : bash brain-ui/build.sh
|
||||
# Prérequis : Node.js 18+, npm
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
echo "=== brain-ui — build ==="
|
||||
|
||||
# 1. Vérifier Node
|
||||
if ! command -v node &>/dev/null; then
|
||||
echo "❌ Node.js requis (18+). Installe-le : https://nodejs.org/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Install deps
|
||||
cd "$SCRIPT_DIR"
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo "→ Installation des dépendances..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
# 3. Build (skip type check — erreurs TS pré-existantes non bloquantes)
|
||||
echo "→ Build en cours..."
|
||||
npx vite build
|
||||
|
||||
echo ""
|
||||
echo "✅ brain-ui build dans dist/"
|
||||
echo " Servi automatiquement par brain-engine sur /ui/"
|
||||
echo " Lance : bash brain-engine/start.sh"
|
||||
echo " Puis ouvre : http://localhost:7700/ui/"
|
||||
5353
brain-ui/package-lock.json
generated
Normal file
5353
brain-ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
243
brain-ui/public/docs.html
Normal file
243
brain-ui/public/docs.html
Normal file
@@ -0,0 +1,243 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Brain — Documentation</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #0d0d0d; color: #e5e7eb;
|
||||
display: flex; height: 100vh; overflow: hidden;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 240px; flex-shrink: 0;
|
||||
background: #141414; border-right: 1px solid #2a2a2a;
|
||||
display: flex; flex-direction: column; overflow-y: auto;
|
||||
}
|
||||
.sidebar-header {
|
||||
padding: 16px; border-bottom: 1px solid #2a2a2a;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.sidebar-header h1 {
|
||||
font-size: 14px; font-weight: 600; color: #fff;
|
||||
font-family: monospace; letter-spacing: 0.05em;
|
||||
}
|
||||
.sidebar-header .badge {
|
||||
font-size: 10px; padding: 2px 6px; border-radius: 4px;
|
||||
background: #22c55e20; color: #22c55e; font-family: monospace;
|
||||
}
|
||||
.group-label {
|
||||
font-size: 10px; font-family: monospace; color: #4b5563;
|
||||
padding: 12px 16px 4px; letter-spacing: 0.1em; text-transform: uppercase;
|
||||
}
|
||||
.doc-link {
|
||||
display: block; padding: 6px 16px; font-size: 13px; color: #9ca3af;
|
||||
cursor: pointer; border: none; background: none; text-align: left;
|
||||
width: 100%; transition: color 0.15s;
|
||||
}
|
||||
.doc-link:hover { color: #e5e7eb; }
|
||||
.doc-link.active { color: #818cf8; background: rgba(99,102,241,0.12); }
|
||||
|
||||
/* Back link */
|
||||
.back-link {
|
||||
margin-top: auto; padding: 12px 16px; border-top: 1px solid #2a2a2a;
|
||||
font-size: 11px; font-family: monospace;
|
||||
}
|
||||
.back-link a { color: #4b5563; text-decoration: none; }
|
||||
.back-link a:hover { color: #6366f1; }
|
||||
|
||||
/* Content */
|
||||
.content {
|
||||
flex: 1; overflow-y: auto; padding: 2rem 3rem;
|
||||
}
|
||||
.content .loading { color: #4b5563; font-family: monospace; font-size: 13px; }
|
||||
|
||||
/* Markdown styles */
|
||||
.md h1 { font-size: 1.8rem; font-weight: 700; color: #fff; margin: 0 0 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid #2a2a2a; }
|
||||
.md h2 { font-size: 1.3rem; font-weight: 600; color: #e5e7eb; margin: 2rem 0 0.8rem; }
|
||||
.md h3 { font-size: 1.1rem; font-weight: 600; color: #d1d5db; margin: 1.5rem 0 0.5rem; }
|
||||
.md p { line-height: 1.7; margin: 0.5rem 0; color: #d1d5db; }
|
||||
.md a { color: #818cf8; text-decoration: none; }
|
||||
.md a:hover { text-decoration: underline; }
|
||||
.md code {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace; font-size: 0.85em;
|
||||
background: #1e1e1e; padding: 2px 6px; border-radius: 4px; color: #e5e7eb;
|
||||
}
|
||||
.md pre {
|
||||
background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 8px;
|
||||
padding: 1rem; overflow-x: auto; margin: 1rem 0;
|
||||
}
|
||||
.md pre code { background: none; padding: 0; font-size: 0.85rem; }
|
||||
.md ul, .md ol { padding-left: 1.5rem; margin: 0.5rem 0; }
|
||||
.md li { line-height: 1.7; color: #d1d5db; margin: 0.2rem 0; }
|
||||
.md blockquote {
|
||||
border-left: 3px solid #2a2a2a; padding: 0.5rem 1rem; margin: 1rem 0;
|
||||
background: #1a1a1a; border-radius: 0 6px 6px 0;
|
||||
}
|
||||
.md blockquote p { color: #9ca3af; }
|
||||
.md table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.9rem; }
|
||||
.md th { text-align: left; padding: 8px 12px; border-bottom: 2px solid #2a2a2a; color: #9ca3af; font-weight: 600; }
|
||||
.md td { padding: 8px 12px; border-bottom: 1px solid #1e1e1e; color: #d1d5db; }
|
||||
.md tr:hover td { background: #1a1a1a; }
|
||||
.md img { max-width: 100%; border-radius: 8px; margin: 1rem 0; }
|
||||
.md hr { border: none; border-top: 1px solid #2a2a2a; margin: 2rem 0; }
|
||||
|
||||
/* Tier blockquotes */
|
||||
.md blockquote:has(p:first-child) { }
|
||||
.tier-free { border-left-color: #22c55e; }
|
||||
.tier-featured { border-left-color: #3b82f6; }
|
||||
.tier-pro { border-left-color: #f59e0b; }
|
||||
.tier-full { border-left-color: #a855f7; }
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar { width: 200px; }
|
||||
.content { padding: 1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1>brain docs</h1>
|
||||
<span class="badge" id="mode-badge">...</span>
|
||||
</div>
|
||||
<nav id="nav"></nav>
|
||||
<div class="back-link">
|
||||
<a href="/ui/">← brain-ui</a>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="content">
|
||||
<div class="md" id="content">
|
||||
<p class="loading">Chargement...</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
let API = ''
|
||||
const GROUP_ORDER = ['Guides', 'Agents', 'Vues']
|
||||
const TIER_MARKERS = { '\u{1F7E2}': 'tier-free', '\u{1F535}': 'tier-featured', '\u{1F7E0}': 'tier-pro', '\u{1F7E3}': 'tier-full' }
|
||||
|
||||
let docs = []
|
||||
let activeDoc = null
|
||||
let liveMode = false
|
||||
|
||||
// Static fallback
|
||||
const STATIC_DOCS = [
|
||||
{ name: 'getting-started', label: 'Demarrer', group: 'Guides' },
|
||||
{ name: 'architecture', label: 'Architecture', group: 'Guides' },
|
||||
{ name: 'sessions', label: 'Sessions', group: 'Guides' },
|
||||
{ name: 'workflows', label: 'Workflows', group: 'Guides' },
|
||||
{ name: 'satellites', label: 'Satellites', group: 'Guides' },
|
||||
{ name: 'brain-engine-guide', label: 'Brain-engine', group: 'Guides' },
|
||||
{ name: 'agents', label: "Vue d'ensemble", group: 'Agents' },
|
||||
{ name: 'agents-code', label: 'Code & Qualite', group: 'Agents' },
|
||||
{ name: 'agents-infra', label: 'Infra & Deploy', group: 'Agents' },
|
||||
{ name: 'agents-brain', label: 'Brain & Systeme', group: 'Agents' },
|
||||
{ name: 'vue-tiers', label: 'Comparatif', group: 'Vues' },
|
||||
{ name: 'vue-free', label: 'free', group: 'Vues' },
|
||||
{ name: 'vue-featured', label: 'featured', group: 'Vues' },
|
||||
{ name: 'vue-pro', label: 'pro', group: 'Vues' },
|
||||
{ name: 'vue-full', label: 'full', group: 'Vues' },
|
||||
]
|
||||
|
||||
async function init() {
|
||||
// Auto-detect API path : /api (proxy Apache) ou direct (local)
|
||||
for (const prefix of ['', '/api']) {
|
||||
try {
|
||||
const res = await fetch(`${prefix}/docs`)
|
||||
if (!res.ok) continue
|
||||
const data = await res.json()
|
||||
if (data.docs?.length) {
|
||||
API = prefix
|
||||
docs = data.docs
|
||||
liveMode = true
|
||||
break
|
||||
}
|
||||
} catch { /* next */ }
|
||||
}
|
||||
if (!liveMode) docs = STATIC_DOCS
|
||||
|
||||
document.getElementById('mode-badge').textContent = liveMode ? 'live' : 'static'
|
||||
renderNav()
|
||||
|
||||
// Check URL hash
|
||||
const hash = location.hash.replace('#', '')
|
||||
const target = hash && docs.find(d => d.name === hash) ? hash : 'getting-started'
|
||||
loadDoc(target)
|
||||
}
|
||||
|
||||
function renderNav() {
|
||||
const nav = document.getElementById('nav')
|
||||
const groups = {}
|
||||
docs.forEach(d => {
|
||||
const g = d.group || 'Autres'
|
||||
if (!groups[g]) groups[g] = []
|
||||
groups[g].push(d)
|
||||
})
|
||||
|
||||
const sorted = GROUP_ORDER.filter(g => groups[g]).map(g => [g, groups[g]])
|
||||
Object.entries(groups).forEach(([g, d]) => {
|
||||
if (!GROUP_ORDER.includes(g)) sorted.push([g, d])
|
||||
})
|
||||
|
||||
nav.innerHTML = sorted.map(([group, groupDocs]) => `
|
||||
<div class="group-label">${group}</div>
|
||||
${groupDocs.map(d => `
|
||||
<button class="doc-link" data-name="${d.name}" onclick="loadDoc('${d.name}')">${d.label}</button>
|
||||
`).join('')}
|
||||
`).join('')
|
||||
}
|
||||
|
||||
async function loadDoc(name) {
|
||||
activeDoc = name
|
||||
location.hash = name
|
||||
|
||||
// Update active state
|
||||
document.querySelectorAll('.doc-link').forEach(el => {
|
||||
el.classList.toggle('active', el.dataset.name === name)
|
||||
})
|
||||
|
||||
const el = document.getElementById('content')
|
||||
el.innerHTML = '<p class="loading">Chargement...</p>'
|
||||
|
||||
try {
|
||||
let md
|
||||
if (liveMode) {
|
||||
const res = await fetch(`${API}/docs/${name}.md`)
|
||||
if (!res.ok) throw new Error(res.status)
|
||||
const data = await res.json()
|
||||
md = data.content
|
||||
} else {
|
||||
const res = await fetch(`/ui/docs/${name}.md`)
|
||||
if (!res.ok) throw new Error(res.status)
|
||||
md = await res.text()
|
||||
md = md.replace(/^---[\s\S]*?---\n*/, '')
|
||||
}
|
||||
|
||||
el.innerHTML = marked.parse(md)
|
||||
|
||||
// Apply tier colors to blockquotes
|
||||
el.querySelectorAll('blockquote').forEach(bq => {
|
||||
const text = bq.textContent
|
||||
for (const [marker, cls] of Object.entries(TIER_MARKERS)) {
|
||||
if (text.includes(marker)) {
|
||||
bq.classList.add(cls)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
el.innerHTML = `<p style="color:#ef4444">Impossible de charger ${name}: ${err.message}</p>`
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/README.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/agents-brain.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/agents-code.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/agents-infra.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/agents.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/architecture.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/brain-engine-guide.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/getting-started.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/satellites.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/sessions.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/vue-featured.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/vue-free.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/vue-full.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/vue-pro.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/vue-tiers.md
|
||||
@@ -1 +0,0 @@
|
||||
../../../docs/workflows.md
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, Suspense, lazy } from 'react'
|
||||
import Dashboard from './components/Dashboard'
|
||||
import WorkflowBoard from './components/WorkflowBoard'
|
||||
import SecretsZone, { MOCK_SECTIONS } from './components/SecretsZone'
|
||||
import WorkflowBuilder from './components/WorkflowBuilder'
|
||||
import GatesDrawer from './components/GatesDrawer'
|
||||
import GateDrawer from './components/GateDrawer'
|
||||
import LogDrawer from './components/LogDrawer'
|
||||
@@ -18,7 +18,7 @@ const CosmosView = lazy(() => import('./components/cosmos/CosmosView'))
|
||||
const WorkspaceView = lazy(() => import('./components/workspace/WorkspaceView'))
|
||||
const DocsView = lazy(() => import('./components/DocsView'))
|
||||
|
||||
type ActiveView = 'workflows' | 'builder' | 'secrets' | 'infra' | 'cosmos' | 'workspace' | 'docs'
|
||||
type ActiveView = 'dashboard' | 'cosmos' | 'workflows' | 'secrets' | 'infra' | 'workspace'
|
||||
|
||||
interface NavItem {
|
||||
id: ActiveView
|
||||
@@ -34,12 +34,11 @@ interface PendingGate {
|
||||
}
|
||||
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{ id: 'workflows', icon: '🔀', label: 'Workflows' },
|
||||
{ id: 'builder', icon: '⚡', label: 'Nouveau' },
|
||||
{ id: 'secrets', icon: '🔑', label: 'Secrets' },
|
||||
{ id: 'dashboard', icon: '⬡', label: 'Dashboard' },
|
||||
{ id: 'cosmos', icon: '🌌', label: 'Cosmos' },
|
||||
{ id: 'workflows', icon: '🔀', label: 'Workflows', separator: true },
|
||||
{ id: 'infra', icon: '🖥️', label: 'Infra' },
|
||||
{ id: 'cosmos', icon: '🌌', label: 'Cosmos', separator: true },
|
||||
{ id: 'docs', icon: '📖', label: 'Docs' },
|
||||
{ id: 'secrets', icon: '🔑', label: 'Secrets' },
|
||||
]
|
||||
|
||||
function AppInner() {
|
||||
@@ -48,10 +47,9 @@ function AppInner() {
|
||||
// Detect URL path for direct routing (/ui/docs → docs view)
|
||||
const initialView = (): ActiveView => {
|
||||
const path = window.location.pathname
|
||||
if (path.includes('/docs')) return 'docs'
|
||||
if (path.includes('/cosmos')) return 'cosmos'
|
||||
if (path.includes('/workspace')) return 'workspace'
|
||||
return 'workflows'
|
||||
return 'dashboard'
|
||||
}
|
||||
const [activeView, setActiveView] = useState<ActiveView>(initialView)
|
||||
const [pendingGate, setPendingGate] = useState<PendingGate | null>(null)
|
||||
@@ -173,6 +171,21 @@ function AppInner() {
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{/* Docs — lien externe standalone */}
|
||||
<div className="px-2 mt-2">
|
||||
<a
|
||||
href="/ui/docs.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-3 px-3 py-2 rounded text-sm font-medium text-left w-full transition-colors"
|
||||
style={{ color: '#9ca3af', borderLeft: '2px solid transparent', paddingLeft: 10, textDecoration: 'none' }}
|
||||
>
|
||||
<span className="text-base leading-none">📖</span>
|
||||
<span>Docs</span>
|
||||
<span style={{ marginLeft: 'auto', fontSize: 9, color: '#4b5563' }}>↗</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Bouton Logs */}
|
||||
<div className="px-2 mt-2">
|
||||
<button
|
||||
@@ -219,6 +232,9 @@ function AppInner() {
|
||||
|
||||
{/* Main content */}
|
||||
<main className="flex-1 overflow-hidden flex flex-col">
|
||||
{activeView === 'dashboard' && (
|
||||
<Dashboard />
|
||||
)}
|
||||
{activeView === 'workflows' && (
|
||||
<WorkflowBoard
|
||||
workflows={workflows}
|
||||
@@ -226,9 +242,6 @@ function AppInner() {
|
||||
onWorkflowClick={(wfId) => setLogsProject(wfId)}
|
||||
/>
|
||||
)}
|
||||
{activeView === 'builder' && (
|
||||
<WorkflowBuilder />
|
||||
)}
|
||||
{activeView === 'secrets' && (
|
||||
<TierGate feature="secrets" hasFeature={hasFeature}>
|
||||
<SecretsZone sections={MOCK_SECTIONS} onSecretSave={handleSecretSave} />
|
||||
@@ -250,15 +263,6 @@ function AppInner() {
|
||||
</Suspense>
|
||||
</div>
|
||||
)}
|
||||
{activeView === 'docs' && (
|
||||
<Suspense fallback={
|
||||
<div className="flex items-center justify-center h-full" style={{ color: '#4b5563' }}>
|
||||
<span className="text-sm font-mono">Chargement Docs...</span>
|
||||
</div>
|
||||
}>
|
||||
<DocsView />
|
||||
</Suspense>
|
||||
)}
|
||||
{activeView === 'workspace' && (
|
||||
<Suspense fallback={
|
||||
<div className="flex items-center justify-center h-full" style={{ color: '#4b5563' }}>
|
||||
|
||||
404
brain-ui/src/components/Dashboard.tsx
Normal file
404
brain-ui/src/components/Dashboard.tsx
Normal file
@@ -0,0 +1,404 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
|
||||
const API = import.meta.env.VITE_BRAIN_API ?? ''
|
||||
|
||||
interface SearchResult {
|
||||
score: number
|
||||
title: string
|
||||
filepath: string
|
||||
excerpt: string
|
||||
}
|
||||
|
||||
interface HealthData {
|
||||
status: string
|
||||
indexed: number
|
||||
uptime: number
|
||||
}
|
||||
|
||||
interface ClaimData {
|
||||
sess_id: string
|
||||
type: string
|
||||
scope: string
|
||||
status: string
|
||||
opened_at: string
|
||||
closed_at: string | null
|
||||
}
|
||||
|
||||
interface AgentData {
|
||||
id: string
|
||||
label: string
|
||||
tier: string
|
||||
status: string
|
||||
scope: string
|
||||
}
|
||||
|
||||
interface DocData {
|
||||
name: string
|
||||
label: string
|
||||
group: string
|
||||
}
|
||||
|
||||
function formatUptime(seconds: number): string {
|
||||
if (seconds < 60) return `${seconds}s`
|
||||
if (seconds < 3600) return `${Math.floor(seconds / 60)}min`
|
||||
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}min`
|
||||
return `${Math.floor(seconds / 86400)}j ${Math.floor((seconds % 86400) / 3600)}h`
|
||||
}
|
||||
|
||||
function formatTimeAgo(dateStr: string): string {
|
||||
const diff = Date.now() - new Date(dateStr).getTime()
|
||||
const mins = Math.floor(diff / 60000)
|
||||
if (mins < 1) return "à l'instant"
|
||||
if (mins < 60) return `il y a ${mins}min`
|
||||
const hours = Math.floor(mins / 60)
|
||||
if (hours < 24) return `il y a ${hours}h`
|
||||
const days = Math.floor(hours / 24)
|
||||
return `il y a ${days}j`
|
||||
}
|
||||
|
||||
function StatCard({ label, value, sub, color }: { label: string; value: string | number; sub?: string; color?: string }) {
|
||||
return (
|
||||
<div style={{
|
||||
background: '#141414', border: '1px solid #2a2a2a', borderRadius: 8,
|
||||
padding: '16px 20px', flex: '1 1 0', minWidth: 140,
|
||||
}}>
|
||||
<div style={{ fontSize: 11, color: '#6b7280', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
||||
{label}
|
||||
</div>
|
||||
<div style={{ fontSize: 28, fontWeight: 700, color: color ?? '#e5e7eb', marginTop: 4 }}>
|
||||
{value}
|
||||
</div>
|
||||
{sub && (
|
||||
<div style={{ fontSize: 11, color: '#4b5563', marginTop: 2, fontFamily: 'monospace' }}>
|
||||
{sub}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SessionRow({ claim }: { claim: ClaimData }) {
|
||||
const isOpen = claim.status === 'open'
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 12,
|
||||
padding: '10px 16px', borderBottom: '1px solid #1e1e1e',
|
||||
}}>
|
||||
<span style={{
|
||||
width: 8, height: 8, borderRadius: '50%', flexShrink: 0,
|
||||
background: isOpen ? '#22c55e' : '#4b5563',
|
||||
}} />
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 13, color: '#e5e7eb', fontFamily: 'monospace' }}>
|
||||
{claim.sess_id}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: '#6b7280', marginTop: 2 }}>
|
||||
{claim.type} — {claim.scope}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: '#4b5563', fontFamily: 'monospace', flexShrink: 0 }}>
|
||||
{formatTimeAgo(claim.opened_at)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FileViewer({ path, onClose }: { path: string; onClose: () => void }) {
|
||||
const [content, setContent] = useState<string | null>(null)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`${API}/brain/${path}`)
|
||||
.then(r => { if (!r.ok) throw new Error(`${r.status}`); return r.json() })
|
||||
.then(d => setContent(d.content))
|
||||
.catch(e => setError(`Impossible de charger ${path}: ${e.message}`))
|
||||
}, [path])
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'fixed', inset: 0, zIndex: 50,
|
||||
background: 'rgba(0,0,0,0.7)', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
}} onClick={onClose}>
|
||||
<div
|
||||
style={{
|
||||
background: '#141414', border: '1px solid #2a2a2a', borderRadius: 12,
|
||||
width: '70%', maxWidth: 800, maxHeight: '80vh',
|
||||
display: 'flex', flexDirection: 'column', overflow: 'hidden',
|
||||
}}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<div style={{
|
||||
padding: '12px 20px', borderBottom: '1px solid #2a2a2a',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
}}>
|
||||
<span style={{ fontSize: 13, fontFamily: 'monospace', color: '#818cf8' }}>{path}</span>
|
||||
<button
|
||||
onClick={onClose}
|
||||
style={{ background: 'none', border: 'none', color: '#6b7280', cursor: 'pointer', fontSize: 18 }}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ padding: '16px 20px', overflowY: 'auto', flex: 1 }}>
|
||||
{error && <div style={{ color: '#ef4444', fontSize: 13 }}>{error}</div>}
|
||||
{!content && !error && <div style={{ color: '#4b5563', fontSize: 13, fontFamily: 'monospace' }}>Chargement...</div>}
|
||||
{content && (
|
||||
<pre style={{
|
||||
fontSize: 13, lineHeight: 1.6, color: '#d1d5db',
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||
whiteSpace: 'pre-wrap', wordBreak: 'break-word', margin: 0,
|
||||
}}>
|
||||
{content}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SearchBar() {
|
||||
const [query, setQuery] = useState('')
|
||||
const [results, setResults] = useState<SearchResult[]>([])
|
||||
const [searching, setSearching] = useState(false)
|
||||
const [searched, setSearched] = useState(false)
|
||||
const [viewingFile, setViewingFile] = useState<string | null>(null)
|
||||
|
||||
const search = useCallback(async (q: string) => {
|
||||
if (q.trim().length < 2) { setResults([]); setSearched(false); return }
|
||||
setSearching(true)
|
||||
try {
|
||||
const res = await fetch(`${API}/search?q=${encodeURIComponent(q)}&top=6`)
|
||||
if (!res.ok) throw new Error()
|
||||
const data = await res.json()
|
||||
setResults(data.results ?? [])
|
||||
setSearched(true)
|
||||
} catch {
|
||||
setResults([])
|
||||
} finally {
|
||||
setSearching(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => { if (query.trim().length >= 2) search(query) }, 400)
|
||||
return () => clearTimeout(timer)
|
||||
}, [query, search])
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
background: '#141414', border: '1px solid #2a2a2a', borderRadius: 8,
|
||||
padding: '8px 16px',
|
||||
}}>
|
||||
<span style={{ color: '#4b5563', fontSize: 16 }}>🔍</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Rechercher dans le brain..."
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') search(query) }}
|
||||
style={{
|
||||
flex: 1, background: 'transparent', border: 'none', outline: 'none',
|
||||
color: '#e5e7eb', fontSize: 14, fontFamily: 'inherit',
|
||||
}}
|
||||
/>
|
||||
{searching && <span style={{ color: '#4b5563', fontSize: 12, fontFamily: 'monospace' }}>...</span>}
|
||||
</div>
|
||||
{searched && results.length > 0 && (
|
||||
<div style={{
|
||||
marginTop: 8, background: '#141414', border: '1px solid #2a2a2a',
|
||||
borderRadius: 8, overflow: 'hidden',
|
||||
}}>
|
||||
{results.map((r, i) => (
|
||||
<div key={i} style={{
|
||||
padding: '12px 16px', borderBottom: i < results.length - 1 ? '1px solid #1e1e1e' : 'none',
|
||||
cursor: 'pointer', transition: 'background 0.15s',
|
||||
}}
|
||||
onClick={() => setViewingFile(r.filepath)}
|
||||
onMouseEnter={e => (e.currentTarget.style.background = '#1a1a1a')}
|
||||
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
|
||||
<span style={{ fontSize: 12, fontFamily: 'monospace', color: '#818cf8' }}>
|
||||
{r.filepath}
|
||||
</span>
|
||||
<span style={{
|
||||
fontSize: 10, fontFamily: 'monospace', color: '#4b5563',
|
||||
marginLeft: 'auto',
|
||||
}}>
|
||||
{(r.score * 100).toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: '#9ca3af', lineHeight: 1.5 }}>
|
||||
{r.excerpt.slice(0, 200)}{r.excerpt.length > 200 ? '...' : ''}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{searched && results.length === 0 && !searching && (
|
||||
<div style={{ marginTop: 8, fontSize: 13, color: '#4b5563', fontFamily: 'monospace', padding: '8px 16px' }}>
|
||||
Aucun résultat pour "{query}"
|
||||
</div>
|
||||
)}
|
||||
{viewingFile && <FileViewer path={viewingFile} onClose={() => setViewingFile(null)} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Dashboard() {
|
||||
const [health, setHealth] = useState<HealthData | null>(null)
|
||||
const [claims, setClaims] = useState<ClaimData[]>([])
|
||||
const [agents, setAgents] = useState<AgentData[]>([])
|
||||
const [docs, setDocs] = useState<DocData[]>([])
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
Promise.allSettled([
|
||||
fetch(`${API}/health`).then(r => r.json()),
|
||||
fetch(`${API}/bsi/claims`).then(r => r.ok ? r.json() : []),
|
||||
fetch(`${API}/agents`).then(r => r.ok ? r.json() : []),
|
||||
fetch(`${API}/docs`).then(r => r.ok ? r.json() : { docs: [] }),
|
||||
]).then(([h, c, a, d]) => {
|
||||
if (h.status === 'fulfilled') setHealth(h.value)
|
||||
if (c.status === 'fulfilled') setClaims(Array.isArray(c.value) ? c.value : [])
|
||||
if (a.status === 'fulfilled') setAgents(Array.isArray(a.value) ? a.value : [])
|
||||
if (d.status === 'fulfilled') setDocs(d.value?.docs ?? [])
|
||||
}).catch(() => setError('Impossible de charger les données'))
|
||||
}, [])
|
||||
|
||||
const openClaims = claims.filter(c => c.status === 'open')
|
||||
const recentClaims = claims.slice(0, 8)
|
||||
const agentsByTier = agents.reduce<Record<string, number>>((acc, a) => {
|
||||
acc[a.tier] = (acc[a.tier] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return (
|
||||
<div style={{ padding: '2rem 3rem', overflowY: 'auto', height: '100%' }}>
|
||||
{/* Header */}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<h1 style={{ fontSize: 20, fontWeight: 700, color: '#fff', margin: 0 }}>
|
||||
Dashboard
|
||||
</h1>
|
||||
<p style={{ fontSize: 12, color: '#4b5563', fontFamily: 'monospace', marginTop: 4 }}>
|
||||
{health ? `brain-engine up — ${formatUptime(health.uptime)}` : 'connexion...'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div style={{ color: '#ef4444', fontSize: 13, marginBottom: 16 }}>{error}</div>
|
||||
)}
|
||||
|
||||
{/* Search */}
|
||||
<SearchBar />
|
||||
|
||||
{/* Stats row */}
|
||||
<div style={{ display: 'flex', gap: 12, marginBottom: 24, flexWrap: 'wrap' }}>
|
||||
<StatCard
|
||||
label="Embeddings"
|
||||
value={health?.indexed?.toLocaleString() ?? '—'}
|
||||
sub="chunks indexés"
|
||||
color="#818cf8"
|
||||
/>
|
||||
<StatCard
|
||||
label="Agents"
|
||||
value={agents.length || '—'}
|
||||
sub={Object.entries(agentsByTier).map(([t, n]) => `${n} ${t}`).join(' · ') || undefined}
|
||||
color="#22c55e"
|
||||
/>
|
||||
<StatCard
|
||||
label="Docs"
|
||||
value={docs.length || '—'}
|
||||
sub="pages live"
|
||||
color="#f59e0b"
|
||||
/>
|
||||
<StatCard
|
||||
label="Sessions"
|
||||
value={openClaims.length}
|
||||
sub={openClaims.length > 0 ? 'actives' : 'aucune active'}
|
||||
color={openClaims.length > 0 ? '#22c55e' : '#6b7280'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Two columns */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
|
||||
{/* Recent sessions */}
|
||||
<div style={{ background: '#141414', border: '1px solid #2a2a2a', borderRadius: 8, overflow: 'hidden' }}>
|
||||
<div style={{
|
||||
padding: '12px 16px', borderBottom: '1px solid #2a2a2a',
|
||||
fontSize: 12, fontFamily: 'monospace', color: '#6b7280',
|
||||
textTransform: 'uppercase', letterSpacing: '0.05em',
|
||||
}}>
|
||||
Sessions récentes
|
||||
</div>
|
||||
{recentClaims.length === 0 ? (
|
||||
<div style={{ padding: 16, fontSize: 13, color: '#4b5563' }}>
|
||||
Aucune session enregistrée
|
||||
</div>
|
||||
) : (
|
||||
recentClaims.map(c => <SessionRow key={c.sess_id} claim={c} />)
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Quick links */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{/* Agents by scope */}
|
||||
<div style={{ background: '#141414', border: '1px solid #2a2a2a', borderRadius: 8, overflow: 'hidden' }}>
|
||||
<div style={{
|
||||
padding: '12px 16px', borderBottom: '1px solid #2a2a2a',
|
||||
fontSize: 12, fontFamily: 'monospace', color: '#6b7280',
|
||||
textTransform: 'uppercase', letterSpacing: '0.05em',
|
||||
}}>
|
||||
Agents par scope
|
||||
</div>
|
||||
<div style={{ padding: 16, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
||||
{Object.entries(
|
||||
agents.reduce<Record<string, number>>((acc, a) => {
|
||||
acc[a.scope || 'unknown'] = (acc[a.scope || 'unknown'] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
).map(([scope, count]) => (
|
||||
<div key={scope} style={{ textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 20, fontWeight: 700, color: '#e5e7eb' }}>{count}</div>
|
||||
<div style={{ fontSize: 10, color: '#6b7280', fontFamily: 'monospace' }}>{scope}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Docs groups */}
|
||||
<div style={{ background: '#141414', border: '1px solid #2a2a2a', borderRadius: 8, overflow: 'hidden' }}>
|
||||
<div style={{
|
||||
padding: '12px 16px', borderBottom: '1px solid #2a2a2a',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
}}>
|
||||
<span style={{ fontSize: 12, fontFamily: 'monospace', color: '#6b7280', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
||||
Documentation
|
||||
</span>
|
||||
<a href="/docs" target="_blank" rel="noopener noreferrer"
|
||||
style={{ fontSize: 11, color: '#818cf8', textDecoration: 'none', fontFamily: 'monospace' }}>
|
||||
Ouvrir ↗
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ padding: 16, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
||||
{Object.entries(
|
||||
docs.reduce<Record<string, number>>((acc, d) => {
|
||||
acc[d.group || 'Autres'] = (acc[d.group || 'Autres'] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
).map(([group, count]) => (
|
||||
<div key={group} style={{ textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 20, fontWeight: 700, color: '#e5e7eb' }}>{count}</div>
|
||||
<div style={{ fontSize: 10, color: '#6b7280', fontFamily: 'monospace' }}>{group}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -212,7 +212,14 @@ export default function DocsView() {
|
||||
return <article className="docs-markdown"><TierSingle tierName={tierName} /></article>
|
||||
}
|
||||
if (liveMode && activeDoc === 'agents') {
|
||||
return <article className="docs-markdown"><AgentCatalog /></article>
|
||||
return (
|
||||
<article className="docs-markdown">
|
||||
{!loading && content && (
|
||||
<ReactMarkdown components={mdComponents}>{content}</ReactMarkdown>
|
||||
)}
|
||||
<AgentCatalog />
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
// Mode standard — markdown
|
||||
|
||||
@@ -2,10 +2,12 @@ import { useState } from 'react'
|
||||
import type { CosmosPoint, ZoneKey } from '../../types'
|
||||
|
||||
const ZONE_BADGE_COLORS: Record<ZoneKey, { bg: string; text: string }> = {
|
||||
public: { bg: 'rgba(229,231,235,0.1)', text: '#e5e7eb' },
|
||||
work: { bg: 'rgba(99,102,241,0.15)', text: '#6366f1' },
|
||||
kernel: { bg: 'rgba(239,68,68,0.15)', text: '#ef4444' },
|
||||
unknown: { bg: 'rgba(75,85,99,0.2)', text: '#6b7280' },
|
||||
public: { bg: 'rgba(229,231,235,0.1)', text: '#e5e7eb' },
|
||||
work: { bg: 'rgba(99,102,241,0.15)', text: '#6366f1' },
|
||||
kernel: { bg: 'rgba(239,68,68,0.15)', text: '#ef4444' },
|
||||
instance: { bg: 'rgba(168,85,247,0.15)', text: '#a855f7' },
|
||||
satellite: { bg: 'rgba(34,197,94,0.15)', text: '#22c55e' },
|
||||
unknown: { bg: 'rgba(75,85,99,0.2)', text: '#6b7280' },
|
||||
}
|
||||
|
||||
function getNearestNeighbors(target: CosmosPoint, all: CosmosPoint[], n = 10): CosmosPoint[] {
|
||||
|
||||
@@ -3,10 +3,12 @@ import * as THREE from 'three'
|
||||
import type { CosmosPoint, ZoneKey } from '../../types'
|
||||
|
||||
const ZONE_COLORS: Record<ZoneKey, string> = {
|
||||
public: '#6366f1',
|
||||
work: '#22c55e',
|
||||
kernel: '#f59e0b',
|
||||
unknown: '#6b7280',
|
||||
public: '#6366f1',
|
||||
work: '#22c55e',
|
||||
kernel: '#f59e0b',
|
||||
instance: '#a855f7',
|
||||
satellite: '#3b82f6',
|
||||
unknown: '#6b7280',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
||||
45
contexts/session-capital.yml
Normal file
45
contexts/session-capital.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# session-capital.yml — Contexte BHP pour sessions capital professionnel
|
||||
# Trigger : "brain boot mode capital"
|
||||
# Focus : bilan, objectifs, CV, capital professionnel, progression long terme
|
||||
# Cible : ~23% contexte max au boot
|
||||
|
||||
session_type: capital
|
||||
description: "Session capital — bilan, objectifs, CV, capital professionnel"
|
||||
tier_required: featured # coach.md complet + capital-scribe = tier featured (RAG + progression)
|
||||
|
||||
# L0 — Invariant (~5%)
|
||||
L0:
|
||||
- PATHS.md
|
||||
- brain-compose.local.yml
|
||||
- KERNEL.md
|
||||
|
||||
# L1 — Session type (~18%)
|
||||
L1:
|
||||
- now.md # bridge session précédente
|
||||
- agents/coach.md # coach complet — bilan et cap stratégique (pas boot-summary)
|
||||
- profil/objectifs.md # objectifs actifs + prochaines étapes
|
||||
- profil/capital.md # preuves CV, milestones, capital accumulé
|
||||
- progression/README.md # état métabolique + ratio sessions + tendances
|
||||
|
||||
# L2 — non applicable (capital = session introspective, pas de scope projet)
|
||||
L2:
|
||||
template: null
|
||||
extras: []
|
||||
fallback: null
|
||||
|
||||
# L3 — On demand
|
||||
# progression/ détaillée, sessions passées, capital-scribe pour mise à jour capital.md
|
||||
L3:
|
||||
hint: "Charger à la demande : progression/ détaillée, sessions passées, capital-scribe"
|
||||
|
||||
# --- Note capital ---
|
||||
# capital-scribe actif automatiquement si modification capital.md détectée en session.
|
||||
# Pas de projets/ en L2 — le coaching capital est orthogonal aux projets actifs.
|
||||
# session-coach = réflexion stratégique ; session-capital = capital pro + CV + milestones.
|
||||
|
||||
# --- Métriques cibles ---
|
||||
context_target:
|
||||
L0: "~5%"
|
||||
L1: "~18%"
|
||||
L2: "0%"
|
||||
total_boot: "~23%"
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
session_type: coach
|
||||
description: "Session coaching — progression, réflexion, cap stratégique, clarté"
|
||||
tier_required: pro # coaching complet (coach.md 365 lignes) = feature pro
|
||||
tier_required: featured # coaching complet (coach.md) = tier featured (RAG + progression)
|
||||
|
||||
# L0 — Invariant (~5%)
|
||||
L0:
|
||||
|
||||
46
contexts/session-deploy.yml
Normal file
46
contexts/session-deploy.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
# session-deploy.yml — Contexte BHP pour sessions de déploiement
|
||||
# Trigger : "brain boot mode deploy[/<project>]"
|
||||
# Cible : ~25% contexte max au boot
|
||||
|
||||
session_type: deploy
|
||||
description: "Session de déploiement — VPS, Docker, SSL, CI/CD, infra"
|
||||
tier_required: pro # deploy = agents vps/ci-cd/monitoring (tous pro)
|
||||
|
||||
# L0 — Invariant (~5%)
|
||||
L0:
|
||||
- PATHS.md
|
||||
- brain-compose.local.yml
|
||||
- KERNEL.md
|
||||
|
||||
# L1 — Session type (~15%)
|
||||
L1:
|
||||
- now.md # état dernière session — ce qui a changé, ce qui est en attente
|
||||
- focus.md # projets actifs — savoir quoi déployer
|
||||
- agents/vps.md # VPS, Apache, Docker, SSL, vhost, certbot # tier: pro
|
||||
- agents/ci-cd.md # pipelines, GitHub Actions, Gitea CI # tier: pro
|
||||
- agents/monitoring.md # Kuma, alertes, logs post-déploiement # tier: pro
|
||||
|
||||
# L2 — Project scope (~8%) — optionnel sur projet déclaré
|
||||
L2:
|
||||
template: "projets/{project}.md"
|
||||
extras:
|
||||
- "todo/{project}.md"
|
||||
fallback: null
|
||||
|
||||
# L3 — On demand
|
||||
# Exemples : agents/security.md (audit pré-deploy), agents/mail.md (DNS/DKIM),
|
||||
# agents/pm2.md, agents/migration.md, config spécifique
|
||||
L3:
|
||||
hint: "Charger à la demande : security pre-deploy, mail/DNS, pm2, migration schema"
|
||||
|
||||
# --- Règle deploy ---
|
||||
# agents/security.md NOT en L1 par défaut — deploy ≠ audit sécurité.
|
||||
# Charger si audit pré-déploiement explicitement demandé (→ session-audit).
|
||||
# agents/monitoring.md en L1 : toujours vérifier les alertes après deploy.
|
||||
|
||||
# --- Métriques cibles ---
|
||||
context_target:
|
||||
L0: "~5%"
|
||||
L1: "~12%"
|
||||
L2: "~8%"
|
||||
total_boot: "~25%"
|
||||
52
contexts/session-handoff.yml
Normal file
52
contexts/session-handoff.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
# session-handoff.yml — Contexte BHP pour sessions HANDOFF
|
||||
# Trigger : "brain boot mode HANDOFF[/<handoff-id>]"
|
||||
# Cible : ~15% contexte max au boot — minimum viable pour reprendre
|
||||
|
||||
session_type: HANDOFF
|
||||
description: "Reprise d'une session via handoff — contexte minimal + fichier handoff cible"
|
||||
tier_required: free # handoff = protocole BSI de base — disponible pour tous
|
||||
|
||||
# L0 — Invariant (~5%)
|
||||
L0:
|
||||
- PATHS.md
|
||||
- brain-compose.local.yml
|
||||
- KERNEL.md
|
||||
|
||||
# L1 — Session type (~8%) — chirurgical : seulement ce qu'il faut pour reprendre
|
||||
L1:
|
||||
- BRAIN-INDEX.md # trouver le handoff actif
|
||||
|
||||
# L2 — Handoff scope (~5%) — fichier handoff si déclaré dans le signal
|
||||
# Signal : "HANDOFF/<handoff-id>" → charger le fichier handoff directement
|
||||
L2:
|
||||
template: "handoffs/{handoff_id}.md"
|
||||
extras: []
|
||||
fallback:
|
||||
- handoffs/LATEST.md # si pas d'ID déclaré → dernier handoff
|
||||
|
||||
# L3 — On demand
|
||||
# Exemples : context complet projet, agents métier, focus.md
|
||||
# Principe : le fichier handoff CONTIENT les pointeurs vers L3.
|
||||
# La nouvelle session lit le handoff → décide elle-même quoi charger.
|
||||
L3:
|
||||
hint: "Lire le handoff d'abord. Il indique quoi charger en L3."
|
||||
|
||||
# --- Règle HANDOFF ---
|
||||
# Le handoff est la source de vérité pour la reprise.
|
||||
# Ne pas charger focus.md, metabolism, agents au boot — le handoff décide.
|
||||
# Après lecture du handoff → promouvoir le contexte nécessaire en L2 implicite.
|
||||
# Le claim ouvert doit référencer le handoff : parent_satellite ou story_angle.
|
||||
|
||||
# --- Format handoff attendu ---
|
||||
# handoffs/<id>.md doit contenir :
|
||||
# - Contexte de la session précédente (état, décisions, bloquants)
|
||||
# - Fichiers à charger (L3 → L2 pour cette session)
|
||||
# - Todos ouverts prioritaires
|
||||
# - Signal de session recommandé pour la suite
|
||||
|
||||
# --- Métriques cibles ---
|
||||
context_target:
|
||||
L0: "~5%"
|
||||
L1: "~5%"
|
||||
L2: "~5%"
|
||||
total_boot: "~15%"
|
||||
45
contexts/session-infra.yml
Normal file
45
contexts/session-infra.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# session-infra.yml — Contexte BHP pour sessions infrastructure
|
||||
# Trigger : "brain boot mode infra"
|
||||
# Focus : ops quotidien — monitoring, maintenance VPS, config, santé système
|
||||
# Distinct de session-deploy (deploy = ship code ; infra = ops/health/maintenance)
|
||||
# Cible : ~22% contexte max au boot
|
||||
|
||||
session_type: infra
|
||||
description: "Session infrastructure — ops quotidien, maintenance VPS, santé système"
|
||||
tier_required: pro # vps, pm2, monitoring = agents pro
|
||||
|
||||
# L0 — Invariant (~5%)
|
||||
L0:
|
||||
- PATHS.md
|
||||
- brain-compose.local.yml
|
||||
- KERNEL.md
|
||||
|
||||
# L1 — Session type (~14%)
|
||||
L1:
|
||||
- now.md # bridge session précédente
|
||||
- agents/coach-boot.md # présence légère — pas de bilan complet en infra
|
||||
- agents/vps.md # VPS, Apache, Docker, SSL, vhost, certbot # tier: pro
|
||||
- agents/pm2.md # process manager, restart, logs # tier: pro
|
||||
- focus.md # projets actifs — contexte des services en cours
|
||||
|
||||
# L2 — Project scope — optionnel si scope projet déclaré
|
||||
L2:
|
||||
template: "projets/{project}.md"
|
||||
extras: []
|
||||
fallback: null
|
||||
|
||||
# L3 — On demand
|
||||
# agents/monitoring.md, agents/mail.md (DNS/DKIM), agents/ci-cd.md, agents/migration.md
|
||||
L3:
|
||||
hint: "Charger à la demande : monitoring, mail/DNS, ci-cd, migration schema, security pre-audit"
|
||||
|
||||
# --- Distinction infra / deploy ---
|
||||
# infra = état du système, maintenance, vérifications, config — pas de CI/CD au boot
|
||||
# deploy = livraison code, CI/CD, releases, pipeline — agents/ci-cd.md en L1
|
||||
|
||||
# --- Métriques cibles ---
|
||||
context_target:
|
||||
L0: "~5%"
|
||||
L1: "~14%"
|
||||
L2: "~3%"
|
||||
total_boot: "~22%"
|
||||
@@ -22,6 +22,9 @@ L1:
|
||||
- focus.md # projets actifs + prochaine frontière
|
||||
- BRAIN-INDEX.md # claims actifs + signaux
|
||||
- todo/README.md # index intentions (warm — fichiers projet sur demande)
|
||||
- agents/guide.md # presentation systeme — accueil fresh fork + on demand
|
||||
- agents/catalogist.md # exploration registres (agents, tiers, features)
|
||||
- agents/pathfinder.md # routage intentionnel — oriente vers la bonne session
|
||||
|
||||
# L2 — Project scope (~2%) — déclenché si project déclaré dans le signal
|
||||
L2:
|
||||
|
||||
45
contexts/session-urgence.yml
Normal file
45
contexts/session-urgence.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# session-urgence.yml — Contexte BHP pour sessions incident/urgence
|
||||
# Trigger : "brain boot mode urgence"
|
||||
# Focus : incident prod, production down, hotfix critique
|
||||
# Mode conserve : automatique — cible context < 40%
|
||||
# Cible : ~15% contexte max au boot (mode conserve)
|
||||
|
||||
session_type: urgence
|
||||
description: "Session urgence — incident prod, production down, hotfix critique"
|
||||
tier_required: pro # vps, pm2, debug = agents pro
|
||||
|
||||
# L0 — Invariant (~5%)
|
||||
L0:
|
||||
- PATHS.md
|
||||
- brain-compose.local.yml
|
||||
- KERNEL.md
|
||||
|
||||
# L1 — Session type (~10%) — minimal, on va vite
|
||||
# Coach absent : urgence avant bilan pédagogique — invocation explicite si besoin
|
||||
L1:
|
||||
- agents/vps.md # VPS, Apache — infra down # tier: pro
|
||||
- agents/pm2.md # process manager, restart, logs # tier: pro
|
||||
- agents/debug.md # diagnostic, crash, comportement inattendu
|
||||
|
||||
# L2 — non applicable (urgence = focus immédiat, pas de scope projet)
|
||||
L2:
|
||||
template: null
|
||||
extras: []
|
||||
fallback: null
|
||||
|
||||
# L3 — On demand — déclenché après résolution
|
||||
L3:
|
||||
hint: "Après résolution : scribe (post-mortem), security (si faille), monitoring (état alertes)"
|
||||
|
||||
# --- Règles urgence ---
|
||||
conserve: auto # mode conserve activé automatiquement — cible context < 40%
|
||||
coach: silent # coach silencieux sauf invocation explicite après résolution
|
||||
# post-mortem : scribe déclenché sur signal "résolution confirmée" ou "c'est résolu"
|
||||
# secrets-guardian : actif en passif — rotation clés si compromis suspects
|
||||
|
||||
# --- Métriques cibles ---
|
||||
context_target:
|
||||
L0: "~5%"
|
||||
L1: "~10%"
|
||||
L2: "0%"
|
||||
total_boot: "~15%"
|
||||
@@ -16,10 +16,14 @@ L0:
|
||||
L1:
|
||||
- now.md # bridge session précédente
|
||||
- focus.md # focus actuel, todos prioritaires
|
||||
- agents/coach.md # coach complet byTask — observe le projet en cours
|
||||
- agents/coach-boot.md # coach boot-summary — observe le projet en cours (free)
|
||||
- agents/debug.md # bug, crash, comportement inattendu
|
||||
- metabolism/README.md # état métabolique, énergie session
|
||||
|
||||
# L1_featured — chargé si tier: featured+ — coach complet remplace coach-boot
|
||||
L1_featured:
|
||||
- agents/coach.md # coach complet byTask — observe le projet en cours
|
||||
|
||||
# L1_pro — Session type (~5%) — chargé uniquement si tier: pro déclaré
|
||||
# Pas de code-review ni security pour tier free — chargement explicite sur demande sinon
|
||||
L1_pro:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# docs/ — Documentation humaine
|
||||
|
||||
> Guides lisibles sans contexte brain. Pour forks, onboarding, ou quand tu veux comprendre comment ca marche.
|
||||
> L'histoire du projet → [story.tetardtek.com](https://story.tetardtek.com)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# Le brain en 30 secondes
|
||||
|
||||
Un brain, c'est un systeme de **specialistes IA** qui travaillent ensemble. Chaque specialiste (agent) fait une chose bien : debugger, reviewer du code, deployer, ecrire des tests. Tu n'en charges jamais plus de 5 a la fois — le brain sait lesquels activer selon ce que tu fais.
|
||||
## Pourquoi un brain plutot que Claude seul ?
|
||||
|
||||
Tu forkes le brain, tu codes. Les agents se chargent automatiquement.
|
||||
Claude est puissant. Mais a chaque session, il repart de zero. Tu re-expliques ton projet, ta stack, tes conventions. Tu repetes les memes consignes. Tu perds du contexte a chaque compaction.
|
||||
|
||||
Le brain resout ca : **un systeme de specialistes IA qui persistent entre sessions.** Chaque specialiste (agent) fait une chose bien — debugger, reviewer du code, deployer, ecrire des tests. Ils connaissent tes regles, ta stack, tes decisions passees. Tu n'en charges jamais plus de 5 a la fois — le brain sait lesquels activer selon ce que tu fais.
|
||||
|
||||
Tu forkes le brain, tu codes. Les agents se chargent automatiquement. Ton contexte survit aux sessions.
|
||||
|
||||
---
|
||||
|
||||
@@ -10,25 +14,25 @@ Tu forkes le brain, tu codes. Les agents se chargent automatiquement.
|
||||
|
||||
> 🟢 **free — Tu forkes, ca marche**
|
||||
>
|
||||
> **14 agents + 8 systeme. 6 sessions.** Pas de cle API, pas de config.
|
||||
> **17 agents + 9 systeme. 6 sessions.** Pas de cle API, pas de config.
|
||||
>
|
||||
> Debug, brainstorm, scribes automatiques, protection secrets, creation d'agents custom. Le coach observe en arriere-plan.
|
||||
> Debug, brainstorm, scribes automatiques, protection secrets, creation d'agents custom. 3 agents d'onboarding (guide, catalogist, pathfinder) pour t'orienter. Le coach observe en arriere-plan.
|
||||
|
||||
> 🔵 **featured — Le brain te connait**
|
||||
>
|
||||
> **18 agents + systeme. 8 sessions.** Le coach se reveille.
|
||||
> **21 agents + systeme. 8 sessions.** Le coach se reveille.
|
||||
>
|
||||
> Bilans de session, objectifs concrets, progression tracee. Le brain se souvient de tes acquis entre sessions grace a la distillation RAG.
|
||||
|
||||
> 🟠 **pro — L'atelier complet**
|
||||
>
|
||||
> **40 agents + systeme. 12 sessions.** Tu ship en prod.
|
||||
> **42 agents + systeme. 14 sessions.** Tu ship en prod.
|
||||
>
|
||||
> Code review (7 priorites), audit securite (8 priorites OWASP), tests automatises, 3 optimiseurs perf, deploy VPS + CI/CD + SSL, sessions urgence et infra.
|
||||
|
||||
> 🟣 **full — Ton brain, tes regles**
|
||||
>
|
||||
> **75 agents (tous). 15 sessions.** Tu es owner.
|
||||
> **81 agents (tous). 15 sessions.** Tu es owner.
|
||||
>
|
||||
> Modification du kernel, copilotage long (mode pilote), supervision multi-phase (hypervisor), coach proactif qui anticipe.
|
||||
|
||||
@@ -66,6 +70,11 @@ Charge les agents security et code-review
|
||||
|
||||
**Ils ne chargent que l'essentiel.** Un agent de 200 lignes → ~25 lignes au boot. Le reste se charge quand tu en as besoin.
|
||||
|
||||
**Premier fork ? 3 agents t'orientent.**
|
||||
- `guide` — presente le systeme, repond a "c'est quoi ce truc ?"
|
||||
- `catalogist` — explore ce qui est disponible (agents, tiers, features)
|
||||
- `pathfinder` — t'oriente vers la bonne session selon ce que tu veux faire
|
||||
|
||||
---
|
||||
|
||||
## Explore les agents par famille
|
||||
@@ -80,10 +89,19 @@ Charge les agents security et code-review
|
||||
|
||||
---
|
||||
|
||||
## Pour aller plus loin
|
||||
|
||||
**L'histoire du projet** — [story.tetardtek.com](https://story.tetardtek.com) raconte le pourquoi, le parcours, les decisions. Si tu veux comprendre la vision avant de fork.
|
||||
|
||||
---
|
||||
|
||||
## Nouveautes
|
||||
|
||||
| Date | Quoi de neuf |
|
||||
|------|-------------|
|
||||
| 2026-03-21 | 3 agents onboarding (guide, catalogist, pathfinder) — le brain accueille les nouveaux |
|
||||
| 2026-03-21 | Docs live — git pull = docs a jour, zero rebuild |
|
||||
| 2026-03-21 | VPS scission — vitrine template publique separee du brain prod |
|
||||
| 2026-03-20 | Agents 87% plus legers au boot |
|
||||
| 2026-03-20 | Coach adaptatif — 5 comportements selon la session |
|
||||
| 2026-03-20 | Fermeture fiable — sequence deterministe |
|
||||
|
||||
@@ -10,7 +10,7 @@ Le brain c'est 3 couches :
|
||||
|
||||
**1. Le kernel** — l'identite
|
||||
- Les regles qui ne changent pas (KERNEL.md, constitution, PATHS.md)
|
||||
- Les agents specialises (~75 fichiers .md)
|
||||
- Les agents specialises (~81 fichiers .md)
|
||||
- Le profil de collaboration
|
||||
- Le brain-compose.yml (config, tiers, modes)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Demarrer avec le brain — le vrai tuto
|
||||
|
||||
> Du fork au premier `brain boot`. 10 minutes.
|
||||
> Envie de comprendre le projet avant de fork ? → [story.tetardtek.com](https://story.tetardtek.com)
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +148,6 @@ kill $(cat /tmp/brain-engine.pid)
|
||||
Ouvre un **nouveau terminal** (brain-engine tourne dans l'autre) :
|
||||
|
||||
```bash
|
||||
cd ~/Dev/Brain
|
||||
claude
|
||||
```
|
||||
|
||||
@@ -157,6 +157,8 @@ Claude Code s'ouvre. Tape :
|
||||
brain boot
|
||||
```
|
||||
|
||||
> Tu n'as pas besoin d'etre dans le dossier brain. `brain boot` fonctionne depuis n'importe quel repertoire — les chemins dans `~/.claude/CLAUDE.md` sont absolus.
|
||||
|
||||
### Ce que tu dois voir
|
||||
|
||||
```
|
||||
@@ -211,8 +213,8 @@ cp profil/CLAUDE.md.example ~/.claude/CLAUDE.md
|
||||
# 3. Lancer le dashboard (optionnel, a chaque session)
|
||||
bash brain-engine/start.sh
|
||||
|
||||
# 4. Lancer Claude Code (a chaque session)
|
||||
cd ~/Dev/Brain && claude
|
||||
# 4. Lancer Claude Code (a chaque session, depuis n'importe ou)
|
||||
claude
|
||||
# Puis : brain boot
|
||||
```
|
||||
|
||||
@@ -242,6 +244,20 @@ git fetch upstream
|
||||
git merge upstream/main
|
||||
```
|
||||
|
||||
### J'utilise Gitea self-hosted et git clone echoue ?
|
||||
|
||||
Gitea en Docker ecoute souvent sur un port SSH non standard (2222 au lieu de 22). Ajoute dans `~/.ssh/config` :
|
||||
|
||||
```
|
||||
Host git.example.com
|
||||
HostName git.example.com
|
||||
Port 2222
|
||||
User git
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
Puis ajoute la host key : `ssh-keyscan -p 2222 git.example.com >> ~/.ssh/known_hosts`
|
||||
|
||||
### Ou est la documentation complete ?
|
||||
|
||||
- Dashboard : `http://localhost:7700/ui/` → onglet Docs
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 🟢 free — Ce que tu as
|
||||
|
||||
> 🟢 **14 agents invocables + 8 systeme. 6 sessions. Pas de cle, pas de config.**
|
||||
> 🟢 **17 agents invocables + 9 systeme. 6 sessions. Pas de cle, pas de config.**
|
||||
|
||||
---
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
- `pattern-scribe` — detection patterns recurrents
|
||||
- `time-anchor` — conscience temporelle, recontextualisation
|
||||
|
||||
**S'orienter**
|
||||
|
||||
- `guide` — presentation du systeme, accueil fresh fork
|
||||
- `catalogist` — exploration des registres (agents, tiers, features)
|
||||
- `pathfinder` — routage intentionnel, oriente vers la bonne session
|
||||
|
||||
---
|
||||
|
||||
## Agents systeme
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
## 🟢 free — Ce que tu as
|
||||
|
||||
> 🟢 **14 agents invocables + 8 systeme. 6 sessions.**
|
||||
> 🟢 **17 agents invocables + 9 systeme. 6 sessions.**
|
||||
|
||||
### Sessions
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
- `pattern-scribe` — detection patterns recurrents
|
||||
- `time-anchor` — conscience temporelle, recontextualisation
|
||||
|
||||
**S'orienter**
|
||||
- `guide` — presentation du systeme, accueil fresh fork
|
||||
- `catalogist` — exploration des registres (agents, tiers, features)
|
||||
- `pathfinder` — routage intentionnel, oriente vers la bonne session
|
||||
|
||||
### Agents systeme (tournent a chaque boot)
|
||||
|
||||
- `helloWorld` — briefing, claim BSI
|
||||
|
||||
@@ -1,27 +1,46 @@
|
||||
---
|
||||
name: CLAUDE-example
|
||||
type: config
|
||||
context_tier: cold
|
||||
---
|
||||
|
||||
# CLAUDE.md — Bootstrap
|
||||
# Copier vers ~/.claude/CLAUDE.md puis remplacer <BRAIN_ROOT> par le chemin réel
|
||||
|
||||
> **Fichier global** — `~/.claude/CLAUDE.md` — charge par Claude Code peu importe le cwd.
|
||||
> `brain boot` fonctionne depuis n'importe quel repertoire : les chemins ci-dessous sont absolus.
|
||||
> Ne jamais creer de CLAUDE.md projet qui override ce fichier sans raison explicite.
|
||||
|
||||
## Configuration machine (seul endroit a modifier sur une nouvelle machine)
|
||||
|
||||
brain_root: <BRAIN_ROOT>
|
||||
brain_name: <BRAIN_NAME> ← prod / dev-laptop / template-test / etc.
|
||||
brain_name: <BRAIN_NAME>
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap (obligatoire, dans l'ordre)
|
||||
|
||||
0. `<BRAIN_ROOT>/PATHS.md` — chemins machine
|
||||
1. `<BRAIN_ROOT>/profil/collaboration.md` — regles de travail
|
||||
2. `<BRAIN_ROOT>/agents/coach-boot.md` — presence permanente (boot-summary L0)
|
||||
3. `<BRAIN_ROOT>/agents/secrets-guardian.md` — gardien secrets, permanent
|
||||
4. `<BRAIN_ROOT>/agents/helloWorld.md` — briefing, focus, todos, CHECKPOINT, feature flags
|
||||
0. `<BRAIN_ROOT>/PATHS.md` — chemins machine
|
||||
1. `<BRAIN_ROOT>/profil/collaboration.md` — regles de travail
|
||||
2. `<BRAIN_ROOT>/agents/coach-boot.md` — presence permanente (boot-summary L0)
|
||||
3. `<BRAIN_ROOT>/agents/secrets-guardian.md` — gardien secrets, permanent, lit MYSECRETS silencieusement
|
||||
4. `<BRAIN_ROOT>/agents/helloWorld.md` — briefing, focus, todos, CHECKPOINT, feature flags
|
||||
|
||||
helloWorld prend le relais : etat projets, todos prioritaires, detection de session, feature_set, CHECKPOINT.
|
||||
Ne pas demander a l'utilisateur de se redecrire — tout est dans le brain.
|
||||
|
||||
> **Satellites manquants :** si todo/, toolkit/, progression/, reviews/ n'existent pas,
|
||||
> le brain continue sans eux. helloWorld signale ce qui manque et propose de les creer.
|
||||
> Ce n'est PAS une erreur — c'est un fresh fork.
|
||||
> **BSI boot claim — non negociable** : apres le briefing, ouvrir un claim via `bash scripts/bsi-claim.sh open` (ADR-042).
|
||||
> brain.db = source unique. Pas de commit git, pas de push. Voir protocole complet dans helloWorld.md ## Boot claim automatique.
|
||||
|
||||
> **brain boot navigate** (ou `brain boot mode navigate`) → charger `contexts/session-navigate.yml`
|
||||
> via le BHP helloWorld — fenetre legere (~18%), time-anchor actif, coach en boot-summary uniquement.
|
||||
|
||||
> **brain boot sudo** (ou `brain boot mode edit-brain`) → charger `contexts/session-edit-brain.yml`
|
||||
> Writes autorises partout dans le brain. Kernel (PATHS.md, KERNEL.md, CLAUDE.md) → confirmation explicite.
|
||||
|
||||
> **brain boot mode kernel** → charger `contexts/session-kernel.yml`
|
||||
> Lecture seule sur le kernel. Toute tentative write kernel → refus + redirection session-edit-brain.
|
||||
|
||||
> **brain boot mode projet** → alias de `brain boot mode work` — meme session, meme contexte.
|
||||
|
||||
> Source unique de verite : brain `<BRAIN_NAME>` a `<BRAIN_ROOT>`.
|
||||
> Si d'autres repertoires brain sont visibles sur le systeme — les ignorer.
|
||||
@@ -29,14 +48,39 @@ Ne pas demander a l'utilisateur de se redecrire — tout est dans le brain.
|
||||
|
||||
---
|
||||
|
||||
## Agents 🔴 chauds — detection automatique sur domaine
|
||||
## MCP brain — interface cognitive live
|
||||
|
||||
Le brain expose 7 outils MCP sur `<BRAIN_API_URL>/mcp/`.
|
||||
A utiliser EN PLUS du bootstrap fichiers — pas a la place (CLAUDE.md reste Layer 0).
|
||||
|
||||
**Reflexes de session :**
|
||||
- `brain_boot()` → enrichit le contexte avec les chunks RAG les plus pertinents
|
||||
- `brain_focus()` → direction active + projets + blockers (live, pas le fichier stale)
|
||||
- `brain_workflows()` → sprints en cours + etats des steps
|
||||
- `brain_agents('X')` → charge le contenu d'un agent en contexte
|
||||
- `brain_decisions(5)` → ADRs recentes
|
||||
- `brain_search('q')` → RAG semantique sur n'importe quel sujet
|
||||
- `brain_write('p', c)` → ecriture fichier brain [owner only]
|
||||
|
||||
**Quand utiliser :**
|
||||
- Debut de session longue → `brain_boot()` + `brain_focus()` + `brain_workflows()`
|
||||
- Avant de charger un agent → `brain_agents('nom-agent')` si contexte insuffisant
|
||||
- Recherche de contexte precis → `brain_search('sujet')`
|
||||
- Mise a jour brain en session → `brain_write('fichier.md', contenu)`
|
||||
|
||||
> MCP enrichit le contexte — ne remplace pas Layer 0. Les fichiers restent la source de verite.
|
||||
|
||||
---
|
||||
|
||||
## Agents chauds — detection automatique sur domaine
|
||||
|
||||
| Domaine detecte | Agent |
|
||||
|-----------------|-------|
|
||||
| VPS, Apache, Docker, SSL, vhost, certbot, deploy | `agents/vps.md` |
|
||||
| Mail, SMTP, IMAP, Stalwart, DNS, SPF, DKIM | `agents/mail.md` |
|
||||
| Review code, qualite, PR, validation avant prod | `agents/code-review.md` |
|
||||
| Securite, faille, JWT, OAuth, OWASP, secrets | `agents/security.md` |
|
||||
| Securite, faille, JWT, OAuth, OWASP | `agents/security.md` |
|
||||
| .env, secrets, credentials, token manquant, API key, MYSECRETS | `agents/secrets-guardian.md` |
|
||||
| Tests, Jest, Vitest, coverage, TDD | `agents/testing.md` |
|
||||
| Bug, erreur, crash, comportement inattendu | `agents/debug.md` |
|
||||
| Refacto, dette technique, DDD | `agents/refacto.md` |
|
||||
@@ -50,8 +94,9 @@ Ne pas demander a l'utilisateur de se redecrire — tout est dans le brain.
|
||||
| Stack frontend, shadcn, Tailwind, UI libs | `agents/frontend-stack.md` |
|
||||
| i18n, traductions, cles manquantes | `agents/i18n.md` |
|
||||
| README, doc API, Swagger | `agents/doc.md` |
|
||||
| Game design, GDD, mecanique, equilibrage, progression jeu | `agents/game-designer.md` |
|
||||
|
||||
Agents 🔵 stables → invocation manuelle uniquement. Index complet : `agents/AGENTS.md`
|
||||
Agents stables → invocation manuelle uniquement. Index complet : `agents/AGENTS.md`
|
||||
Invocation explicite : "charge l'agent X" → lire `agents/X.md` immediatement.
|
||||
|
||||
Convention /btw : message prefixe `/btw` → agent `aside` — reponse 2-3 lignes, capture todo si actionnable, retour session explicite (`→ on reprend.`)
|
||||
@@ -65,3 +110,6 @@ Convention /btw : message prefixe `/btw` → agent `aside` — reponse 2-3 ligne
|
||||
- Ne jamais exposer secrets, tokens, cles privees
|
||||
- Fait non verifie → `Information manquante`
|
||||
- Incertitude → `Niveau de confiance: faible/moyen/eleve`
|
||||
- Secret detecte (code, chat, shell, output outil) ACCIDENTELLEMENT → SESSION SUSPENDUE — afficher l'interruption, ne jamais continuer avant confirmation explicite
|
||||
- Exception : "session securite active" declaree explicitement → suspension levee pour la session. Les valeurs ne s'affichent JAMAIS dans le chat meme en mode securite. Read tool sur MYSECRETS interdit meme en mode securite — utiliser Bash silencieux uniquement.
|
||||
- identityShow: on (defaut owner) — agents utilisent leurs marqueurs visuels complets (prefixes, emojis). Consequence de kerneluser: true dans brain-compose.yml.
|
||||
|
||||
@@ -92,6 +92,42 @@ if [ -d "$BRAIN_ROOT/wiki" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Docs ---
|
||||
echo ""
|
||||
echo "── docs/ ───────────────────────────────────────"
|
||||
if [ -d "$BRAIN_ROOT/docs" ]; then
|
||||
if [ -z "$DRY" ]; then
|
||||
mkdir -p "$TEMPLATE_DIR/docs"
|
||||
rsync -a --delete "$BRAIN_ROOT/docs/" "$TEMPLATE_DIR/docs/"
|
||||
fi
|
||||
doc_count=$(ls "$BRAIN_ROOT/docs/"*.md 2>/dev/null | wc -l | tr -d ' ')
|
||||
echo " ✅ $doc_count docs"
|
||||
fi
|
||||
|
||||
# --- Contexts ---
|
||||
echo ""
|
||||
echo "── contexts/ ──────────────────────────────────"
|
||||
if [ -d "$BRAIN_ROOT/contexts" ]; then
|
||||
if [ -z "$DRY" ]; then
|
||||
mkdir -p "$TEMPLATE_DIR/contexts"
|
||||
rsync -a --delete "$BRAIN_ROOT/contexts/" "$TEMPLATE_DIR/contexts/"
|
||||
fi
|
||||
ctx_count=$(ls "$BRAIN_ROOT/contexts/"*.yml 2>/dev/null | wc -l | tr -d ' ')
|
||||
echo " ✅ $ctx_count contexts"
|
||||
fi
|
||||
|
||||
# --- Brain-UI (source + dist) ---
|
||||
echo ""
|
||||
echo "── brain-ui/ ──────────────────────────────────"
|
||||
if [ -d "$BRAIN_ROOT/brain-ui" ]; then
|
||||
if [ -z "$DRY" ]; then
|
||||
rsync -a --delete \
|
||||
--exclude='node_modules/' \
|
||||
"$BRAIN_ROOT/brain-ui/" "$TEMPLATE_DIR/brain-ui/"
|
||||
fi
|
||||
echo " ✅ brain-ui (src + dist)"
|
||||
fi
|
||||
|
||||
# --- Gitkeep ---
|
||||
[ -z "$DRY" ] && mkdir -p "$TEMPLATE_DIR/locks" && \
|
||||
touch "$TEMPLATE_DIR/locks/.gitkeep"
|
||||
|
||||
44
setup.sh
44
setup.sh
@@ -19,7 +19,10 @@ if [ ! -f "$BRAIN_ROOT/brain-compose.local.yml" ]; then
|
||||
MACHINE=$(hostname)
|
||||
sed -i "s|<MACHINE_NAME>|$MACHINE|g" "$BRAIN_ROOT/brain-compose.local.yml"
|
||||
sed -i "s|<YYYY-MM-DD>|$(date +%Y-%m-%d)|g" "$BRAIN_ROOT/brain-compose.local.yml"
|
||||
echo "✅ brain-compose.local.yml cree"
|
||||
# brain_name : dérivé du nom du dossier parent (ex: ~/Dev/Brain → brain)
|
||||
BRAIN_NAME=$(basename "$BRAIN_ROOT" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
|
||||
sed -i "s|<BRAIN_NAME>|$BRAIN_NAME|g" "$BRAIN_ROOT/brain-compose.local.yml"
|
||||
echo "✅ brain-compose.local.yml cree (brain_name: $BRAIN_NAME)"
|
||||
else
|
||||
echo "✅ brain-compose.local.yml existe deja"
|
||||
fi
|
||||
@@ -52,19 +55,42 @@ if [ ! -f "$BRAIN_ROOT/profil/collaboration.md" ] && [ -f "$BRAIN_ROOT/profil/co
|
||||
echo " → profil/collaboration.md cree depuis l'exemple"
|
||||
fi
|
||||
echo "✅ Satellites prets"
|
||||
echo ""
|
||||
echo " Les satellites sont des dossiers ou le brain ecrit :"
|
||||
echo " todo/ → tes intentions de session"
|
||||
echo " progression/ → ton parcours et tes metriques"
|
||||
echo " toolkit/ → tes patterns valides en prod"
|
||||
echo " reviews/ → audits de tes agents"
|
||||
echo " Ils fonctionnent sans Git. Pour les versionner : docs/satellites.md"
|
||||
|
||||
# 3. Build dashboard
|
||||
echo ""
|
||||
echo "=== Dashboard ==="
|
||||
if [ -d "$BRAIN_ROOT/brain-ui/dist" ]; then
|
||||
echo "✅ brain-ui deja build"
|
||||
else
|
||||
if command -v node &>/dev/null && command -v npm &>/dev/null; then
|
||||
bash "$BRAIN_ROOT/brain-ui/build.sh"
|
||||
else
|
||||
echo "⚠️ Node.js/npm absent — le dashboard ne sera pas disponible."
|
||||
echo " Installe Node.js 18+ puis relance : bash brain-ui/build.sh"
|
||||
if [ -d "$BRAIN_ROOT/brain-ui" ]; then
|
||||
# Creer .env.local si absent — pointe vers brain-engine local
|
||||
if [ ! -f "$BRAIN_ROOT/brain-ui/.env.local" ]; then
|
||||
cat > "$BRAIN_ROOT/brain-ui/.env.local" << 'ENVEOF'
|
||||
# VITE_BRAIN_API vide = requetes relatives (meme serveur)
|
||||
# brain-engine sert l'UI ET l'API sur le meme port
|
||||
VITE_BRAIN_API=
|
||||
VITE_USE_MOCK=false
|
||||
ENVEOF
|
||||
echo "✅ brain-ui/.env.local cree"
|
||||
fi
|
||||
if [ -d "$BRAIN_ROOT/brain-ui/dist" ]; then
|
||||
echo "✅ brain-ui deja build"
|
||||
else
|
||||
if command -v node &>/dev/null && command -v npm &>/dev/null; then
|
||||
echo "→ Build brain-ui..."
|
||||
cd "$BRAIN_ROOT/brain-ui" && npm install --silent && npm run build && cd "$BRAIN_ROOT"
|
||||
echo "✅ brain-ui build"
|
||||
else
|
||||
echo "⚠️ Node.js/npm absent — le dashboard ne sera pas disponible."
|
||||
echo " Installe Node.js 18+ puis relance setup.sh"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "⚠️ brain-ui/ absent — dashboard non disponible."
|
||||
fi
|
||||
|
||||
# 3. Init brain-engine
|
||||
|
||||
Reference in New Issue
Block a user