Compare commits
7 Commits
0b066f729a
...
73ebc50069
| Author | SHA1 | Date | |
|---|---|---|---|
| 73ebc50069 | |||
| e0087794c8 | |||
| 7e4986e8c6 | |||
| e40e22c949 | |||
| 667e84aa30 | |||
| a043fd0285 | |||
| 1eada64913 |
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
@@ -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
|
||||
|
||||
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%"
|
||||
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)
|
||||
|
||||
---
|
||||
|
||||
@@ -55,7 +56,7 @@ bash setup.sh
|
||||
Le script fait tout automatiquement :
|
||||
|
||||
1. **Cree `brain-compose.local.yml`** — ta config machine (chemins auto-detectes)
|
||||
2. **Cree les dossiers satellites** — todo/, progression/, toolkit/, reviews/, workspace/ ([pourquoi ?](satellites.md))
|
||||
2. **Cree les dossiers satellites** — todo/, progression/, toolkit/, reviews/, workspace/
|
||||
3. **Copie `profil/collaboration.md`** — regles de travail
|
||||
4. **Build le dashboard** — `brain-ui/` (npm install + vite build)
|
||||
5. **Init brain-engine** — cree l'environnement Python + brain.db
|
||||
@@ -98,45 +99,48 @@ Brain-engine c'est le serveur local qui fait tourner l'API, le dashboard, et la
|
||||
### Demarrer
|
||||
|
||||
```bash
|
||||
cd ~/Dev/Brain
|
||||
bash brain-engine/start.sh
|
||||
```
|
||||
|
||||
Brain-engine demarre en arriere-plan. Tu vois :
|
||||
Tu vois :
|
||||
|
||||
```
|
||||
✅ brain-engine demarre (PID 12345)
|
||||
Logs : tail -f brain-engine/brain-engine.log
|
||||
Arreter : bash brain-engine/stop.sh
|
||||
=== Lancement brain-engine sur port 7700 ===
|
||||
Health : http://localhost:7700/health
|
||||
Dashboard : http://localhost:7700/ui/
|
||||
```
|
||||
|
||||
> **Le terminal reste occupe** — brain-engine tourne au premier plan. Ouvre un autre terminal pour la suite.
|
||||
|
||||
### Verifier
|
||||
|
||||
```bash
|
||||
bash brain-engine/status.sh
|
||||
```
|
||||
|
||||
Ou ouvre ton navigateur : `http://localhost:7700/ui/`
|
||||
Ouvre ton navigateur : `http://localhost:7700/ui/`
|
||||
Tu vois le dashboard avec l'onglet Docs — c'est cette documentation.
|
||||
|
||||
### Arreter
|
||||
|
||||
```bash
|
||||
bash brain-engine/stop.sh
|
||||
```
|
||||
|
||||
### Mode debug (optionnel)
|
||||
|
||||
Si tu veux voir les logs en direct dans le terminal :
|
||||
|
||||
```bash
|
||||
bash brain-engine/start.sh --foreground
|
||||
```
|
||||
|
||||
`Ctrl+C` pour arreter.
|
||||
Reviens dans le terminal ou brain-engine tourne et fais `Ctrl+C`. C'est tout.
|
||||
|
||||
> Brain-engine n'est pas obligatoire pour utiliser le brain avec Claude Code.
|
||||
> C'est un bonus (dashboard, search, API). Tu peux faire `brain boot` sans.
|
||||
|
||||
### Lancer en arriere-plan (optionnel)
|
||||
|
||||
Si tu ne veux pas bloquer un terminal :
|
||||
|
||||
```bash
|
||||
cd ~/Dev/Brain
|
||||
nohup bash brain-engine/start.sh > /tmp/brain-engine.log 2>&1 &
|
||||
echo $! > /tmp/brain-engine.pid
|
||||
```
|
||||
|
||||
Pour l'arreter :
|
||||
|
||||
```bash
|
||||
kill $(cat /tmp/brain-engine.pid)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Etape 6 — Premier brain boot
|
||||
@@ -220,11 +224,8 @@ claude
|
||||
|
||||
### Brain-engine tourne encore en fond, comment l'arreter ?
|
||||
|
||||
```bash
|
||||
bash brain-engine/stop.sh
|
||||
```
|
||||
|
||||
Pour verifier s'il tourne : `bash brain-engine/status.sh`
|
||||
Si tu l'as lance au premier plan : `Ctrl+C` dans son terminal.
|
||||
Si tu l'as lance en arriere-plan : `kill $(cat /tmp/brain-engine.pid)`
|
||||
En dernier recours : `pkill -f 'python3.*server.py'`
|
||||
|
||||
### Je vois "MYSECRETS absent" — c'est grave ?
|
||||
@@ -243,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
|
||||
|
||||
@@ -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"
|
||||
|
||||
20
setup.sh
20
setup.sh
@@ -66,15 +66,21 @@ 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"
|
||||
if [ -d "$BRAIN_ROOT/brain-ui" ]; then
|
||||
if [ -d "$BRAIN_ROOT/brain-ui/dist" ]; then
|
||||
echo "✅ brain-ui deja build"
|
||||
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 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