@cccarv82/freya 1.0.50 → 1.0.52

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.
Files changed (2) hide show
  1. package/cli/web.js +73 -19
  2. package/package.json +1 -1
package/cli/web.js CHANGED
@@ -8,6 +8,28 @@ const { spawn } = require('child_process');
8
8
  const { searchWorkspace } = require('../scripts/lib/search-utils');
9
9
  const { searchIndex } = require('../scripts/lib/index-utils');
10
10
 
11
+ function readAppVersion() {
12
+ const pkgPath = path.join(__dirname, '..', 'package.json');
13
+ try {
14
+ const json = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
15
+ if (json && typeof json.version === 'string' && json.version.trim() !== '') return json.version.trim();
16
+ } catch {
17
+ // Fall back below.
18
+ }
19
+ return 'unknown';
20
+ }
21
+
22
+ function escapeHtml(str) {
23
+ return String(str)
24
+ .replace(/&/g, '&')
25
+ .replace(/</g, '&lt;')
26
+ .replace(/>/g, '&gt;')
27
+ .replace(/"/g, '&quot;')
28
+ .replace(/'/g, '&#39;');
29
+ }
30
+
31
+ const APP_VERSION = readAppVersion();
32
+
11
33
  function guessNpmCmd() {
12
34
  // We'll execute via cmd.exe on Windows for reliability.
13
35
  return process.platform === 'win32' ? 'npm' : 'npm';
@@ -631,11 +653,11 @@ async function copilotSearch(workspaceDir, query, opts = {}) {
631
653
  `Consulta do usuário: "${q}"`,
632
654
  '',
633
655
  'Responda APENAS com JSON válido (sem code fences) no formato:',
634
- '{"summary":"<1-2 frases humanas>","matches":[{"file":"<caminho relativo>","date":"YYYY-MM-DD ou vazio","snippet":"<trecho curto>"}]}',
656
+ '{"answer":"<resposta humana, clara e direta>","evidence":[{"file":"<caminho relativo>","date":"YYYY-MM-DD ou vazio","detail":"<frase curta e única com a evidência>"}],"matches":[{"file":"<caminho relativo>","date":"YYYY-MM-DD ou vazio","snippet":"<trecho curto>"}]}',
635
657
  `Limite de matches: ${limit}.`,
636
- 'O resumo deve soar humano, bem escrito e mencionar a quantidade de registros encontrados.',
658
+ 'A resposta deve soar humana, bem escrita, sem repetições, e mencionar a quantidade de registros encontrados.',
637
659
  'Priorize responder exatamente à pergunta (ex.: data e ID da última CHG).',
638
- 'Evite repetir o mesmo trecho; escolha evidências mais úteis.',
660
+ 'Na seção evidence, escreva evidências curtas e não repetidas (máx 5).',
639
661
  'A lista deve estar ordenada por relevância.'
640
662
  ].join('\n');
641
663
 
@@ -682,36 +704,60 @@ async function copilotSearch(workspaceDir, query, opts = {}) {
682
704
  .filter((m) => m.file && isAllowedChatSearchPath(m.file))
683
705
  .slice(0, limit);
684
706
 
685
- const summary = String(parsed.summary || '').trim();
686
- return { ok: true, summary, matches };
707
+ const evidenceRaw = Array.isArray(parsed.evidence) ? parsed.evidence : [];
708
+ const evidence = evidenceRaw
709
+ .map((m) => {
710
+ const fileRaw = String(m && m.file ? m.file : '').trim();
711
+ const dateRaw = String(m && m.date ? m.date : '').trim();
712
+ const detailRaw = String(m && m.detail ? m.detail : '').trim();
713
+ let rel = fileRaw;
714
+ if (fileRaw.startsWith(workspaceDir)) {
715
+ rel = path.relative(workspaceDir, fileRaw).replace(/\\/g, '/');
716
+ }
717
+ return { file: rel.replace(/\\/g, '/'), date: dateRaw, detail: detailRaw };
718
+ })
719
+ .filter((m) => m.file && m.detail && isAllowedChatSearchPath(m.file))
720
+ .slice(0, 5);
721
+
722
+ const answer = String(parsed.answer || '').trim();
723
+ return { ok: true, answer, matches, evidence };
687
724
  }
688
725
 
689
- function buildChatAnswer(query, matches, summary) {
726
+ function buildChatAnswer(query, matches, summary, evidence, answer) {
690
727
  const count = matches.length;
691
728
  let summaryText = String(summary || '').trim();
692
- if (!summaryText) {
693
- if (count === 0) {
694
- summaryText = `Não encontrei registros relacionados a "${query}".`;
695
- } else {
696
- summaryText = `Encontrei ${count} registro(s) relacionados a "${query}".`;
729
+ let answerText = String(answer || '').trim();
730
+ if (!answerText) {
731
+ if (!summaryText) {
732
+ if (count === 0) {
733
+ summaryText = `Não encontrei registros relacionados a "${query}".`;
734
+ } else {
735
+ summaryText = `Encontrei ${count} registro(s) relacionados a "${query}".`;
736
+ }
697
737
  }
738
+ answerText = summaryText;
698
739
  }
699
740
 
700
741
  const lines = [];
701
742
  lines.push(`Encontrei ${count} registro(s).`);
702
- lines.push(`Resumo: ${summaryText}`);
743
+ lines.push(`Resumo: ${answerText}`);
744
+
745
+ const evidences = Array.isArray(evidence) && evidence.length
746
+ ? evidence
747
+ : matches.slice(0, 5).map((m) => ({ file: m.file, date: m.date, detail: m.snippet }));
703
748
 
704
- if (!matches.length) return lines.join('\n');
749
+ if (!evidences.length) return lines.join('\n');
705
750
 
706
751
  lines.push('');
707
752
  lines.push('Principais evidências:');
708
- for (const m of matches.slice(0, 5)) {
753
+ for (const m of evidences.slice(0, 5)) {
709
754
  const parts = [];
710
755
  if (m.date) parts.push(`**${m.date}**`);
711
756
  if (m.file) parts.push('`' + m.file + '`');
712
757
  const prefix = parts.length ? parts.join(' — ') + ':' : '';
713
- const snippet = m.snippet ? String(m.snippet).trim() : '';
714
- lines.push(`- ${prefix} ${snippet}`);
758
+ const detail = (m.detail || m.snippet || '').toString().trim();
759
+ if (!detail) continue;
760
+ lines.push(`- ${prefix} ${detail}`);
715
761
  }
716
762
 
717
763
  return lines.join('\n');
@@ -771,10 +817,11 @@ async function pickDirectoryNative() {
771
817
  function html(defaultDir) {
772
818
  // Aesthetic: “clean workstation” — light-first UI inspired by modern productivity apps.
773
819
  const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
774
- return buildHtml(safeDefault);
820
+ return buildHtml(safeDefault, APP_VERSION);
775
821
  }
776
822
 
777
- function buildHtml(safeDefault) {
823
+ function buildHtml(safeDefault, appVersion) {
824
+ const safeVersion = escapeHtml(appVersion || 'unknown');
778
825
  return `<!doctype html>
779
826
  <html>
780
827
  <head>
@@ -837,6 +884,7 @@ function buildHtml(safeDefault) {
837
884
  <div class="topbar">
838
885
  <div class="brand"><span class="spark"></span> Assistente de status local-first</div>
839
886
  <div class="actions">
887
+ <span class="chip" id="chipVersion">v${safeVersion}</span>
840
888
  <span class="chip" id="chipPort">127.0.0.1:3872</span>
841
889
  <button class="toggle" id="themeToggle" onclick="toggleTheme()">Escuro</button>
842
890
  </div>
@@ -1735,7 +1783,13 @@ async function cmdWeb({ port, dir, open, dev }) {
1735
1783
  const copilotResult = await copilotSearch(workspaceDir, query, { limit: 8 });
1736
1784
  if (copilotResult.ok) {
1737
1785
  const matches = copilotResult.matches || [];
1738
- const answer = buildChatAnswer(query, matches, copilotResult.summary);
1786
+ const answer = buildChatAnswer(
1787
+ query,
1788
+ matches,
1789
+ copilotResult.summary,
1790
+ copilotResult.evidence,
1791
+ copilotResult.answer
1792
+ );
1739
1793
  return safeJson(res, 200, { ok: true, sessionId, answer, matches });
1740
1794
  }
1741
1795
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.50",
3
+ "version": "1.0.52",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",