@cccarv82/freya 1.0.52 → 1.0.53

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 +83 -19
  2. package/package.json +1 -1
package/cli/web.js CHANGED
@@ -30,6 +30,12 @@ function escapeHtml(str) {
30
30
 
31
31
  const APP_VERSION = readAppVersion();
32
32
 
33
+ const CHAT_ID_PATTERNS = [
34
+ /\bPTI\d{4,}-\d+\b/gi,
35
+ /\bINC\d+\b/gi,
36
+ /\bCHG\d+\b/gi
37
+ ];
38
+
33
39
  function guessNpmCmd() {
34
40
  // We'll execute via cmd.exe on Windows for reliability.
35
41
  return process.platform === 'win32' ? 'npm' : 'npm';
@@ -637,6 +643,61 @@ function isAllowedChatSearchPath(relPath) {
637
643
  return relPath.startsWith('data/') || relPath.startsWith('logs/') || relPath.startsWith('docs/');
638
644
  }
639
645
 
646
+ function extractChatIds(text) {
647
+ const tokens = new Set();
648
+ const q = String(text || '');
649
+ for (const re of CHAT_ID_PATTERNS) {
650
+ const matches = q.match(re);
651
+ if (matches) {
652
+ for (const m of matches) tokens.add(m.toUpperCase());
653
+ }
654
+ }
655
+ return Array.from(tokens);
656
+ }
657
+
658
+ function extractProjectToken(text) {
659
+ const raw = String(text || '');
660
+ const m = raw.match(/project\s*[:=]\s*([A-Za-z0-9_\/-]+)/i);
661
+ if (m && m[1]) return m[1].trim();
662
+ const m2 = raw.match(/project\s*\(([^)]+)\)/i);
663
+ if (m2 && m2[1]) return m2[1].trim();
664
+ return '';
665
+ }
666
+
667
+ function projectFromPath(relPath) {
668
+ const p = String(relPath || '');
669
+ const m = p.match(/data\/Clients\/([^/]+)\/([^/]+)/i);
670
+ if (m && m[1] && m[2]) return `${m[1]}/${m[2]}`;
671
+ return '';
672
+ }
673
+
674
+ function matchKey(m) {
675
+ const ids = extractChatIds(`${m.file || ''} ${m.snippet || ''}`);
676
+ if (ids.length) return `id:${ids[0]}`;
677
+ const proj = projectFromPath(m.file) || extractProjectToken(`${m.file || ''} ${m.snippet || ''}`);
678
+ if (proj) return `proj:${proj.toLowerCase()}`;
679
+ return `file:${m.file || ''}`;
680
+ }
681
+
682
+ function mergeMatches(primary, secondary, limit = 8) {
683
+ const list = [];
684
+ const seen = new Set();
685
+ const push = (m) => {
686
+ if (!m || !m.file || !isAllowedChatSearchPath(m.file)) return;
687
+ const key = matchKey(m);
688
+ if (seen.has(key)) return;
689
+ seen.add(key);
690
+ list.push(m);
691
+ };
692
+
693
+ (primary || []).forEach(push);
694
+ (secondary || []).forEach(push);
695
+
696
+ const total = list.length;
697
+ const trimmed = list.slice(0, Math.max(1, Math.min(20, limit)));
698
+ return { matches: trimmed, total };
699
+ }
700
+
640
701
  async function copilotSearch(workspaceDir, query, opts = {}) {
641
702
  const q = String(query || '').trim();
642
703
  if (!q) return { ok: false, error: 'Missing query' };
@@ -723,8 +784,8 @@ async function copilotSearch(workspaceDir, query, opts = {}) {
723
784
  return { ok: true, answer, matches, evidence };
724
785
  }
725
786
 
726
- function buildChatAnswer(query, matches, summary, evidence, answer) {
727
- const count = matches.length;
787
+ function buildChatAnswer(query, matches, summary, evidence, answer, totalCount) {
788
+ const count = typeof totalCount === 'number' ? totalCount : matches.length;
728
789
  let summaryText = String(summary || '').trim();
729
790
  let answerText = String(answer || '').trim();
730
791
  if (!answerText) {
@@ -740,7 +801,7 @@ function buildChatAnswer(query, matches, summary, evidence, answer) {
740
801
 
741
802
  const lines = [];
742
803
  lines.push(`Encontrei ${count} registro(s).`);
743
- lines.push(`Resumo: ${answerText}`);
804
+ lines.push(`Resposta curta: ${answerText}`);
744
805
 
745
806
  const evidences = Array.isArray(evidence) && evidence.length
746
807
  ? evidence
@@ -749,15 +810,15 @@ function buildChatAnswer(query, matches, summary, evidence, answer) {
749
810
  if (!evidences.length) return lines.join('\n');
750
811
 
751
812
  lines.push('');
752
- lines.push('Principais evidências:');
813
+ lines.push('Detalhes:');
753
814
  for (const m of evidences.slice(0, 5)) {
754
- const parts = [];
755
- if (m.date) parts.push(`**${m.date}**`);
756
- if (m.file) parts.push('`' + m.file + '`');
757
- const prefix = parts.length ? parts.join(' — ') + ':' : '';
758
815
  const detail = (m.detail || m.snippet || '').toString().trim();
759
816
  if (!detail) continue;
760
- lines.push(`- ${prefix} ${detail}`);
817
+ const meta = [];
818
+ if (m.file) meta.push(m.file);
819
+ if (m.date) meta.push(m.date);
820
+ const suffix = meta.length ? ` (${meta.join(' · ')})` : '';
821
+ lines.push(`- ${detail}${suffix}`);
761
822
  }
762
823
 
763
824
  return lines.join('\n');
@@ -1781,24 +1842,27 @@ async function cmdWeb({ port, dir, open, dev }) {
1781
1842
  if (!query) return safeJson(res, 400, { error: 'Missing query' });
1782
1843
 
1783
1844
  const copilotResult = await copilotSearch(workspaceDir, query, { limit: 8 });
1845
+ const indexMatches = searchIndex(workspaceDir, query, { limit: 12 });
1846
+ const baseMatches = indexMatches.length
1847
+ ? indexMatches
1848
+ : searchWorkspace(workspaceDir, query, { limit: 12 });
1849
+
1784
1850
  if (copilotResult.ok) {
1785
- const matches = copilotResult.matches || [];
1851
+ const merged = mergeMatches(copilotResult.matches || [], baseMatches, 8);
1786
1852
  const answer = buildChatAnswer(
1787
1853
  query,
1788
- matches,
1854
+ merged.matches,
1789
1855
  copilotResult.summary,
1790
1856
  copilotResult.evidence,
1791
- copilotResult.answer
1857
+ copilotResult.answer,
1858
+ merged.total
1792
1859
  );
1793
- return safeJson(res, 200, { ok: true, sessionId, answer, matches });
1860
+ return safeJson(res, 200, { ok: true, sessionId, answer, matches: merged.matches });
1794
1861
  }
1795
1862
 
1796
- const indexMatches = searchIndex(workspaceDir, query, { limit: 8 });
1797
- const matches = indexMatches.length
1798
- ? indexMatches
1799
- : searchWorkspace(workspaceDir, query, { limit: 8 });
1800
- const answer = buildChatAnswer(query, matches, '');
1801
- return safeJson(res, 200, { ok: true, sessionId, answer, matches });
1863
+ const merged = mergeMatches(baseMatches, [], 8);
1864
+ const answer = buildChatAnswer(query, merged.matches, '', [], '', merged.total);
1865
+ return safeJson(res, 200, { ok: true, sessionId, answer, matches: merged.matches });
1802
1866
  }
1803
1867
 
1804
1868
  // Chat persistence (per session)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.52",
3
+ "version": "1.0.53",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",