refactor(ux): consolidate BMAD skills, update design system, and clean up Prisma generated client

This commit is contained in:
Sepehr Ramezani
2026-04-19 19:21:27 +02:00
parent 5296c4da2c
commit 25529a24b8
2476 changed files with 127934 additions and 101962 deletions

43
mcp-server/check-notes.js Normal file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env node
/**
* Vérifier les propriétés des notes
*/
import { PrismaClient } from '../keep-notes/prisma/client-generated/index.js';
const prisma = new PrismaClient({
datasources: {
db: { url: 'file:/Users/sepehr/dev/Keep/keep-notes/prisma/dev.db' },
},
});
async function checkNotes() {
const notes = await prisma.note.findMany({
where: {
title: { startsWith: '📘' },
},
select: {
id: true,
title: true,
isMarkdown: true,
type: true,
color: true,
labels: true,
},
});
console.log('📋 Notes trouvées:\n');
for (const note of notes) {
console.log(`Titre: ${note.title}`);
console.log(` isMarkdown: ${note.isMarkdown}`);
console.log(` type: ${note.type}`);
console.log(` color: ${note.color}`);
console.log(` labels: ${note.labels}`);
console.log(` id: ${note.id}`);
console.log('');
}
}
checkNotes()
.catch(console.error)
.finally(() => prisma.$disconnect());

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env node
/**
* Supprimer toutes les notes créées
*/
import { PrismaClient } from '../keep-notes/prisma/client-generated/index.js';
const prisma = new PrismaClient({
datasources: {
db: { url: 'file:/Users/sepehr/dev/Keep/keep-notes/prisma/dev.db' },
},
});
async function deleteNotes() {
console.log('🗑️ Suppression des notes créées...\n');
// Trouver le notebook "Documentation"
const notebook = await prisma.notebook.findFirst({
where: { name: 'Documentation' },
});
if (!notebook) {
console.log('❌ Notebook "Documentation" non trouvé');
return;
}
// Supprimer toutes les notes de ce notebook
const result = await prisma.note.deleteMany({
where: {
notebookId: notebook.id,
},
});
console.log(`${result.count} notes supprimées`);
}
deleteNotes()
.catch(console.error)
.finally(() => prisma.$disconnect());

560
mcp-server/import-docs.js Normal file
View File

@@ -0,0 +1,560 @@
#!/usr/bin/env node
/**
* Script d'import des documents dans Memento
* Utilise Prisma pour créer les notes
*/
import { PrismaClient } from '../keep-notes/prisma/client-generated/index.js';
const prisma = new PrismaClient({
datasources: {
db: { url: 'file:/Users/sepehr/dev/Keep/keep-notes/prisma/dev.db' },
},
});
const documents = [
{
title: '📘 Guide Utilisateur MCP',
content: `Ce guide explique comment utiliser le serveur MCP Memento.
## Commandes disponibles (18 outils)
### Gestion des Notes
• create_note - Créer une note
• get_notes - Lister les notes
• get_note - Obtenir une note par ID
• update_note - Mettre à jour
• delete_note - Supprimer
• search_notes - Rechercher
### Gestion des Notebooks
• create_notebook - Créer un notebook
• get_notebooks - Lister
• get_notebook - Obtenir par ID
• update_notebook - Mettre à jour
• delete_notebook - Supprimer
### Gestion des Labels
• create_label - Créer un label
• get_labels - Lister
• delete_label - Supprimer
### Commandes AI
• generate_title_suggestions
• reformulate_text
• generate_tags
## Exemple
{ "name": "create_note", "arguments": { "title": "Ma Note", "content": "..." } }`,
color: 'blue',
labels: ['mcp', 'documentation'],
},
{
title: '📁 Source Tree Analysis',
content: `Analyse de l'arborescence du projet Keep.
## Structure
keep/
├── keep-notes/ # Next.js App
├── mcp-server/ # Serveur MCP
└── docs/ # Documentation
## Technologies
• Next.js 16 + React 19
• TypeScript 5
• Prisma ORM
• SQLite
• Tailwind CSS
• MCP SDK 1.0.4
## Points d'intégration
• DB SQLite partagée
• Protocole MCP
• API REST
• Intégration N8N`,
color: 'green',
labels: ['architecture', 'projet'],
},
{
title: '🎯 Project Overview',
content: `Memento - Application de notes avec AI.
## Architecture
• Pattern: JAMstack + App Router
• Rendu: SSR + Streaming
• DB: SQLite + Prisma
• Auth: NextAuth.js
## Fonctionnalités
• Notes texte & checklists
• Couleurs personnalisables
• Images et liens
• Rappels récurrents
• Format Markdown
## Organisation
• Notebooks hiérarchiques
• Labels et tags
• Archivage
• Épinglage
## AI & Intelligence
• Recherche sémantique
• Génération de titres
• Reformulation
• Memory Echo
• Batch organize`,
color: 'purple',
labels: ['overview', 'memento'],
},
{
title: '🚀 Deployment Guide',
content: `Guide de déploiement Memento.
## Prérequis
• Node.js 20+
• npm
• SQLite3
## Installation keep-notes
cd keep-notes
npm install
npx prisma generate
npx prisma db push
npm run dev
## Installation mcp-server
cd mcp-server
npm install
export DATABASE_URL="file:..."
npm run start:http
## Variables d'environnement
DATABASE_URL
NEXTAUTH_SECRET
NEXTAUTH_URL
PORT=4242
APP_BASE_URL
## Docker
Dockerfile disponible pour containerisation.`,
color: 'orange',
labels: ['deployment', 'guide'],
},
{
title: '💻 Development Guide',
content: `Guide développement keep-notes.
## Stack
• Next.js 16 + React 19
• TypeScript 5
• Tailwind CSS
• Radix UI
• Framer Motion
## Structure
app/
├── (main)/
├── api/
├── layout.tsx
globals.css
components/
├── ui/
├── notes/
├── notebooks/
lib/
├── prisma.ts
├── auth.ts
└── utils.ts
## Patterns
Server Components (défaut)
Client Components ('use client')
Server Actions ('use server')`,
color: 'teal',
labels: ['development', 'guide'],
},
{
title: '🔌 Integration Architecture',
content: `Architecture d'intégration.
## Communication
keep-notes ◄──► SQLite DB ◄──► mcp-server
## Protocole MCP
Client AI ──► MCP Server (4242) ──► SQLite
## Intégration N8N
N8N Workflow ──► MCP Server ──► SQLite
## Flux de données
1. keep-notes → Server Action → Prisma → DB
2. MCP Request → Prisma → DB → Response
3. Temps réel via DB partagée`,
color: 'blue',
labels: ['architecture', 'integration'],
},
{
title: '📡 API Contracts Keep Notes',
content: `API REST keep-notes.
## Base URL
http://localhost:3000/api
## Endpoints Notes
GET /api/notes
POST /api/notes
PUT /api/notes/:id
DELETE /api/notes/:id
## AI Features
POST /api/ai/title-suggestions
POST /api/ai/tags
POST /api/ai/reformulate
## Codes d'erreur
200 - Succès
400 - Requête invalide
401 - Non authentifié
404 - Non trouvé
500 - Erreur serveur`,
color: 'yellow',
labels: ['api', 'contracts'],
},
{
title: '🏗️ Architecture Keep Notes',
content: `Architecture détaillée.
## Pattern
JAMstack + App Router
## Flux UI → Server Action → Prisma → SQLite
## Server Components
• Accès direct DB
• Pas de JS client
• SEO-friendly
## Client Components
• Interactivité
• useState/useEffect
• APIs navigateur
## Server Actions
'use server'
async function createNote() {
await prisma.note.create({...})
}`,
color: 'purple',
labels: ['architecture', 'keep-notes'],
},
{
title: '🔍 Code Review Cleanup Report',
content: `Rapport nettoyage code.
## Issues trouvées
57 fichiers avec code debug
## Routes à supprimer (14)
/api/debug/*
/api/test/route.ts
## Scripts à supprimer (8)
check-*.js
debug-*.js
test-*.js
## Console statements (41)
Nettoyer tous les console.log
## Checklist
☐ Routes debug
☐ Scripts debug
☐ Console.log
☐ Imports inutilisés
☐ TODO/FIXME
☐ Test build prod`,
color: 'red',
labels: ['cleanup', 'review'],
},
{
title: '🗄️ Data Models',
content: `Modèles de données Prisma.
## User
id, name, email, password, role
Relations: notes[], notebooks[], labels[]
## Note
id, title, content, color
isPinned, isArchived, type
JSON: checkItems, labels, images, links
Relations: user, notebook
## Notebook
id, name, icon, color, order
Relations: user, notes[], labels[]
## Label
id, name, color
Relations: notebook
## Relations
User 1:N Note
User 1:N Notebook
Notebook 1:N Note`,
color: 'gray',
labels: ['database', 'models'],
},
{
title: '🧩 Component Inventory',
content: `Inventaire des composants.
## UI Components
Button, Card, Dialog
Input, Select, Checkbox
## Business Components
NoteCard, NotebookItem
LabelBadge, NoteEditor
## Layout Components
Sidebar, Header, MainContent
MasonryGrid, Tabs
## Hooks
useNotes, useNotebooks
useLabels, useSearch
## Utils
formatDate, generateId
parseContent, debounce`,
color: 'teal',
labels: ['components', 'inventory'],
},
{
title: '🔧 Architecture MCP Server',
content: `Serveur MCP v3.1.0
## Transport
HTTP Streamable (port 4242)
## 37 Outils
12 Notes
6 Notebooks
4 Labels
11 AI
1 Reminders
3 API Keys
## Optimisations
✓ Connection pooling
✓ Batch operations
✓ API key caching (60s)
✓ Parallel queries
✓ Request timeout (10s)
## Fichiers
index.js, index-sse.js
tools.js, auth.js`,
color: 'blue',
labels: ['mcp', 'architecture'],
},
{
title: '📋 API Contracts MCP Server',
content: `Contrats API MCP.
## Protocole
MCP SDK 1.0.4
## Transport
Streamable HTTP
## URL
http://localhost:4242/mcp
## Auth
Headers: x-api-key ou x-user-id
## Réponse
{
"content": [{
"type": "text",
"text": "..."
}]
}
## Erreurs
McpError avec ErrorCode`,
color: 'yellow',
labels: ['api', 'mcp'],
},
{
title: '💰 Monetization Analysis',
content: `Stratégies monétisation.
## Options
☕ Pay me a coffee
⭐ Freemium
☁️ Cloud Hosting
🤖 AI Credits
🏢 White-Label
## Plateformes
Ko-fi (0% frais)
Buy Me a Coffee
GitHub Sponsors
## Revenus estimés
Year 1: $200-500/mois
Year 2: $1,000-2,000/mois
## Recommandé
Ko-fi pour commencer`,
color: 'green',
labels: ['monetization', 'business'],
},
{
title: '📚 Index Documentation',
content: `Table des matières.
## 15 Documents
### Architecture (5)
• Source Tree Analysis
• Project Overview
• Integration Architecture
• Architecture Keep Notes
• Architecture MCP Server
### APIs (3)
• API Contracts Keep Notes
• API Contracts MCP Server
• Data Models
### Guides (4)
• Guide Utilisateur MCP
• Deployment Guide
• Development Guide
• Component Inventory
### Rapports (3)
• Code Review Cleanup
• Monetization Analysis
• MCP Optimization Report`,
color: 'gray',
labels: ['index', 'documentation'],
},
{
title: '⚡ MCP Optimization Report',
content: `Rapport optimisation v3.1.0
## Problèmes résolus
✓ N+1 queries
✓ Batch operations
✓ API key O(n) → O(1)
✓ HTTP timeouts
## Optimisations
• Connection pooling
• Parallel queries
• Caching 60s TTL
• Session cleanup
## Gains
• 99% moins de requêtes
• 80% plus rapide (import)
• 95% lookup API keys
## Fichiers modifiés
auth.js, tools.js
index.js, index-sse.js`,
color: 'purple',
labels: ['optimization', 'mcp'],
},
];
async function importDocuments() {
console.log('📝 Import des documents dans Memento...\n');
// Trouver ou créer le user
let user = await prisma.user.findFirst();
if (!user) {
console.log('❌ Aucun utilisateur trouvé dans la base');
process.exit(1);
}
// Trouver ou créer le notebook "Documentation"
let notebook = await prisma.notebook.findFirst({
where: { name: 'Documentation' },
});
if (!notebook) {
const highestOrder = await prisma.notebook.findFirst({
orderBy: { order: 'desc' },
select: { order: true },
});
notebook = await prisma.notebook.create({
data: {
name: 'Documentation',
icon: '📚',
color: '#3B82F6',
order: (highestOrder?.order ?? -1) + 1,
userId: user.id,
},
});
console.log('📁 Notebook "Documentation" créé\n');
} else {
console.log('📁 Notebook "Documentation" trouvé\n');
}
// Créer les notes
let created = 0;
let skipped = 0;
for (const doc of documents) {
// Vérifier si la note existe déjà
const existing = await prisma.note.findFirst({
where: {
title: doc.title,
notebookId: notebook.id,
},
});
if (existing) {
console.log(`⏭️ Déjà existant: ${doc.title}`);
skipped++;
continue;
}
await prisma.note.create({
data: {
title: doc.title,
content: doc.content,
color: doc.color,
labels: JSON.stringify(doc.labels),
notebookId: notebook.id,
userId: user.id,
type: 'text',
isPinned: false,
isArchived: false,
},
});
console.log(`✅ Créé: ${doc.title}`);
created++;
}
console.log(`\n📊 Résumé:`);
console.log(` Créés: ${created}`);
console.log(` Skippés: ${skipped}`);
console.log(` Total: ${documents.length}`);
console.log(`\n✨ Import terminé !`);
}
importDocuments()
.catch(console.error)
.finally(() => prisma.$disconnect());

View File

@@ -0,0 +1,148 @@
#!/usr/bin/env node
/**
* Script d'import COMPLET des documents dans Memento
* Importe le contenu intégral de chaque fichier .md
*/
import { PrismaClient } from '../keep-notes/prisma/client-generated/index.js';
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const prisma = new PrismaClient({
datasources: {
db: { url: 'file:/Users/sepehr/dev/Keep/keep-notes/prisma/dev.db' },
},
});
// Liste des documents avec leurs chemins
const documents = [
{ file: 'guide-utilisateur-mcp.md', title: '📘 Guide Utilisateur MCP', color: 'blue' },
{ file: 'source-tree-analysis.md', title: '📁 Source Tree Analysis', color: 'green' },
{ file: 'project-overview.md', title: '🎯 Project Overview', color: 'purple' },
{ file: 'deployment-guide.md', title: '🚀 Deployment Guide', color: 'orange' },
{ file: 'development-guide-keep-notes.md', title: '💻 Development Guide', color: 'teal' },
{ file: 'integration-architecture.md', title: '🔌 Integration Architecture', color: 'blue' },
{ file: 'api-contracts-keep-notes.md', title: '📡 API Contracts Keep Notes', color: 'yellow' },
{ file: 'architecture-keep-notes.md', title: '🏗️ Architecture Keep Notes', color: 'purple' },
{ file: 'code-review-cleanup-report.md', title: '🔍 Code Review Cleanup Report', color: 'red' },
{ file: 'data-models.md', title: '🗄️ Data Models', color: 'gray' },
{ file: 'component-inventory.md', title: '🧩 Component Inventory', color: 'teal' },
{ file: 'architecture-mcp-server.md', title: '🔧 Architecture MCP Server', color: 'blue' },
{ file: 'api-contracts-mcp-server.md', title: '📋 API Contracts MCP Server', color: 'yellow' },
{ file: 'monetization-analysis.md', title: '💰 Monetization Analysis', color: 'green' },
{ file: 'index.md', title: '📚 Index Documentation', color: 'gray' },
{ file: 'mcp-optimization-report.md', title: '⚡ MCP Optimization Report', color: 'purple' },
];
async function importDocuments() {
console.log('📝 Import COMPLET des documents dans Memento...\n');
// Trouver le user
let user = await prisma.user.findFirst();
if (!user) {
console.log('❌ Aucun utilisateur trouvé');
process.exit(1);
}
// Trouver ou créer le notebook "Documentation"
let notebook = await prisma.notebook.findFirst({
where: { name: 'Documentation' },
});
if (!notebook) {
const highestOrder = await prisma.notebook.findFirst({
orderBy: { order: 'desc' },
select: { order: true },
});
notebook = await prisma.notebook.create({
data: {
name: 'Documentation',
icon: '📚',
color: '#3B82F6',
order: (highestOrder?.order ?? -1) + 1,
userId: user.id,
},
});
console.log('📁 Notebook "Documentation" créé\n');
} else {
console.log('📁 Notebook "Documentation" trouvé\n');
}
let created = 0;
let updated = 0;
let errors = 0;
for (const doc of documents) {
try {
const filePath = join('/Users/sepehr/dev/Keep/docs', doc.file);
let content;
try {
content = readFileSync(filePath, 'utf-8');
} catch (e) {
console.log(`⚠️ Fichier non trouvé: ${doc.file}`);
errors++;
continue;
}
// Vérifier si la note existe déjà
const existing = await prisma.note.findFirst({
where: {
title: doc.title,
notebookId: notebook.id,
},
});
if (existing) {
// Mettre à jour avec le contenu complet
await prisma.note.update({
where: { id: existing.id },
data: {
content: content,
color: doc.color,
labels: JSON.stringify(['documentation']),
updatedAt: new Date(),
},
});
console.log(`🔄 Mis à jour: ${doc.title} (${content.length} caractères)`);
updated++;
} else {
// Créer nouvelle note
await prisma.note.create({
data: {
title: doc.title,
content: content,
color: doc.color,
labels: JSON.stringify(['documentation']),
notebookId: notebook.id,
userId: user.id,
type: 'text',
isPinned: false,
isArchived: false,
},
});
console.log(`✅ Créé: ${doc.title} (${content.length} caractères)`);
created++;
}
} catch (error) {
console.log(`❌ Erreur ${doc.title}: ${error.message}`);
errors++;
}
}
console.log(`\n📊 Résumé:`);
console.log(` Créés: ${created}`);
console.log(` Mis à jour: ${updated}`);
console.log(` Erreurs: ${errors}`);
console.log(` Total: ${documents.length}`);
console.log(`\n✨ Import terminé !`);
}
importDocuments()
.catch(console.error)
.finally(() => prisma.$disconnect());

35
mcp-server/opencode.json Normal file
View File

@@ -0,0 +1,35 @@
{
"$schema": "https://opencode.ai/config.json",
"agent": {
"analyst": {
"prompt": "{file:./.bmad-core/agents/analyst.md}"
},
"architect": {
"prompt": "{file:./.bmad-core/agents/architect.md}"
},
"bmad-master": {
"prompt": "{file:./.bmad-core/agents/bmad-master.md}"
},
"bmad-orchestrator": {
"prompt": "{file:./.bmad-core/agents/bmad-orchestrator.md}"
},
"dev": {
"prompt": "{file:./.bmad-core/agents/dev.md}"
},
"pm": {
"prompt": "{file:./.bmad-core/agents/pm.md}"
},
"po": {
"prompt": "{file:./.bmad-core/agents/po.md}"
},
"qa": {
"prompt": "{file:./.bmad-core/agents/qa.md}"
},
"sm": {
"prompt": "{file:./.bmad-core/agents/sm.md}"
},
"ux-expert": {
"prompt": "{file:./.bmad-core/agents/ux-expert.md}"
}
}
}

5
mcp-server/start.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
export PORT=4242
export DATABASE_URL="file:../keep-notes/prisma/dev.db"
export MCP_LOG_LEVEL=info
node index-sse.js

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env node
/**
* Script d'import COMPLET des documents Markdown dans Memento
* Les notes sont créées avec isMarkdown: true
*/
import { PrismaClient } from '../keep-notes/prisma/client-generated/index.js';
import { readFileSync } from 'fs';
import { join } from 'path';
const prisma = new PrismaClient({
datasources: {
db: { url: 'file:/Users/sepehr/dev/Keep/keep-notes/prisma/dev.db' },
},
});
// Liste des documents
const documents = [
{ file: 'guide-utilisateur-mcp.md', title: '📘 Guide Utilisateur MCP', color: 'blue' },
{ file: 'source-tree-analysis.md', title: '📁 Source Tree Analysis', color: 'green' },
{ file: 'project-overview.md', title: '🎯 Project Overview', color: 'purple' },
{ file: 'deployment-guide.md', title: '🚀 Deployment Guide', color: 'orange' },
{ file: 'development-guide-keep-notes.md', title: '💻 Development Guide', color: 'teal' },
{ file: 'integration-architecture.md', title: '🔌 Integration Architecture', color: 'blue' },
{ file: 'api-contracts-keep-notes.md', title: '📡 API Contracts Keep Notes', color: 'yellow' },
{ file: 'architecture-keep-notes.md', title: '🏗️ Architecture Keep Notes', color: 'purple' },
{ file: 'code-review-cleanup-report.md', title: '🔍 Code Review Cleanup Report', color: 'red' },
{ file: 'data-models.md', title: '🗄️ Data Models', color: 'gray' },
{ file: 'component-inventory.md', title: '🧩 Component Inventory', color: 'teal' },
{ file: 'architecture-mcp-server.md', title: '🔧 Architecture MCP Server', color: 'blue' },
{ file: 'api-contracts-mcp-server.md', title: '📋 API Contracts MCP Server', color: 'yellow' },
{ file: 'monetization-analysis.md', title: '💰 Monetization Analysis', color: 'green' },
{ file: 'index.md', title: '📚 Index Documentation', color: 'gray' },
{ file: 'mcp-optimization-report.md', title: '⚡ MCP Optimization Report', color: 'purple' },
];
async function importDocuments() {
console.log('📝 Import des documents Markdown dans Memento...\n');
// Trouver le user
let user = await prisma.user.findFirst();
if (!user) {
console.log('❌ Aucun utilisateur trouvé');
process.exit(1);
}
// Trouver le notebook "Documentation"
let notebook = await prisma.notebook.findFirst({
where: { name: 'Documentation' },
});
if (!notebook) {
console.log('❌ Notebook "Documentation" non trouvé');
process.exit(1);
}
console.log('📁 Notebook "Documentation" trouvé\n');
let updated = 0;
let errors = 0;
for (const doc of documents) {
try {
const filePath = join('/Users/sepehr/dev/Keep/docs', doc.file);
let content;
try {
content = readFileSync(filePath, 'utf-8');
} catch (e) {
console.log(`⚠️ Fichier non trouvé: ${doc.file}`);
errors++;
continue;
}
// Chercher la note existante
const existing = await prisma.note.findFirst({
where: {
title: doc.title,
notebookId: notebook.id,
},
});
if (existing) {
// Mettre à jour avec isMarkdown: true
await prisma.note.update({
where: { id: existing.id },
data: {
content: content,
color: doc.color,
labels: JSON.stringify(['documentation']),
isMarkdown: true, // ✅ FORMAT MARKDOWN ACTIVÉ
updatedAt: new Date(),
},
});
console.log(`🔄 Mis à jour (Markdown): ${doc.title}`);
updated++;
} else {
console.log(`⚠️ Note non trouvée: ${doc.title}`);
}
} catch (error) {
console.log(`❌ Erreur ${doc.title}: ${error.message}`);
errors++;
}
}
console.log(`\n📊 Résumé:`);
console.log(` Mis à jour en Markdown: ${updated}`);
console.log(` Erreurs: ${errors}`);
console.log(`\n✨ Toutes les notes sont maintenant en format Markdown !`);
}
importDocuments()
.catch(console.error)
.finally(() => prisma.$disconnect());