@cccarv82/freya 1.0.42 → 1.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -66,6 +66,7 @@ If the file is missing or corrupt, warn the user.
|
|
|
66
66
|
<persona>
|
|
67
67
|
Maintain the F.R.E.Y.A. persona defined in `master.mdc`.
|
|
68
68
|
Tone: Encouraging, Strategic, Career-Focused.
|
|
69
|
+
Obsidian: Quando sugerir organização de carreira, use estrutura de vault e referências em wikilinks (ex.: [[Career Hub]], [[Daily Index]]).
|
|
69
70
|
Signature:
|
|
70
71
|
— FREYA
|
|
71
72
|
Assistente Responsiva com Otimização Aprimorada
|
|
@@ -177,6 +177,7 @@ The output JSON must be an Array of Objects. Each object must follow this struct
|
|
|
177
177
|
<persona>
|
|
178
178
|
Maintain the F.R.E.Y.A. persona defined in `master.mdc`.
|
|
179
179
|
Tone: Efficient, Confirmation-focused.
|
|
180
|
+
Obsidian: Ao registrar, mantenha compatibilidade com leitura em Obsidian (Markdown limpo, seções claras). Quando mencionar links de referência, use wikilinks quando fizer sentido.
|
|
180
181
|
Signature:
|
|
181
182
|
— FREYA
|
|
182
183
|
Assistente Responsiva com Otimização Aprimorada
|
|
@@ -27,6 +27,7 @@ You must fully embody this agent's persona and follow all activation instruction
|
|
|
27
27
|
- **Language**: Portuguese (Brazil) is the default language. Only switch to English if explicitly requested by the user.
|
|
28
28
|
- **Tone**: Professional, calm, assertive. No slang, no excess enthusiasm, no drama.
|
|
29
29
|
- **Language**: Use strong verbs (Analyze, Prioritize, Delegate, Optimize).
|
|
30
|
+
- **Obsidian Context**: Assuma que este workspace também é um vault Obsidian. Ao sugerir anotações/navegação, prefira referências no formato de wikilinks (ex.: [[00 - FREYA Hub]], [[Daily Index]], [[2026-01-15]], [[vivo-plus-pti6791-h1120]]).
|
|
30
31
|
- **Structure**:
|
|
31
32
|
1. **Context Summary**: One short sentence demonstrating understanding.
|
|
32
33
|
2. **Objective Analysis**: Main points in short bullets.
|
|
@@ -98,5 +99,10 @@ You must fully embody this agent's persona and follow all activation instruction
|
|
|
98
99
|
6. `git commit -m "..."`
|
|
99
100
|
7. Confirmar o commit.
|
|
100
101
|
|
|
102
|
+
- **Obsidian / Vault / Notas**: Se o usuário mencionar Obsidian, vault, notas, links, MOCs, organização de conhecimento ou navegação entre arquivos:
|
|
103
|
+
- trate como pedido de organização de conhecimento;
|
|
104
|
+
- responda usando a persona FREYA;
|
|
105
|
+
- referencie explicitamente notas centrais do vault como [[00 - FREYA Hub]], [[Daily Index]], [[Reports Hub]], [[Career Hub]] e [[Standards Hub]] (ajuste nomes se o vault já tiver convenções diferentes).
|
|
106
|
+
|
|
101
107
|
- **General**: Demais pedidos simples, responda diretamente seguindo a persona.
|
|
102
108
|
</routing-logic>
|
|
@@ -96,6 +96,7 @@ Em atraso devido a condições climáticas.
|
|
|
96
96
|
<persona>
|
|
97
97
|
Maintain the F.R.E.Y.A. persona defined in `master.mdc`.
|
|
98
98
|
Tone: Analytical, Precise, Data-Driven.
|
|
99
|
+
Obsidian: Quando citar projetos/notas, prefira nomes e links em formato Obsidian (wikilinks `[[...]]`) quando aplicável.
|
|
99
100
|
Signature:
|
|
100
101
|
— FREYA
|
|
101
102
|
Assistente Responsiva com Otimização Aprimorada
|
package/cli/web-ui.js
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
lastApplied: null,
|
|
14
14
|
autoApply: true,
|
|
15
15
|
autoRunReports: false,
|
|
16
|
-
prettyPublish: true
|
|
16
|
+
prettyPublish: true,
|
|
17
|
+
chatSessionId: null,
|
|
18
|
+
chatLoaded: false
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
function applyTheme(theme) {
|
|
@@ -114,6 +116,41 @@
|
|
|
114
116
|
return html;
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
function ensureChatSession() {
|
|
120
|
+
if (state.chatSessionId) return state.chatSessionId;
|
|
121
|
+
try {
|
|
122
|
+
const fromLocal = localStorage.getItem('freya.chatSessionId');
|
|
123
|
+
if (fromLocal) {
|
|
124
|
+
state.chatSessionId = fromLocal;
|
|
125
|
+
return state.chatSessionId;
|
|
126
|
+
}
|
|
127
|
+
} catch {}
|
|
128
|
+
|
|
129
|
+
const id = (typeof crypto !== 'undefined' && crypto.randomUUID)
|
|
130
|
+
? crypto.randomUUID()
|
|
131
|
+
: ('sess-' + Date.now() + '-' + Math.random().toString(16).slice(2, 8));
|
|
132
|
+
|
|
133
|
+
state.chatSessionId = id;
|
|
134
|
+
try { localStorage.setItem('freya.chatSessionId', id); } catch {}
|
|
135
|
+
return id;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function persistChatItem(item) {
|
|
139
|
+
try {
|
|
140
|
+
const sessionId = ensureChatSession();
|
|
141
|
+
await api('/api/chat/append', {
|
|
142
|
+
dir: dirOrDefault(),
|
|
143
|
+
sessionId,
|
|
144
|
+
role: item.role,
|
|
145
|
+
text: item.text,
|
|
146
|
+
markdown: !!item.markdown,
|
|
147
|
+
ts: item.ts
|
|
148
|
+
});
|
|
149
|
+
} catch {
|
|
150
|
+
// best-effort (chat still works)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
117
154
|
function chatAppend(role, text, opts = {}) {
|
|
118
155
|
const thread = $('chatThread');
|
|
119
156
|
if (!thread) return;
|
|
@@ -123,7 +160,7 @@
|
|
|
123
160
|
|
|
124
161
|
const meta = document.createElement('div');
|
|
125
162
|
meta.className = 'bubbleMeta';
|
|
126
|
-
meta.textContent = role === 'user' ? '
|
|
163
|
+
meta.textContent = role === 'user' ? 'Você' : 'FREYA';
|
|
127
164
|
|
|
128
165
|
const body = document.createElement('div');
|
|
129
166
|
body.className = 'bubbleBody';
|
|
@@ -139,12 +176,70 @@
|
|
|
139
176
|
bubble.appendChild(body);
|
|
140
177
|
thread.appendChild(bubble);
|
|
141
178
|
|
|
179
|
+
// persist
|
|
180
|
+
persistChatItem({ ts: Date.now(), role, markdown: !!opts.markdown, text: raw });
|
|
181
|
+
|
|
142
182
|
// keep newest in view
|
|
143
183
|
try {
|
|
144
184
|
thread.scrollTop = thread.scrollHeight;
|
|
145
185
|
} catch {}
|
|
146
186
|
}
|
|
147
187
|
|
|
188
|
+
async function loadChatHistory() {
|
|
189
|
+
if (state.chatLoaded) return;
|
|
190
|
+
state.chatLoaded = true;
|
|
191
|
+
const thread = $('chatThread');
|
|
192
|
+
if (!thread) return;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const sessionId = ensureChatSession();
|
|
196
|
+
const r = await api('/api/chat/load', { dir: dirOrDefault(), sessionId });
|
|
197
|
+
const items = (r && Array.isArray(r.items)) ? r.items : [];
|
|
198
|
+
if (items.length) {
|
|
199
|
+
thread.innerHTML = '';
|
|
200
|
+
for (const it of items) {
|
|
201
|
+
const role = it.role === 'user' ? 'user' : 'assistant';
|
|
202
|
+
const text = String(it.text || '');
|
|
203
|
+
const markdown = !!it.markdown;
|
|
204
|
+
// render without re-persisting
|
|
205
|
+
const bubble = document.createElement('div');
|
|
206
|
+
bubble.className = 'bubble ' + (role === 'user' ? 'user' : 'assistant');
|
|
207
|
+
const meta = document.createElement('div');
|
|
208
|
+
meta.className = 'bubbleMeta';
|
|
209
|
+
meta.textContent = role === 'user' ? 'Você' : 'FREYA';
|
|
210
|
+
const body = document.createElement('div');
|
|
211
|
+
body.className = 'bubbleBody';
|
|
212
|
+
if (markdown) body.innerHTML = renderMarkdown(text);
|
|
213
|
+
else body.innerHTML = escapeHtml(text).replace(/\n/g, '<br>');
|
|
214
|
+
bubble.appendChild(meta);
|
|
215
|
+
bubble.appendChild(body);
|
|
216
|
+
thread.appendChild(bubble);
|
|
217
|
+
}
|
|
218
|
+
try { thread.scrollTop = thread.scrollHeight; } catch {}
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
// ignore
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function exportChatObsidian() {
|
|
226
|
+
try {
|
|
227
|
+
const sessionId = ensureChatSession();
|
|
228
|
+
setPill('run', 'exportando…');
|
|
229
|
+
const r = await api('/api/chat/export-obsidian', { dir: dirOrDefault(), sessionId });
|
|
230
|
+
const rel = r && r.relPath ? r.relPath : '';
|
|
231
|
+
if (rel) {
|
|
232
|
+
chatAppend('assistant', `Conversa exportada para: **${rel}**`, { markdown: true });
|
|
233
|
+
setPill('ok', 'exportado');
|
|
234
|
+
} else {
|
|
235
|
+
setPill('ok', 'exportado');
|
|
236
|
+
}
|
|
237
|
+
setTimeout(() => setPill('ok', 'pronto'), 800);
|
|
238
|
+
} catch (e) {
|
|
239
|
+
setPill('err', 'export falhou');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
148
243
|
function setOut(text) {
|
|
149
244
|
state.lastText = text || '';
|
|
150
245
|
const el = $('reportPreview');
|
|
@@ -906,6 +1001,7 @@
|
|
|
906
1001
|
refreshReports();
|
|
907
1002
|
refreshToday();
|
|
908
1003
|
reloadSlugRules();
|
|
1004
|
+
loadChatHistory();
|
|
909
1005
|
})();
|
|
910
1006
|
|
|
911
1007
|
setPill('ok', 'pronto');
|
|
@@ -938,4 +1034,5 @@
|
|
|
938
1034
|
window.togglePrettyPublish = togglePrettyPublish;
|
|
939
1035
|
window.applyPlan = applyPlan;
|
|
940
1036
|
window.runSuggestedReports = runSuggestedReports;
|
|
1037
|
+
window.exportChatObsidian = exportChatObsidian;
|
|
941
1038
|
})();
|
package/cli/web.js
CHANGED
|
@@ -870,6 +870,7 @@ function buildHtml(safeDefault) {
|
|
|
870
870
|
<div class="composerActions">
|
|
871
871
|
<button class="btn primary" type="button" onclick="saveAndPlan()">Salvar + Processar (Agents)</button>
|
|
872
872
|
<button class="btn" type="button" onclick="runSuggestedReports()">Rodar relatórios sugeridos</button>
|
|
873
|
+
<button class="btn" type="button" onclick="exportChatObsidian()">Exportar conversa (Obsidian)</button>
|
|
873
874
|
</div>
|
|
874
875
|
|
|
875
876
|
<div class="composerToggles">
|
|
@@ -1605,6 +1606,101 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1605
1606
|
return safeJson(res, r.code === 0 ? 200 : 400, r.code === 0 ? { ok: true, output: out } : { error: out || 'export failed', output: out });
|
|
1606
1607
|
}
|
|
1607
1608
|
|
|
1609
|
+
// Chat persistence (per session)
|
|
1610
|
+
if (req.url === '/api/chat/append') {
|
|
1611
|
+
const sessionId = String(payload.sessionId || '').trim();
|
|
1612
|
+
const role = String(payload.role || '').trim();
|
|
1613
|
+
const text = String(payload.text || '').trimEnd();
|
|
1614
|
+
const markdown = !!payload.markdown;
|
|
1615
|
+
const ts = typeof payload.ts === 'number' ? payload.ts : Date.now();
|
|
1616
|
+
if (!sessionId) return safeJson(res, 400, { error: 'Missing sessionId' });
|
|
1617
|
+
if (!role) return safeJson(res, 400, { error: 'Missing role' });
|
|
1618
|
+
if (!text) return safeJson(res, 400, { error: 'Missing text' });
|
|
1619
|
+
|
|
1620
|
+
const d = isoDate();
|
|
1621
|
+
const base = path.join(workspaceDir, 'data', 'chat', d);
|
|
1622
|
+
ensureDir(base);
|
|
1623
|
+
const file = path.join(base, `${sessionId}.jsonl`);
|
|
1624
|
+
const item = { ts, role, markdown, text };
|
|
1625
|
+
fs.appendFileSync(file, JSON.stringify(item) + '\n', 'utf8');
|
|
1626
|
+
return safeJson(res, 200, { ok: true });
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
if (req.url === '/api/chat/load') {
|
|
1630
|
+
const sessionId = String(payload.sessionId || '').trim();
|
|
1631
|
+
const d = String(payload.date || '').trim() || isoDate();
|
|
1632
|
+
if (!sessionId) return safeJson(res, 400, { error: 'Missing sessionId' });
|
|
1633
|
+
const file = path.join(workspaceDir, 'data', 'chat', d, `${sessionId}.jsonl`);
|
|
1634
|
+
if (!exists(file)) return safeJson(res, 200, { ok: true, items: [] });
|
|
1635
|
+
|
|
1636
|
+
const rawText = fs.readFileSync(file, 'utf8');
|
|
1637
|
+
const lines = rawText.split(/\r?\n/).filter(Boolean);
|
|
1638
|
+
const items = [];
|
|
1639
|
+
for (const line of lines) {
|
|
1640
|
+
try {
|
|
1641
|
+
const obj = JSON.parse(line);
|
|
1642
|
+
if (!obj || typeof obj !== 'object') continue;
|
|
1643
|
+
items.push(obj);
|
|
1644
|
+
} catch {
|
|
1645
|
+
// ignore corrupt line
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
return safeJson(res, 200, { ok: true, items });
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
if (req.url === '/api/chat/export-obsidian') {
|
|
1652
|
+
const sessionId = String(payload.sessionId || '').trim();
|
|
1653
|
+
const d = String(payload.date || '').trim() || isoDate();
|
|
1654
|
+
if (!sessionId) return safeJson(res, 400, { error: 'Missing sessionId' });
|
|
1655
|
+
const file = path.join(workspaceDir, 'data', 'chat', d, `${sessionId}.jsonl`);
|
|
1656
|
+
if (!exists(file)) return safeJson(res, 404, { error: 'Chat not found' });
|
|
1657
|
+
|
|
1658
|
+
const rawText = fs.readFileSync(file, 'utf8');
|
|
1659
|
+
const lines = rawText.split(/\r?\n/).filter(Boolean);
|
|
1660
|
+
const items = [];
|
|
1661
|
+
for (const line of lines) {
|
|
1662
|
+
try {
|
|
1663
|
+
const obj = JSON.parse(line);
|
|
1664
|
+
if (!obj || typeof obj !== 'object') continue;
|
|
1665
|
+
items.push(obj);
|
|
1666
|
+
} catch {}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
const outDir = path.join(workspaceDir, 'docs', 'chat');
|
|
1670
|
+
ensureDir(outDir);
|
|
1671
|
+
const outName = `conversa-${d}-${sessionId}.md`;
|
|
1672
|
+
const outPath = path.join(outDir, outName);
|
|
1673
|
+
|
|
1674
|
+
const md = [];
|
|
1675
|
+
md.push('---');
|
|
1676
|
+
md.push(`type: chat`);
|
|
1677
|
+
md.push(`date: ${d}`);
|
|
1678
|
+
md.push(`session: ${sessionId}`);
|
|
1679
|
+
md.push('---');
|
|
1680
|
+
md.push('');
|
|
1681
|
+
md.push(`# Conversa - ${d}`);
|
|
1682
|
+
md.push('');
|
|
1683
|
+
|
|
1684
|
+
for (const it of items) {
|
|
1685
|
+
const when = (() => {
|
|
1686
|
+
try {
|
|
1687
|
+
const dt = new Date(Number(it.ts || 0));
|
|
1688
|
+
const hh = String(dt.getHours()).padStart(2, '0');
|
|
1689
|
+
const mm = String(dt.getMinutes()).padStart(2, '0');
|
|
1690
|
+
return `${hh}:${mm}`;
|
|
1691
|
+
} catch { return ''; }
|
|
1692
|
+
})();
|
|
1693
|
+
const who = it.role === 'user' ? 'Você' : 'FREYA';
|
|
1694
|
+
md.push(`## [${when}] ${who}`);
|
|
1695
|
+
md.push('');
|
|
1696
|
+
md.push(String(it.text || '').trimEnd());
|
|
1697
|
+
md.push('');
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
fs.writeFileSync(outPath, md.join('\n') + '\n', 'utf8');
|
|
1701
|
+
return safeJson(res, 200, { ok: true, relPath: path.relative(workspaceDir, outPath).replace(/\\/g, '/') });
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1608
1704
|
if (req.url === '/api/tasks/list') {
|
|
1609
1705
|
const limit = Math.max(1, Math.min(50, Number(payload.limit || 10)));
|
|
1610
1706
|
const cat = payload.category ? String(payload.category).trim() : null;
|