Restructurare completă: kanban→dashboard, notes→kb, ANAF→tools/
- Mutare și reorganizare foldere proiecte - Actualizare path-uri în TOOLS.md - Sincronizare configurații agenți - 79 fișiere actualizate
This commit is contained in:
28
AGENTS.md
28
AGENTS.md
@@ -217,6 +217,14 @@ Când primesc un link YouTube:
|
||||
3. Nota trebuie să conțină: TL;DR, pași concreți, comenzi, puncte cheie
|
||||
4. Nu dau rezumate generice - surprind **esența** și **detaliile acționabile**
|
||||
5. **DUPĂ SALVARE** rulez: `python3 ~/clawd/tools/update_notes_index.py` (actualizează index.json pentru kb.html)
|
||||
6. **DEEP LINK:** Când dau link spre notă, folosesc formatul:
|
||||
`https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/YYYY-MM-DD_titlu-slug.md`
|
||||
(files.html deschide previzualizarea, notes.html nu)
|
||||
7. **INSIGHTS:** Analizez nota și extrag idei aplicabile în `kb/insights/YYYY-MM-DD.md`:
|
||||
- Fișier zilnic (ca memory/)
|
||||
- Categorisez pe domeniu (@work, @health, @growth, @sprijin, @scout)
|
||||
- Pentru fiecare idee: ce e, cum se aplică, propunere concretă
|
||||
- **Link la sursă:** fiecare secțiune include deep link la nota originală
|
||||
|
||||
### 📦 Git Commits (~/clawd → gitea.romfast.ro/romfast/clawd)
|
||||
- **NU face commit automat** - întreabă-l pe Marius când să dau commit
|
||||
@@ -229,16 +237,20 @@ Când primesc un link YouTube:
|
||||
### 📋 Task Tracking (OBLIGATORIU)
|
||||
Când primesc o acțiune/cerere de la Marius:
|
||||
1. **React:** Reacționez cu 👍 la mesaj (WhatsApp/Discord)
|
||||
2. **Start:** Adaug task în kanban (in-progress) cu `python3 kanban/update_task.py add "titlu"`
|
||||
2. **Start:** Adaug task în kanban (in-progress) cu `python3 dashboard/update_task.py add "titlu"`
|
||||
3. **Lucrez:** Execut cererea
|
||||
4. **Done:** Marchez task-ul terminat cu `python3 kanban/update_task.py done <task-id>`
|
||||
4. **Done:** Marchez task-ul terminat cu `python3 dashboard/update_task.py done <task-id>`
|
||||
|
||||
Când se execută orice job cron:
|
||||
1. **Start:** Creează task în kanban (Progress) cu numele job-ului
|
||||
2. **Rulează:** Execută task-ul
|
||||
3. **Done:** Mută task-ul în Done cu rezultatul
|
||||
|
||||
Astfel Marius poate vedea în https://moltbot.tailf7372d.ts.net/echo/ ce job-uri au rulat și când.
|
||||
**TOATE acțiunile trebuie notate** - dashboard-ul arată statistici:
|
||||
- Task-uri completate: azi / săptămâna / luna
|
||||
- Număr de insights procesate
|
||||
|
||||
Astfel Marius poate vedea în https://moltbot.tailf7372d.ts.net/echo/ ce s-a lucrat și când.
|
||||
|
||||
**Things to check (rotate through these, 2-4 times per day):**
|
||||
- **Emails** - Any urgent unread messages?
|
||||
@@ -281,3 +293,13 @@ The goal: Be helpful without being annoying. Check in a few times a day, do usef
|
||||
## Make It Yours
|
||||
|
||||
This is a starting point. Add your own conventions, style, and rules as you figure out what works.
|
||||
|
||||
### 📁 Reguli directoare (verifică .rules.json)
|
||||
Când salvez în `kb/projects/`, verific dacă există `.rules.json`:
|
||||
- Citesc `filenameRule` pentru cum să numesc fișierul
|
||||
- Citesc `validTypes` pentru ce tipuri sunt valide
|
||||
- Scriptul inferă automat tipul din filename dacă `inferTypeFromFilename: true`
|
||||
|
||||
**Exemplu grup-sprijin:**
|
||||
- Filename: `meditatie-nume-descriptiv.md` → automat @meditatie
|
||||
- Tipuri valide: meditatie, exercitiu, reflectie, intrebare, fisa
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
### 📦 Git status (seara)
|
||||
- [ ] Fișiere uncommitted? Dacă da, întreabă dacă fac commit.
|
||||
|
||||
### 📚 KB Index (la fiecare heartbeat)
|
||||
- [ ] Verifică dacă vreun fișier din kb/ e mai nou decât kb/index.json
|
||||
- [ ] Dacă da → `python3 tools/update_notes_index.py`
|
||||
- [ ] Comandă rapidă: `find kb/ -name "*.md" -newer kb/index.json | head -1`
|
||||
|
||||
---
|
||||
|
||||
## Tracking ultimele verificări
|
||||
|
||||
27
TOOLS.md
27
TOOLS.md
@@ -24,25 +24,26 @@ python3 tools/email_send.py "dest@email.com" "Subiect" "Corp mesaj"
|
||||
|
||||
**Pentru orice altă adresă:** Citesc și raportez, aștept aprobare.
|
||||
|
||||
### Kanban & Web
|
||||
### Dashboard & Web
|
||||
- **Task Board:** https://moltbot.tailf7372d.ts.net/echo/
|
||||
- **Notes:** https://moltbot.tailf7372d.ts.net/echo/notes.html
|
||||
- **KB (Notes):** https://moltbot.tailf7372d.ts.net/echo/notes.html
|
||||
- **Files:** https://moltbot.tailf7372d.ts.net/echo/files.html
|
||||
- **API:** `kanban/api.py`
|
||||
- **Update task:** `python3 kanban/update_task.py`
|
||||
- **API:** `dashboard/api.py`
|
||||
- **Update task:** `python3 dashboard/update_task.py`
|
||||
|
||||
**Reguli dashboard:**
|
||||
- Tab Activity afișează task-uri din tasks.json, sortate descrescător după timestamp
|
||||
- Când creez/completez task-uri, să am timestamp complet (ISO format cu oră)
|
||||
|
||||
### Notes (toate tipurile)
|
||||
- **Folder:** `notes/` (subdirectoare: `youtube/`, `retete/`, etc.)
|
||||
### KB - Knowledge Base (toate tipurile de conținut)
|
||||
- **Folder:** `kb/` (subdirectoare: `youtube/`, `retete/`, `projects/`)
|
||||
- **Update index:** `python3 tools/update_notes_index.py`
|
||||
- **Pagina web:** https://moltbot.tailf7372d.ts.net/echo/notes.html
|
||||
- **Tags domeniu:** `@work`, `@health`, `@growth`, `@sprijin`, `@scout`
|
||||
- **Tags tip:** `@project`, `@fisa`, `@exercitiu`, `@meditatie`, `@reflectie`
|
||||
|
||||
**IMPORTANT:** Când salvez orice notă (rețete, youtube, etc.), trebuie să:
|
||||
1. Salvez în subdirectorul potrivit din `notes/`
|
||||
**IMPORTANT:** Când salvez orice notă (rețete, youtube, proiecte, etc.), trebuie să:
|
||||
1. Salvez în subdirectorul potrivit din `kb/`
|
||||
2. Rulez `python3 tools/update_notes_index.py` pentru a actualiza indexul
|
||||
3. Dau link-ul către pagina notes.html
|
||||
|
||||
@@ -56,7 +57,7 @@ python3 tools/email_send.py "dest@email.com" "Subiect" "Corp mesaj"
|
||||
|
||||
**TOOLS.md:** `agents/echo-work/TOOLS.md`
|
||||
|
||||
- **ANAF Monitor:** `anaf-monitor/monitor.py` - verificare la fiecare 6 ore
|
||||
- **ANAF Monitor:** `tools/anaf-monitor/monitor.py` - verificare la fiecare 6 ore
|
||||
- Monitorizează: D100, D101, D200, D390, D406, situații financiare, E-Factura
|
||||
|
||||
---
|
||||
@@ -84,9 +85,11 @@ python3 tools/email_send.py "dest@email.com" "Subiect" "Corp mesaj"
|
||||
|
||||
**TOOLS.md:** `agents/echo-sprijin/TOOLS.md`
|
||||
|
||||
- **Pagină dedicată:** https://moltbot.tailf7372d.ts.net/echo/grup-sprijin.html
|
||||
- **Fișe activități:** `kanban/grup-sprijin/`
|
||||
- **Template:** `kanban/grup-sprijin/template-fisa.md`
|
||||
- **Pagină dedicată:** https://moltbot.tailf7372d.ts.net/echo/notes.html (filtrează @grup-sprijin)
|
||||
- **Proiect:** `kb/projects/grup-sprijin/`
|
||||
- **Biblioteca activități:** `kb/projects/grup-sprijin/biblioteca.json`
|
||||
- **Fișe:** `kb/projects/grup-sprijin/fise/`
|
||||
- **Template:** `kb/projects/grup-sprijin/template-fisa.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
10
USER.md
10
USER.md
@@ -35,6 +35,16 @@
|
||||
- **Preferă:** Să repare decât să construiască de la zero
|
||||
- **Preferă:** Mai multă muncă la preț bun de la clienți existenți
|
||||
|
||||
## Reguli pentru propuneri (OBLIGATORIU pentru Echo)
|
||||
|
||||
- **80/20 STRICT:** Propun DOAR ce aduce impact mare cu efort mic
|
||||
- **Simplitate:** NU complica fluxul, NU adăuga pași noi fără motiv solid
|
||||
- **Integrare:** Orice propunere trebuie să se muleze pe fluxul CURENT
|
||||
- **Judecată critică:** Filtrez EU mai întâi, nu propun tot ce citesc
|
||||
- **Recomandare explicită:** La fiecare propunere zic: ✅ RECOMAND sau ⚠️ AȘTEPT sau ❌ NU RECOMAND + de ce
|
||||
- **Context complet:** Timp, efort TU, impact flux, beneficiu concret
|
||||
- **Anti-complexitate:** Dacă adaugă complexitate > beneficiu, NU propun
|
||||
|
||||
## Interese și domenii
|
||||
|
||||
- **Coaching & Comunicare:** NLP, Sleight of Mouth, comunicare nonviolentă
|
||||
|
||||
@@ -1 +1 @@
|
||||
../../kanban
|
||||
../../dashboard
|
||||
@@ -1 +1 @@
|
||||
../../notes
|
||||
../../kb
|
||||
@@ -1 +1 @@
|
||||
../../projects
|
||||
../../kb/projects
|
||||
@@ -3,6 +3,15 @@
|
||||
- **Name:** Marius
|
||||
- **Timezone:** Europe/Bucharest (UTC+2/+3)
|
||||
|
||||
## Profil
|
||||
|
||||
- 25 ani programator, principiul 80/20
|
||||
- E daltă nu ciocan (rafinează, simplifică)
|
||||
- Îi place natura, jocul, SF/fantasy
|
||||
- Lider cercetași
|
||||
- Pattern: face munca, nu cere bani
|
||||
- Caută cine vrea să fie (avatarul ideal)
|
||||
|
||||
## Sănătate
|
||||
|
||||
### Condiții curente
|
||||
@@ -22,11 +31,37 @@
|
||||
- **Post negru** - practica, efecte, planificare
|
||||
- Abordări holistice, legătura minte-corp
|
||||
|
||||
### Preferințe
|
||||
## Filosofie & Spiritualitate
|
||||
|
||||
- Discuții deschise despre alternative, fără a înlocui medicina convențională
|
||||
- Tracking și pattern-uri mai mult decât sfaturi directe
|
||||
### Stoicism (vrea să aprofundeze)
|
||||
- **Marcus Aurelius** - Meditations (reflecție, auto-disciplină)
|
||||
- **Seneca** - Letters from a Stoic (practică zilnică, moarte, timp)
|
||||
- **Epictetus** - Enchiridion, Discourses (ce controlăm vs ce nu)
|
||||
- **Ryan Holiday** - stoicism modern aplicat
|
||||
|
||||
### Poeți & Mistici
|
||||
- **Rumi, Hafiz** - poeți sufi persani (iubire, căutare interioară)
|
||||
- **Kahlil Gibran** - The Prophet (viață, relații, muncă)
|
||||
- **Thich Nhat Hanh** - Zen, mindfulness, prezență
|
||||
- **Lao Tzu** - Tao Te Ching (wu wei, flux natural)
|
||||
|
||||
### Gânditori moderni
|
||||
- **James Clear** - Atomic Habits, sisteme vs obiective
|
||||
- **Naval Ravikant** - filosofie pentru antreprenori
|
||||
- **Alan Watts** - filosofie estică pentru occidentali
|
||||
|
||||
### Psihologie & Dezvoltare
|
||||
- **NLP** - reframing, Sleight of Mouth
|
||||
- **Personocrație** - autenticitate
|
||||
- **Comunicare nonviolentă** - nevoi, empatie
|
||||
|
||||
## Preferințe mesaje
|
||||
|
||||
- Varietate din TOATE domeniile, nu doar sănătate fizică
|
||||
- Perspectiva: sănătate, spiritualitate, igienă mentală/emoțională
|
||||
- Surse variate, nu doar Rumi/NMG/Martel
|
||||
- Concret și aplicat, nu abstract
|
||||
- Validare, nu alarmism
|
||||
|
||||
---
|
||||
*Updated: 2026-01-30*
|
||||
*Updated: 2026-01-31*
|
||||
|
||||
@@ -1 +1 @@
|
||||
../../kanban
|
||||
../../dashboard
|
||||
@@ -1 +1 @@
|
||||
../../notes
|
||||
../../kb
|
||||
@@ -1 +1 @@
|
||||
../../projects
|
||||
../../kb/projects
|
||||
@@ -1 +1 @@
|
||||
../../kanban
|
||||
../../dashboard
|
||||
@@ -1 +1 @@
|
||||
../../notes
|
||||
../../kb
|
||||
@@ -1 +1 @@
|
||||
../../projects
|
||||
../../kb/projects
|
||||
@@ -1 +1 @@
|
||||
../../kanban
|
||||
../../dashboard
|
||||
@@ -1 +1 @@
|
||||
../../notes
|
||||
../../kb
|
||||
@@ -1 +1 @@
|
||||
../../projects
|
||||
../../kb/projects
|
||||
@@ -1 +1 @@
|
||||
../../kanban
|
||||
../../dashboard
|
||||
@@ -1 +1 @@
|
||||
../../notes
|
||||
../../kb
|
||||
@@ -1 +1 @@
|
||||
../../projects
|
||||
../../kb/projects
|
||||
@@ -16,8 +16,8 @@ from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
TOOLS_DIR = BASE_DIR / 'tools'
|
||||
NOTES_DIR = BASE_DIR / 'notes' / 'youtube'
|
||||
KANBAN_DIR = BASE_DIR / 'kanban'
|
||||
NOTES_DIR = BASE_DIR / 'kb' / 'youtube'
|
||||
KANBAN_DIR = BASE_DIR / 'dashboard'
|
||||
|
||||
class TaskBoardHandler(SimpleHTTPRequestHandler):
|
||||
|
||||
@@ -26,9 +26,44 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
||||
self.handle_youtube()
|
||||
elif self.path == '/api/files':
|
||||
self.handle_files_post()
|
||||
elif self.path == '/api/refresh-index':
|
||||
self.handle_refresh_index()
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
def handle_refresh_index(self):
|
||||
"""Regenerate kb/index.json"""
|
||||
try:
|
||||
script = TOOLS_DIR / 'update_notes_index.py'
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(script)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Parse output for stats
|
||||
output = result.stdout
|
||||
total_match = re.search(r'with (\d+) notes', output)
|
||||
total = int(total_match.group(1)) if total_match else 0
|
||||
|
||||
self.send_json({
|
||||
'success': True,
|
||||
'message': f'Index regenerat cu {total} notițe',
|
||||
'total': total,
|
||||
'output': output
|
||||
})
|
||||
else:
|
||||
self.send_json({
|
||||
'success': False,
|
||||
'error': result.stderr or 'Unknown error'
|
||||
}, 500)
|
||||
except subprocess.TimeoutExpired:
|
||||
self.send_json({'success': False, 'error': 'Timeout'}, 500)
|
||||
except Exception as e:
|
||||
self.send_json({'success': False, 'error': str(e)}, 500)
|
||||
|
||||
def handle_files_post(self):
|
||||
"""Save file content."""
|
||||
try:
|
||||
@@ -63,8 +98,10 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
if self.path == '/api/status':
|
||||
self.send_json({'status': 'ok', 'time': datetime.now().isoformat()})
|
||||
elif self.path == '/api/git':
|
||||
elif self.path == '/api/git' or self.path.startswith('/api/git?'):
|
||||
self.handle_git_status()
|
||||
elif self.path == '/api/agents' or self.path.startswith('/api/agents?'):
|
||||
self.handle_agents_status()
|
||||
elif self.path.startswith('/api/files'):
|
||||
self.handle_files_get()
|
||||
elif self.path.startswith('/api/'):
|
||||
@@ -129,6 +166,55 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
||||
except Exception as e:
|
||||
self.send_json({'error': str(e)}, 500)
|
||||
|
||||
def handle_agents_status(self):
|
||||
"""Get agents status - fast version reading session files directly."""
|
||||
try:
|
||||
# Define known agents
|
||||
agents_config = [
|
||||
{'id': 'echo', 'name': 'Echo', 'emoji': '🌀'},
|
||||
{'id': 'echo-work', 'name': 'Work', 'emoji': '⚡'},
|
||||
{'id': 'echo-health', 'name': 'Health', 'emoji': '❤️'},
|
||||
{'id': 'echo-growth', 'name': 'Growth', 'emoji': '🪜'},
|
||||
{'id': 'echo-sprijin', 'name': 'Sprijin', 'emoji': '⭕'},
|
||||
{'id': 'echo-scout', 'name': 'Scout', 'emoji': '⚜️'},
|
||||
]
|
||||
|
||||
# Check active sessions by reading session files directly (fast)
|
||||
active_agents = set()
|
||||
sessions_base = Path.home() / '.clawdbot' / 'agents'
|
||||
|
||||
if sessions_base.exists():
|
||||
for agent_dir in sessions_base.iterdir():
|
||||
if agent_dir.is_dir():
|
||||
sessions_file = agent_dir / 'sessions' / 'sessions.json'
|
||||
if sessions_file.exists():
|
||||
try:
|
||||
data = json.loads(sessions_file.read_text())
|
||||
# sessions.json is an object with session keys
|
||||
now = datetime.now().timestamp() * 1000
|
||||
for key, sess in data.items():
|
||||
if isinstance(sess, dict):
|
||||
last_active = sess.get('updatedAt', 0)
|
||||
if now - last_active < 30 * 60 * 1000: # 30 min
|
||||
active_agents.add(agent_dir.name)
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
# Build response
|
||||
agents = []
|
||||
for cfg in agents_config:
|
||||
agents.append({
|
||||
'id': cfg['id'],
|
||||
'name': cfg['name'],
|
||||
'emoji': cfg['emoji'],
|
||||
'active': cfg['id'] in active_agents
|
||||
})
|
||||
|
||||
self.send_json({'agents': agents})
|
||||
except Exception as e:
|
||||
self.send_json({'error': str(e)}, 500)
|
||||
|
||||
def handle_files_get(self):
|
||||
"""List files or get file content."""
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
1
dashboard/conversations
Symbolic link
1
dashboard/conversations
Symbolic link
@@ -0,0 +1 @@
|
||||
../conversations
|
||||
@@ -330,16 +330,12 @@
|
||||
</a>
|
||||
<a href="notes.html" class="nav-item">
|
||||
<i data-lucide="file-text"></i>
|
||||
<span>Notes</span>
|
||||
<span>KB</span>
|
||||
</a>
|
||||
<a href="files.html" class="nav-item active">
|
||||
<i data-lucide="folder"></i>
|
||||
<span>Files</span>
|
||||
</a>
|
||||
<a href="grup-sprijin.html" class="nav-item">
|
||||
<i data-lucide="heart-handshake"></i>
|
||||
<span>Grup</span>
|
||||
</a>
|
||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||
<i data-lucide="sun" id="themeIcon"></i>
|
||||
</button>
|
||||
@@ -583,9 +579,16 @@
|
||||
const isMarkdown = path.endsWith('.md');
|
||||
document.getElementById('previewBtn').style.display = isMarkdown ? 'flex' : 'none';
|
||||
|
||||
// Reset preview state
|
||||
document.getElementById('editorBody').classList.remove('preview-active');
|
||||
document.getElementById('previewBtn').classList.remove('active');
|
||||
// Auto-activate preview for markdown files
|
||||
if (isMarkdown) {
|
||||
const preview = document.getElementById('markdownPreview');
|
||||
preview.innerHTML = marked.parse(data.content);
|
||||
document.getElementById('editorBody').classList.add('preview-active');
|
||||
document.getElementById('previewBtn').classList.add('active');
|
||||
} else {
|
||||
document.getElementById('editorBody').classList.remove('preview-active');
|
||||
document.getElementById('previewBtn').classList.remove('active');
|
||||
}
|
||||
|
||||
if (data.truncated) {
|
||||
setStatus('Fișier trunchiat', 'error');
|
||||
@@ -14,6 +14,61 @@
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
/* Stats Summary */
|
||||
.stats-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--space-3);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.stats-summary {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-4);
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--accent-subtle);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-icon svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: var(--text-xl);
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
@@ -154,6 +209,33 @@
|
||||
color: #818cf8;
|
||||
}
|
||||
|
||||
.status-section-icon.agents {
|
||||
background: rgba(168, 85, 247, 0.15);
|
||||
color: #a855f7;
|
||||
}
|
||||
|
||||
.agents-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.agent-chip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background: var(--bg-elevated);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.agent-chip .emoji { font-size: 14px; }
|
||||
.agent-chip .name { font-weight: 500; color: var(--text-primary); }
|
||||
.agent-chip .status { color: var(--text-muted); }
|
||||
.agent-chip.active { background: rgba(34, 197, 94, 0.1); border: 1px solid rgba(34, 197, 94, 0.3); }
|
||||
.agent-chip.active .status { color: #22c55e; }
|
||||
|
||||
.status-section-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -794,16 +876,12 @@
|
||||
</a>
|
||||
<a href="notes.html" class="nav-item">
|
||||
<i data-lucide="file-text"></i>
|
||||
<span>Notes</span>
|
||||
<span>KB</span>
|
||||
</a>
|
||||
<a href="files.html" class="nav-item">
|
||||
<i data-lucide="folder"></i>
|
||||
<span>Files</span>
|
||||
</a>
|
||||
<a href="grup-sprijin.html" class="nav-item">
|
||||
<i data-lucide="heart-handshake"></i>
|
||||
<span>Grup</span>
|
||||
</a>
|
||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||
<i data-lucide="sun" id="themeIcon"></i>
|
||||
</button>
|
||||
@@ -894,6 +972,39 @@
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Summary -->
|
||||
<div class="stats-summary" id="statsSummary">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon"><i data-lucide="check-circle"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statToday">0</div>
|
||||
<div class="stat-label">Azi</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon"><i data-lucide="calendar"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statWeek">0</div>
|
||||
<div class="stat-label">Săptămâna</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon"><i data-lucide="trending-up"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statMonth">0</div>
|
||||
<div class="stat-label">Luna</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon"><i data-lucide="lightbulb"></i></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="statInsights">0</div>
|
||||
<div class="stat-label">Insights</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1140,7 +1251,7 @@
|
||||
|
||||
async function loadGitStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/git?' + Date.now());
|
||||
const response = await fetch('./api/git?' + Date.now());
|
||||
if (!response.ok) throw new Error('API error');
|
||||
const git = await response.json();
|
||||
|
||||
@@ -1270,6 +1381,42 @@
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
async function loadAgentsStatus() {
|
||||
try {
|
||||
const response = await fetch('./api/agents?' + Date.now());
|
||||
if (!response.ok) throw new Error('API error');
|
||||
const data = await response.json();
|
||||
|
||||
const agents = data.agents || [];
|
||||
const activeCount = agents.filter(a => a.active).length;
|
||||
|
||||
// Update badge
|
||||
const badge = document.getElementById('agentsBadge');
|
||||
badge.textContent = `${activeCount}/${agents.length}`;
|
||||
badge.className = 'status-badge ' + (activeCount > 0 ? 'ok' : 'warning');
|
||||
|
||||
// Update subtitle
|
||||
const subtitle = document.getElementById('agentsSubtitle');
|
||||
const activeNames = agents.filter(a => a.active).map(a => a.name).join(', ');
|
||||
subtitle.textContent = activeCount > 0 ? `Activi: ${activeNames}` : 'Niciun agent activ';
|
||||
|
||||
// Update grid
|
||||
const grid = document.getElementById('agentsGrid');
|
||||
grid.innerHTML = agents.map(agent => `
|
||||
<div class="agent-chip ${agent.active ? 'active' : ''}">
|
||||
<span class="emoji">${agent.emoji || '🤖'}</span>
|
||||
<span class="name">${agent.name}</span>
|
||||
<span class="status">${agent.active ? '●' : '○'}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
} catch (e) {
|
||||
console.log('Agents status error:', e);
|
||||
document.getElementById('agentsBadge').textContent = '-';
|
||||
document.getElementById('agentsSubtitle').textContent = 'Nu se poate încărca';
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatusSummary() {
|
||||
const gitBadge = document.getElementById('gitBadge');
|
||||
const anafBadge = document.getElementById('anafBadge');
|
||||
@@ -1366,9 +1513,51 @@
|
||||
|
||||
function refreshActivity() {
|
||||
loadActivity();
|
||||
loadStats();
|
||||
showToast('Activitate reîmprospătată');
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
// Load tasks
|
||||
const tasksRes = await fetch('tasks.json');
|
||||
const tasksData = await tasksRes.json();
|
||||
|
||||
// Get done tasks
|
||||
const doneColumn = tasksData.columns.find(c => c.id === 'done');
|
||||
const doneTasks = doneColumn ? doneColumn.tasks : [];
|
||||
|
||||
const now = new Date();
|
||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const weekStart = new Date(todayStart);
|
||||
weekStart.setDate(weekStart.getDate() - weekStart.getDay() + 1); // Monday
|
||||
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
|
||||
let today = 0, week = 0, month = 0;
|
||||
|
||||
doneTasks.forEach(task => {
|
||||
if (!task.completed) return;
|
||||
const completed = new Date(task.completed);
|
||||
if (completed >= todayStart) today++;
|
||||
if (completed >= weekStart) week++;
|
||||
if (completed >= monthStart) month++;
|
||||
});
|
||||
|
||||
document.getElementById('statToday').textContent = today;
|
||||
document.getElementById('statWeek').textContent = week;
|
||||
document.getElementById('statMonth').textContent = month;
|
||||
|
||||
// Count insights
|
||||
const insightsRes = await fetch('/echo/api/files?path=kb/insights');
|
||||
if (insightsRes.ok) {
|
||||
const insightsData = await insightsRes.json();
|
||||
document.getElementById('statInsights').textContent = insightsData.files ? insightsData.files.length : 0;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Stats load error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function renderActivity() {
|
||||
const body = document.getElementById('activityBody');
|
||||
|
||||
@@ -1644,6 +1833,7 @@
|
||||
loadStatus();
|
||||
loadIssues();
|
||||
loadActivity();
|
||||
loadStats();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1
dashboard/memory
Symbolic link
1
dashboard/memory
Symbolic link
@@ -0,0 +1 @@
|
||||
../memory
|
||||
1
dashboard/notes-data
Symbolic link
1
dashboard/notes-data
Symbolic link
@@ -0,0 +1 @@
|
||||
../kb
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Echo · Notes</title>
|
||||
<title>Echo · KB</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||
<script src="swipe-nav.js"></script>
|
||||
@@ -155,9 +155,39 @@
|
||||
}
|
||||
|
||||
.note-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-1);
|
||||
display: inline;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.note-tag {
|
||||
font-size: var(--text-sm);
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.note-tag.domain {
|
||||
color: #f97316;
|
||||
}
|
||||
|
||||
.note-tag.type {
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
.note-tag.tag {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.note-tags-toggle {
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.note-tags-toggle:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.note-tags-full {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
/* Empty section */
|
||||
@@ -287,74 +317,197 @@
|
||||
|
||||
.markdown-body strong { color: var(--text-primary); }
|
||||
|
||||
/* Tag filter pills */
|
||||
.tag-filter {
|
||||
margin-bottom: var(--space-5);
|
||||
/* Filter bar with pills */
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-4);
|
||||
padding: var(--space-3);
|
||||
background: var(--bg-surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.tag-filter-header {
|
||||
[data-theme="light"] .filter-bar {
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.filter-separator {
|
||||
color: var(--text-muted);
|
||||
opacity: 0.3;
|
||||
margin: 0 var(--space-1);
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
padding: var(--space-1) var(--space-2);
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: var(--radius-full);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-theme="light"] .filter-pill {
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.filter-pill:hover {
|
||||
background: var(--bg-surface-hover);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.filter-pill.active {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.filter-pill.dimmed {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.filter-pill.dimmed:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Category pills - teal */
|
||||
.filter-pill.category {
|
||||
background: rgba(20, 184, 166, 0.15);
|
||||
border-color: rgba(20, 184, 166, 0.4);
|
||||
color: #14b8a6;
|
||||
}
|
||||
.filter-pill.category:hover {
|
||||
background: rgba(20, 184, 166, 0.25);
|
||||
}
|
||||
.filter-pill.category.active {
|
||||
background: #14b8a6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Project pills - blue */
|
||||
.filter-pill.project {
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
border-color: rgba(59, 130, 246, 0.4);
|
||||
color: #3b82f6;
|
||||
}
|
||||
.filter-pill.project:hover {
|
||||
background: rgba(59, 130, 246, 0.25);
|
||||
}
|
||||
.filter-pill.project.active {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Domain pills - orange */
|
||||
.filter-pill.domain {
|
||||
background: rgba(249, 115, 22, 0.15);
|
||||
border-color: rgba(249, 115, 22, 0.4);
|
||||
color: #f97316;
|
||||
}
|
||||
.filter-pill.domain:hover {
|
||||
background: rgba(249, 115, 22, 0.25);
|
||||
}
|
||||
.filter-pill.domain.active {
|
||||
background: #f97316;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Type pills - purple */
|
||||
.filter-pill.type {
|
||||
background: rgba(139, 92, 246, 0.15);
|
||||
border-color: rgba(139, 92, 246, 0.4);
|
||||
color: #8b5cf6;
|
||||
}
|
||||
.filter-pill.type:hover {
|
||||
background: rgba(139, 92, 246, 0.25);
|
||||
}
|
||||
.filter-pill.type.active {
|
||||
background: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Tag pills - gray */
|
||||
.filter-pill.tag {
|
||||
background: rgba(100, 116, 139, 0.15);
|
||||
border-color: rgba(100, 116, 139, 0.4);
|
||||
color: #94a3b8;
|
||||
}
|
||||
.filter-pill.tag:hover {
|
||||
background: rgba(100, 116, 139, 0.25);
|
||||
}
|
||||
.filter-pill.tag.active {
|
||||
background: #64748b;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.filter-pill-count {
|
||||
font-size: var(--text-xs);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-bottom: var(--space-2);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.tag-filter-header svg {
|
||||
.filter-btn {
|
||||
padding: var(--space-2);
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background: var(--bg-surface-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.filter-btn.clear:hover {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.filter-btn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.more-tags-toggle {
|
||||
color: var(--text-muted);
|
||||
transition: transform var(--transition-fast);
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
padding: var(--space-1) var(--space-2);
|
||||
}
|
||||
|
||||
.tag-filter.collapsed .tag-filter-header svg {
|
||||
transform: rotate(-90deg);
|
||||
.more-tags-toggle:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tag-pills-more {
|
||||
display: none;
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.tag-pills-more.expanded {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
/* More tags toggle button - same style as pills */
|
||||
.tag-pill.more-toggle {
|
||||
background: rgba(100, 116, 139, 0.2);
|
||||
border-color: rgba(100, 116, 139, 0.4);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.tag-pill.more-toggle:hover {
|
||||
background: rgba(100, 116, 139, 0.3);
|
||||
}
|
||||
.tag-pill.more-toggle.expanded {
|
||||
background: rgba(100, 116, 139, 0.4);
|
||||
}
|
||||
|
||||
/* Dimmed pills - tags not in visible notes */
|
||||
.tag-pill.dimmed {
|
||||
opacity: 0.35;
|
||||
}
|
||||
.tag-pill.dimmed:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.filter-count {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
margin-left: var(--space-1);
|
||||
}
|
||||
|
||||
.tag-filter-label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
margin-bottom: var(--space-2);
|
||||
display: block;
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.tag-pills {
|
||||
@@ -497,16 +650,12 @@
|
||||
</a>
|
||||
<a href="notes.html" class="nav-item active">
|
||||
<i data-lucide="file-text"></i>
|
||||
<span>Notes</span>
|
||||
<span>KB</span>
|
||||
</a>
|
||||
<a href="files.html" class="nav-item">
|
||||
<i data-lucide="folder"></i>
|
||||
<span>Files</span>
|
||||
</a>
|
||||
<a href="grup-sprijin.html" class="nav-item">
|
||||
<i data-lucide="heart-handshake"></i>
|
||||
<span>Grup</span>
|
||||
</a>
|
||||
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||
<i data-lucide="sun" id="themeIcon"></i>
|
||||
</button>
|
||||
@@ -515,15 +664,29 @@
|
||||
|
||||
<main class="main">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Notes</h1>
|
||||
<h1 class="page-title">Knowledge Base</h1>
|
||||
<div class="search-bar">
|
||||
<input type="text" class="input" id="searchInput" placeholder="Caută în notițe..." oninput="filterNotes()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tag-filter">
|
||||
<div class="tag-pills" id="mainPills"></div>
|
||||
<div class="tag-pills-more" id="tagPills"></div>
|
||||
<div class="filter-bar" id="filterBar">
|
||||
<div class="filter-group" id="categoryPills"></div>
|
||||
<span class="filter-separator" id="sep1">|</span>
|
||||
<div class="filter-group" id="projectPills"></div>
|
||||
<span class="filter-separator" id="sep2">|</span>
|
||||
<div class="filter-group" id="typePills"></div>
|
||||
<span class="filter-separator" id="sep3">|</span>
|
||||
<div class="filter-group" id="tagPills"></div>
|
||||
<span class="more-tags-toggle" id="moreTagsToggle" onclick="toggleMoreTags()"></span>
|
||||
<div class="filter-actions">
|
||||
<button class="filter-btn clear" onclick="clearFilters()" title="Resetează" id="clearBtn" style="display: none;">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
<button class="filter-btn" onclick="refreshIndex()" title="Reîncarcă">
|
||||
<i data-lucide="refresh-cw" id="refreshIcon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="notesContainer">
|
||||
@@ -594,171 +757,267 @@
|
||||
notesIndex = [];
|
||||
}
|
||||
}
|
||||
let selectedTags = new Set();
|
||||
// Current filter state
|
||||
let selectedFilters = {
|
||||
category: null,
|
||||
project: null,
|
||||
domain: null,
|
||||
type: null,
|
||||
tags: new Set()
|
||||
};
|
||||
let showAllTags = false;
|
||||
|
||||
// Extract all tags with counts (including domains and categories)
|
||||
function getAllTags() {
|
||||
const tagCounts = {};
|
||||
notesIndex.forEach(note => {
|
||||
// Category tags (📁youtube, 📁retete)
|
||||
if (note.category) {
|
||||
const catTag = '📁' + note.category;
|
||||
tagCounts[catTag] = (tagCounts[catTag] || 0) + 1;
|
||||
}
|
||||
// Domain tags (@work, @health, etc.)
|
||||
if (note.domains) {
|
||||
note.domains.forEach(domain => {
|
||||
const domainTag = '@' + domain;
|
||||
tagCounts[domainTag] = (tagCounts[domainTag] || 0) + 1;
|
||||
});
|
||||
}
|
||||
// Regular tags
|
||||
note.tags.forEach(tag => {
|
||||
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
||||
});
|
||||
// Get counts for each filter value
|
||||
function getFilterCounts(filteredNotes = null) {
|
||||
const notes = filteredNotes || notesIndex;
|
||||
const counts = {
|
||||
categories: {},
|
||||
projects: {},
|
||||
domains: {},
|
||||
types: {},
|
||||
tags: {}
|
||||
};
|
||||
|
||||
notes.forEach(note => {
|
||||
if (note.category) counts.categories[note.category] = (counts.categories[note.category] || 0) + 1;
|
||||
if (note.project) counts.projects[note.project] = (counts.projects[note.project] || 0) + 1;
|
||||
if (note.domains) note.domains.forEach(d => counts.domains[d] = (counts.domains[d] || 0) + 1);
|
||||
if (note.types) note.types.forEach(t => counts.types[t] = (counts.types[t] || 0) + 1);
|
||||
if (note.tags) note.tags.forEach(t => counts.tags[t] = (counts.tags[t] || 0) + 1);
|
||||
});
|
||||
// Sort: categories first (📁), then domains (@), then by count
|
||||
return Object.entries(tagCounts)
|
||||
.sort((a, b) => {
|
||||
const aIsCat = a[0].startsWith('📁');
|
||||
const bIsCat = b[0].startsWith('📁');
|
||||
const aIsDomain = a[0].startsWith('@');
|
||||
const bIsDomain = b[0].startsWith('@');
|
||||
if (aIsCat && !bIsCat) return -1;
|
||||
if (!aIsCat && bIsCat) return 1;
|
||||
if (aIsDomain && !bIsDomain) return -1;
|
||||
if (!aIsDomain && bIsDomain) return 1;
|
||||
return b[1] - a[1];
|
||||
})
|
||||
.map(([tag, count]) => ({ tag, count }));
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
// Render tag pills
|
||||
function renderTagPills(visibleNotes = null) {
|
||||
const mainContainer = document.getElementById('mainPills');
|
||||
const moreContainer = document.getElementById('tagPills');
|
||||
const tags = getAllTags();
|
||||
// Render filter pills
|
||||
function renderFilterPills(filteredNotes = null) {
|
||||
const counts = getFilterCounts();
|
||||
const visibleCounts = filteredNotes ? getFilterCounts(filteredNotes) : counts;
|
||||
|
||||
// Calculate which tags appear in visible notes
|
||||
const visibleTags = new Set();
|
||||
if (visibleNotes && visibleNotes.length > 0) {
|
||||
visibleNotes.forEach(note => {
|
||||
note.tags.forEach(t => visibleTags.add(t));
|
||||
(note.domains || []).forEach(d => visibleTags.add('@' + d));
|
||||
if (note.category) visibleTags.add('📁' + note.category);
|
||||
});
|
||||
// Categories
|
||||
const catHtml = Object.entries(counts.categories)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([cat, count]) => {
|
||||
const isActive = selectedFilters.category === cat;
|
||||
const isVisible = visibleCounts.categories[cat] > 0;
|
||||
const dimmed = filteredNotes && !isVisible && !isActive ? 'dimmed' : '';
|
||||
return `<span class="filter-pill category ${isActive ? 'active' : ''} ${dimmed}" onclick="toggleFilter('category', '${cat}')">
|
||||
📁${cat} <span class="filter-pill-count">${count}</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
document.getElementById('categoryPills').innerHTML = catHtml;
|
||||
|
||||
// Projects (only show if category=projects or no category selected)
|
||||
const showProjects = !selectedFilters.category || selectedFilters.category === 'projects';
|
||||
const projHtml = showProjects && Object.keys(counts.projects).length > 0 ? Object.entries(counts.projects)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([proj, count]) => {
|
||||
const isActive = selectedFilters.project === proj;
|
||||
const isVisible = visibleCounts.projects[proj] > 0;
|
||||
const dimmed = filteredNotes && !isVisible && !isActive ? 'dimmed' : '';
|
||||
return `<span class="filter-pill project ${isActive ? 'active' : ''} ${dimmed}" onclick="toggleFilter('project', '${proj}')">
|
||||
📂${proj} <span class="filter-pill-count">${count}</span>
|
||||
</span>`;
|
||||
}).join('') : '';
|
||||
document.getElementById('projectPills').innerHTML = projHtml;
|
||||
document.getElementById('sep1').style.display = projHtml ? '' : 'none';
|
||||
|
||||
// Domains (@work, @health, etc.) - orange
|
||||
const domainHtml = Object.entries(counts.domains)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([domain, count]) => {
|
||||
const isActive = selectedFilters.domain === domain;
|
||||
const isVisible = visibleCounts.domains[domain] > 0;
|
||||
const dimmed = filteredNotes && !isVisible && !isActive ? 'dimmed' : '';
|
||||
return `<span class="filter-pill domain ${isActive ? 'active' : ''} ${dimmed}" onclick="toggleFilter('domain', '${domain}')">
|
||||
@${domain} <span class="filter-pill-count">${count}</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
|
||||
// Types (@meditatie, @exercitiu, etc.)
|
||||
const typeHtml = Object.entries(counts.types)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([type, count]) => {
|
||||
const isActive = selectedFilters.type === type;
|
||||
const isVisible = visibleCounts.types[type] > 0;
|
||||
const dimmed = filteredNotes && !isVisible && !isActive ? 'dimmed' : '';
|
||||
return `<span class="filter-pill type ${isActive ? 'active' : ''} ${dimmed}" onclick="toggleFilter('type', '${type}')">
|
||||
@${type} <span class="filter-pill-count">${count}</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
|
||||
// Combine domains and types in typePills
|
||||
document.getElementById('typePills').innerHTML = domainHtml + typeHtml;
|
||||
document.getElementById('sep2').style.display = (domainHtml || typeHtml) ? '' : 'none';
|
||||
|
||||
// Tags - collapsed by default, show only count
|
||||
const tagEntries = Object.entries(counts.tags).sort((a, b) => b[1] - a[1]);
|
||||
const activeTagsCount = selectedFilters.tags.size;
|
||||
|
||||
let tagHtml = '';
|
||||
if (showAllTags) {
|
||||
// Show all tags
|
||||
tagHtml = tagEntries.map(([tag, count]) => {
|
||||
const isActive = selectedFilters.tags.has(tag);
|
||||
const isVisible = visibleCounts.tags[tag] > 0;
|
||||
const dimmed = filteredNotes && !isVisible && !isActive ? 'dimmed' : '';
|
||||
return `<span class="filter-pill tag ${isActive ? 'active' : ''} ${dimmed}" onclick="toggleFilter('tag', '${tag}')">
|
||||
#${tag} <span class="filter-pill-count">${count}</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
} else if (activeTagsCount > 0) {
|
||||
// Show only active tags
|
||||
tagHtml = [...selectedFilters.tags].map(tag => {
|
||||
const count = counts.tags[tag] || 0;
|
||||
return `<span class="filter-pill tag active" onclick="toggleFilter('tag', '${tag}')">
|
||||
#${tag} <span class="filter-pill-count">${count}</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
}
|
||||
const hasFilter = visibleNotes !== null;
|
||||
document.getElementById('tagPills').innerHTML = tagHtml;
|
||||
document.getElementById('sep3').style.display = (tagHtml || tagEntries.length > 0) ? '' : 'none';
|
||||
|
||||
// Separate: categories + domains vs regular tags
|
||||
const mainTags = tags.filter(({tag}) => tag.startsWith('📁') || tag.startsWith('@'));
|
||||
const moreTags = tags.filter(({tag}) => !tag.startsWith('📁') && !tag.startsWith('@'));
|
||||
|
||||
// Check if more section is expanded
|
||||
const isExpanded = moreContainer.classList.contains('expanded');
|
||||
const activeMoreCount = [...selectedTags].filter(t => !t.startsWith('📁') && !t.startsWith('@')).length;
|
||||
|
||||
// Render main pills (categories + domains)
|
||||
let mainHtml = mainTags.map(({ tag, count }) => {
|
||||
let pillClass = 'tag-pill';
|
||||
if (tag.startsWith('📁')) pillClass += ' category';
|
||||
else if (tag.startsWith('@')) pillClass += ' domain';
|
||||
if (selectedTags.has(tag)) pillClass += ' active';
|
||||
// Dim tags not in visible notes (unless it's already selected)
|
||||
if (hasFilter && !visibleTags.has(tag) && !selectedTags.has(tag)) pillClass += ' dimmed';
|
||||
return `<span class="${pillClass}" onclick="toggleTag('${tag}')">
|
||||
${tag} <span class="tag-pill-count">(${count})</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
|
||||
// Add "more tags" toggle button inline
|
||||
if (moreTags.length > 0) {
|
||||
const visibleMoreCount = moreTags.filter(({tag}) => visibleTags.has(tag)).length;
|
||||
const moreLabel = activeMoreCount > 0
|
||||
? `+${moreTags.length} tags (${activeMoreCount} active)`
|
||||
: (hasFilter && visibleMoreCount > 0 ? `+${visibleMoreCount}/${moreTags.length} tags` : `+${moreTags.length} tags`);
|
||||
mainHtml += `<span class="tag-pill more-toggle ${isExpanded ? 'expanded' : ''}" onclick="toggleMoreTags()">
|
||||
${isExpanded ? '−' : '+'} ${moreLabel}
|
||||
</span>`;
|
||||
}
|
||||
|
||||
if (selectedTags.size > 0) {
|
||||
mainHtml += `<button class="clear-filters" onclick="clearTagFilters()">✕ Clear</button>`;
|
||||
}
|
||||
mainContainer.innerHTML = mainHtml;
|
||||
|
||||
// Render more pills (regular tags)
|
||||
const moreHtml = moreTags.map(({ tag, count }) => {
|
||||
let pillClass = 'tag-pill';
|
||||
if (selectedTags.has(tag)) pillClass += ' active';
|
||||
if (hasFilter && !visibleTags.has(tag) && !selectedTags.has(tag)) pillClass += ' dimmed';
|
||||
return `<span class="${pillClass}" onclick="toggleTag('${tag}')">
|
||||
${tag} <span class="tag-pill-count">(${count})</span>
|
||||
</span>`;
|
||||
}).join('');
|
||||
moreContainer.innerHTML = moreHtml;
|
||||
}
|
||||
|
||||
// Toggle tag selection
|
||||
function toggleTag(tag) {
|
||||
if (selectedTags.has(tag)) {
|
||||
selectedTags.delete(tag);
|
||||
// More tags toggle
|
||||
const moreToggle = document.getElementById('moreTagsToggle');
|
||||
if (tagEntries.length > 0) {
|
||||
const hiddenCount = activeTagsCount > 0 ? tagEntries.length - activeTagsCount : tagEntries.length;
|
||||
if (showAllTags) {
|
||||
moreToggle.textContent = `-${hiddenCount} tags`;
|
||||
} else {
|
||||
moreToggle.textContent = `+${hiddenCount} tags`;
|
||||
}
|
||||
moreToggle.style.display = '';
|
||||
} else {
|
||||
selectedTags.add(tag);
|
||||
moreToggle.style.display = 'none';
|
||||
}
|
||||
|
||||
// Clear button
|
||||
const hasFilters = selectedFilters.category || selectedFilters.project ||
|
||||
selectedFilters.domain || selectedFilters.type || selectedFilters.tags.size > 0;
|
||||
document.getElementById('clearBtn').style.display = hasFilters ? '' : 'none';
|
||||
}
|
||||
|
||||
// Toggle filter
|
||||
function toggleFilter(filterType, value) {
|
||||
if (filterType === 'tag') {
|
||||
if (selectedFilters.tags.has(value)) {
|
||||
selectedFilters.tags.delete(value);
|
||||
} else {
|
||||
selectedFilters.tags.add(value);
|
||||
}
|
||||
} else {
|
||||
selectedFilters[filterType] = selectedFilters[filterType] === value ? null : value;
|
||||
// Reset child filters when parent changes
|
||||
if (filterType === 'category' && value !== 'projects') {
|
||||
selectedFilters.project = null;
|
||||
}
|
||||
}
|
||||
renderTagPills();
|
||||
filterNotes();
|
||||
}
|
||||
|
||||
// Clear all tag filters
|
||||
function clearTagFilters() {
|
||||
selectedTags.clear();
|
||||
renderTagPills();
|
||||
filterNotes();
|
||||
}
|
||||
|
||||
// Toggle more tags section
|
||||
// Toggle more tags
|
||||
function toggleMoreTags() {
|
||||
const moreContainer = document.getElementById('tagPills');
|
||||
moreContainer.classList.toggle('expanded');
|
||||
renderTagPills(lastFilteredNotes);
|
||||
showAllTags = !showAllTags;
|
||||
renderFilterPills(lastFilteredNotes);
|
||||
}
|
||||
|
||||
// Filter notes by search and tags
|
||||
// Toggle tags in note card
|
||||
function toggleNoteTags(noteId) {
|
||||
const full = document.getElementById('tags_' + noteId);
|
||||
const toggle = full.parentElement;
|
||||
const count = toggle.querySelector('.note-tags-count');
|
||||
if (full.style.display === 'none') {
|
||||
full.style.display = 'inline';
|
||||
count.style.display = 'none';
|
||||
} else {
|
||||
full.style.display = 'none';
|
||||
count.style.display = 'inline';
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all filters
|
||||
function clearFilters() {
|
||||
selectedFilters = { category: null, project: null, domain: null, type: null, tags: new Set() };
|
||||
showAllTags = false;
|
||||
document.getElementById('searchInput').value = '';
|
||||
filterNotes();
|
||||
}
|
||||
|
||||
// Refresh index from server
|
||||
async function refreshIndex() {
|
||||
const icon = document.getElementById('refreshIcon');
|
||||
if (icon) icon.style.animation = 'spin 1s linear infinite';
|
||||
|
||||
try {
|
||||
const response = await fetch('api/refresh-index', { method: 'POST' });
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
await loadNotesIndex();
|
||||
filterNotes();
|
||||
lucide.createIcons();
|
||||
console.log('Index refreshed:', data.message);
|
||||
} else {
|
||||
console.error('Refresh failed:', data.error);
|
||||
alert('Eroare: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Refresh error:', e);
|
||||
alert('Eroare la refresh: ' + e.message);
|
||||
} finally {
|
||||
if (icon) icon.style.animation = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Filter notes
|
||||
function filterNotes() {
|
||||
const query = document.getElementById('searchInput').value.toLowerCase().trim();
|
||||
|
||||
let filtered = notesIndex;
|
||||
|
||||
// Filter by selected tags (AND logic) - includes categories and domains
|
||||
if (selectedTags.size > 0) {
|
||||
filtered = filtered.filter(note => {
|
||||
const allNoteTags = [
|
||||
...note.tags,
|
||||
...(note.domains || []).map(d => '@' + d),
|
||||
note.category ? '📁' + note.category : null
|
||||
].filter(Boolean);
|
||||
return [...selectedTags].every(tag => allNoteTags.includes(tag));
|
||||
// Filter by category
|
||||
if (selectedFilters.category) {
|
||||
filtered = filtered.filter(n => n.category === selectedFilters.category);
|
||||
}
|
||||
|
||||
// Filter by project
|
||||
if (selectedFilters.project) {
|
||||
filtered = filtered.filter(n => n.project === selectedFilters.project);
|
||||
}
|
||||
|
||||
// Filter by domain (@work, @health, etc.)
|
||||
if (selectedFilters.domain) {
|
||||
filtered = filtered.filter(n => n.domains && n.domains.includes(selectedFilters.domain));
|
||||
}
|
||||
|
||||
// Filter by type (@meditatie, @exercitiu, etc.)
|
||||
if (selectedFilters.type) {
|
||||
filtered = filtered.filter(n => n.types && n.types.includes(selectedFilters.type));
|
||||
}
|
||||
|
||||
// Filter by tags (AND)
|
||||
if (selectedFilters.tags.size > 0) {
|
||||
filtered = filtered.filter(n => {
|
||||
const noteTags = n.tags || [];
|
||||
return [...selectedFilters.tags].every(t => noteTags.includes(t));
|
||||
});
|
||||
}
|
||||
|
||||
// Filter by search query
|
||||
// Filter by search
|
||||
if (query) {
|
||||
filtered = filtered.filter(note => {
|
||||
const titleMatch = note.title.toLowerCase().includes(query);
|
||||
const tagsMatch = note.tags.some(t => t.toLowerCase().includes(query));
|
||||
const contentMatch = (notesCache[note.file] || '').toLowerCase().includes(query);
|
||||
filtered = filtered.filter(n => {
|
||||
const titleMatch = n.title.toLowerCase().includes(query);
|
||||
const tagsMatch = (n.tags || []).some(t => t.toLowerCase().includes(query));
|
||||
const contentMatch = (notesCache[n.file] || '').toLowerCase().includes(query);
|
||||
return titleMatch || tagsMatch || contentMatch;
|
||||
});
|
||||
}
|
||||
|
||||
renderNotesAccordion(filtered);
|
||||
|
||||
// Save filtered notes for tag pills
|
||||
lastFilteredNotes = filtered;
|
||||
|
||||
// Update tag pills to show which tags are in visible notes
|
||||
renderTagPills(filtered);
|
||||
renderFilterPills(filtered);
|
||||
renderNotesAccordion(filtered);
|
||||
}
|
||||
|
||||
// Group notes by date category
|
||||
@@ -847,12 +1106,26 @@
|
||||
}
|
||||
|
||||
function renderNoteCard(note) {
|
||||
// Domains (portocaliu), Types (mov), Tags colapsate cu expand
|
||||
const tags = note.tags || [];
|
||||
const noteId = note.file.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
const tagsHtml = tags.length > 0
|
||||
? `<span class="note-tags-toggle" onclick="event.stopPropagation(); toggleNoteTags('${noteId}')">
|
||||
<span class="note-tags-count">${tags.length} tags</span>
|
||||
<span class="note-tags-full" id="tags_${noteId}" style="display:none">${tags.join(' · ')}</span>
|
||||
</span>`
|
||||
: '';
|
||||
|
||||
const allTags = [
|
||||
...(note.domains || []).map(d => `<span class="note-tag domain">${d}</span>`),
|
||||
...(note.types || []).map(t => `<span class="note-tag type">${t}</span>`),
|
||||
tagsHtml
|
||||
].filter(Boolean).join('');
|
||||
|
||||
return `
|
||||
<div class="note-card" onclick="openNote('${note.file}')">
|
||||
<div class="note-title">${note.title}</div>
|
||||
<div class="note-tags">
|
||||
${note.tags.map(t => `<span class="tag">${t}</span>`).join('')}
|
||||
</div>
|
||||
<div class="note-tags">${allTags}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -878,7 +1151,7 @@
|
||||
try {
|
||||
let content = notesCache[file];
|
||||
if (!content) {
|
||||
const response = await fetch(notesBasePath + file);
|
||||
const response = await fetch(file);
|
||||
content = await response.text();
|
||||
notesCache[file] = content;
|
||||
}
|
||||
@@ -897,7 +1170,7 @@
|
||||
async function preloadNotes() {
|
||||
for (const note of notesIndex) {
|
||||
try {
|
||||
const response = await fetch(notesBasePath + note.file);
|
||||
const response = await fetch(note.file);
|
||||
notesCache[note.file] = await response.text();
|
||||
} catch (e) {
|
||||
notesCache[note.file] = '';
|
||||
@@ -929,8 +1202,9 @@
|
||||
// Init - load index first, then render
|
||||
async function init() {
|
||||
await loadNotesIndex();
|
||||
renderTagPills();
|
||||
renderNotesAccordion();
|
||||
renderFilterPills();
|
||||
renderNotesAccordion(notesIndex);
|
||||
lucide.createIcons();
|
||||
preloadNotes();
|
||||
checkHash();
|
||||
}
|
||||
1
dashboard/youtube-notes
Symbolic link
1
dashboard/youtube-notes
Symbolic link
@@ -0,0 +1 @@
|
||||
../kb/youtube
|
||||
@@ -1,118 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "ancorare-emotii",
|
||||
"title": "Exercițiu de ancorare a emoțiilor",
|
||||
"type": "exercitiu",
|
||||
"tags": [
|
||||
"NLP",
|
||||
"ancorare",
|
||||
"emotii",
|
||||
"corp"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Să simți o emoție pozitivă pe care ți-o dorești și apoi să faci exercițiul cu ancorarea emoției. Când vrei să simți liniște, satisfacție, bucurie, energie - să revină în corp.\n\nPași:\n1. Gândește-te la un moment în care ai simțit emoția dorită\n2. Simte-o în corp\n3. Ancoreaz-o (gest, cuvânt, imagine)\n4. Testează ancora"
|
||||
},
|
||||
{
|
||||
"id": "meditatie-demnitate",
|
||||
"title": "Meditația cu demnitatea",
|
||||
"type": "meditatie",
|
||||
"tags": [
|
||||
"meditatie",
|
||||
"demnitate",
|
||||
"sine"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Meditație despre demnitate personală.\n\n(De dezvoltat - Marius să adauge textul complet)"
|
||||
},
|
||||
{
|
||||
"id": "meditatie-eu-sunt-mai-mare",
|
||||
"title": "Eu sunt mai mare decât gândurile și emoțiile mele",
|
||||
"type": "meditatie",
|
||||
"tags": [
|
||||
"meditatie",
|
||||
"ganduri",
|
||||
"emotii",
|
||||
"distantare"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Meditație: Eu sunt mai mare decât gândurile mele, mai mare decât emoțiile mele.\n\nIdee: Creezi distanță între tine și gânduri/emoții. Tu ești observatorul, nu gândul."
|
||||
},
|
||||
{
|
||||
"id": "meditatie-mindfulness",
|
||||
"title": "Moment de mindfulness",
|
||||
"type": "meditatie",
|
||||
"tags": [
|
||||
"meditatie",
|
||||
"mindfulness",
|
||||
"prezent"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Un moment de mindfulness - prezență în aici și acum.\n\n(De dezvoltat)"
|
||||
},
|
||||
{
|
||||
"id": "intrebare-bucurie-azi",
|
||||
"title": "Ce ai făcut azi care ți-a adus bucurie?",
|
||||
"type": "intrebare",
|
||||
"tags": [
|
||||
"introspectie",
|
||||
"bucurie",
|
||||
"energie",
|
||||
"recunostinta"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Întrebări în secvență:\n1. Ce îți dorești să simți? (bucurie, energie, entuziasm, motivație)\n2. Când ai mai simțit asta?\n3. Ce ai făcut ASTĂZI care să îți aducă acea emoție?"
|
||||
},
|
||||
{
|
||||
"id": "intrebare-copil-interior",
|
||||
"title": "Ce îți aducea bucurie când erai mic?",
|
||||
"type": "intrebare",
|
||||
"tags": [
|
||||
"introspectie",
|
||||
"copilarie",
|
||||
"pasiune",
|
||||
"bucurie"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Ce îți doreai să faci când erai mic? Ce îți aducea bucurie, entuziasm, satisfacție?\n\nCare era emoția? Ce poți să faci ACUM care să îți dea aceeași emoție?\n\nAdu-ți aminte de copilul care erai (poate mai ești și acum). Ce dorește să facă? Ce simțea? Când ai simțit acele emoții?"
|
||||
},
|
||||
{
|
||||
"id": "reflectie-barbati-energie",
|
||||
"title": "Credințe despre bărbați și cerut ajutor",
|
||||
"type": "reflectie",
|
||||
"tags": [
|
||||
"credinte",
|
||||
"masculin",
|
||||
"ajutor",
|
||||
"energie",
|
||||
"vulnerabilitate"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Credințe limitatoare:\n- Bărbații sunt puternici\n- Bărbații trebuie să facă totul singuri\n- Bărbații nu trebuie să ceară ajutor\n\nȘi pentru femei - când trebuie să facă totul singure, nu cer ajutor, se încarcă, trag, împing dintr-o energie masculină.\n\nE OK să faci, dar te oprește de la resursele de energie și creativitate.\n\nÎntrebare: Unde tragi singur când ai putea cere ajutor?"
|
||||
},
|
||||
{
|
||||
"id": "reflectie-oglinda",
|
||||
"title": "Tot ce văd la tine am și eu în mine",
|
||||
"type": "reflectie",
|
||||
"tags": [
|
||||
"oglinda",
|
||||
"proiectie",
|
||||
"emotii",
|
||||
"autocunoastere"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Tot ce văd la tine am și eu în mine:\n- Sentimentul de vină, de rușine\n- Nu în aceleași situații\n- Furie, frustrare, jenă\n- Dar și bucurie\n\nCeilalți sunt oglinzi pentru noi."
|
||||
},
|
||||
{
|
||||
"id": "beneficiu-grup-siguranta",
|
||||
"title": "Beneficiul grupului - siguranță",
|
||||
"type": "reflectie",
|
||||
"tags": [
|
||||
"grup",
|
||||
"siguranta",
|
||||
"energie",
|
||||
"vulnerabilitate"
|
||||
],
|
||||
"used": null,
|
||||
"content": "Câtă energie consum să arăt ce trebuie?\n\nÎn grup nu mai este nevoie de energie să mențin scutul sus, să țin sub apă balonul.\n\nÎntr-un grup de sprijin, mă simt în siguranță, nu sunt judecat."
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
../notes
|
||||
@@ -1 +0,0 @@
|
||||
../notes/youtube
|
||||
62
kb/backlog.md
Normal file
62
kb/backlog.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Backlog - Propuneri pentru viitor
|
||||
|
||||
**Tags:** @work @health @growth @sprijin @scout #backlog #propuneri
|
||||
|
||||
Propuneri păstrate pentru viitor. Format: `[ ]` de făcut, `[x]` făcut, `[—]` renunțat.
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Urgent + Important
|
||||
|
||||
*Gol momentan - se adaugă din insights când zici "azi prioritar"*
|
||||
|
||||
---
|
||||
|
||||
## 📌 Important
|
||||
|
||||
### @work
|
||||
- [ ] Verificare securitate Clawdbot (port, trustedProxies)
|
||||
|
||||
### @growth
|
||||
- [ ] Exercițiu Priming în morning-coaching (3 recunoștințe)
|
||||
|
||||
### @sprijin
|
||||
- [ ] Fișă "blocare vs deblocare" cu exercițiu fiziologie
|
||||
|
||||
---
|
||||
|
||||
## 💡 Nice-to-have
|
||||
|
||||
### @work
|
||||
- [ ] Template spec-driven development în kb/projects/
|
||||
- [ ] Job proactive coding noaptea (ora 23)
|
||||
- [ ] Spellbook - prompt templates cu variabile
|
||||
|
||||
### @health
|
||||
- [ ] Checklist post negru (tranziție, clisme, apă)
|
||||
|
||||
### @growth
|
||||
- [ ] Dezvoltă personalitățile agenților (stil, nu doar funcțional)
|
||||
|
||||
### @sprijin
|
||||
- [ ] Întrebare "Ce moment greu s-a dovedit cadou?"
|
||||
- [ ] Exercițiu "Pentru cine altcineva faci asta?"
|
||||
|
||||
### @scout
|
||||
- [ ] Activitate hero's journey pentru cercetași
|
||||
|
||||
---
|
||||
|
||||
## ✅ Făcut
|
||||
|
||||
*Se mută aici când completez*
|
||||
|
||||
---
|
||||
|
||||
## ❌ Renunțat
|
||||
|
||||
*Se mută aici când nu mai e relevant*
|
||||
|
||||
---
|
||||
|
||||
*Ultima actualizare: 2026-01-31*
|
||||
10
kb/coaching/.rules.json
Normal file
10
kb/coaching/.rules.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"defaultDomains": ["health"],
|
||||
"defaultTypes": ["coaching"],
|
||||
"defaultTags": [],
|
||||
"inferTypeFromFilename": true,
|
||||
"filenameTypeMap": {
|
||||
"dimineata": "coaching",
|
||||
"seara": "reflectie"
|
||||
}
|
||||
}
|
||||
573
kb/index.json
Normal file
573
kb/index.json
Normal file
@@ -0,0 +1,573 @@
|
||||
{
|
||||
"notes": [
|
||||
{
|
||||
"file": "notes-data/insights/2026-01-31.md",
|
||||
"title": "Insights 2026-01-31",
|
||||
"date": "2026-01-31",
|
||||
"tags": [
|
||||
"insights",
|
||||
"propuneri"
|
||||
],
|
||||
"domains": [
|
||||
"growth",
|
||||
"health",
|
||||
"work",
|
||||
"sprijin",
|
||||
"scout"
|
||||
],
|
||||
"types": [],
|
||||
"category": "insights",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "*Scanare completă: 9 note YouTube | 2026-01-31*"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md",
|
||||
"title": "The Secret to an Extraordinary Life - Tony & Sage Robbins",
|
||||
"date": "2026-01-31",
|
||||
"tags": [],
|
||||
"domains": [
|
||||
"growth"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "Secretul unei vieți extraordinare nu e banii, poziția sau puterea - **e emoția**. Tony Robbins explică cum starea fizică, focusul și limbajul determină cum ne simțim. Pentru a te debloca: schimbă-ți c..."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/vending-master/README.md",
|
||||
"title": "Proiect: Vending Master - Integrare Website → ROA",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"vending-master",
|
||||
"integrare"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "projects",
|
||||
"project": "vending-master",
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "[conversations/2026-01-30-conversatie-completa.md](https://moltbot.tailf7372d.ts.net/echo/files.html#conversations/2026-01-30-conversatie-completa.md)"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/README.md",
|
||||
"title": "Grup de Sprijin - Lideri Cercetași",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": ""
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/fise/fisa-2026-02-05-ancorare-oglinda.md",
|
||||
"title": "Fișă Întâlnire Grup Sprijin",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"fisa"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "fise",
|
||||
"video": "",
|
||||
"tldr": ""
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/meditatie-mindfulness.md",
|
||||
"title": "Moment de mindfulness",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"mindfulness",
|
||||
"prezent",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"meditatie"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "(De dezvoltat)"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/meditatie-eu-sunt-mai-mare.md",
|
||||
"title": "Eu sunt mai mare decât gândurile și emoțiile mele",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"ganduri",
|
||||
"emotii",
|
||||
"distantare",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"meditatie"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "Idee: Creezi distanță între tine și gânduri/emoții. Tu ești observatorul, nu gândul."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/meditatie-demnitate.md",
|
||||
"title": "Meditația cu demnitatea",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"demnitate",
|
||||
"sine",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"meditatie"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "(De dezvoltat - Marius să adauge textul complet)"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/ancorare-emotii.md",
|
||||
"title": "Exercițiu de ancorare a emoțiilor",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"NLP",
|
||||
"ancorare",
|
||||
"emotii",
|
||||
"corp",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"exercitiu"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "4. Testează ancora"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/intrebare-copil-interior.md",
|
||||
"title": "Ce îți aducea bucurie când erai mic?",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"introspectie",
|
||||
"copilarie",
|
||||
"pasiune",
|
||||
"bucurie",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"intrebare"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "Adu-ți aminte de copilul care erai (poate mai ești și acum). Ce dorește să facă? Ce simțea? Când ai simțit acele emoții?"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/reflectie-oglinda.md",
|
||||
"title": "Tot ce văd la tine am și eu în mine",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"oglinda",
|
||||
"proiectie",
|
||||
"emotii",
|
||||
"autocunoastere",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"reflectie"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "Ceilalți sunt oglinzi pentru noi."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/intrebare-bucurie-azi.md",
|
||||
"title": "Ce ai făcut azi care ți-a adus bucurie?",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"introspectie",
|
||||
"bucurie",
|
||||
"energie",
|
||||
"recunostinta",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"intrebare"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "3. Ce ai făcut ASTĂZI care să îți aducă acea emoție?"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/beneficiu-grup-siguranta.md",
|
||||
"title": "Beneficiul grupului - siguranță",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"grup",
|
||||
"siguranta",
|
||||
"energie",
|
||||
"vulnerabilitate",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"reflectie"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "Într-un grup de sprijin, mă simt în siguranță, nu sunt judecat."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/projects/grup-sprijin/biblioteca/reflectie-barbati-energie.md",
|
||||
"title": "Credințe despre bărbați și cerut ajutor",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"credinte",
|
||||
"masculin",
|
||||
"ajutor",
|
||||
"energie",
|
||||
"vulnerabilitate",
|
||||
"grup-sprijin"
|
||||
],
|
||||
"domains": [
|
||||
"sprijin"
|
||||
],
|
||||
"types": [
|
||||
"reflectie"
|
||||
],
|
||||
"category": "projects",
|
||||
"project": "grup-sprijin",
|
||||
"subdir": "biblioteca",
|
||||
"video": "",
|
||||
"tldr": "Întrebare: Unde tragi singur când ai putea cere ajutor?"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/retete/2026-01-30_ciorba-burta-falsa-cu-pui.md",
|
||||
"title": "Ciorbă de Burtă Falsă cu Pui și Ciuperci Pleurotus",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"ciorba",
|
||||
"reteta",
|
||||
"pleurotus",
|
||||
"pui"
|
||||
],
|
||||
"domains": [
|
||||
"health"
|
||||
],
|
||||
"types": [],
|
||||
"category": "retete",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "- Se poate face și de post: fără carne, cu lapte vegetal în loc de smântână"
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-30_claude-code-do-work-pattern.md",
|
||||
"title": "The Most Powerful Claude Code Pattern I've Found",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"claude-code",
|
||||
"skills",
|
||||
"workflow",
|
||||
"automation",
|
||||
"do-work"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://youtu.be/I9-tdhxiH7w",
|
||||
"tldr": "Un pattern puternic pentru Claude Code: **Do Work** - o coadă de task-uri pe care Claude le procesează automat, unul câte unul, în sub-agenți cu context curat. Ideea cheie: **construiește tool-uri pen..."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-30_clawdbot-5-use-cases.md",
|
||||
"title": "5 Insane ClawdBot Use Cases You Need To Do Immediately",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"clawdbot",
|
||||
"automation",
|
||||
"productivity",
|
||||
"ai-assistant"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://www.youtube.com/watch?v=b-l9sGh1-UY",
|
||||
"tldr": "5 use case-uri pentru ClawdBot care îl transformă dintr-un simplu chatbot într-un asistent proactiv care lucrează pentru tine chiar și când dormi."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-30_clawdbot-personal-os-kitze.md",
|
||||
"title": "How I Use Clawdbot to Run My Business and Life 24/7",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"clawdbot",
|
||||
"productivity",
|
||||
"personas",
|
||||
"automation"
|
||||
],
|
||||
"domains": [
|
||||
"work",
|
||||
"growth"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://youtu.be/YRhGtHfs1Lw",
|
||||
"tldr": "Kitze folosește **UN SINGUR gateway Clawdbot** cu **MULTIPLE PERSONAS** pe Telegram/Discord. Fiecare personă are:\n- Personalitate diferită (avatar, stil de vorbit)\n- Skills diferite (acces la tool-uri..."
|
||||
},
|
||||
{
|
||||
"file": "memory/2026-01-30.md",
|
||||
"title": "2026-01-30",
|
||||
"date": "2026-01-30",
|
||||
"tags": [],
|
||||
"domains": [],
|
||||
"types": [
|
||||
"memory"
|
||||
],
|
||||
"category": "memory",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "- **Proactivitate activată**: Marius vrea să fiu proactiv - să propun automatizări, tools, să conectez punctele din discuții. Budget Claude Max $100/lună."
|
||||
},
|
||||
{
|
||||
"file": "conversations/2026-01-30-conversatie-completa.md",
|
||||
"title": "2026-01-30 - Conversație completă dimineață",
|
||||
"date": "2026-01-30",
|
||||
"tags": [],
|
||||
"domains": [],
|
||||
"types": [
|
||||
"conversation"
|
||||
],
|
||||
"category": "conversations",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "6. **Nevoie:** Accountability - check-in-uri regulate ca să nu amâne."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-29_cloudflare-tunnel-localhost-public.md",
|
||||
"title": "Cloudflare Tunnel: Make Localhost Public Without Port Forwarding",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"cloudflare",
|
||||
"tunnel",
|
||||
"localhost",
|
||||
"networking",
|
||||
"devops"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://youtu.be/etluT8UC-nw",
|
||||
"tldr": "Cloudflare Tunnel permite expunerea unui server local (localhost) pe internet printr-un domeniu public, fără port forwarding, fără configurare router, fără expunerea IP-ului public. App-ul rămâne pe m..."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-29_gsd-framework-claude-code.md",
|
||||
"title": "Forget Ralph Loops: The New GSD Framework for Claude Code",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"claude-code",
|
||||
"gsd",
|
||||
"framework",
|
||||
"sub-agents",
|
||||
"automation"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://www.youtube.com/watch?v=l94A53kIUB0",
|
||||
"tldr": "GSD (Get Shit Done) este un framework open-source pentru Claude Code care orchestrează sub-agenți pentru a completa proiecte urmând spec-driven development. Rezolvă problema \"context bloat\" prin rular..."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-29_clawdbot-security-vulnerabilities.md",
|
||||
"title": "It Got Worse (Clawdbot) - Security Vulnerabilities",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"clawdbot",
|
||||
"security",
|
||||
"vulnerabilities",
|
||||
"hacking"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://youtu.be/rPAKq2oQVBs",
|
||||
"tldr": "Video critic despre vulnerabilitățile de securitate ale Clawdbot - sute/mii de instanțe au fost compromise. Probleme principale: porturi default, parole lipsă, reverse proxy misconfigurat, skills mali..."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-29_greseli-post-apa.md",
|
||||
"title": "Greșeli frecvente în timpul postului doar cu apă",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"post",
|
||||
"water-fasting",
|
||||
"sănătate",
|
||||
"detox"
|
||||
],
|
||||
"domains": [
|
||||
"health"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://youtu.be/4QjkI0sf64M",
|
||||
"tldr": "Greșelile frecvente pe care le fac oamenii când țin post terapeutic cu apă și cum să le eviți. Puncte cheie: pregătire corectă, curățarea colonului, calitatea apei, și importanța scopului spiritual."
|
||||
},
|
||||
{
|
||||
"file": "notes-data/youtube/2026-01-29_remotion-skill-claude-code.md",
|
||||
"title": "How people are generating videos with Claude Code (Remotion Skill)",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"remotion",
|
||||
"claude-code",
|
||||
"video",
|
||||
"automation"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"types": [],
|
||||
"category": "youtube",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "https://youtu.be/7OR-L0AySn8",
|
||||
"tldr": "Remotion Skill permite generarea de videouri programatic cu Claude Code. Funcționează prin React components → video export. Demo live: Claude creează animații YouTube (like, subscribe, cursor) doar di..."
|
||||
},
|
||||
{
|
||||
"file": "memory/2026-01-29.md",
|
||||
"title": "2026-01-29 — Prima zi",
|
||||
"date": "2026-01-29",
|
||||
"tags": [],
|
||||
"domains": [],
|
||||
"types": [
|
||||
"memory"
|
||||
],
|
||||
"category": "memory",
|
||||
"project": null,
|
||||
"subdir": null,
|
||||
"video": "",
|
||||
"tldr": "- [ ] Explora ce alte automatizări ar ajuta"
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"total": 26,
|
||||
"by_domain": {
|
||||
"work": 9,
|
||||
"health": 3,
|
||||
"growth": 3,
|
||||
"sprijin": 12,
|
||||
"scout": 1
|
||||
},
|
||||
"by_category": {
|
||||
"coaching": 0,
|
||||
"insights": 1,
|
||||
"projects": 12,
|
||||
"retete": 1,
|
||||
"youtube": 9,
|
||||
"memory": 2,
|
||||
"conversations": 1
|
||||
}
|
||||
},
|
||||
"domains": [
|
||||
"work",
|
||||
"health",
|
||||
"growth",
|
||||
"sprijin",
|
||||
"scout"
|
||||
],
|
||||
"types": [
|
||||
"exercitiu",
|
||||
"meditatie",
|
||||
"reflectie",
|
||||
"intrebare",
|
||||
"fisa",
|
||||
"project",
|
||||
"memory",
|
||||
"conversation",
|
||||
"coaching"
|
||||
],
|
||||
"categories": [
|
||||
"coaching",
|
||||
"insights",
|
||||
"projects",
|
||||
"retete",
|
||||
"youtube",
|
||||
"memory",
|
||||
"conversations"
|
||||
]
|
||||
}
|
||||
59
kb/insights/2026-01-31.md
Normal file
59
kb/insights/2026-01-31.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Insights 2026-01-31
|
||||
|
||||
**Tags:** @growth @health @work @sprijin @scout #insights #propuneri
|
||||
|
||||
Idei extrase din note YouTube. Format: `[ ]` neprocesat, `[x]` făcut, `[→]` backlog, `[—]` skip.
|
||||
|
||||
---
|
||||
|
||||
## @work - Productivitate & Automatizări
|
||||
|
||||
- [ ] ⚡ Verificare securitate Clawdbot (port, trustedProxies) - [Security](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_clawdbot-security-vulnerabilities.md)
|
||||
- [ ] 📌 Template spec-driven development în kb/projects/ - [GSD](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_gsd-framework-claude-code.md)
|
||||
- [ ] 💡 Job proactive coding noaptea (ora 23) - [5 Use Cases](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-5-use-cases.md)
|
||||
- [ ] 💡 Spellbook - prompt templates cu variabile - [Kitze](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-personal-os-kitze.md)
|
||||
|
||||
---
|
||||
|
||||
## @health - Sănătate
|
||||
|
||||
- [ ] 📌 Reminder "ridică-te, mergi 2 min" în respirații - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||
- [ ] 💡 Checklist post negru (tranziție, clisme, apă) - [Post Apă](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_greseli-post-apa.md)
|
||||
|
||||
---
|
||||
|
||||
## @growth - Dezvoltare personală
|
||||
|
||||
- [ ] ⚡ Exercițiu Priming în morning-coaching (3 recunoștințe) - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||
- [ ] 📌 Pattern Interrupt tehnica (fiziologie, focus, limbaj) - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||
- [ ] 💡 Dezvoltă personalitățile agenților (stil, nu doar funcțional) - [Kitze](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-personal-os-kitze.md)
|
||||
|
||||
---
|
||||
|
||||
## @sprijin - Grup sprijin
|
||||
|
||||
- [ ] ⚡ Fișă "blocare vs deblocare" cu exercițiu fiziologie - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||
- [ ] 📌 Întrebare "Ce moment greu s-a dovedit cadou?" - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||
- [ ] 💡 Exercițiu "Pentru cine altcineva faci asta?" - [Post Apă](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_greseli-post-apa.md)
|
||||
|
||||
---
|
||||
|
||||
## @scout - Cercetași
|
||||
|
||||
- [ ] 💡 Activitate hero's journey pentru cercetași - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||
|
||||
---
|
||||
|
||||
## Legendă
|
||||
|
||||
- `[ ]` = neprocesat
|
||||
- `[x]` = făcut
|
||||
- `[→]` = mutat în backlog
|
||||
- `[—]` = renunțat/skip
|
||||
- ⚡ = urgent+important
|
||||
- 📌 = important
|
||||
- 💡 = nice-to-have
|
||||
|
||||
---
|
||||
|
||||
*Scanare: 9 note YouTube | 2026-01-31*
|
||||
5
kb/projects/.rules.json
Normal file
5
kb/projects/.rules.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"description": "Proiecte active",
|
||||
"filenameRule": "Pentru proiecte cu tipuri, pune tipul în numele fișierului",
|
||||
"inferTypeFromFilename": true
|
||||
}
|
||||
15
kb/projects/grup-sprijin/.rules.json
Normal file
15
kb/projects/grup-sprijin/.rules.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"description": "Proiect grup de sprijin - lideri cercetași",
|
||||
"defaultDomains": ["sprijin"],
|
||||
"defaultTags": ["grup-sprijin"],
|
||||
"filenameRule": "Pune tipul în numele fișierului: meditatie-xxx.md, exercitiu-xxx.md, etc.",
|
||||
"validTypes": ["meditatie", "exercitiu", "reflectie", "intrebare", "fisa"],
|
||||
"inferTypeFromFilename": true,
|
||||
"filenameTypeMap": {
|
||||
"meditatie": "meditatie",
|
||||
"exercitiu": "exercitiu",
|
||||
"intrebare": "intrebare",
|
||||
"reflectie": "reflectie",
|
||||
"fisa": "fisa"
|
||||
}
|
||||
}
|
||||
13
kb/projects/grup-sprijin/biblioteca/ancorare-emotii.md
Normal file
13
kb/projects/grup-sprijin/biblioteca/ancorare-emotii.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Exercițiu de ancorare a emoțiilor
|
||||
|
||||
**Tags:** @exercitiu @grup-sprijin #NLP #ancorare #emotii #corp
|
||||
|
||||
---
|
||||
|
||||
Să simți o emoție pozitivă pe care ți-o dorești și apoi să faci exercițiul cu ancorarea emoției. Când vrei să simți liniște, satisfacție, bucurie, energie - să revină în corp.
|
||||
|
||||
Pași:
|
||||
1. Gândește-te la un moment în care ai simțit emoția dorită
|
||||
2. Simte-o în corp
|
||||
3. Ancoreaz-o (gest, cuvânt, imagine)
|
||||
4. Testează ancora
|
||||
@@ -0,0 +1,11 @@
|
||||
# Beneficiul grupului - siguranță
|
||||
|
||||
**Tags:** @reflectie @grup-sprijin #grup #siguranta #energie #vulnerabilitate
|
||||
|
||||
---
|
||||
|
||||
Câtă energie consum să arăt ce trebuie?
|
||||
|
||||
În grup nu mai este nevoie de energie să mențin scutul sus, să țin sub apă balonul.
|
||||
|
||||
Într-un grup de sprijin, mă simt în siguranță, nu sunt judecat.
|
||||
10
kb/projects/grup-sprijin/biblioteca/intrebare-bucurie-azi.md
Normal file
10
kb/projects/grup-sprijin/biblioteca/intrebare-bucurie-azi.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Ce ai făcut azi care ți-a adus bucurie?
|
||||
|
||||
**Tags:** @intrebare @grup-sprijin #introspectie #bucurie #energie #recunostinta
|
||||
|
||||
---
|
||||
|
||||
Întrebări în secvență:
|
||||
1. Ce îți dorești să simți? (bucurie, energie, entuziasm, motivație)
|
||||
2. Când ai mai simțit asta?
|
||||
3. Ce ai făcut ASTĂZI care să îți aducă acea emoție?
|
||||
@@ -0,0 +1,11 @@
|
||||
# Ce îți aducea bucurie când erai mic?
|
||||
|
||||
**Tags:** @intrebare @grup-sprijin #introspectie #copilarie #pasiune #bucurie
|
||||
|
||||
---
|
||||
|
||||
Ce îți doreai să faci când erai mic? Ce îți aducea bucurie, entuziasm, satisfacție?
|
||||
|
||||
Care era emoția? Ce poți să faci ACUM care să îți dea aceeași emoție?
|
||||
|
||||
Adu-ți aminte de copilul care erai (poate mai ești și acum). Ce dorește să facă? Ce simțea? Când ai simțit acele emoții?
|
||||
@@ -0,0 +1,9 @@
|
||||
# Meditația cu demnitatea
|
||||
|
||||
**Tags:** @meditatie @grup-sprijin #meditatie #demnitate #sine
|
||||
|
||||
---
|
||||
|
||||
Meditație despre demnitate personală.
|
||||
|
||||
(De dezvoltat - Marius să adauge textul complet)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Eu sunt mai mare decât gândurile și emoțiile mele
|
||||
|
||||
**Tags:** @meditatie @grup-sprijin #meditatie #ganduri #emotii #distantare
|
||||
|
||||
---
|
||||
|
||||
Meditație: Eu sunt mai mare decât gândurile mele, mai mare decât emoțiile mele.
|
||||
|
||||
Idee: Creezi distanță între tine și gânduri/emoții. Tu ești observatorul, nu gândul.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Moment de mindfulness
|
||||
|
||||
**Tags:** @meditatie @grup-sprijin #meditatie #mindfulness #prezent
|
||||
|
||||
---
|
||||
|
||||
Un moment de mindfulness - prezență în aici și acum.
|
||||
|
||||
(De dezvoltat)
|
||||
@@ -0,0 +1,16 @@
|
||||
# Credințe despre bărbați și cerut ajutor
|
||||
|
||||
**Tags:** @reflectie @grup-sprijin #credinte #masculin #ajutor #energie #vulnerabilitate
|
||||
|
||||
---
|
||||
|
||||
Credințe limitatoare:
|
||||
- Bărbații sunt puternici
|
||||
- Bărbații trebuie să facă totul singuri
|
||||
- Bărbații nu trebuie să ceară ajutor
|
||||
|
||||
Și pentru femei - când trebuie să facă totul singure, nu cer ajutor, se încarcă, trag, împing dintr-o energie masculină.
|
||||
|
||||
E OK să faci, dar te oprește de la resursele de energie și creativitate.
|
||||
|
||||
Întrebare: Unde tragi singur când ai putea cere ajutor?
|
||||
13
kb/projects/grup-sprijin/biblioteca/reflectie-oglinda.md
Normal file
13
kb/projects/grup-sprijin/biblioteca/reflectie-oglinda.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Tot ce văd la tine am și eu în mine
|
||||
|
||||
**Tags:** @reflectie @grup-sprijin #oglinda #proiectie #emotii #autocunoastere
|
||||
|
||||
---
|
||||
|
||||
Tot ce văd la tine am și eu în mine:
|
||||
- Sentimentul de vină, de rușine
|
||||
- Nu în aceleași situații
|
||||
- Furie, frustrare, jenă
|
||||
- Dar și bucurie
|
||||
|
||||
Ceilalți sunt oglinzi pentru noi.
|
||||
5
kb/projects/vending-master/.rules.json
Normal file
5
kb/projects/vending-master/.rules.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"description": "Proiect integrare Vending Master cu ROA",
|
||||
"defaultDomains": ["work"],
|
||||
"defaultTags": ["vending-master", "integrare"]
|
||||
}
|
||||
5
kb/retete/.rules.json
Normal file
5
kb/retete/.rules.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"defaultDomains": ["health"],
|
||||
"defaultTypes": [],
|
||||
"defaultTags": []
|
||||
}
|
||||
5
kb/youtube/.rules.json
Normal file
5
kb/youtube/.rules.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"defaultDomains": [],
|
||||
"defaultTypes": [],
|
||||
"defaultTags": []
|
||||
}
|
||||
123
kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md
Normal file
123
kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# The Secret to an Extraordinary Life - Tony & Sage Robbins
|
||||
|
||||
**Sursă:** https://youtu.be/FkcmW0Bbpao
|
||||
**Cu:** Tony Robbins, Sage Robbins, Evaluna Montaner, Mau y Ricky
|
||||
**Data notei:** 2026-01-31
|
||||
**Tags:** @growth, @development, @mindset
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Secretul unei vieți extraordinare nu e banii, poziția sau puterea - **e emoția**. Tony Robbins explică cum starea fizică, focusul și limbajul determină cum ne simțim. Pentru a te debloca: schimbă-ți corpul, schimbă-ți focusul, schimbă-ți cuvintele.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Puncte Cheie
|
||||
|
||||
### 1. Găsirea scopului în momente dificile
|
||||
|
||||
> "Everything happens for a reason and a purpose greater than you know, but it's your job to find that reason."
|
||||
|
||||
- Viața se întâmplă **pentru noi**, nu **cu noi**
|
||||
- Hero's journey: viața pare OK → ceva se întâmplă → "call to adventure" (care pare moarte/sfârșit)
|
||||
- **Nu ai un singur scop** - ai multiple scopuri în diferite contexte
|
||||
- Caută scopul mai degrabă decât să încerci să "găsești" un scop unic
|
||||
|
||||
### 2. Cum să te deblocezi (Pattern Interrupt)
|
||||
|
||||
**Cele 3 lucruri care controlează cum te simți:**
|
||||
|
||||
1. **Fiziologia** (corpul)
|
||||
- Depresia are o postură: umeri căzuți, cap în jos, respirație superficială
|
||||
- Energia/entuziasmul: energie sus, corp deschis
|
||||
- **Schimbă corpul PRIMUL** - mișcă-te, respiră diferit
|
||||
|
||||
2. **Focusul** (ce și cum)
|
||||
- Nu doar CE vezi, ci CUM: imaginea e mare/mică? Aproape/departe? Color/alb-negru?
|
||||
- Anxietatea: imagine mare, aproape, se îndepărtează (pierdere)
|
||||
- Încrederea: imagine mare, se apropie de tine
|
||||
|
||||
3. **Limbajul**
|
||||
- Cuvintele atașate experienței DEVIN experiența
|
||||
- "Nutritious snacks" vs "Delicious snacks" - percepție diferită
|
||||
- Ce-ți spui când ești anxios vs când știi că vei reuși?
|
||||
|
||||
**Tehnici rapide de deblocare:**
|
||||
- Schimbare de temperatură (apă rece, saună) - resetează instant
|
||||
- Mișcare (plimbare) - nu poți rămâne blocat când te miști
|
||||
- "Can we begin again?" - reset în relații
|
||||
|
||||
### 3. Priming (ritual zilnic Tony Robbins)
|
||||
|
||||
Proces de 10 minute în fiecare dimineață:
|
||||
- Intră într-o stare pozitivă
|
||||
- Simte 3 lucruri pentru care ești recunoscător (nu doar le gândește, LE SIMTE)
|
||||
- Recunoștința elimină frica și furia
|
||||
|
||||
> "Work on gratitude and happiness is automatic."
|
||||
|
||||
### 4. Relații și lucrul împreună
|
||||
|
||||
- Businessul = misiune, nu doar muncă
|
||||
- Fă momentele să conteze (hot tub catch-up, priviri, atingeri)
|
||||
- Ritualuri de conectare (cina fără telefoane)
|
||||
- "I love you too much to go this place" - oprește escaladarea
|
||||
- Apreciază puzzle piece-ul fiecăruia - daruri diferite
|
||||
|
||||
### 5. Începe de unde ești
|
||||
|
||||
Povestea lui Tony:
|
||||
- La 11 ani, familie săracă, Thanksgiving fără mâncare
|
||||
- Un străin a adus mâncare → "Strangers care"
|
||||
- La 17 ani: a hrănit 2 familii
|
||||
- Apoi 4, 8, apoi 1 milion/an, 42 milioane în 37 ani
|
||||
- Provocare: 100 miliarde mese în 10 ani (62 miliarde în 3 ani deja)
|
||||
|
||||
> "If I won't give a dime out of a dollar, how will I ever give a million out of 10?"
|
||||
|
||||
### 6. Cele 5 arii de stăpânit în viață
|
||||
|
||||
1. **Corpul** - fără energie, nimic nu merge
|
||||
2. **Emoțiile** - determină calitatea vieții
|
||||
3. **Relațiile** - cea mai mare bucurie
|
||||
4. **Timpul** - nu-l gestiona, CREEAZĂ viața
|
||||
5. **Finanțele/Cariera + Spiritualitatea** - creștere și contribuție
|
||||
|
||||
---
|
||||
|
||||
## 💡 Acțiuni Practice
|
||||
|
||||
1. **Dimineața:** Priming 10 min - simte 3 lucruri pentru care ești recunoscător
|
||||
2. **Când ești blocat:** Mișcă-te fizic, schimbă temperatura, mergi la plimbare
|
||||
3. **În conflicte:** "Can we begin again?" sau "I love you too much for this"
|
||||
4. **Pentru copii:** Respiră CU ei, nu doar le spune să respire
|
||||
5. **Goals:** Stabilește obiective nerealiste cu timeline strâns (12-36 luni max)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Citate Memorabile
|
||||
|
||||
> "What's wrong is always available. So is what's right."
|
||||
|
||||
> "You don't experience life. You experience the life you focus on."
|
||||
|
||||
> "My worst day became my best day."
|
||||
|
||||
> "Scarcity is a lie."
|
||||
|
||||
> "We're not made to manage life. We're made to create."
|
||||
|
||||
> "Religion means to celebrate. You can't celebrate if you're not grateful."
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Resurse menționate
|
||||
|
||||
- **Time to Rise Summit** - timetorisesummit.com (gratis, 3 zile x 3 ore)
|
||||
- **Unleash the Power Within** - seminar live
|
||||
- **Life Force** (carte Tony Robbins despre sănătate)
|
||||
|
||||
---
|
||||
|
||||
*Video: ~1h conversation, familia Montaner cu Tony & Sage Robbins*
|
||||
30
memory/2026-01-31.md
Normal file
30
memory/2026-01-31.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Memory 2026-01-31
|
||||
|
||||
## Decizii
|
||||
|
||||
### Dashboard statistici
|
||||
- Adăugat secțiune statistici în index.html: task-uri azi/săptămâna/luna + insights
|
||||
- Se calculează automat din tasks.json (coloana done)
|
||||
|
||||
### Format insights cu tracking
|
||||
- Checkboxes: `[ ]` neprocesat, `[x]` făcut, `[→]` backlog, `[—]` skip
|
||||
- Prioritate: ⚡ urgent, 📌 important, 💡 nice-to-have
|
||||
- Fiecare propunere cu link la sursă
|
||||
|
||||
### Rapoarte actualizate (morning + evening)
|
||||
- **Pas 0 NOU:** Scanare automată kb/youtube/ ultimele 48h
|
||||
- Verifică dacă nota apare deja în insights
|
||||
- Dacă nu → extrage și adaugă în insights/YYYY-MM-DD.md
|
||||
- Verifică doar `[ ]` neprocesate din insights (AZI + IERI) + backlog
|
||||
- După răspunsul lui Marius: marchează `[x]`/`[→]`/`[—]`
|
||||
|
||||
### Backlog.md restructurat
|
||||
- Secțiuni: Urgent+Important, Important, Nice-to-have, Făcut, Renunțat
|
||||
- Același format checkbox ca insights
|
||||
|
||||
## De făcut
|
||||
- Verificare că rapoartele funcționează corect la 08:30 și 20:00
|
||||
|
||||
## Învățat
|
||||
- Marius preferă totul într-un singur loc (scanare în raport, nu job separat)
|
||||
- Insights trebuie să aibă status clar ca să nu se repete propunerile
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"lastChecks": {
|
||||
"agents_sync": "2026-01-30",
|
||||
"agents_sync": "2026-01-31",
|
||||
"email": null,
|
||||
"calendar": null,
|
||||
"git": 1738241820
|
||||
|
||||
186
notes/index.json
186
notes/index.json
@@ -1,186 +0,0 @@
|
||||
{
|
||||
"notes": [
|
||||
{
|
||||
"file": "youtube/2026-01-30_clawdbot-personal-os-kitze.md",
|
||||
"title": "How I Use Clawdbot to Run My Business and Life 24/7",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"clawdbot",
|
||||
"productivity",
|
||||
"personas",
|
||||
"automation"
|
||||
],
|
||||
"domains": [
|
||||
"work",
|
||||
"growth"
|
||||
],
|
||||
"video": "https://youtu.be/YRhGtHfs1Lw",
|
||||
"tldr": "Kitze folosește **UN SINGUR gateway Clawdbot** cu **MULTIPLE PERSONAS** pe Telegram/Discord. Fiecare personă are:\n- Personalitate diferită (avatar, stil de vorbit)\n- Skills diferite (acces la tool-uri...",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "retete/2026-01-30_ciorba-burta-falsa-cu-pui.md",
|
||||
"title": "Ciorbă de Burtă Falsă cu Pui și Ciuperci Pleurotus",
|
||||
"date": "2026-01-30",
|
||||
"tags": [
|
||||
"ciorba",
|
||||
"reteta",
|
||||
"pleurotus",
|
||||
"pui"
|
||||
],
|
||||
"domains": [
|
||||
"health"
|
||||
],
|
||||
"video": "",
|
||||
"tldr": "",
|
||||
"category": "retete"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2026-01-29_remotion-skill-claude-code.md",
|
||||
"title": "How people are generating videos with Claude Code (Remotion Skill)",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"remotion",
|
||||
"claude-code",
|
||||
"video",
|
||||
"automation"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"video": "https://youtu.be/7OR-L0AySn8",
|
||||
"tldr": "Remotion Skill permite generarea de videouri programatic cu Claude Code. Funcționează prin React components → video export. Demo live: Claude creează animații YouTube (like, subscribe, cursor) doar di...",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2026-01-29_gsd-framework-claude-code.md",
|
||||
"title": "Forget Ralph Loops: The New GSD Framework for Claude Code",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"claude-code",
|
||||
"gsd",
|
||||
"framework",
|
||||
"sub-agents",
|
||||
"automation"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"video": "https://www.youtube.com/watch?v=l94A53kIUB0",
|
||||
"tldr": "GSD (Get Shit Done) este un framework open-source pentru Claude Code care orchestrează sub-agenți pentru a completa proiecte urmând spec-driven development. Rezolvă problema \"context bloat\" prin rular...",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2026-01-29_greseli-post-apa.md",
|
||||
"title": "Greșeli frecvente în timpul postului doar cu apă",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"post",
|
||||
"water-fasting",
|
||||
"sănătate",
|
||||
"detox"
|
||||
],
|
||||
"domains": [
|
||||
"health"
|
||||
],
|
||||
"video": "https://youtu.be/4QjkI0sf64M",
|
||||
"tldr": "Greșelile frecvente pe care le fac oamenii când țin post terapeutic cu apă și cum să le eviți. Puncte cheie: pregătire corectă, curățarea colonului, calitatea apei, și importanța scopului spiritual.",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2026-01-29_cloudflare-tunnel-localhost-public.md",
|
||||
"title": "Cloudflare Tunnel: Make Localhost Public Without Port Forwarding",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"cloudflare",
|
||||
"tunnel",
|
||||
"localhost",
|
||||
"networking",
|
||||
"devops"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"video": "https://youtu.be/etluT8UC-nw",
|
||||
"tldr": "Cloudflare Tunnel permite expunerea unui server local (localhost) pe internet printr-un domeniu public, fără port forwarding, fără configurare router, fără expunerea IP-ului public. App-ul rămâne pe m...",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2026-01-29_clawdbot-security-vulnerabilities.md",
|
||||
"title": "It Got Worse (Clawdbot) - Security Vulnerabilities",
|
||||
"date": "2026-01-29",
|
||||
"tags": [
|
||||
"clawdbot",
|
||||
"security",
|
||||
"vulnerabilities",
|
||||
"hacking"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"video": "https://youtu.be/rPAKq2oQVBs",
|
||||
"tldr": "Video critic despre vulnerabilitățile de securitate ale Clawdbot - sute/mii de instanțe au fost compromise. Probleme principale: porturi default, parole lipsă, reverse proxy misconfigurat, skills mali...",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2025-01-30_clawdbot-5-use-cases.md",
|
||||
"title": "5 Insane ClawdBot Use Cases You Need To Do Immediately",
|
||||
"date": "2025-01-30",
|
||||
"tags": [
|
||||
"clawdbot",
|
||||
"automation",
|
||||
"productivity",
|
||||
"ai-assistant"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"video": "https://www.youtube.com/watch?v=b-l9sGh1-UY",
|
||||
"tldr": "5 use case-uri pentru ClawdBot care îl transformă dintr-un simplu chatbot într-un asistent proactiv care lucrează pentru tine chiar și când dormi.",
|
||||
"category": "youtube"
|
||||
},
|
||||
{
|
||||
"file": "youtube/2025-01-30_claude-code-do-work-pattern.md",
|
||||
"title": "The Most Powerful Claude Code Pattern I've Found",
|
||||
"date": "2025-01-30",
|
||||
"tags": [
|
||||
"claude-code",
|
||||
"skills",
|
||||
"workflow",
|
||||
"automation",
|
||||
"do-work"
|
||||
],
|
||||
"domains": [
|
||||
"work"
|
||||
],
|
||||
"video": "https://youtu.be/I9-tdhxiH7w",
|
||||
"tldr": "Un pattern puternic pentru Claude Code: **Do Work** - o coadă de task-uri pe care Claude le procesează automat, unul câte unul, în sub-agenți cu context curat. Ideea cheie: **construiește tool-uri pen...",
|
||||
"category": "youtube"
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"total": 9,
|
||||
"by_domain": {
|
||||
"work": 7,
|
||||
"health": 2,
|
||||
"growth": 1,
|
||||
"sprijin": 0,
|
||||
"scout": 0
|
||||
},
|
||||
"by_category": {
|
||||
"youtube": 8,
|
||||
"retete": 1
|
||||
}
|
||||
},
|
||||
"domains": [
|
||||
"work",
|
||||
"health",
|
||||
"growth",
|
||||
"sprijin",
|
||||
"scout"
|
||||
],
|
||||
"categories": [
|
||||
"youtube",
|
||||
"retete"
|
||||
]
|
||||
}
|
||||
@@ -1,25 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generează index.json pentru notes din fișierele .md
|
||||
Generează index.json pentru KB din fișierele .md
|
||||
Scanează: kb/, memory/, conversations/
|
||||
Extrage titlu, dată, tags, și domenii (@work, @health, etc.)
|
||||
Scanează TOATE subdirectoarele din notes/ (youtube, retete, etc.)
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
NOTES_ROOT = Path(__file__).parent.parent / "notes"
|
||||
INDEX_FILE = NOTES_ROOT / "index.json"
|
||||
|
||||
# Subdirectoare de scanat (adaugă altele aici)
|
||||
SCAN_DIRS = ['youtube', 'retete']
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
KB_ROOT = BASE_DIR / "kb"
|
||||
MEMORY_DIR = BASE_DIR / "memory"
|
||||
CONVERSATIONS_DIR = BASE_DIR / "conversations"
|
||||
INDEX_FILE = KB_ROOT / "index.json"
|
||||
|
||||
# Domenii de agenți
|
||||
VALID_DOMAINS = ['work', 'health', 'growth', 'sprijin', 'scout']
|
||||
|
||||
def extract_metadata(filepath):
|
||||
# Tipuri speciale (pentru grup-sprijin etc.)
|
||||
VALID_TYPES = ['exercitiu', 'meditatie', 'reflectie', 'intrebare', 'fisa', 'project', 'memory', 'conversation', 'coaching']
|
||||
|
||||
# Cache for rules files
|
||||
_rules_cache = {}
|
||||
|
||||
def load_rules(filepath):
|
||||
"""Încarcă regulile din .rules.json din directorul fișierului sau părinți"""
|
||||
dir_path = filepath.parent
|
||||
|
||||
# Check cache
|
||||
if str(dir_path) in _rules_cache:
|
||||
return _rules_cache[str(dir_path)]
|
||||
|
||||
# Look for .rules.json in current dir and parents (up to kb/)
|
||||
rules = {
|
||||
"defaultDomains": [],
|
||||
"defaultTypes": [],
|
||||
"defaultTags": [],
|
||||
"inferTypeFromFilename": False,
|
||||
"filenameTypeMap": {}
|
||||
}
|
||||
|
||||
# Collect rules from all levels (child rules override parent)
|
||||
rules_chain = []
|
||||
current = dir_path
|
||||
while current >= KB_ROOT:
|
||||
rules_file = current / ".rules.json"
|
||||
if rules_file.exists():
|
||||
try:
|
||||
with open(rules_file, 'r', encoding='utf-8') as f:
|
||||
rules_chain.insert(0, json.load(f)) # Parent first
|
||||
except:
|
||||
pass
|
||||
current = current.parent
|
||||
|
||||
# Merge rules (child overrides parent)
|
||||
for r in rules_chain:
|
||||
for key in rules:
|
||||
if key in r:
|
||||
if isinstance(rules[key], list):
|
||||
# Extend lists (don't override)
|
||||
rules[key] = list(set(rules[key] + r[key]))
|
||||
else:
|
||||
rules[key] = r[key]
|
||||
|
||||
_rules_cache[str(dir_path)] = rules
|
||||
return rules
|
||||
|
||||
def extract_metadata(filepath, category, subcategory=None):
|
||||
"""Extrage metadata din fișierul markdown"""
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
@@ -31,6 +81,7 @@ def extract_metadata(filepath):
|
||||
# Extrage tags (linia cu **Tags:** sau tags:)
|
||||
tags = []
|
||||
domains = []
|
||||
types = []
|
||||
tags_match = re.search(r'\*\*Tags?:\*\*\s*(.+)$|^Tags?:\s*(.+)$', content, re.MULTILINE | re.IGNORECASE)
|
||||
if tags_match:
|
||||
tags_str = tags_match.group(1) or tags_match.group(2)
|
||||
@@ -38,96 +89,199 @@ def extract_metadata(filepath):
|
||||
# Extrage domenii (@work, @health, etc.)
|
||||
domain_matches = re.findall(r'@(\w+)', tags_str)
|
||||
domains = [d for d in domain_matches if d in VALID_DOMAINS]
|
||||
types = [d for d in domain_matches if d in VALID_TYPES]
|
||||
|
||||
# Extrage tags normale (#tag) - exclude domeniile
|
||||
# Extrage tags normale (#tag)
|
||||
all_tags = re.findall(r'#([\w-]+)', tags_str)
|
||||
tags = [t for t in all_tags if t not in VALID_DOMAINS]
|
||||
tags = [t for t in all_tags if t not in VALID_DOMAINS and t not in VALID_TYPES]
|
||||
|
||||
# Extrage data din filename (YYYY-MM-DD_slug.md)
|
||||
date_match = re.match(r'(\d{4}-\d{2}-\d{2})_', filepath.name)
|
||||
# Aplică reguli din .rules.json (dacă există)
|
||||
rules = load_rules(filepath)
|
||||
|
||||
# Adaugă domains implicite (dacă nu sunt deja)
|
||||
for d in rules.get("defaultDomains", []):
|
||||
if d not in domains:
|
||||
domains.append(d)
|
||||
|
||||
# Adaugă types implicite
|
||||
for t in rules.get("defaultTypes", []):
|
||||
if t not in types:
|
||||
types.append(t)
|
||||
|
||||
# Adaugă tags implicite
|
||||
for t in rules.get("defaultTags", []):
|
||||
if t not in tags:
|
||||
tags.append(t)
|
||||
|
||||
# Inferă type din filename (dacă e configurat)
|
||||
if rules.get("inferTypeFromFilename"):
|
||||
filename_lower = filepath.stem.lower()
|
||||
for pattern, type_name in rules.get("filenameTypeMap", {}).items():
|
||||
if pattern in filename_lower and type_name not in types:
|
||||
types.append(type_name)
|
||||
break
|
||||
|
||||
# Extrage data din filename (YYYY-MM-DD_slug.md sau YYYY-MM-DD.md)
|
||||
date_match = re.match(r'(\d{4}-\d{2}-\d{2})', filepath.name)
|
||||
date = date_match.group(1) if date_match else ""
|
||||
|
||||
# Pentru fișiere fără dată în nume, folosește mtime
|
||||
if not date:
|
||||
mtime = filepath.stat().st_mtime
|
||||
date = datetime.fromtimestamp(mtime).strftime('%Y-%m-%d')
|
||||
|
||||
# Extrage video URL
|
||||
video_match = re.search(r'\*\*(?:Video|Link):\*\*\s*(https?://[^\s]+)', content)
|
||||
video_url = video_match.group(1) if video_match else ""
|
||||
|
||||
# Extrage TL;DR (primele 200 caractere)
|
||||
tldr_match = re.search(r'##\s*📋?\s*TL;DR\s*\n+(.+?)(?=\n##|\n---|\Z)', content, re.DOTALL)
|
||||
# Extrage TL;DR sau primele 200 caractere de conținut
|
||||
tldr = ""
|
||||
tldr_match = re.search(r'##\s*📋?\s*TL;DR\s*\n+(.+?)(?=\n##|\n---|\Z)', content, re.DOTALL)
|
||||
if tldr_match:
|
||||
tldr = tldr_match.group(1).strip()[:200]
|
||||
if len(tldr_match.group(1).strip()) > 200:
|
||||
tldr += "..."
|
||||
else:
|
||||
# Fallback: primul paragraf după titlu
|
||||
para_match = re.search(r'^#.+\n+(.+?)(?=\n\n|\n#|\Z)', content, re.DOTALL)
|
||||
if para_match:
|
||||
tldr = para_match.group(1).strip()[:200]
|
||||
if len(tldr) >= 200:
|
||||
tldr += "..."
|
||||
|
||||
# Construiește path-ul relativ pentru web (din dashboard/)
|
||||
# Dashboard are symlinks: notes-data -> ../kb, memory -> ../memory, conversations -> ../conversations
|
||||
rel_path = str(filepath.relative_to(BASE_DIR))
|
||||
# Transformă kb/... în notes-data/... pentru web
|
||||
if rel_path.startswith('kb/'):
|
||||
rel_path = 'notes-data/' + rel_path[3:]
|
||||
|
||||
return {
|
||||
"file": filepath.name,
|
||||
"file": rel_path,
|
||||
"title": title,
|
||||
"date": date,
|
||||
"tags": tags,
|
||||
"domains": domains,
|
||||
"types": types,
|
||||
"category": category,
|
||||
"project": subcategory, # primul nivel sub projects/ (grup-sprijin, vending-master)
|
||||
"subdir": None, # se setează în scan_directory pentru niveluri mai adânci
|
||||
"video": video_url,
|
||||
"tldr": tldr
|
||||
}
|
||||
|
||||
def generate_index():
|
||||
"""Generează index.json din toate fișierele .md din toate subdirectoarele"""
|
||||
def scan_directory(dir_path, category, subcategory=None, recursive=False):
|
||||
"""Scanează un director pentru fișiere .md"""
|
||||
notes = []
|
||||
|
||||
# Stats per domeniu
|
||||
domain_stats = {d: 0 for d in VALID_DOMAINS}
|
||||
# Stats per categorie
|
||||
category_stats = {}
|
||||
if not dir_path.exists():
|
||||
return notes
|
||||
|
||||
for subdir in SCAN_DIRS:
|
||||
notes_dir = NOTES_ROOT / subdir
|
||||
if not notes_dir.exists():
|
||||
print(f" (skipping {subdir}/ - not found)")
|
||||
continue
|
||||
# Defaults pentru categorii speciale (memory/, conversations/)
|
||||
category_defaults = {
|
||||
"memory": {"types": ["memory"], "domains": []},
|
||||
"conversations": {"types": ["conversation"], "domains": []}
|
||||
}
|
||||
|
||||
print(f"Scanning notes/{subdir}/...")
|
||||
category_stats[subdir] = 0
|
||||
|
||||
for filepath in sorted(notes_dir.glob("*.md"), reverse=True):
|
||||
if filepath.name == 'index.json':
|
||||
if recursive:
|
||||
# Scanează recursiv
|
||||
for filepath in dir_path.rglob("*.md"):
|
||||
if filepath.name.startswith('.') or 'template' in filepath.name.lower():
|
||||
continue
|
||||
try:
|
||||
metadata = extract_metadata(filepath)
|
||||
# Adaugă categoria (subdirectorul)
|
||||
metadata['category'] = subdir
|
||||
# Modifică path-ul fișierului să includă subdirectorul
|
||||
metadata['file'] = f"{subdir}/{filepath.name}"
|
||||
notes.append(metadata)
|
||||
# Determină project și subdir din path
|
||||
# Ex: projects/grup-sprijin/biblioteca/file.md
|
||||
# project = grup-sprijin, subdir = biblioteca
|
||||
rel_to_dir = filepath.relative_to(dir_path)
|
||||
parts = rel_to_dir.parts[:-1] # exclude filename
|
||||
|
||||
# Update stats
|
||||
category_stats[subdir] += 1
|
||||
for d in metadata['domains']:
|
||||
project = parts[0] if len(parts) > 0 else None
|
||||
subdir = parts[1] if len(parts) > 1 else None
|
||||
|
||||
metadata = extract_metadata(filepath, category, project)
|
||||
metadata['subdir'] = subdir
|
||||
notes.append(metadata)
|
||||
except Exception as e:
|
||||
print(f" ! Error processing {filepath}: {e}")
|
||||
else:
|
||||
# Scanează doar fișierele din director (nu subdirectoare)
|
||||
for filepath in sorted(dir_path.glob("*.md"), reverse=True):
|
||||
if filepath.name.startswith('.') or 'template' in filepath.name.lower():
|
||||
continue
|
||||
try:
|
||||
metadata = extract_metadata(filepath, category, subcategory)
|
||||
# Aplică defaults pentru categoria specială
|
||||
if category in category_defaults:
|
||||
defaults = category_defaults[category]
|
||||
for t in defaults.get("types", []):
|
||||
if t not in metadata["types"]:
|
||||
metadata["types"].append(t)
|
||||
for d in defaults.get("domains", []):
|
||||
if d not in metadata["domains"]:
|
||||
metadata["domains"].append(d)
|
||||
notes.append(metadata)
|
||||
except Exception as e:
|
||||
print(f" ! Error processing {filepath}: {e}")
|
||||
|
||||
return notes
|
||||
|
||||
def generate_index():
|
||||
"""Generează index.json din toate sursele"""
|
||||
all_notes = []
|
||||
|
||||
# Stats
|
||||
domain_stats = {d: 0 for d in VALID_DOMAINS}
|
||||
category_stats = {}
|
||||
|
||||
# Scanează TOATE subdirectoarele din kb/ recursiv
|
||||
print("Scanning kb/ (all subdirectories)...")
|
||||
for subdir in sorted(KB_ROOT.iterdir()):
|
||||
if subdir.is_dir() and not subdir.name.startswith('.'):
|
||||
category = subdir.name
|
||||
print(f" [{category}]")
|
||||
notes = scan_directory(subdir, category, recursive=True)
|
||||
all_notes.extend(notes)
|
||||
category_stats[category] = len(notes)
|
||||
for n in notes:
|
||||
sub = f"/{n['subcategory']}" if n.get('subcategory') else ""
|
||||
print(f" + {n['title'][:42]}...")
|
||||
for d in n['domains']:
|
||||
domain_stats[d] += 1
|
||||
|
||||
domains_str = ' '.join([f'@{d}' for d in metadata['domains']]) if metadata['domains'] else ''
|
||||
print(f" + {metadata['title'][:40]}... {domains_str}")
|
||||
except Exception as e:
|
||||
print(f" ! Error processing {filepath.name}: {e}")
|
||||
# 4. Scanează memory/
|
||||
print("Scanning memory/...")
|
||||
memory_notes = scan_directory(MEMORY_DIR, "memory")
|
||||
all_notes.extend(memory_notes)
|
||||
category_stats["memory"] = len(memory_notes)
|
||||
for n in memory_notes:
|
||||
print(f" + {n['title'][:45]}...")
|
||||
|
||||
# 5. Scanează conversations/
|
||||
print("Scanning conversations/...")
|
||||
conv_notes = scan_directory(CONVERSATIONS_DIR, "conversations")
|
||||
all_notes.extend(conv_notes)
|
||||
category_stats["conversations"] = len(conv_notes)
|
||||
for n in conv_notes:
|
||||
print(f" + {n['title'][:45]}...")
|
||||
|
||||
# Sortează după dată descrescător
|
||||
notes.sort(key=lambda x: x['date'], reverse=True)
|
||||
all_notes.sort(key=lambda x: x['date'], reverse=True)
|
||||
|
||||
# Adaugă metadata globală
|
||||
output = {
|
||||
"notes": notes,
|
||||
"notes": all_notes,
|
||||
"stats": {
|
||||
"total": len(notes),
|
||||
"total": len(all_notes),
|
||||
"by_domain": domain_stats,
|
||||
"by_category": category_stats
|
||||
},
|
||||
"domains": VALID_DOMAINS,
|
||||
"categories": SCAN_DIRS
|
||||
"types": VALID_TYPES,
|
||||
"categories": list(category_stats.keys())
|
||||
}
|
||||
|
||||
with open(INDEX_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(output, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n✅ Generated {INDEX_FILE} with {len(notes)} notes")
|
||||
print(f" Domains: {domain_stats}")
|
||||
print(f"\n✅ Generated {INDEX_FILE} with {len(all_notes)} notes")
|
||||
print(f" Categories: {category_stats}")
|
||||
return output
|
||||
|
||||
|
||||
Reference in New Issue
Block a user