@cccarv82/freya 2.13.1 → 2.13.2

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.
package/cli/web-ui.css CHANGED
@@ -588,6 +588,7 @@ body {
588
588
  display: flex;
589
589
  flex-direction: column;
590
590
  gap: 18px;
591
+ flex: 1;
591
592
  }
592
593
 
593
594
  .promptShell {
package/cli/web-ui.js CHANGED
@@ -162,6 +162,70 @@
162
162
  return html;
163
163
  }
164
164
 
165
+ function formatPlanForDisplay(rawPlan) {
166
+ try {
167
+ const text = String(rawPlan || '');
168
+ const start = text.indexOf('{');
169
+ if (start === -1) return null;
170
+ let depth = 0, inStr = false, esc = false, jsonStr = null;
171
+ for (let i = start; i < text.length; i++) {
172
+ const ch = text[i];
173
+ if (esc) { esc = false; continue; }
174
+ if (ch === '\\') { esc = true; continue; }
175
+ if (ch === '"') { inStr = !inStr; continue; }
176
+ if (inStr) continue;
177
+ if (ch === '{') depth++;
178
+ if (ch === '}') { depth--; if (depth === 0) { jsonStr = text.slice(start, i + 1); break; } }
179
+ }
180
+ if (!jsonStr) return null;
181
+ const plan = JSON.parse(jsonStr);
182
+ const actions = Array.isArray(plan.actions) ? plan.actions : [];
183
+ if (actions.length === 0) return null;
184
+
185
+ const icons = {
186
+ append_daily_log: '\u{1F4DD}', appenddailylog: '\u{1F4DD}',
187
+ create_task: '\u2705', createtask: '\u2705',
188
+ create_blocker: '\u{1F6A7}', createblocker: '\u{1F6A7}',
189
+ suggest_report: '\u{1F4CA}', suggestreport: '\u{1F4CA}',
190
+ oracle_query: '\u{1F50D}', oraclequery: '\u{1F50D}'
191
+ };
192
+
193
+ const lines = actions.map(function(a, i) {
194
+ var type = String(a.type || '').trim().toLowerCase().replace(/_/g, '');
195
+ var icon = icons[type] || icons[String(a.type || '').trim()] || '\u2022';
196
+ var num = i + 1;
197
+ var typeNorm = type.replace(/_/g, '');
198
+
199
+ if (typeNorm === 'appenddailylog') {
200
+ var t = String(a.text || '').slice(0, 140);
201
+ return num + '. ' + icon + ' **Registrar no log:** ' + t + (String(a.text || '').length > 140 ? '...' : '');
202
+ }
203
+ if (typeNorm === 'createtask') {
204
+ var desc = String(a.description || '').slice(0, 120);
205
+ var pri = a.priority ? ' (prioridade: **' + String(a.priority).toUpperCase() + '**)' : '';
206
+ var cat = a.category ? ' [' + a.category + ']' : '';
207
+ return num + '. ' + icon + ' **Criar tarefa:** ' + desc + pri + cat;
208
+ }
209
+ if (typeNorm === 'createblocker') {
210
+ var title = String(a.title || a.description || '').slice(0, 120);
211
+ var sev = a.severity ? ' (severidade: **' + String(a.severity).toUpperCase() + '**)' : '';
212
+ return num + '. ' + icon + ' **Registrar blocker:** ' + title + sev;
213
+ }
214
+ if (typeNorm === 'suggestreport') {
215
+ return num + '. ' + icon + ' **Sugerir relatorio:** ' + String(a.name || a.reportType || '');
216
+ }
217
+ if (typeNorm === 'oraclequery') {
218
+ return num + '. ' + icon + ' **Consultar oracle:** ' + String(a.query || '').slice(0, 120);
219
+ }
220
+ return num + '. \u2022 **' + String(a.type || 'acao') + ':** ' + JSON.stringify(a).slice(0, 100);
221
+ });
222
+
223
+ return lines.join('\n');
224
+ } catch (e) {
225
+ return null;
226
+ }
227
+ }
228
+
165
229
  function ensureChatSession() {
166
230
  if (state.chatSessionId) return state.chatSessionId;
167
231
  try {
@@ -1507,29 +1571,63 @@
1507
1571
  + (t.category ? ' · <span style="font-family:var(--mono);">' + escapeHtml(t.category) + '</span>' : '')
1508
1572
  + '</div>'
1509
1573
  + '</div></div>'
1510
- + '<div style="display:flex; gap:6px; flex-shrink:0;">'
1574
+ + '<div class="task-actions" style="display:flex; gap:6px; flex-shrink:0; align-items:center;">'
1511
1575
  + '<button class="btn small complete-btn" type="button" style="padding:3px 10px; font-size:11px;" title="Marcar como concluída">✓ Concluir</button>'
1512
1576
  + '<button class="btn small edit-btn" type="button" style="padding:3px 8px; font-size:11px;">✎</button>'
1513
1577
  + '</div>';
1514
1578
  row.querySelector('.edit-btn').onclick = () => editTask(t);
1515
- row.querySelector('.complete-btn').onclick = async () => {
1516
- const btn = row.querySelector('.complete-btn');
1517
- btn.disabled = true;
1518
- btn.textContent = '…';
1519
- try {
1520
- setPill('run', 'concluindo…');
1521
- await api('/api/tasks/complete', { dir: dirOrDefault(), id: t.id });
1522
- row.style.opacity = '0.4';
1523
- row.style.pointerEvents = 'none';
1524
- await refreshToday();
1525
- setPill('ok', 'concluída');
1526
- setTimeout(() => setPill('ok', 'pronto'), 800);
1527
- } catch (e) {
1528
- btn.disabled = false;
1529
- btn.textContent = '✓ Concluir';
1530
- setPill('err', 'falhou');
1531
- }
1579
+
1580
+ var attachCompleteHandler = function() {
1581
+ var cBtn = row.querySelector('.complete-btn');
1582
+ if (!cBtn) return;
1583
+ cBtn.onclick = function() {
1584
+ var actionsDiv = row.querySelector('.task-actions');
1585
+ actionsDiv.innerHTML =
1586
+ '<input type="text" class="comment-input" placeholder="Comentario (opcional)" '
1587
+ + 'style="font-size:11px; padding:3px 8px; background:var(--bg); border:1px solid var(--border); '
1588
+ + 'color:var(--text); width:180px; outline:none; font-family:var(--mono);" />'
1589
+ + '<button class="btn small confirm-btn" type="button" style="padding:3px 10px; font-size:11px; '
1590
+ + 'background:var(--accent); color:#000; font-weight:700;">Confirmar</button>'
1591
+ + '<button class="btn small cancel-btn" type="button" style="padding:3px 8px; font-size:11px; '
1592
+ + 'color:var(--muted);">\u2715</button>';
1593
+
1594
+ var input = actionsDiv.querySelector('.comment-input');
1595
+ input.focus();
1596
+
1597
+ var doComplete = async function() {
1598
+ var comment = input.value.trim();
1599
+ actionsDiv.querySelector('.confirm-btn').disabled = true;
1600
+ actionsDiv.querySelector('.confirm-btn').textContent = '\u2026';
1601
+ try {
1602
+ setPill('run', 'concluindo\u2026');
1603
+ var body = { dir: dirOrDefault(), id: t.id };
1604
+ if (comment) body.comment = comment;
1605
+ await api('/api/tasks/complete', body);
1606
+ row.style.opacity = '0.4';
1607
+ row.style.pointerEvents = 'none';
1608
+ await refreshToday();
1609
+ setPill('ok', 'conclu\u00edda');
1610
+ setTimeout(function() { setPill('ok', 'pronto'); }, 800);
1611
+ } catch (e) {
1612
+ actionsDiv.querySelector('.confirm-btn').disabled = false;
1613
+ actionsDiv.querySelector('.confirm-btn').textContent = 'Confirmar';
1614
+ setPill('err', 'falhou');
1615
+ }
1616
+ };
1617
+
1618
+ actionsDiv.querySelector('.confirm-btn').onclick = doComplete;
1619
+ input.onkeydown = function(e) { if (e.key === 'Enter') doComplete(); };
1620
+ actionsDiv.querySelector('.cancel-btn').onclick = function() {
1621
+ actionsDiv.innerHTML =
1622
+ '<button class="btn small complete-btn" type="button" style="padding:3px 10px; font-size:11px;" '
1623
+ + 'title="Marcar como conclu\u00edda">\u2713 Concluir</button>'
1624
+ + '<button class="btn small edit-btn" type="button" style="padding:3px 8px; font-size:11px;">\u270E</button>';
1625
+ actionsDiv.querySelector('.edit-btn').onclick = function() { editTask(t); };
1626
+ attachCompleteHandler();
1627
+ };
1628
+ };
1532
1629
  };
1630
+ attachCompleteHandler();
1533
1631
  return row;
1534
1632
  };
1535
1633
 
@@ -2328,7 +2426,8 @@
2328
2426
 
2329
2427
  // Show plan output in Preview panel
2330
2428
  const header = r.ok === false ? '## Agent Plan (planner unavailable)\n\n' : '## Agent Plan (draft)\n\n';
2331
- const planOut = header + (r.plan || '');
2429
+ const formatted = r.ok !== false ? formatPlanForDisplay(r.plan) : null;
2430
+ const planOut = header + (formatted || r.plan || '');
2332
2431
  setOut(planOut);
2333
2432
  chatAppend('assistant', planOut, { markdown: true });
2334
2433
  ta.value = '';
package/cli/web.js CHANGED
@@ -3620,7 +3620,8 @@ async function cmdWeb({ port, dir, open, dev }) {
3620
3620
  completedAt: t.completed_at,
3621
3621
  projectSlug: t.project_slug,
3622
3622
  priority: meta.priority,
3623
- streamSlug: meta.streamSlug
3623
+ streamSlug: meta.streamSlug,
3624
+ comments: Array.isArray(meta.comments) ? meta.comments : []
3624
3625
  };
3625
3626
  });
3626
3627
 
@@ -3631,10 +3632,24 @@ async function cmdWeb({ port, dir, open, dev }) {
3631
3632
  const id = String(payload.id || '').trim();
3632
3633
  if (!id) return safeJson(res, 400, { error: 'Missing id' });
3633
3634
 
3635
+ const comment = typeof payload.comment === 'string' ? payload.comment.trim() : '';
3634
3636
  const now = new Date().toISOString();
3635
- const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ? WHERE id = ?`).run(now, id);
3636
3637
 
3637
- if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
3638
+ if (comment) {
3639
+ const row = dl.db.prepare('SELECT metadata FROM tasks WHERE id = ?').get(id);
3640
+ if (!row) return safeJson(res, 404, { error: 'Task not found' });
3641
+ let meta = {};
3642
+ try { meta = row.metadata ? JSON.parse(row.metadata) : {}; } catch (_) { meta = {}; }
3643
+ if (!Array.isArray(meta.comments)) meta.comments = [];
3644
+ meta.comments.push({ text: comment, createdAt: now });
3645
+ const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ?, metadata = ? WHERE id = ?`)
3646
+ .run(now, JSON.stringify(meta), id);
3647
+ if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
3648
+ } else {
3649
+ const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ? WHERE id = ?`).run(now, id);
3650
+ if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
3651
+ }
3652
+
3638
3653
  return safeJson(res, 200, { ok: true, task: { id, status: 'COMPLETED', completedAt: now } });
3639
3654
  }
3640
3655
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "2.13.1",
3
+ "version": "2.13.2",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js && node scripts/validate-structure.js",