@cccarv82/freya 1.0.46 → 1.0.50
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/index.js +3 -2
- package/cli/web.js +121 -20
- package/package.json +1 -1
package/cli/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
|
|
5
6
|
const { cmdInit } = require('./init');
|
|
@@ -59,7 +60,7 @@ async function run(argv) {
|
|
|
59
60
|
const command = args[0];
|
|
60
61
|
|
|
61
62
|
if (!command || command === 'help' || flags.has('--help') || flags.has('-h')) {
|
|
62
|
-
process.stdout.
|
|
63
|
+
fs.writeSync(process.stdout.fd, usage());
|
|
63
64
|
return;
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -95,7 +96,7 @@ async function run(argv) {
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
process.stderr.write(`Unknown command: ${command}\n`);
|
|
98
|
-
process.stdout.
|
|
99
|
+
fs.writeSync(process.stdout.fd, usage());
|
|
99
100
|
process.exitCode = 1;
|
|
100
101
|
}
|
|
101
102
|
|
package/cli/web.js
CHANGED
|
@@ -608,6 +608,115 @@ function run(cmd, args, cwd) {
|
|
|
608
608
|
});
|
|
609
609
|
}
|
|
610
610
|
|
|
611
|
+
function isAllowedChatSearchPath(relPath) {
|
|
612
|
+
if (!relPath) return false;
|
|
613
|
+
if (relPath.startsWith('..')) return false;
|
|
614
|
+
if (relPath.startsWith('data/chat/')) return false;
|
|
615
|
+
return relPath.startsWith('data/') || relPath.startsWith('logs/') || relPath.startsWith('docs/');
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async function copilotSearch(workspaceDir, query, opts = {}) {
|
|
619
|
+
const q = String(query || '').trim();
|
|
620
|
+
if (!q) return { ok: false, error: 'Missing query' };
|
|
621
|
+
|
|
622
|
+
const limit = Math.max(1, Math.min(20, Number(opts.limit || 8)));
|
|
623
|
+
const cmd = process.env.COPILOT_CMD || 'copilot';
|
|
624
|
+
|
|
625
|
+
const prompt = [
|
|
626
|
+
'Você é um buscador local de arquivos.',
|
|
627
|
+
'Objetivo: encontrar registros relevantes para a consulta do usuário.',
|
|
628
|
+
'Escopo: procure SOMENTE nos diretórios data/, logs/ e docs/ do workspace.',
|
|
629
|
+
'Exclua data/chat/ (conversa), a menos que o usuário peça explicitamente por chat.',
|
|
630
|
+
'Use ferramentas para ler/consultar arquivos, mas não modifique nada.',
|
|
631
|
+
`Consulta do usuário: "${q}"`,
|
|
632
|
+
'',
|
|
633
|
+
'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>"}]}',
|
|
635
|
+
`Limite de matches: ${limit}.`,
|
|
636
|
+
'O resumo deve soar humano, bem escrito e mencionar a quantidade de registros encontrados.',
|
|
637
|
+
'Priorize responder exatamente à pergunta (ex.: data e ID da última CHG).',
|
|
638
|
+
'Evite repetir o mesmo trecho; escolha evidências mais úteis.',
|
|
639
|
+
'A lista deve estar ordenada por relevância.'
|
|
640
|
+
].join('\n');
|
|
641
|
+
|
|
642
|
+
const args = [
|
|
643
|
+
'-s',
|
|
644
|
+
'--no-color',
|
|
645
|
+
'--stream',
|
|
646
|
+
'off',
|
|
647
|
+
'-p',
|
|
648
|
+
prompt,
|
|
649
|
+
'--allow-all-tools',
|
|
650
|
+
'--add-dir',
|
|
651
|
+
workspaceDir
|
|
652
|
+
];
|
|
653
|
+
|
|
654
|
+
const r = await run(cmd, args, workspaceDir);
|
|
655
|
+
const out = (r.stdout + r.stderr).trim();
|
|
656
|
+
if (r.code !== 0) return { ok: false, error: out || 'Copilot returned non-zero exit code.' };
|
|
657
|
+
|
|
658
|
+
const jsonText = extractFirstJsonObject(out) || out;
|
|
659
|
+
let parsed;
|
|
660
|
+
try {
|
|
661
|
+
parsed = JSON.parse(jsonText);
|
|
662
|
+
} catch {
|
|
663
|
+
try {
|
|
664
|
+
parsed = JSON.parse(escapeJsonControlChars(jsonText));
|
|
665
|
+
} catch (e) {
|
|
666
|
+
return { ok: false, error: e.message || 'Copilot output not valid JSON.' };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const matchesRaw = Array.isArray(parsed.matches) ? parsed.matches : [];
|
|
671
|
+
const matches = matchesRaw
|
|
672
|
+
.map((m) => {
|
|
673
|
+
const fileRaw = String(m && m.file ? m.file : '').trim();
|
|
674
|
+
const dateRaw = String(m && m.date ? m.date : '').trim();
|
|
675
|
+
const snippetRaw = String(m && m.snippet ? m.snippet : '').trim();
|
|
676
|
+
let rel = fileRaw;
|
|
677
|
+
if (fileRaw.startsWith(workspaceDir)) {
|
|
678
|
+
rel = path.relative(workspaceDir, fileRaw).replace(/\\/g, '/');
|
|
679
|
+
}
|
|
680
|
+
return { file: rel.replace(/\\/g, '/'), date: dateRaw, snippet: snippetRaw };
|
|
681
|
+
})
|
|
682
|
+
.filter((m) => m.file && isAllowedChatSearchPath(m.file))
|
|
683
|
+
.slice(0, limit);
|
|
684
|
+
|
|
685
|
+
const summary = String(parsed.summary || '').trim();
|
|
686
|
+
return { ok: true, summary, matches };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function buildChatAnswer(query, matches, summary) {
|
|
690
|
+
const count = matches.length;
|
|
691
|
+
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}".`;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const lines = [];
|
|
701
|
+
lines.push(`Encontrei ${count} registro(s).`);
|
|
702
|
+
lines.push(`Resumo: ${summaryText}`);
|
|
703
|
+
|
|
704
|
+
if (!matches.length) return lines.join('\n');
|
|
705
|
+
|
|
706
|
+
lines.push('');
|
|
707
|
+
lines.push('Principais evidências:');
|
|
708
|
+
for (const m of matches.slice(0, 5)) {
|
|
709
|
+
const parts = [];
|
|
710
|
+
if (m.date) parts.push(`**${m.date}**`);
|
|
711
|
+
if (m.file) parts.push('`' + m.file + '`');
|
|
712
|
+
const prefix = parts.length ? parts.join(' — ') + ':' : '';
|
|
713
|
+
const snippet = m.snippet ? String(m.snippet).trim() : '';
|
|
714
|
+
lines.push(`- ${prefix} ${snippet}`);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return lines.join('\n');
|
|
718
|
+
}
|
|
719
|
+
|
|
611
720
|
function openBrowser(url) {
|
|
612
721
|
const { cmd, args } = guessOpenCmd();
|
|
613
722
|
try {
|
|
@@ -1623,26 +1732,18 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1623
1732
|
const query = String(payload.query || '').trim();
|
|
1624
1733
|
if (!query) return safeJson(res, 400, { error: 'Missing query' });
|
|
1625
1734
|
|
|
1735
|
+
const copilotResult = await copilotSearch(workspaceDir, query, { limit: 8 });
|
|
1736
|
+
if (copilotResult.ok) {
|
|
1737
|
+
const matches = copilotResult.matches || [];
|
|
1738
|
+
const answer = buildChatAnswer(query, matches, copilotResult.summary);
|
|
1739
|
+
return safeJson(res, 200, { ok: true, sessionId, answer, matches });
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1626
1742
|
const indexMatches = searchIndex(workspaceDir, query, { limit: 8 });
|
|
1627
1743
|
const matches = indexMatches.length
|
|
1628
1744
|
? indexMatches
|
|
1629
1745
|
: searchWorkspace(workspaceDir, query, { limit: 8 });
|
|
1630
|
-
|
|
1631
|
-
return safeJson(res, 200, { ok: true, sessionId, answer: 'Não encontrei registro', matches: [] });
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
const lines = [];
|
|
1635
|
-
lines.push(`Encontrei ${matches.length} registro(s):`);
|
|
1636
|
-
for (const m of matches) {
|
|
1637
|
-
const parts = [];
|
|
1638
|
-
if (m.date) parts.push(`**${m.date}**`);
|
|
1639
|
-
if (m.file) parts.push('`' + m.file + '`');
|
|
1640
|
-
const prefix = parts.length ? parts.join(' — ') + ':' : '';
|
|
1641
|
-
const snippet = m.snippet ? String(m.snippet).trim() : '';
|
|
1642
|
-
lines.push(`- ${prefix} ${snippet}`);
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
const answer = lines.join('\n');
|
|
1746
|
+
const answer = buildChatAnswer(query, matches, '');
|
|
1646
1747
|
return safeJson(res, 200, { ok: true, sessionId, answer, matches });
|
|
1647
1748
|
}
|
|
1648
1749
|
|
|
@@ -1948,17 +2049,17 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1948
2049
|
const targetOk = looksLikeFreyaWorkspace(target);
|
|
1949
2050
|
const empty = looksEmptyWorkspace(target);
|
|
1950
2051
|
if (!targetOk && !empty) {
|
|
1951
|
-
process.stdout.
|
|
2052
|
+
fs.writeSync(process.stdout.fd, `Dev seed: skipped (workspace not empty and not initialized) -> ${target}\n`);
|
|
1952
2053
|
} else {
|
|
1953
2054
|
seedDevWorkspace(target);
|
|
1954
|
-
process.stdout.
|
|
2055
|
+
fs.writeSync(process.stdout.fd, `Dev seed: created demo files in ${target}\n`);
|
|
1955
2056
|
}
|
|
1956
2057
|
} catch (e) {
|
|
1957
|
-
process.stdout.
|
|
2058
|
+
fs.writeSync(process.stdout.fd, `Dev seed failed: ${e.message || String(e)}\n`);
|
|
1958
2059
|
}
|
|
1959
2060
|
}
|
|
1960
2061
|
|
|
1961
|
-
process.stdout.
|
|
2062
|
+
fs.writeSync(process.stdout.fd, `FREYA web running at ${url}\n`);
|
|
1962
2063
|
if (open) openBrowser(url);
|
|
1963
2064
|
}
|
|
1964
2065
|
|