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
|
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**
|
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)
|
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)
|
### 📦 Git Commits (~/clawd → gitea.romfast.ro/romfast/clawd)
|
||||||
- **NU face commit automat** - întreabă-l pe Marius când să dau commit
|
- **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)
|
### 📋 Task Tracking (OBLIGATORIU)
|
||||||
Când primesc o acțiune/cerere de la Marius:
|
Când primesc o acțiune/cerere de la Marius:
|
||||||
1. **React:** Reacționez cu 👍 la mesaj (WhatsApp/Discord)
|
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
|
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:
|
Când se execută orice job cron:
|
||||||
1. **Start:** Creează task în kanban (Progress) cu numele job-ului
|
1. **Start:** Creează task în kanban (Progress) cu numele job-ului
|
||||||
2. **Rulează:** Execută task-ul
|
2. **Rulează:** Execută task-ul
|
||||||
3. **Done:** Mută task-ul în Done cu rezultatul
|
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):**
|
**Things to check (rotate through these, 2-4 times per day):**
|
||||||
- **Emails** - Any urgent unread messages?
|
- **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
|
## Make It Yours
|
||||||
|
|
||||||
This is a starting point. Add your own conventions, style, and rules as you figure out what works.
|
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)
|
### 📦 Git status (seara)
|
||||||
- [ ] Fișiere uncommitted? Dacă da, întreabă dacă fac commit.
|
- [ ] 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
|
## 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.
|
**Pentru orice altă adresă:** Citesc și raportez, aștept aprobare.
|
||||||
|
|
||||||
### Kanban & Web
|
### Dashboard & Web
|
||||||
- **Task Board:** https://moltbot.tailf7372d.ts.net/echo/
|
- **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
|
- **Files:** https://moltbot.tailf7372d.ts.net/echo/files.html
|
||||||
- **API:** `kanban/api.py`
|
- **API:** `dashboard/api.py`
|
||||||
- **Update task:** `python3 kanban/update_task.py`
|
- **Update task:** `python3 dashboard/update_task.py`
|
||||||
|
|
||||||
**Reguli dashboard:**
|
**Reguli dashboard:**
|
||||||
- Tab Activity afișează task-uri din tasks.json, sortate descrescător după timestamp
|
- 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ă)
|
- Când creez/completez task-uri, să am timestamp complet (ISO format cu oră)
|
||||||
|
|
||||||
### Notes (toate tipurile)
|
### KB - Knowledge Base (toate tipurile de conținut)
|
||||||
- **Folder:** `notes/` (subdirectoare: `youtube/`, `retete/`, etc.)
|
- **Folder:** `kb/` (subdirectoare: `youtube/`, `retete/`, `projects/`)
|
||||||
- **Update index:** `python3 tools/update_notes_index.py`
|
- **Update index:** `python3 tools/update_notes_index.py`
|
||||||
- **Pagina web:** https://moltbot.tailf7372d.ts.net/echo/notes.html
|
- **Pagina web:** https://moltbot.tailf7372d.ts.net/echo/notes.html
|
||||||
- **Tags domeniu:** `@work`, `@health`, `@growth`, `@sprijin`, `@scout`
|
- **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ă:
|
**IMPORTANT:** Când salvez orice notă (rețete, youtube, proiecte, etc.), trebuie să:
|
||||||
1. Salvez în subdirectorul potrivit din `notes/`
|
1. Salvez în subdirectorul potrivit din `kb/`
|
||||||
2. Rulez `python3 tools/update_notes_index.py` pentru a actualiza indexul
|
2. Rulez `python3 tools/update_notes_index.py` pentru a actualiza indexul
|
||||||
3. Dau link-ul către pagina notes.html
|
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`
|
**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
|
- 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`
|
**TOOLS.md:** `agents/echo-sprijin/TOOLS.md`
|
||||||
|
|
||||||
- **Pagină dedicată:** https://moltbot.tailf7372d.ts.net/echo/grup-sprijin.html
|
- **Pagină dedicată:** https://moltbot.tailf7372d.ts.net/echo/notes.html (filtrează @grup-sprijin)
|
||||||
- **Fișe activități:** `kanban/grup-sprijin/`
|
- **Proiect:** `kb/projects/grup-sprijin/`
|
||||||
- **Template:** `kanban/grup-sprijin/template-fisa.md`
|
- **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ă:** Să repare decât să construiască de la zero
|
||||||
- **Preferă:** Mai multă muncă la preț bun de la clienți existenți
|
- **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
|
## Interese și domenii
|
||||||
|
|
||||||
- **Coaching & Comunicare:** NLP, Sleight of Mouth, comunicare nonviolentă
|
- **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
|
- **Name:** Marius
|
||||||
- **Timezone:** Europe/Bucharest (UTC+2/+3)
|
- **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
|
## Sănătate
|
||||||
|
|
||||||
### Condiții curente
|
### Condiții curente
|
||||||
@@ -22,11 +31,37 @@
|
|||||||
- **Post negru** - practica, efecte, planificare
|
- **Post negru** - practica, efecte, planificare
|
||||||
- Abordări holistice, legătura minte-corp
|
- Abordări holistice, legătura minte-corp
|
||||||
|
|
||||||
### Preferințe
|
## Filosofie & Spiritualitate
|
||||||
|
|
||||||
- Discuții deschise despre alternative, fără a înlocui medicina convențională
|
### Stoicism (vrea să aprofundeze)
|
||||||
- Tracking și pattern-uri mai mult decât sfaturi directe
|
- **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
|
- 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
|
BASE_DIR = Path(__file__).parent.parent
|
||||||
TOOLS_DIR = BASE_DIR / 'tools'
|
TOOLS_DIR = BASE_DIR / 'tools'
|
||||||
NOTES_DIR = BASE_DIR / 'notes' / 'youtube'
|
NOTES_DIR = BASE_DIR / 'kb' / 'youtube'
|
||||||
KANBAN_DIR = BASE_DIR / 'kanban'
|
KANBAN_DIR = BASE_DIR / 'dashboard'
|
||||||
|
|
||||||
class TaskBoardHandler(SimpleHTTPRequestHandler):
|
class TaskBoardHandler(SimpleHTTPRequestHandler):
|
||||||
|
|
||||||
@@ -26,9 +26,44 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
self.handle_youtube()
|
self.handle_youtube()
|
||||||
elif self.path == '/api/files':
|
elif self.path == '/api/files':
|
||||||
self.handle_files_post()
|
self.handle_files_post()
|
||||||
|
elif self.path == '/api/refresh-index':
|
||||||
|
self.handle_refresh_index()
|
||||||
else:
|
else:
|
||||||
self.send_error(404)
|
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):
|
def handle_files_post(self):
|
||||||
"""Save file content."""
|
"""Save file content."""
|
||||||
try:
|
try:
|
||||||
@@ -63,8 +98,10 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
if self.path == '/api/status':
|
if self.path == '/api/status':
|
||||||
self.send_json({'status': 'ok', 'time': datetime.now().isoformat()})
|
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()
|
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'):
|
elif self.path.startswith('/api/files'):
|
||||||
self.handle_files_get()
|
self.handle_files_get()
|
||||||
elif self.path.startswith('/api/'):
|
elif self.path.startswith('/api/'):
|
||||||
@@ -129,6 +166,55 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.send_json({'error': str(e)}, 500)
|
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):
|
def handle_files_get(self):
|
||||||
"""List files or get file content."""
|
"""List files or get file content."""
|
||||||
from urllib.parse import urlparse, parse_qs
|
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>
|
||||||
<a href="notes.html" class="nav-item">
|
<a href="notes.html" class="nav-item">
|
||||||
<i data-lucide="file-text"></i>
|
<i data-lucide="file-text"></i>
|
||||||
<span>Notes</span>
|
<span>KB</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="files.html" class="nav-item active">
|
<a href="files.html" class="nav-item active">
|
||||||
<i data-lucide="folder"></i>
|
<i data-lucide="folder"></i>
|
||||||
<span>Files</span>
|
<span>Files</span>
|
||||||
</a>
|
</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">
|
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
<i data-lucide="sun" id="themeIcon"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -583,9 +579,16 @@
|
|||||||
const isMarkdown = path.endsWith('.md');
|
const isMarkdown = path.endsWith('.md');
|
||||||
document.getElementById('previewBtn').style.display = isMarkdown ? 'flex' : 'none';
|
document.getElementById('previewBtn').style.display = isMarkdown ? 'flex' : 'none';
|
||||||
|
|
||||||
// Reset preview state
|
// Auto-activate preview for markdown files
|
||||||
document.getElementById('editorBody').classList.remove('preview-active');
|
if (isMarkdown) {
|
||||||
document.getElementById('previewBtn').classList.remove('active');
|
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) {
|
if (data.truncated) {
|
||||||
setStatus('Fișier trunchiat', 'error');
|
setStatus('Fișier trunchiat', 'error');
|
||||||
@@ -14,6 +14,61 @@
|
|||||||
padding: var(--space-5);
|
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 {
|
.page-header {
|
||||||
margin-bottom: var(--space-4);
|
margin-bottom: var(--space-4);
|
||||||
}
|
}
|
||||||
@@ -154,6 +209,33 @@
|
|||||||
color: #818cf8;
|
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 {
|
.status-section-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -794,16 +876,12 @@
|
|||||||
</a>
|
</a>
|
||||||
<a href="notes.html" class="nav-item">
|
<a href="notes.html" class="nav-item">
|
||||||
<i data-lucide="file-text"></i>
|
<i data-lucide="file-text"></i>
|
||||||
<span>Notes</span>
|
<span>KB</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="files.html" class="nav-item">
|
<a href="files.html" class="nav-item">
|
||||||
<i data-lucide="folder"></i>
|
<i data-lucide="folder"></i>
|
||||||
<span>Files</span>
|
<span>Files</span>
|
||||||
</a>
|
</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">
|
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
<i data-lucide="sun" id="themeIcon"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -894,6 +972,39 @@
|
|||||||
<!-- Populated by JS -->
|
<!-- Populated by JS -->
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1140,7 +1251,7 @@
|
|||||||
|
|
||||||
async function loadGitStatus() {
|
async function loadGitStatus() {
|
||||||
try {
|
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');
|
if (!response.ok) throw new Error('API error');
|
||||||
const git = await response.json();
|
const git = await response.json();
|
||||||
|
|
||||||
@@ -1270,6 +1381,42 @@
|
|||||||
lucide.createIcons();
|
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() {
|
function updateStatusSummary() {
|
||||||
const gitBadge = document.getElementById('gitBadge');
|
const gitBadge = document.getElementById('gitBadge');
|
||||||
const anafBadge = document.getElementById('anafBadge');
|
const anafBadge = document.getElementById('anafBadge');
|
||||||
@@ -1366,9 +1513,51 @@
|
|||||||
|
|
||||||
function refreshActivity() {
|
function refreshActivity() {
|
||||||
loadActivity();
|
loadActivity();
|
||||||
|
loadStats();
|
||||||
showToast('Activitate reîmprospătată');
|
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() {
|
function renderActivity() {
|
||||||
const body = document.getElementById('activityBody');
|
const body = document.getElementById('activityBody');
|
||||||
|
|
||||||
@@ -1644,6 +1833,7 @@
|
|||||||
loadStatus();
|
loadStatus();
|
||||||
loadIssues();
|
loadIssues();
|
||||||
loadActivity();
|
loadActivity();
|
||||||
|
loadStats();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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">
|
<link rel="stylesheet" href="common.css">
|
||||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||||
<script src="swipe-nav.js"></script>
|
<script src="swipe-nav.js"></script>
|
||||||
@@ -155,9 +155,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note-tags {
|
.note-tags {
|
||||||
display: flex;
|
display: inline;
|
||||||
flex-wrap: wrap;
|
line-height: 1.4;
|
||||||
gap: var(--space-1);
|
}
|
||||||
|
|
||||||
|
.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 */
|
/* Empty section */
|
||||||
@@ -287,74 +317,197 @@
|
|||||||
|
|
||||||
.markdown-body strong { color: var(--text-primary); }
|
.markdown-body strong { color: var(--text-primary); }
|
||||||
|
|
||||||
/* Tag filter pills */
|
/* Filter bar with pills */
|
||||||
.tag-filter {
|
.filter-bar {
|
||||||
margin-bottom: var(--space-5);
|
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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-2);
|
gap: var(--space-2);
|
||||||
cursor: pointer;
|
margin-left: auto;
|
||||||
user-select: none;
|
|
||||||
margin-bottom: var(--space-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.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;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-tags-toggle {
|
||||||
color: var(--text-muted);
|
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 {
|
.more-tags-toggle:hover {
|
||||||
transform: rotate(-90deg);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-pills-more {
|
@keyframes spin {
|
||||||
display: none;
|
from { transform: rotate(0deg); }
|
||||||
margin-top: var(--space-2);
|
to { transform: rotate(360deg); }
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-pills {
|
.tag-pills {
|
||||||
@@ -497,16 +650,12 @@
|
|||||||
</a>
|
</a>
|
||||||
<a href="notes.html" class="nav-item active">
|
<a href="notes.html" class="nav-item active">
|
||||||
<i data-lucide="file-text"></i>
|
<i data-lucide="file-text"></i>
|
||||||
<span>Notes</span>
|
<span>KB</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="files.html" class="nav-item">
|
<a href="files.html" class="nav-item">
|
||||||
<i data-lucide="folder"></i>
|
<i data-lucide="folder"></i>
|
||||||
<span>Files</span>
|
<span>Files</span>
|
||||||
</a>
|
</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">
|
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
|
||||||
<i data-lucide="sun" id="themeIcon"></i>
|
<i data-lucide="sun" id="themeIcon"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -515,15 +664,29 @@
|
|||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1 class="page-title">Notes</h1>
|
<h1 class="page-title">Knowledge Base</h1>
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<input type="text" class="input" id="searchInput" placeholder="Caută în notițe..." oninput="filterNotes()">
|
<input type="text" class="input" id="searchInput" placeholder="Caută în notițe..." oninput="filterNotes()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tag-filter">
|
<div class="filter-bar" id="filterBar">
|
||||||
<div class="tag-pills" id="mainPills"></div>
|
<div class="filter-group" id="categoryPills"></div>
|
||||||
<div class="tag-pills-more" id="tagPills"></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>
|
||||||
|
|
||||||
<div id="notesContainer">
|
<div id="notesContainer">
|
||||||
@@ -594,171 +757,267 @@
|
|||||||
notesIndex = [];
|
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)
|
// Get counts for each filter value
|
||||||
function getAllTags() {
|
function getFilterCounts(filteredNotes = null) {
|
||||||
const tagCounts = {};
|
const notes = filteredNotes || notesIndex;
|
||||||
notesIndex.forEach(note => {
|
const counts = {
|
||||||
// Category tags (📁youtube, 📁retete)
|
categories: {},
|
||||||
if (note.category) {
|
projects: {},
|
||||||
const catTag = '📁' + note.category;
|
domains: {},
|
||||||
tagCounts[catTag] = (tagCounts[catTag] || 0) + 1;
|
types: {},
|
||||||
}
|
tags: {}
|
||||||
// Domain tags (@work, @health, etc.)
|
};
|
||||||
if (note.domains) {
|
|
||||||
note.domains.forEach(domain => {
|
notes.forEach(note => {
|
||||||
const domainTag = '@' + domain;
|
if (note.category) counts.categories[note.category] = (counts.categories[note.category] || 0) + 1;
|
||||||
tagCounts[domainTag] = (tagCounts[domainTag] || 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);
|
||||||
// Regular tags
|
if (note.tags) note.tags.forEach(t => counts.tags[t] = (counts.tags[t] || 0) + 1);
|
||||||
note.tags.forEach(tag => {
|
|
||||||
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
// Sort: categories first (📁), then domains (@), then by count
|
|
||||||
return Object.entries(tagCounts)
|
return counts;
|
||||||
.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 }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render tag pills
|
// Render filter pills
|
||||||
function renderTagPills(visibleNotes = null) {
|
function renderFilterPills(filteredNotes = null) {
|
||||||
const mainContainer = document.getElementById('mainPills');
|
const counts = getFilterCounts();
|
||||||
const moreContainer = document.getElementById('tagPills');
|
const visibleCounts = filteredNotes ? getFilterCounts(filteredNotes) : counts;
|
||||||
const tags = getAllTags();
|
|
||||||
|
|
||||||
// Calculate which tags appear in visible notes
|
// Categories
|
||||||
const visibleTags = new Set();
|
const catHtml = Object.entries(counts.categories)
|
||||||
if (visibleNotes && visibleNotes.length > 0) {
|
.sort((a, b) => b[1] - a[1])
|
||||||
visibleNotes.forEach(note => {
|
.map(([cat, count]) => {
|
||||||
note.tags.forEach(t => visibleTags.add(t));
|
const isActive = selectedFilters.category === cat;
|
||||||
(note.domains || []).forEach(d => visibleTags.add('@' + d));
|
const isVisible = visibleCounts.categories[cat] > 0;
|
||||||
if (note.category) visibleTags.add('📁' + note.category);
|
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
|
// More tags toggle
|
||||||
const mainTags = tags.filter(({tag}) => tag.startsWith('📁') || tag.startsWith('@'));
|
const moreToggle = document.getElementById('moreTagsToggle');
|
||||||
const moreTags = tags.filter(({tag}) => !tag.startsWith('📁') && !tag.startsWith('@'));
|
if (tagEntries.length > 0) {
|
||||||
|
const hiddenCount = activeTagsCount > 0 ? tagEntries.length - activeTagsCount : tagEntries.length;
|
||||||
// Check if more section is expanded
|
if (showAllTags) {
|
||||||
const isExpanded = moreContainer.classList.contains('expanded');
|
moreToggle.textContent = `-${hiddenCount} tags`;
|
||||||
const activeMoreCount = [...selectedTags].filter(t => !t.startsWith('📁') && !t.startsWith('@')).length;
|
} else {
|
||||||
|
moreToggle.textContent = `+${hiddenCount} tags`;
|
||||||
// Render main pills (categories + domains)
|
}
|
||||||
let mainHtml = mainTags.map(({ tag, count }) => {
|
moreToggle.style.display = '';
|
||||||
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);
|
|
||||||
} else {
|
} 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();
|
filterNotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear all tag filters
|
// Toggle more tags
|
||||||
function clearTagFilters() {
|
|
||||||
selectedTags.clear();
|
|
||||||
renderTagPills();
|
|
||||||
filterNotes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle more tags section
|
|
||||||
function toggleMoreTags() {
|
function toggleMoreTags() {
|
||||||
const moreContainer = document.getElementById('tagPills');
|
showAllTags = !showAllTags;
|
||||||
moreContainer.classList.toggle('expanded');
|
renderFilterPills(lastFilteredNotes);
|
||||||
renderTagPills(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() {
|
function filterNotes() {
|
||||||
const query = document.getElementById('searchInput').value.toLowerCase().trim();
|
const query = document.getElementById('searchInput').value.toLowerCase().trim();
|
||||||
|
|
||||||
let filtered = notesIndex;
|
let filtered = notesIndex;
|
||||||
|
|
||||||
// Filter by selected tags (AND logic) - includes categories and domains
|
// Filter by category
|
||||||
if (selectedTags.size > 0) {
|
if (selectedFilters.category) {
|
||||||
filtered = filtered.filter(note => {
|
filtered = filtered.filter(n => n.category === selectedFilters.category);
|
||||||
const allNoteTags = [
|
}
|
||||||
...note.tags,
|
|
||||||
...(note.domains || []).map(d => '@' + d),
|
// Filter by project
|
||||||
note.category ? '📁' + note.category : null
|
if (selectedFilters.project) {
|
||||||
].filter(Boolean);
|
filtered = filtered.filter(n => n.project === selectedFilters.project);
|
||||||
return [...selectedTags].every(tag => allNoteTags.includes(tag));
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (query) {
|
||||||
filtered = filtered.filter(note => {
|
filtered = filtered.filter(n => {
|
||||||
const titleMatch = note.title.toLowerCase().includes(query);
|
const titleMatch = n.title.toLowerCase().includes(query);
|
||||||
const tagsMatch = note.tags.some(t => t.toLowerCase().includes(query));
|
const tagsMatch = (n.tags || []).some(t => t.toLowerCase().includes(query));
|
||||||
const contentMatch = (notesCache[note.file] || '').toLowerCase().includes(query);
|
const contentMatch = (notesCache[n.file] || '').toLowerCase().includes(query);
|
||||||
return titleMatch || tagsMatch || contentMatch;
|
return titleMatch || tagsMatch || contentMatch;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNotesAccordion(filtered);
|
|
||||||
|
|
||||||
// Save filtered notes for tag pills
|
|
||||||
lastFilteredNotes = filtered;
|
lastFilteredNotes = filtered;
|
||||||
|
renderFilterPills(filtered);
|
||||||
// Update tag pills to show which tags are in visible notes
|
renderNotesAccordion(filtered);
|
||||||
renderTagPills(filtered);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group notes by date category
|
// Group notes by date category
|
||||||
@@ -847,12 +1106,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderNoteCard(note) {
|
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 `
|
return `
|
||||||
<div class="note-card" onclick="openNote('${note.file}')">
|
<div class="note-card" onclick="openNote('${note.file}')">
|
||||||
<div class="note-title">${note.title}</div>
|
<div class="note-title">${note.title}</div>
|
||||||
<div class="note-tags">
|
<div class="note-tags">${allTags}</div>
|
||||||
${note.tags.map(t => `<span class="tag">${t}</span>`).join('')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -878,7 +1151,7 @@
|
|||||||
try {
|
try {
|
||||||
let content = notesCache[file];
|
let content = notesCache[file];
|
||||||
if (!content) {
|
if (!content) {
|
||||||
const response = await fetch(notesBasePath + file);
|
const response = await fetch(file);
|
||||||
content = await response.text();
|
content = await response.text();
|
||||||
notesCache[file] = content;
|
notesCache[file] = content;
|
||||||
}
|
}
|
||||||
@@ -897,7 +1170,7 @@
|
|||||||
async function preloadNotes() {
|
async function preloadNotes() {
|
||||||
for (const note of notesIndex) {
|
for (const note of notesIndex) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(notesBasePath + note.file);
|
const response = await fetch(note.file);
|
||||||
notesCache[note.file] = await response.text();
|
notesCache[note.file] = await response.text();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notesCache[note.file] = '';
|
notesCache[note.file] = '';
|
||||||
@@ -929,8 +1202,9 @@
|
|||||||
// Init - load index first, then render
|
// Init - load index first, then render
|
||||||
async function init() {
|
async function init() {
|
||||||
await loadNotesIndex();
|
await loadNotesIndex();
|
||||||
renderTagPills();
|
renderFilterPills();
|
||||||
renderNotesAccordion();
|
renderNotesAccordion(notesIndex);
|
||||||
|
lucide.createIcons();
|
||||||
preloadNotes();
|
preloadNotes();
|
||||||
checkHash();
|
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": {
|
"lastChecks": {
|
||||||
"agents_sync": "2026-01-30",
|
"agents_sync": "2026-01-31",
|
||||||
"email": null,
|
"email": null,
|
||||||
"calendar": null,
|
"calendar": null,
|
||||||
"git": 1738241820
|
"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
|
#!/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.)
|
Extrage titlu, dată, tags, și domenii (@work, @health, etc.)
|
||||||
Scanează TOATE subdirectoarele din notes/ (youtube, retete, etc.)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
NOTES_ROOT = Path(__file__).parent.parent / "notes"
|
BASE_DIR = Path(__file__).parent.parent
|
||||||
INDEX_FILE = NOTES_ROOT / "index.json"
|
KB_ROOT = BASE_DIR / "kb"
|
||||||
|
MEMORY_DIR = BASE_DIR / "memory"
|
||||||
# Subdirectoare de scanat (adaugă altele aici)
|
CONVERSATIONS_DIR = BASE_DIR / "conversations"
|
||||||
SCAN_DIRS = ['youtube', 'retete']
|
INDEX_FILE = KB_ROOT / "index.json"
|
||||||
|
|
||||||
# Domenii de agenți
|
# Domenii de agenți
|
||||||
VALID_DOMAINS = ['work', 'health', 'growth', 'sprijin', 'scout']
|
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"""
|
"""Extrage metadata din fișierul markdown"""
|
||||||
with open(filepath, 'r', encoding='utf-8') as f:
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
@@ -31,6 +81,7 @@ def extract_metadata(filepath):
|
|||||||
# Extrage tags (linia cu **Tags:** sau tags:)
|
# Extrage tags (linia cu **Tags:** sau tags:)
|
||||||
tags = []
|
tags = []
|
||||||
domains = []
|
domains = []
|
||||||
|
types = []
|
||||||
tags_match = re.search(r'\*\*Tags?:\*\*\s*(.+)$|^Tags?:\s*(.+)$', content, re.MULTILINE | re.IGNORECASE)
|
tags_match = re.search(r'\*\*Tags?:\*\*\s*(.+)$|^Tags?:\s*(.+)$', content, re.MULTILINE | re.IGNORECASE)
|
||||||
if tags_match:
|
if tags_match:
|
||||||
tags_str = tags_match.group(1) or tags_match.group(2)
|
tags_str = tags_match.group(1) or tags_match.group(2)
|
||||||
@@ -38,96 +89,199 @@ def extract_metadata(filepath):
|
|||||||
# Extrage domenii (@work, @health, etc.)
|
# Extrage domenii (@work, @health, etc.)
|
||||||
domain_matches = re.findall(r'@(\w+)', tags_str)
|
domain_matches = re.findall(r'@(\w+)', tags_str)
|
||||||
domains = [d for d in domain_matches if d in VALID_DOMAINS]
|
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)
|
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)
|
# Aplică reguli din .rules.json (dacă există)
|
||||||
date_match = re.match(r'(\d{4}-\d{2}-\d{2})_', filepath.name)
|
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 ""
|
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
|
# Extrage video URL
|
||||||
video_match = re.search(r'\*\*(?:Video|Link):\*\*\s*(https?://[^\s]+)', content)
|
video_match = re.search(r'\*\*(?:Video|Link):\*\*\s*(https?://[^\s]+)', content)
|
||||||
video_url = video_match.group(1) if video_match else ""
|
video_url = video_match.group(1) if video_match else ""
|
||||||
|
|
||||||
# Extrage TL;DR (primele 200 caractere)
|
# Extrage TL;DR sau primele 200 caractere de conținut
|
||||||
tldr_match = re.search(r'##\s*📋?\s*TL;DR\s*\n+(.+?)(?=\n##|\n---|\Z)', content, re.DOTALL)
|
|
||||||
tldr = ""
|
tldr = ""
|
||||||
|
tldr_match = re.search(r'##\s*📋?\s*TL;DR\s*\n+(.+?)(?=\n##|\n---|\Z)', content, re.DOTALL)
|
||||||
if tldr_match:
|
if tldr_match:
|
||||||
tldr = tldr_match.group(1).strip()[:200]
|
tldr = tldr_match.group(1).strip()[:200]
|
||||||
if len(tldr_match.group(1).strip()) > 200:
|
else:
|
||||||
tldr += "..."
|
# 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 {
|
return {
|
||||||
"file": filepath.name,
|
"file": rel_path,
|
||||||
"title": title,
|
"title": title,
|
||||||
"date": date,
|
"date": date,
|
||||||
"tags": tags,
|
"tags": tags,
|
||||||
"domains": domains,
|
"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,
|
"video": video_url,
|
||||||
"tldr": tldr
|
"tldr": tldr
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_index():
|
def scan_directory(dir_path, category, subcategory=None, recursive=False):
|
||||||
"""Generează index.json din toate fișierele .md din toate subdirectoarele"""
|
"""Scanează un director pentru fișiere .md"""
|
||||||
notes = []
|
notes = []
|
||||||
|
|
||||||
# Stats per domeniu
|
if not dir_path.exists():
|
||||||
domain_stats = {d: 0 for d in VALID_DOMAINS}
|
return notes
|
||||||
# Stats per categorie
|
|
||||||
category_stats = {}
|
|
||||||
|
|
||||||
for subdir in SCAN_DIRS:
|
# Defaults pentru categorii speciale (memory/, conversations/)
|
||||||
notes_dir = NOTES_ROOT / subdir
|
category_defaults = {
|
||||||
if not notes_dir.exists():
|
"memory": {"types": ["memory"], "domains": []},
|
||||||
print(f" (skipping {subdir}/ - not found)")
|
"conversations": {"types": ["conversation"], "domains": []}
|
||||||
continue
|
}
|
||||||
|
|
||||||
print(f"Scanning notes/{subdir}/...")
|
if recursive:
|
||||||
category_stats[subdir] = 0
|
# Scanează recursiv
|
||||||
|
for filepath in dir_path.rglob("*.md"):
|
||||||
for filepath in sorted(notes_dir.glob("*.md"), reverse=True):
|
if filepath.name.startswith('.') or 'template' in filepath.name.lower():
|
||||||
if filepath.name == 'index.json':
|
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
metadata = extract_metadata(filepath)
|
# Determină project și subdir din path
|
||||||
# Adaugă categoria (subdirectorul)
|
# Ex: projects/grup-sprijin/biblioteca/file.md
|
||||||
metadata['category'] = subdir
|
# project = grup-sprijin, subdir = biblioteca
|
||||||
# Modifică path-ul fișierului să includă subdirectorul
|
rel_to_dir = filepath.relative_to(dir_path)
|
||||||
metadata['file'] = f"{subdir}/{filepath.name}"
|
parts = rel_to_dir.parts[:-1] # exclude filename
|
||||||
|
|
||||||
|
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)
|
notes.append(metadata)
|
||||||
|
|
||||||
# Update stats
|
|
||||||
category_stats[subdir] += 1
|
|
||||||
for d in metadata['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:
|
except Exception as e:
|
||||||
print(f" ! Error processing {filepath.name}: {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
|
||||||
|
|
||||||
|
# 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
|
# 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ă
|
# Adaugă metadata globală
|
||||||
output = {
|
output = {
|
||||||
"notes": notes,
|
"notes": all_notes,
|
||||||
"stats": {
|
"stats": {
|
||||||
"total": len(notes),
|
"total": len(all_notes),
|
||||||
"by_domain": domain_stats,
|
"by_domain": domain_stats,
|
||||||
"by_category": category_stats
|
"by_category": category_stats
|
||||||
},
|
},
|
||||||
"domains": VALID_DOMAINS,
|
"domains": VALID_DOMAINS,
|
||||||
"categories": SCAN_DIRS
|
"types": VALID_TYPES,
|
||||||
|
"categories": list(category_stats.keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(INDEX_FILE, 'w', encoding='utf-8') as f:
|
with open(INDEX_FILE, 'w', encoding='utf-8') as f:
|
||||||
json.dump(output, f, indent=2, ensure_ascii=False)
|
json.dump(output, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
print(f"\n✅ Generated {INDEX_FILE} with {len(notes)} notes")
|
print(f"\n✅ Generated {INDEX_FILE} with {len(all_notes)} notes")
|
||||||
print(f" Domains: {domain_stats}")
|
|
||||||
print(f" Categories: {category_stats}")
|
print(f" Categories: {category_stats}")
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user