@cccarv82/freya 2.3.13 → 2.5.0
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/.agent/rules/freya/agents/coach.mdc +7 -16
- package/.agent/rules/freya/agents/ingestor.mdc +1 -89
- package/.agent/rules/freya/agents/master.mdc +3 -0
- package/.agent/rules/freya/agents/oracle.mdc +7 -23
- package/cli/web-ui.css +965 -182
- package/cli/web-ui.js +551 -173
- package/cli/web.js +863 -536
- package/package.json +7 -4
- package/scripts/build-vector-index.js +85 -0
- package/scripts/export-obsidian.js +6 -16
- package/scripts/generate-blockers-report.js +5 -17
- package/scripts/generate-daily-summary.js +25 -58
- package/scripts/generate-executive-report.js +22 -204
- package/scripts/generate-sm-weekly-report.js +27 -92
- package/scripts/lib/DataLayer.js +92 -0
- package/scripts/lib/DataManager.js +198 -0
- package/scripts/lib/Embedder.js +59 -0
- package/scripts/lib/schema.js +23 -0
- package/scripts/migrate-v1-v2.js +184 -0
- package/scripts/validate-data.js +48 -51
- package/scripts/validate-structure.js +12 -58
- package/templates/base/scripts/build-vector-index.js +85 -0
- package/templates/base/scripts/export-obsidian.js +143 -0
- package/templates/base/scripts/generate-daily-summary.js +25 -58
- package/templates/base/scripts/generate-executive-report.js +14 -225
- package/templates/base/scripts/generate-sm-weekly-report.js +9 -91
- package/templates/base/scripts/index/build-index.js +13 -0
- package/templates/base/scripts/index/update-index.js +15 -0
- package/templates/base/scripts/lib/DataLayer.js +92 -0
- package/templates/base/scripts/lib/DataManager.js +198 -0
- package/templates/base/scripts/lib/Embedder.js +59 -0
- package/templates/base/scripts/lib/index-utils.js +407 -0
- package/templates/base/scripts/lib/schema.js +23 -0
- package/templates/base/scripts/lib/search-utils.js +183 -0
- package/templates/base/scripts/migrate-v1-v2.js +184 -0
- package/templates/base/scripts/validate-data.js +48 -51
- package/templates/base/scripts/validate-structure.js +10 -32
package/cli/web.js
CHANGED
|
@@ -8,6 +8,8 @@ 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
|
const { initWorkspace } = require('./init');
|
|
11
|
+
const { defaultInstance: dl } = require('../scripts/lib/DataLayer');
|
|
12
|
+
const DataManager = require('../scripts/lib/DataManager');
|
|
11
13
|
|
|
12
14
|
function readAppVersion() {
|
|
13
15
|
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
@@ -943,6 +945,153 @@ function timelineHtml(defaultDir) {
|
|
|
943
945
|
return buildTimelineHtml(safeDefault, APP_VERSION);
|
|
944
946
|
}
|
|
945
947
|
|
|
948
|
+
function settingsHtml(defaultDir) {
|
|
949
|
+
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
950
|
+
return buildSettingsHtml(safeDefault, APP_VERSION);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function docsHtml(defaultDir) {
|
|
954
|
+
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
955
|
+
return buildDocsHtml(safeDefault, APP_VERSION);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function buildDocsHtml(safeDefault, appVersion) {
|
|
959
|
+
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
960
|
+
return `<!doctype html>
|
|
961
|
+
<html>
|
|
962
|
+
<head>
|
|
963
|
+
<meta charset="utf-8" />
|
|
964
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
965
|
+
<title>FREYA Documentation</title>
|
|
966
|
+
<link rel="stylesheet" href="/app.css" />
|
|
967
|
+
<style>
|
|
968
|
+
.docs-content {
|
|
969
|
+
max-width: 900px;
|
|
970
|
+
margin: 0 auto;
|
|
971
|
+
padding: 40px 20px;
|
|
972
|
+
line-height: 1.6;
|
|
973
|
+
}
|
|
974
|
+
.docs-card {
|
|
975
|
+
background: var(--paper);
|
|
976
|
+
border: 1px solid var(--border);
|
|
977
|
+
border-radius: 12px;
|
|
978
|
+
padding: 30px;
|
|
979
|
+
margin-bottom: 24px;
|
|
980
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.05);
|
|
981
|
+
}
|
|
982
|
+
.docs-h1 { color: var(--primary); margin-top: 0; }
|
|
983
|
+
.docs-h2 { border-bottom: 1px solid var(--border); padding-bottom: 8px; margin-top: 32px; color: var(--text); }
|
|
984
|
+
.docs-code { background: var(--bg2); padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.9em; }
|
|
985
|
+
.docs-pre { background: var(--bg2); padding: 16px; border-radius: 8px; overflow-x: auto; font-family: monospace; border: 1px solid var(--border); }
|
|
986
|
+
.docs-ul { padding-left: 20px; }
|
|
987
|
+
.docs-ul li { margin-bottom: 10px; }
|
|
988
|
+
.docs-strong { color: var(--accent); }
|
|
989
|
+
</style>
|
|
990
|
+
</head>
|
|
991
|
+
<body data-page="docs">
|
|
992
|
+
<div class="app">
|
|
993
|
+
<div class="frame">
|
|
994
|
+
<div class="shell">
|
|
995
|
+
|
|
996
|
+
<aside class="rail">
|
|
997
|
+
<div class="railTop">
|
|
998
|
+
<div class="railLogo">F</div>
|
|
999
|
+
</div>
|
|
1000
|
+
<div class="railNav">
|
|
1001
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1002
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1003
|
+
</button>
|
|
1004
|
+
<button class="railBtn" id="railReports" type="button" title="Relatórios">
|
|
1005
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1006
|
+
</button>
|
|
1007
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1008
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1009
|
+
</button>
|
|
1010
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1011
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1012
|
+
</button>
|
|
1013
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
1014
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1015
|
+
</button>
|
|
1016
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
1017
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1018
|
+
</button>
|
|
1019
|
+
<button class="railBtn active" id="railDocs" type="button" title="Documentação">
|
|
1020
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1021
|
+
</button>
|
|
1022
|
+
</div>
|
|
1023
|
+
<div class="railBottom">
|
|
1024
|
+
<button id="railSettings" class="railBtn" title="Configurações" onclick="if (document.body.dataset.page !== 'settings') window.location.href='/settings';">
|
|
1025
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
|
|
1026
|
+
</button>
|
|
1027
|
+
</div>
|
|
1028
|
+
</aside>
|
|
1029
|
+
|
|
1030
|
+
<main class="center">
|
|
1031
|
+
<div class="topbar">
|
|
1032
|
+
<div class="brandLine">
|
|
1033
|
+
<span class="spark"></span>
|
|
1034
|
+
<div class="brandStack">
|
|
1035
|
+
<div class="brand">FREYA</div>
|
|
1036
|
+
<div class="brandSub">Documentação Oficial</div>
|
|
1037
|
+
</div>
|
|
1038
|
+
</div>
|
|
1039
|
+
<div class="topActions">
|
|
1040
|
+
<span class="chip clickable" onclick="window.location.href='/docs'" style="background: var(--primary); color: white;">Docs</span>
|
|
1041
|
+
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
1042
|
+
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
1043
|
+
</div>
|
|
1044
|
+
</div>
|
|
1045
|
+
|
|
1046
|
+
<div class="centerBody">
|
|
1047
|
+
<div class="docs-content">
|
|
1048
|
+
<div class="docs-card">
|
|
1049
|
+
<h1 class="docs-h1">📚 Guia do Usuário FREYA</h1>
|
|
1050
|
+
<p>Bem-vindo à FREYA (<strong class="docs-strong">Fast Reporting & Executive Yield Assistant</strong>), sua ferramenta local-first para gestão de status e relatórios executivos.</p>
|
|
1051
|
+
|
|
1052
|
+
<h2 class="docs-h2">🚀 Começo Rápido (CLI)</h2>
|
|
1053
|
+
<p>A FREYA nasceu no terminal. Use os comandos abaixo no seu diretório de trabalho:</p>
|
|
1054
|
+
<ul class="docs-ul">
|
|
1055
|
+
<li><code class="docs-code">freya task "Sua tarefa"</code>: Cria uma tarefa pendente.</li>
|
|
1056
|
+
<li><code class="docs-code">freya blocker "Impedimento"</code>: Registra um bloqueio crítico.</li>
|
|
1057
|
+
<li><code class="docs-code">freya status "Update"</code>: Adiciona item ao histórico de um projeto.</li>
|
|
1058
|
+
<li><code class="docs-code">freya daily</code>: Abre seu log diário para edição.</li>
|
|
1059
|
+
</ul>
|
|
1060
|
+
|
|
1061
|
+
<h2 class="docs-h2">💻 Dashboard Web</h2>
|
|
1062
|
+
<ul class="docs-ul">
|
|
1063
|
+
<li><strong class="docs-strong">Inbox & Prompt</strong>: Cole textos e a FREYA extrai tarefas/bloqueios via análise inteligente.</li>
|
|
1064
|
+
<li><strong class="docs-strong">Oracle (RAG)</strong>: Use o chat para perguntar sobre seu histórico local.</li>
|
|
1065
|
+
<li><strong class="docs-strong">Hoje</strong>: Visualize suas pendências por categoria (DO_NOW, SCHEDULE, etc).</li>
|
|
1066
|
+
</ul>
|
|
1067
|
+
|
|
1068
|
+
<h2 class="docs-h2">📊 Relatórios Automatizados</h2>
|
|
1069
|
+
<p>Gere documentos Markdown prontos para uso:</p>
|
|
1070
|
+
<ul class="docs-ul">
|
|
1071
|
+
<li><strong class="docs-strong">Executivo</strong>: Visão geral para gerência.</li>
|
|
1072
|
+
<li><strong class="docs-strong">SM Weekly</strong>: Indicadores de fluxo e aging.</li>
|
|
1073
|
+
<li><strong class="docs-strong">Blockers</strong>: Lista focada em impedimentos.</li>
|
|
1074
|
+
</ul>
|
|
1075
|
+
<div class="docs-pre">Dica: Use os Webhooks para publicar direto no Discord ou Microsoft Teams.</div>
|
|
1076
|
+
|
|
1077
|
+
<h2 class="docs-h2">⚙️ Configurações</h2>
|
|
1078
|
+
<p>Configure sua workspace, webhooks de notificação e regras de <strong class="docs-strong">Smart Mapping</strong> para associar updates a projetos automaticamente.</p>
|
|
1079
|
+
</div>
|
|
1080
|
+
</div>
|
|
1081
|
+
</div>
|
|
1082
|
+
</main>
|
|
1083
|
+
</div>
|
|
1084
|
+
</div>
|
|
1085
|
+
</div>
|
|
1086
|
+
|
|
1087
|
+
<script>
|
|
1088
|
+
window.__FREYA_DEFAULT_DIR = "${safeDefault}";
|
|
1089
|
+
</script>
|
|
1090
|
+
<script src="/app.js"></script>
|
|
1091
|
+
</body>
|
|
1092
|
+
</html>`;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
946
1095
|
function buildHtml(safeDefault, appVersion) {
|
|
947
1096
|
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
948
1097
|
return `<!doctype html>
|
|
@@ -963,15 +1112,30 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
963
1112
|
<div class="railLogo">F</div>
|
|
964
1113
|
</div>
|
|
965
1114
|
<div class="railNav">
|
|
966
|
-
<button class="railBtn active" id="railDashboard" type="button" title="Dashboard">
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
<button class="railBtn" id="
|
|
970
|
-
|
|
971
|
-
|
|
1115
|
+
<button class="railBtn active" id="railDashboard" type="button" title="Dashboard">
|
|
1116
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1117
|
+
</button>
|
|
1118
|
+
<button class="railBtn" id="railReports" type="button" title="Relatórios">
|
|
1119
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1120
|
+
</button>
|
|
1121
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1122
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1123
|
+
</button>
|
|
1124
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1125
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1126
|
+
</button>
|
|
1127
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
1128
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1129
|
+
</button>
|
|
1130
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
1131
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1132
|
+
</button>
|
|
1133
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
1134
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1135
|
+
</button>
|
|
972
1136
|
</div>
|
|
973
|
-
<div class="railBottom">
|
|
974
|
-
|
|
1137
|
+
<div class="railBottom">\n <button id="railSettings" class="railBtn" title="Configurações" onclick="if (document.body.dataset.page !== \'settings\') window.location.href=\'/settings\';"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button>
|
|
1138
|
+
|
|
975
1139
|
</div>
|
|
976
1140
|
</aside>
|
|
977
1141
|
|
|
@@ -985,66 +1149,80 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
985
1149
|
</div>
|
|
986
1150
|
</div>
|
|
987
1151
|
<div class="topActions">
|
|
1152
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
988
1153
|
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
989
1154
|
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
990
1155
|
</div>
|
|
991
1156
|
</div>
|
|
992
1157
|
|
|
993
1158
|
<div class="centerBody">
|
|
994
|
-
<section
|
|
995
|
-
<div class="promptBar">
|
|
996
|
-
<div
|
|
997
|
-
<div class="
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
<
|
|
1159
|
+
<section style="margin-bottom: 24px; display: grid; grid-template-columns: 1fr 1fr; gap: 16px; height: 380px; max-height: 450px;">
|
|
1160
|
+
<div class="promptBar" style="width: 100%; border-radius: 20px; height: 100%; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden;">
|
|
1161
|
+
<div style="flex: 1; display: flex; flex-direction: column;">
|
|
1162
|
+
<div class="promptMeta">
|
|
1163
|
+
<div class="promptTitle" style="display: flex; align-items: center; gap: 8px;">
|
|
1164
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: var(--primary)"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
|
|
1165
|
+
<span>Inbox & Prompt</span>
|
|
1166
|
+
</div>
|
|
1167
|
+
<div id="status" class="small">pronto</div>
|
|
1168
|
+
</div>
|
|
1169
|
+
<textarea id="inboxText" placeholder="Cole updates do dia (status, blockers, decisões, ideias) ou faça uma pergunta para a Freya..." style="resize:none; flex: 1; min-height: 0;"></textarea>
|
|
1005
1170
|
</div>
|
|
1006
|
-
<div
|
|
1007
|
-
<
|
|
1008
|
-
<
|
|
1009
|
-
|
|
1010
|
-
</
|
|
1011
|
-
<
|
|
1012
|
-
<
|
|
1013
|
-
|
|
1014
|
-
|
|
1171
|
+
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; margin-top: 12px;">
|
|
1172
|
+
<div class="promptActions" style="margin: 0;">
|
|
1173
|
+
<button class="btn primary small" type="button" onclick="saveAndPlan()">Salvar + Processar</button>
|
|
1174
|
+
<button class="btn small" type="button" onclick="runSuggestedReports()">Rodar relatórios sugeridos</button>
|
|
1175
|
+
</div>
|
|
1176
|
+
<div class="promptToggles" style="margin: 0;">
|
|
1177
|
+
<label class="toggleRow">
|
|
1178
|
+
<input id="autoApply" type="checkbox" checked style="width:auto; margin: 0;" onchange="toggleAutoApply()" />
|
|
1179
|
+
Auto-apply
|
|
1180
|
+
</label>
|
|
1181
|
+
<label class="toggleRow">
|
|
1182
|
+
<input id="autoRunReports" type="checkbox" style="width:auto; margin: 0;" onchange="toggleAutoRunReports()" />
|
|
1183
|
+
Auto-run reports
|
|
1184
|
+
</label>
|
|
1185
|
+
</div>
|
|
1015
1186
|
</div>
|
|
1016
1187
|
</div>
|
|
1017
|
-
</section>
|
|
1018
1188
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
<
|
|
1189
|
+
<div class="panel" style="height: 100%; display: flex; flex-direction: column;">
|
|
1190
|
+
<div class="panelHead">
|
|
1191
|
+
<b style="display: flex; align-items: center; gap: 8px;">
|
|
1192
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
|
|
1193
|
+
Consultar Oracle
|
|
1194
|
+
</b>
|
|
1195
|
+
<div class="stack">
|
|
1196
|
+
<button class="btn small" type="button" onclick="exportChatObsidian()">Exportar Log</button>
|
|
1197
|
+
</div>
|
|
1026
1198
|
</div>
|
|
1027
|
-
<div class="
|
|
1028
|
-
<
|
|
1029
|
-
<
|
|
1199
|
+
<div class="panelBody" style="flex:1; display:flex; flex-direction:column; padding:0; background:var(--glass-bg); min-height:0;">
|
|
1200
|
+
<div id="chatThread" style="flex:1; overflow-y:auto; overflow-x:hidden; padding:12px; display:flex; flex-direction:column; gap:8px;"></div>
|
|
1201
|
+
<div style="padding: 12px; border-top: 1px solid var(--glass-border); display:flex; gap: 8px; background: var(--paper);">
|
|
1202
|
+
<input type="text" id="oracleInput" autocomplete="off" style="flex:1;" placeholder="Pergunte algo à Freya..." onkeydown="if(event.key==='Enter') window.askFreyaInline()" />
|
|
1203
|
+
<button class="btn primary small" type="button" onclick="window.askFreyaInline()">Enviar</button>
|
|
1204
|
+
</div>
|
|
1030
1205
|
</div>
|
|
1031
|
-
<div style="height:10px"></div>
|
|
1032
|
-
<div class="help"><b>Sync workspace</b>: atualiza scripts/templates/agents na pasta <code>freya</code> sem sobrescrever <code>data/</code> e <code>logs/</code>.</div>
|
|
1033
|
-
<div class="help"><b>Migrate data</b>: ajusta formatos/schemaVersion quando uma versão nova exige.</div>
|
|
1034
1206
|
</div>
|
|
1207
|
+
</section>
|
|
1035
1208
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
<div
|
|
1039
|
-
<
|
|
1040
|
-
<
|
|
1041
|
-
|
|
1042
|
-
|
|
1209
|
+
<section class="utilityGrid" id="dashboardControls" style="display:grid; grid-template-columns: 1fr; gap: 16px; margin-bottom: 24px;">
|
|
1210
|
+
<div class="utilityCard" style="display:flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 16px;">
|
|
1211
|
+
<div>
|
|
1212
|
+
<div class="utilityHead" style="margin-bottom: 4px; color: var(--text);">Relatórios Rápidos</div>
|
|
1213
|
+
<div class="help" style="margin: 0;">Gere relatórios sintéticos instantaneamente</div>
|
|
1214
|
+
</div>
|
|
1215
|
+
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
|
|
1216
|
+
<button class="btn" style="min-width: 100px; padding: 6px 12px; font-weight: 500;" type="button" onclick="runReport('status')">Executivo</button>
|
|
1217
|
+
<button class="btn" style="min-width: 100px; padding: 6px 12px; font-weight: 500;" type="button" onclick="runReport('sm-weekly')">SM Semanal</button>
|
|
1218
|
+
<button class="btn" style="min-width: 100px; padding: 6px 12px; font-weight: 500; border-color: rgba(239, 68, 68, 0.4); color: #f87171;" type="button" onclick="runReport('blockers')">Bloqueios</button>
|
|
1219
|
+
<button class="btn" style="min-width: 100px; padding: 6px 12px; font-weight: 500;" type="button" onclick="runReport('daily')">Daily</button>
|
|
1043
1220
|
</div>
|
|
1044
|
-
<div class="help" style="margin-top:8px">Clique para gerar e atualizar o preview/publicação.</div>
|
|
1045
1221
|
</div>
|
|
1046
1222
|
</section>
|
|
1047
1223
|
|
|
1224
|
+
|
|
1225
|
+
|
|
1048
1226
|
<div class="centerHead">
|
|
1049
1227
|
<div>
|
|
1050
1228
|
<h1 style="margin:0">Seu dia em um painel</h1>
|
|
@@ -1056,63 +1234,40 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1056
1234
|
</div>
|
|
1057
1235
|
|
|
1058
1236
|
<div class="midGrid">
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1237
|
+
<!-- Hoje Panel gets more prominent focus -->
|
|
1238
|
+
<section class="panel midSpan">
|
|
1239
|
+
<div class="panelHead" style="background: linear-gradient(90deg, var(--paper2), var(--paper)); border-left: 4px solid var(--accent)">
|
|
1240
|
+
<b style="color: var(--text); font-size: 14px;">Foco de Hoje</b>
|
|
1062
1241
|
<div class="stack">
|
|
1063
1242
|
<button class="btn small" type="button" onclick="refreshToday()">Atualizar</button>
|
|
1064
1243
|
</div>
|
|
1065
1244
|
</div>
|
|
1066
|
-
<div class="panelBody
|
|
1067
|
-
<div
|
|
1068
|
-
|
|
1069
|
-
<div style="
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1245
|
+
<div class="panelBody" style="display: flex; flex-direction: column; gap: 16px;">
|
|
1246
|
+
<div id="swimlaneContainer" style="display:flex; flex-direction: column; gap: 12px;"></div>
|
|
1247
|
+
|
|
1248
|
+
<div style="border-top: 1px solid var(--border); padding-top: 16px;">
|
|
1249
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
|
1250
|
+
<div class="small" style="opacity:.8; font-weight: 600; text-transform: uppercase;">Insights Globais de Bloqueios</div>
|
|
1251
|
+
<button class="btn small" type="button" onclick="refreshBlockersInsights()">Atualizar insights</button>
|
|
1252
|
+
</div>
|
|
1253
|
+
<div id="blockersInsights" class="help"></div>
|
|
1254
|
+
</div>
|
|
1076
1255
|
</div>
|
|
1077
1256
|
</section>
|
|
1078
1257
|
|
|
1079
1258
|
<section class="panel">
|
|
1080
1259
|
<div class="panelHead">
|
|
1081
|
-
<b>Relatórios</b>
|
|
1260
|
+
<b>Relatórios Recentes</b>
|
|
1082
1261
|
<div class="stack">
|
|
1083
1262
|
<button class="btn small" type="button" onclick="refreshReports()">Atualizar</button>
|
|
1084
1263
|
</div>
|
|
1085
1264
|
</div>
|
|
1086
|
-
<div class="panelBody panelScroll">
|
|
1087
|
-
<input id="reportsFilter" placeholder="
|
|
1265
|
+
<div class="panelBody panelScroll" style="max-height: 300px;">
|
|
1266
|
+
<input id="reportsFilter" placeholder="Filtrar relatórios..." style="width:100%; margin-bottom:10px" oninput="renderReportsList()" />
|
|
1088
1267
|
<div id="reportsList" style="display:grid; gap:8px"></div>
|
|
1089
|
-
<div class="help">Últimos relatórios em <code>docs/reports</code>. Clique para abrir o preview.</div>
|
|
1090
|
-
</div>
|
|
1091
|
-
</section>
|
|
1092
|
-
|
|
1093
|
-
<section class="panel midSpan">
|
|
1094
|
-
<div class="panelHead">
|
|
1095
|
-
<b>Preview</b>
|
|
1096
|
-
<div class="stack">
|
|
1097
|
-
<button class="btn small" type="button" onclick="copyOut()">Copy</button>
|
|
1098
|
-
<button class="btn small" type="button" onclick="applyPlan()">Apply plan</button>
|
|
1099
|
-
<button class="btn small" type="button" onclick="copyPath()">Copy path</button>
|
|
1100
|
-
<button class="btn small" type="button" onclick="openSelected()">Open file</button>
|
|
1101
|
-
<button class="btn small" type="button" onclick="downloadSelected()">Download .md</button>
|
|
1102
|
-
<button class="btn small" type="button" onclick="clearOut()">Clear</button>
|
|
1103
|
-
</div>
|
|
1104
|
-
</div>
|
|
1105
|
-
<div class="panelBody">
|
|
1106
|
-
<div id="reportPreview" class="log md" style="font-family: var(--sans);"></div>
|
|
1107
|
-
<div class="help">O preview renderiza Markdown básico (headers, listas, code). O botão Copy copia o conteúdo completo.</div>
|
|
1108
1268
|
</div>
|
|
1109
1269
|
</section>
|
|
1110
1270
|
</div>
|
|
1111
|
-
|
|
1112
|
-
<details class="devDrawer" id="devDrawer">
|
|
1113
|
-
<summary>Developer (modo avançado)</summary>
|
|
1114
|
-
<div class="devBody">
|
|
1115
|
-
<div class="devGrid">
|
|
1116
1271
|
<div class="panel">
|
|
1117
1272
|
<div class="panelHead"><b>Configurações de publicação</b></div>
|
|
1118
1273
|
<div class="panelBody">
|
|
@@ -1128,61 +1283,6 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1128
1283
|
<label style="display:flex; align-items:center; gap:10px; user-select:none; margin: 6px 0 12px 0">
|
|
1129
1284
|
<input id="prettyPublish" type="checkbox" checked style="width:auto" onchange="togglePrettyPublish()" />
|
|
1130
1285
|
Publicação bonita (cards/embeds)
|
|
1131
|
-
</label>
|
|
1132
|
-
|
|
1133
|
-
<div class="stack">
|
|
1134
|
-
<button class="btn" type="button" onclick="saveSettings()">Salvar configurações</button>
|
|
1135
|
-
<button class="btn" type="button" onclick="publish('discord')">Publicar selecionado → Discord</button>
|
|
1136
|
-
<button class="btn" type="button" onclick="publish('teams')">Publicar selecionado → Teams</button>
|
|
1137
|
-
</div>
|
|
1138
|
-
</div>
|
|
1139
|
-
</div>
|
|
1140
|
-
|
|
1141
|
-
<div class="panel">
|
|
1142
|
-
<div class="panelHead"><b>Slugs & Export</b></div>
|
|
1143
|
-
<div class="panelBody">
|
|
1144
|
-
<label>Regras de slug do projeto</label>
|
|
1145
|
-
<textarea id="slugRules" rows="8" placeholder="{ \"rules\": [ { \"contains\": \"fideliza\", \"slug\": \"vivo/fidelizacao\" } ] }"></textarea>
|
|
1146
|
-
<div class="help">Regras usadas pra inferir <code>projectSlug</code>. Formato JSON (objeto com <code>rules</code>).</div>
|
|
1147
|
-
<div class="stack" style="margin-top:10px">
|
|
1148
|
-
<button class="btn" type="button" onclick="reloadSlugRules()">Recarregar regras</button>
|
|
1149
|
-
<button class="btn" type="button" onclick="saveSlugRules()">Salvar regras</button>
|
|
1150
|
-
<button class="btn" type="button" onclick="exportObsidian()">Exportar notas (Obsidian)</button>
|
|
1151
|
-
</div>
|
|
1152
|
-
</div>
|
|
1153
|
-
</div>
|
|
1154
|
-
|
|
1155
|
-
<div class="panel">
|
|
1156
|
-
<div class="panelHead"><b>Debug</b></div>
|
|
1157
|
-
<div class="panelBody">
|
|
1158
|
-
<div class="help">Logs ficam em <code>logs/</code> e debug traces em <code>.debuglogs/</code> dentro da workspace.</div>
|
|
1159
|
-
<div class="help">Use <b>Open file</b> / <b>Copy path</b> no Preview para abrir/compartilhar o relatório selecionado.</div>
|
|
1160
|
-
<div class="stack" style="margin-top:10px">
|
|
1161
|
-
<button class="btn" type="button" onclick="rebuildIndex()">Rebuild search index</button>
|
|
1162
|
-
</div>
|
|
1163
|
-
</div>
|
|
1164
|
-
</div>
|
|
1165
|
-
</div>
|
|
1166
|
-
</div>
|
|
1167
|
-
</details>
|
|
1168
|
-
</div>
|
|
1169
|
-
</main>
|
|
1170
|
-
|
|
1171
|
-
<aside class="chatPane">
|
|
1172
|
-
<div class="chatHead">
|
|
1173
|
-
<div>
|
|
1174
|
-
<div class="chatTitle">Conversa</div>
|
|
1175
|
-
<div class="chatSub">Cole seus updates e deixe os Agents planejar/aplicar.</div>
|
|
1176
|
-
</div>
|
|
1177
|
-
</div>
|
|
1178
|
-
|
|
1179
|
-
<div class="chatThread" id="chatThread">
|
|
1180
|
-
<div class="bubble assistant">
|
|
1181
|
-
<div class="bubbleMeta">FREYA</div>
|
|
1182
|
-
<div class="bubbleBody">Cole seus updates (status, blockers, decisões, ideias) e clique em <b>Save + Process</b>.</div>
|
|
1183
|
-
</div>
|
|
1184
|
-
</div>
|
|
1185
|
-
</aside>
|
|
1186
1286
|
|
|
1187
1287
|
</div>
|
|
1188
1288
|
</div>
|
|
@@ -1216,15 +1316,30 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1216
1316
|
<div class="railLogo">F</div>
|
|
1217
1317
|
</div>
|
|
1218
1318
|
<div class="railNav">
|
|
1219
|
-
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
<button class="railBtn" id="
|
|
1223
|
-
|
|
1224
|
-
|
|
1319
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1320
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1321
|
+
</button>
|
|
1322
|
+
<button class="railBtn active" id="railReports" type="button" title="Relatórios">
|
|
1323
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1324
|
+
</button>
|
|
1325
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1326
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1327
|
+
</button>
|
|
1328
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1329
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1330
|
+
</button>
|
|
1331
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
1332
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1333
|
+
</button>
|
|
1334
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
1335
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1336
|
+
</button>
|
|
1337
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
1338
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1339
|
+
</button>
|
|
1225
1340
|
</div>
|
|
1226
|
-
<div class="railBottom">
|
|
1227
|
-
|
|
1341
|
+
<div class="railBottom">\n <button id="railSettings" class="railBtn" title="Configurações">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>\n </button>
|
|
1342
|
+
|
|
1228
1343
|
</div>
|
|
1229
1344
|
</aside>
|
|
1230
1345
|
|
|
@@ -1238,6 +1353,7 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1238
1353
|
</div>
|
|
1239
1354
|
</div>
|
|
1240
1355
|
<div class="topActions">
|
|
1356
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
1241
1357
|
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
1242
1358
|
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
1243
1359
|
</div>
|
|
@@ -1256,8 +1372,12 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1256
1372
|
</div>
|
|
1257
1373
|
</section>
|
|
1258
1374
|
|
|
1259
|
-
<section class="reportsTools">
|
|
1260
|
-
<
|
|
1375
|
+
<section class="reportsTools" style="display:flex; gap:12px; align-items:center;">
|
|
1376
|
+
<div class="tabs" style="display:flex; gap:8px;" id="reportsTabs">
|
|
1377
|
+
<button class="pill ok" id="tabChrono" onclick="setReportsTab('chrono')">Cronológico</button>
|
|
1378
|
+
<button class="pill" id="tabType" onclick="setReportsTab('type')">Por Tipo</button>
|
|
1379
|
+
</div>
|
|
1380
|
+
<input id="reportsFilter" style="flex:1" placeholder="filtrar (ex: daily, executive, 2026-01-29)" oninput="renderReportsPage()" />
|
|
1261
1381
|
</section>
|
|
1262
1382
|
|
|
1263
1383
|
<section class="reportsGrid" id="reportsGrid"></section>
|
|
@@ -1296,15 +1416,29 @@ function buildProjectsHtml(safeDefault, appVersion) {
|
|
|
1296
1416
|
<div class="railLogo">F</div>
|
|
1297
1417
|
</div>
|
|
1298
1418
|
<div class="railNav">
|
|
1299
|
-
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
<button class="railBtn
|
|
1303
|
-
|
|
1304
|
-
|
|
1419
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1420
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1421
|
+
</button>
|
|
1422
|
+
<button class="railBtn" id="railReports" type="button" title="Relatorios">
|
|
1423
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1424
|
+
</button>
|
|
1425
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1426
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1427
|
+
</button>
|
|
1428
|
+
<button class="railBtn active" id="railProjects" type="button" title="Projects">
|
|
1429
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1430
|
+
</button>
|
|
1431
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
1432
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1433
|
+
</button>
|
|
1434
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
1435
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1436
|
+
</button>
|
|
1437
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
1438
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1439
|
+
</button>
|
|
1305
1440
|
</div>
|
|
1306
|
-
<div class="railBottom"
|
|
1307
|
-
<div class="railStatus" id="railStatus" title="status"></div>
|
|
1441
|
+
<div class="railBottom">\n <button id="railSettings" class="railBtn" title="Configurações" onclick="if (document.body.dataset.page !== 'settings') window.location.href='/settings';"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button>\n
|
|
1308
1442
|
</div>
|
|
1309
1443
|
</aside>
|
|
1310
1444
|
|
|
@@ -1318,6 +1452,7 @@ function buildProjectsHtml(safeDefault, appVersion) {
|
|
|
1318
1452
|
</div>
|
|
1319
1453
|
</div>
|
|
1320
1454
|
<div class="topActions">
|
|
1455
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
1321
1456
|
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
1322
1457
|
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
1323
1458
|
</div>
|
|
@@ -1376,12 +1511,27 @@ function buildTimelineHtml(safeDefault, appVersion) {
|
|
|
1376
1511
|
<div class=\"railLogo\">F</div>
|
|
1377
1512
|
</div>
|
|
1378
1513
|
<div class=\"railNav\">
|
|
1379
|
-
<button class=\"railBtn\" id=\"railDashboard\" type=\"button\" title=\"Dashboard\">
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
<button class=\"railBtn\" id=\"
|
|
1383
|
-
|
|
1384
|
-
|
|
1514
|
+
<button class=\"railBtn\" id=\"railDashboard\" type=\"button\" title=\"Dashboard\">
|
|
1515
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1516
|
+
</button>
|
|
1517
|
+
<button class=\"railBtn\" id=\"railReports\" type=\"button\" title=\"Relatorios\">
|
|
1518
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1519
|
+
</button>
|
|
1520
|
+
<button class=\"railBtn\" id=\"railCompanion\" type=\"button\" title=\"Companion\">
|
|
1521
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1522
|
+
</button>
|
|
1523
|
+
<button class=\"railBtn\" id=\"railProjects\" type=\"button\" title=\"Projects\">
|
|
1524
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1525
|
+
</button>
|
|
1526
|
+
<button class=\"railBtn active\" id=\"railTimeline\" type=\"button\" title=\"Timeline\">
|
|
1527
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1528
|
+
</button>
|
|
1529
|
+
<button class=\"railBtn\" id=\"railGraph\" type=\"button\" title=\"Grafo\">
|
|
1530
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1531
|
+
</button>
|
|
1532
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
1533
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1534
|
+
</button>
|
|
1385
1535
|
</div>
|
|
1386
1536
|
<div class=\"railBottom\">
|
|
1387
1537
|
<div class=\"railStatus\" id=\"railStatus\" title=\"status\"></div>
|
|
@@ -1398,6 +1548,7 @@ function buildTimelineHtml(safeDefault, appVersion) {
|
|
|
1398
1548
|
</div>
|
|
1399
1549
|
</div>
|
|
1400
1550
|
<div class=\"topActions\">
|
|
1551
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
1401
1552
|
<span class=\"chip\" id=\"chipVersion\">v${safeVersion}</span>
|
|
1402
1553
|
<span class=\"chip\" id=\"chipPort\">127.0.0.1:3872</span>
|
|
1403
1554
|
</div>
|
|
@@ -1471,15 +1622,29 @@ function buildGraphHtml(safeDefault, appVersion) {
|
|
|
1471
1622
|
<div class="railLogo">F</div>
|
|
1472
1623
|
</div>
|
|
1473
1624
|
<div class="railNav">
|
|
1474
|
-
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
<button class="railBtn" id="
|
|
1478
|
-
|
|
1479
|
-
|
|
1625
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1626
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1627
|
+
</button>
|
|
1628
|
+
<button class="railBtn" id="railReports" type="button" title="Relatorios">
|
|
1629
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1630
|
+
</button>
|
|
1631
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1632
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1633
|
+
</button>
|
|
1634
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1635
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1636
|
+
</button>
|
|
1637
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
1638
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1639
|
+
</button>
|
|
1640
|
+
<button class="railBtn active" id="railGraph" type="button" title="Grafo">
|
|
1641
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1642
|
+
</button>
|
|
1643
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
1644
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1645
|
+
</button>
|
|
1480
1646
|
</div>
|
|
1481
|
-
<div class="railBottom"
|
|
1482
|
-
<div class="railStatus" id="railStatus" title="status"></div>
|
|
1647
|
+
<div class="railBottom">\n <button id="railSettings" class="railBtn" title="Configurações" onclick="if (document.body.dataset.page !== 'settings') window.location.href='/settings';"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button>\n
|
|
1483
1648
|
</div>
|
|
1484
1649
|
</aside>
|
|
1485
1650
|
|
|
@@ -1493,6 +1658,7 @@ function buildGraphHtml(safeDefault, appVersion) {
|
|
|
1493
1658
|
</div>
|
|
1494
1659
|
</div>
|
|
1495
1660
|
<div class="topActions">
|
|
1661
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
1496
1662
|
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
1497
1663
|
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
1498
1664
|
</div>
|
|
@@ -1549,15 +1715,29 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1549
1715
|
<div class="railLogo">F</div>
|
|
1550
1716
|
</div>
|
|
1551
1717
|
<div class="railNav">
|
|
1552
|
-
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
<button class="railBtn" id="
|
|
1556
|
-
|
|
1557
|
-
|
|
1718
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
1719
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
1720
|
+
</button>
|
|
1721
|
+
<button class="railBtn" id="railReports" type="button" title="Relatórios">
|
|
1722
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
1723
|
+
</button>
|
|
1724
|
+
<button class="railBtn active" id="railCompanion" type="button" title="Companion">
|
|
1725
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1726
|
+
</button>
|
|
1727
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1728
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1729
|
+
</button>
|
|
1730
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
1731
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1732
|
+
</button>
|
|
1733
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
1734
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
1735
|
+
</button>
|
|
1736
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
1737
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
1738
|
+
</button>
|
|
1558
1739
|
</div>
|
|
1559
|
-
<div class="railBottom"
|
|
1560
|
-
<div class="railStatus" id="railStatus" title="status"></div>
|
|
1740
|
+
<div class="railBottom">\n <button id="railSettings" class="railBtn" title="Configurações" onclick="if (document.body.dataset.page !== 'settings') window.location.href='/settings';"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg></button>\n
|
|
1561
1741
|
</div>
|
|
1562
1742
|
</aside>
|
|
1563
1743
|
|
|
@@ -1571,6 +1751,7 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1571
1751
|
</div>
|
|
1572
1752
|
</div>
|
|
1573
1753
|
<div class="topActions">
|
|
1754
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
1574
1755
|
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
1575
1756
|
<span class="chip" id="chipPort">127.0.0.1:3872</span>
|
|
1576
1757
|
</div>
|
|
@@ -1582,81 +1763,107 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1582
1763
|
<section class="reportsHeader">
|
|
1583
1764
|
<div>
|
|
1584
1765
|
<div class="reportsTitle">Scrum Master Companion</div>
|
|
1585
|
-
<div class="reportsSubtitle">Painel
|
|
1766
|
+
<div class="reportsSubtitle">Painel rápido para gerar relatórios e checar pendências.</div>
|
|
1586
1767
|
</div>
|
|
1587
1768
|
<div class="reportsActions">
|
|
1588
|
-
<button class="btn small" type="button" onclick="refreshHealthChecklist()">Atualizar</button>
|
|
1769
|
+
<button class="btn small" type="button" onclick="refreshHealthChecklist()">Atualizar Painel</button>
|
|
1770
|
+
</div>
|
|
1589
1771
|
</section>
|
|
1590
1772
|
|
|
1591
|
-
<section class="reportsTools" style="grid-template-columns: repeat(auto-fit,minmax(180px,1fr));">
|
|
1773
|
+
<section class="reportsTools" style="display: grid; grid-template-columns: repeat(auto-fit,minmax(180px,1fr)); gap: 12px; margin-bottom: 24px;">
|
|
1592
1774
|
<button class="btn" type="button" onclick="runReport('daily')">Gerar Daily</button>
|
|
1593
1775
|
<button class="btn" type="button" onclick="runReport('blockers')">Gerar Blockers</button>
|
|
1594
1776
|
<button class="btn" type="button" onclick="runReport('status')">Gerar Status</button>
|
|
1595
1777
|
<button class="btn" type="button" onclick="runReport('sm-weekly')">Gerar SM Weekly</button>
|
|
1596
1778
|
</section>
|
|
1597
1779
|
|
|
1598
|
-
|
|
1780
|
+
<!-- BENTO GRID LAYOUT -->
|
|
1781
|
+
<div style="display: grid; grid-template-columns: 1fr 1.5fr; gap: 24px; align-items: start;">
|
|
1782
|
+
|
|
1783
|
+
<!-- Left Column / Block 1 -->
|
|
1784
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
1785
|
+
<section class="panel">
|
|
1786
|
+
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1787
|
+
<b>Resumo Executivo</b>
|
|
1788
|
+
<button class="btn small" type="button" onclick="refreshExecutiveSummary()">↻</button>
|
|
1789
|
+
</div>
|
|
1790
|
+
<div class="panelBody">
|
|
1791
|
+
<div id="executiveSummary" class="help"></div>
|
|
1792
|
+
</div>
|
|
1793
|
+
</section>
|
|
1794
|
+
|
|
1795
|
+
<section class="panel">
|
|
1796
|
+
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1797
|
+
<b>Radar de Risco</b>
|
|
1798
|
+
<button class="btn small" type="button" onclick="refreshRiskRadar()">↻</button>
|
|
1799
|
+
</div>
|
|
1800
|
+
<div class="panelBody">
|
|
1801
|
+
<div id="riskRadarBox"></div>
|
|
1802
|
+
</div>
|
|
1803
|
+
</section>
|
|
1599
1804
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1805
|
+
<section class="panel">
|
|
1806
|
+
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1807
|
+
<b>Task Heatmap</b>
|
|
1808
|
+
</div>
|
|
1809
|
+
<div class="panelBody">
|
|
1810
|
+
<div id="heatmapGrid"></div>
|
|
1811
|
+
</div>
|
|
1812
|
+
</section>
|
|
1607
1813
|
</div>
|
|
1608
|
-
</section>
|
|
1609
1814
|
|
|
1610
|
-
|
|
1611
|
-
<div
|
|
1612
|
-
<
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
<section class="panel" style="margin-top:16px">
|
|
1621
|
-
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1622
|
-
<b>Anomalias</b>
|
|
1623
|
-
<button class="btn small" type="button" onclick="refreshAnomalies()">Atualizar</button>
|
|
1624
|
-
</div>
|
|
1625
|
-
<div class="panelBody">
|
|
1626
|
-
<div id="anomaliesBox"></div>
|
|
1627
|
-
</div>
|
|
1628
|
-
</section>
|
|
1815
|
+
<!-- Right Column / Block 2 -->
|
|
1816
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
1817
|
+
<section class="panel">
|
|
1818
|
+
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1819
|
+
<b style="color: var(--accent);">Saúde & Checklists</b>
|
|
1820
|
+
</div>
|
|
1821
|
+
<div class="panelBody" style="padding: 0;">
|
|
1822
|
+
<section class="reportsGrid" id="healthChecklist" style="gap: 0; border: none; box-shadow: none;"></section>
|
|
1823
|
+
</div>
|
|
1824
|
+
</section>
|
|
1629
1825
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1826
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
|
1827
|
+
<section class="panel">
|
|
1828
|
+
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1829
|
+
<b>Qualidade de Log</b>
|
|
1830
|
+
<button class="btn small" type="button" onclick="refreshQualityScore()">↻</button>
|
|
1831
|
+
</div>
|
|
1832
|
+
<div class="panelBody">
|
|
1833
|
+
<div id="qualityScoreCard"></div>
|
|
1834
|
+
</div>
|
|
1835
|
+
</section>
|
|
1639
1836
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1837
|
+
<section class="panel">
|
|
1838
|
+
<div class="panelHead" style="display:flex; align-items:center; justify-content:space-between; gap:10px">
|
|
1839
|
+
<b>Anomalias</b>
|
|
1840
|
+
<button class="btn small" type="button" onclick="refreshAnomalies()">↻</button>
|
|
1841
|
+
</div>
|
|
1842
|
+
<div class="panelBody">
|
|
1843
|
+
<div id="anomaliesBox"></div>
|
|
1844
|
+
</div>
|
|
1845
|
+
</section>
|
|
1846
|
+
</div>
|
|
1644
1847
|
</div>
|
|
1645
|
-
|
|
1848
|
+
|
|
1849
|
+
<!-- Full Width Span -->
|
|
1850
|
+
<div style="grid-column: 1 / -1; display: flex; flex-direction: column; gap: 16px;">
|
|
1851
|
+
<section class="panel">
|
|
1852
|
+
<div class="panelHead"><b>Incident Radar</b></div>
|
|
1853
|
+
<div class="panelBody">
|
|
1854
|
+
<div id="incidentsBox" class="log md" style="font-family: var(--sans);"></div>
|
|
1855
|
+
</div>
|
|
1856
|
+
</section>
|
|
1646
1857
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1858
|
+
<section class="panel">
|
|
1859
|
+
<div class="panelHead"><b>Saída de Relatório</b></div>
|
|
1860
|
+
<div class="panelBody">
|
|
1861
|
+
<div id="reportPreview" class="log md" style="font-family: var(--sans);"></div>
|
|
1862
|
+
</div>
|
|
1863
|
+
</section>
|
|
1651
1864
|
</div>
|
|
1652
|
-
</section>
|
|
1653
1865
|
|
|
1654
|
-
|
|
1655
|
-
<div class="panelHead"><b>Saida</b></div>
|
|
1656
|
-
<div class="panelBody">
|
|
1657
|
-
<div id="reportPreview" class="log md" style="font-family: var(--sans);"></div>
|
|
1658
|
-
</div>
|
|
1659
|
-
</section>
|
|
1866
|
+
</div>
|
|
1660
1867
|
</div>
|
|
1661
1868
|
</main>
|
|
1662
1869
|
|
|
@@ -2073,6 +2280,22 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2073
2280
|
return;
|
|
2074
2281
|
}
|
|
2075
2282
|
|
|
2283
|
+
if (req.method === 'GET' && req.url === '/settings') {
|
|
2284
|
+
try { res.__freyaDebug.workspaceDir = normalizeWorkspaceDir(dir || './freya'); } catch { }
|
|
2285
|
+
const body = settingsHtml(dir || './freya');
|
|
2286
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
2287
|
+
res.end(body);
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
if (req.method === 'GET' && req.url === '/docs') {
|
|
2292
|
+
try { res.__freyaDebug.workspaceDir = normalizeWorkspaceDir(dir || './freya'); } catch { }
|
|
2293
|
+
const body = docsHtml(dir || './freya');
|
|
2294
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
2295
|
+
res.end(body);
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2076
2299
|
if (req.method === 'GET' && req.url === '/app.css') {
|
|
2077
2300
|
const css = fs.readFileSync(path.join(__dirname, 'web-ui.css'), 'utf8');
|
|
2078
2301
|
res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
@@ -2166,34 +2389,23 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2166
2389
|
}
|
|
2167
2390
|
|
|
2168
2391
|
if (req.url === '/api/projects/list') {
|
|
2169
|
-
const
|
|
2392
|
+
const rawProjects = dl.db.prepare('SELECT * FROM projects').all();
|
|
2170
2393
|
const items = [];
|
|
2171
|
-
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
stream: doc.stream || null,
|
|
2187
|
-
project: doc.project || null,
|
|
2188
|
-
active: doc.active !== false,
|
|
2189
|
-
currentStatus: doc.currentStatus || '',
|
|
2190
|
-
lastUpdated: doc.lastUpdated || '',
|
|
2191
|
-
tags: Array.isArray(doc.tags) ? doc.tags : [],
|
|
2192
|
-
historyCount: Array.isArray(doc.history) ? doc.history.length : 0
|
|
2193
|
-
});
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2394
|
+
for (const p of rawProjects) {
|
|
2395
|
+
const h = dl.db.prepare('SELECT count(*) as count FROM project_status_history WHERE project_id = ?').get(p.id);
|
|
2396
|
+
const historyCount = h ? h.count : 0;
|
|
2397
|
+
const latestStatus = dl.db.prepare('SELECT status_text FROM project_status_history WHERE project_id = ? ORDER BY date DESC LIMIT 1').get(p.id);
|
|
2398
|
+
|
|
2399
|
+
items.push({
|
|
2400
|
+
slug: p.slug,
|
|
2401
|
+
client: p.client,
|
|
2402
|
+
project: p.name,
|
|
2403
|
+
active: p.is_active === 1,
|
|
2404
|
+
currentStatus: latestStatus ? latestStatus.status_text : '',
|
|
2405
|
+
lastUpdated: p.updated_at,
|
|
2406
|
+
historyCount: historyCount,
|
|
2407
|
+
tags: []
|
|
2408
|
+
});
|
|
2197
2409
|
}
|
|
2198
2410
|
items.sort((a, b) => String(b.lastUpdated || '').localeCompare(String(a.lastUpdated || '')));
|
|
2199
2411
|
return safeJson(res, 200, { ok: true, projects: items });
|
|
@@ -2213,68 +2425,33 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2213
2425
|
};
|
|
2214
2426
|
|
|
2215
2427
|
// 1. Load Projects
|
|
2216
|
-
const
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
while (stack.length) {
|
|
2220
|
-
const dirp = stack.pop();
|
|
2221
|
-
const entries = fs.readdirSync(dirp, { withFileTypes: true });
|
|
2222
|
-
for (const ent of entries) {
|
|
2223
|
-
const full = path.join(dirp, ent.name);
|
|
2224
|
-
if (ent.isDirectory()) stack.push(full);
|
|
2225
|
-
else if (ent.isFile() && ent.name === 'status.json') {
|
|
2226
|
-
const doc = readJsonOrNull(full) || {};
|
|
2227
|
-
const slug = path.relative(base, path.dirname(full)).replace(/\\/g, '/');
|
|
2228
|
-
addNode(slug, slug, 'project');
|
|
2229
|
-
|
|
2230
|
-
// Link tags
|
|
2231
|
-
const tags = Array.isArray(doc.tags) ? doc.tags : [];
|
|
2232
|
-
for (const t of tags) {
|
|
2233
|
-
const tid = 'tag:' + String(t).trim().toLowerCase();
|
|
2234
|
-
addNode(tid, String(t).trim(), 'tag');
|
|
2235
|
-
edges.push({ from: slug, to: tid });
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2428
|
+
const rawProjects = dl.db.prepare('SELECT slug FROM projects').all();
|
|
2429
|
+
for (const p of rawProjects) {
|
|
2430
|
+
if (p && p.slug) addNode(p.slug, p.slug, 'project');
|
|
2240
2431
|
}
|
|
2241
2432
|
|
|
2242
2433
|
// 2. Load Tasks
|
|
2243
|
-
const
|
|
2244
|
-
const
|
|
2245
|
-
const tasks = Array.isArray(taskDoc.tasks) ? taskDoc.tasks : [];
|
|
2246
|
-
|
|
2247
|
-
for (const t of tasks) {
|
|
2248
|
-
// Only show pending high/medium tasks for clarity, or unassigned ones.
|
|
2249
|
-
if (t.status === 'COMPLETED') continue;
|
|
2250
|
-
|
|
2434
|
+
const rawTasks = dl.db.prepare('SELECT * FROM tasks WHERE status != "COMPLETED"').all();
|
|
2435
|
+
for (const t of rawTasks) {
|
|
2251
2436
|
const tid = 'task:' + t.id;
|
|
2252
|
-
addNode(tid, t.
|
|
2437
|
+
addNode(tid, t.description || 'Tarefa', 'task');
|
|
2253
2438
|
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
edges.push({ from: tid, to: slug });
|
|
2439
|
+
if (t.project_slug && nodeIds.has(t.project_slug)) {
|
|
2440
|
+
edges.push({ from: tid, to: t.project_slug });
|
|
2257
2441
|
} else {
|
|
2258
|
-
// Connect to an "Unassigned" node
|
|
2259
2442
|
addNode('unassigned', 'Sin Asignar', 'unassigned');
|
|
2260
2443
|
edges.push({ from: tid, to: 'unassigned' });
|
|
2261
2444
|
}
|
|
2262
2445
|
}
|
|
2263
2446
|
|
|
2264
2447
|
// 3. Load Blockers
|
|
2265
|
-
const
|
|
2266
|
-
const
|
|
2267
|
-
const blockers = Array.isArray(blockerDoc.blockers) ? blockerDoc.blockers : [];
|
|
2268
|
-
|
|
2269
|
-
for (const b of blockers) {
|
|
2270
|
-
if (String(b.status || '').toUpperCase() !== 'OPEN') continue;
|
|
2271
|
-
|
|
2448
|
+
const openBlockers = dl.db.prepare('SELECT * FROM blockers WHERE status = "OPEN"').all();
|
|
2449
|
+
for (const b of openBlockers) {
|
|
2272
2450
|
const bid = 'blocker:' + b.id;
|
|
2273
2451
|
addNode(bid, 'BLQ: ' + (b.title || 'Blocker'), 'blocker');
|
|
2274
2452
|
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
edges.push({ from: bid, to: slug });
|
|
2453
|
+
if (b.project_slug && nodeIds.has(b.project_slug)) {
|
|
2454
|
+
edges.push({ from: bid, to: b.project_slug });
|
|
2278
2455
|
} else {
|
|
2279
2456
|
addNode('unassigned_blockers', 'Bloqueios Soltos', 'unassigned');
|
|
2280
2457
|
edges.push({ from: bid, to: 'unassigned_blockers' });
|
|
@@ -2832,8 +3009,8 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2832
3009
|
const schema = {
|
|
2833
3010
|
actions: [
|
|
2834
3011
|
{ type: 'append_daily_log', text: '<string>' },
|
|
2835
|
-
{ type: 'create_task', description: '<string>', priority: 'HIGH|MEDIUM|LOW', category: 'DO_NOW|SCHEDULE|DELEGATE|IGNORE', projectSlug: '<string optional>' },
|
|
2836
|
-
{ type: 'create_blocker', title: '<string>', severity: 'CRITICAL|HIGH|MEDIUM|LOW', notes: '<string>', projectSlug: '<string optional>' },
|
|
3012
|
+
{ type: 'create_task', description: '<string>', priority: 'HIGH|MEDIUM|LOW', category: 'DO_NOW|SCHEDULE|DELEGATE|IGNORE', projectSlug: '<string optional>', streamSlug: '<string optional, ex: nome da frente ou stream>' },
|
|
3013
|
+
{ type: 'create_blocker', title: '<string>', severity: 'CRITICAL|HIGH|MEDIUM|LOW', notes: '<string>', projectSlug: '<string optional>', streamSlug: '<string optional, ex: nome da frente ou stream>' },
|
|
2837
3014
|
{ type: 'suggest_report', name: 'daily|status|sm-weekly|blockers' },
|
|
2838
3015
|
{ type: 'oracle_query', query: '<string>' }
|
|
2839
3016
|
]
|
|
@@ -2991,18 +3168,6 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2991
3168
|
if (!Array.isArray(actions) || actions.length === 0) {
|
|
2992
3169
|
return safeJson(res, 400, { error: 'Plan has no actions[]' });
|
|
2993
3170
|
}
|
|
2994
|
-
|
|
2995
|
-
const taskFile = path.join(workspaceDir, 'data', 'tasks', 'task-log.json');
|
|
2996
|
-
const blockerFile = path.join(workspaceDir, 'data', 'blockers', 'blocker-log.json');
|
|
2997
|
-
|
|
2998
|
-
const taskLog = readJsonOrNull(taskFile) || { schemaVersion: 1, tasks: [] };
|
|
2999
|
-
if (!Array.isArray(taskLog.tasks)) taskLog.tasks = [];
|
|
3000
|
-
if (typeof taskLog.schemaVersion !== 'number') taskLog.schemaVersion = 1;
|
|
3001
|
-
|
|
3002
|
-
const blockerLog = readJsonOrNull(blockerFile) || { schemaVersion: 1, blockers: [] };
|
|
3003
|
-
if (!Array.isArray(blockerLog.blockers)) blockerLog.blockers = [];
|
|
3004
|
-
if (typeof blockerLog.schemaVersion !== 'number') blockerLog.schemaVersion = 1;
|
|
3005
|
-
|
|
3006
3171
|
function normalizeWhitespace(t) {
|
|
3007
3172
|
return String(t || '').replace(/\s+/g, ' ').trim();
|
|
3008
3173
|
}
|
|
@@ -3015,27 +3180,11 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3015
3180
|
return crypto.createHash('sha1').update(String(text || ''), 'utf8').digest('hex');
|
|
3016
3181
|
}
|
|
3017
3182
|
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
const ms = Date.parse(iso);
|
|
3021
|
-
if (!Number.isFinite(ms)) return false;
|
|
3022
|
-
return (Date.now() - ms) <= 24 * 60 * 60 * 1000;
|
|
3023
|
-
} catch {
|
|
3024
|
-
return false;
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
|
|
3028
|
-
const existingTaskKeys24h = new Set(
|
|
3029
|
-
taskLog.tasks
|
|
3030
|
-
.filter((t) => t && within24h(t.createdAt))
|
|
3031
|
-
.map((t) => sha1(normalizeTextForKey(t.description)))
|
|
3032
|
-
);
|
|
3183
|
+
const recentTasks = dl.db.prepare("SELECT description FROM tasks WHERE created_at >= datetime('now', '-1 day')").all();
|
|
3184
|
+
const existingTaskKeys24h = new Set(recentTasks.map(t => sha1(normalizeTextForKey(t.description))));
|
|
3033
3185
|
|
|
3034
|
-
const
|
|
3035
|
-
|
|
3036
|
-
.filter((b) => b && within24h(b.createdAt))
|
|
3037
|
-
.map((b) => sha1(normalizeTextForKey(b.title)))
|
|
3038
|
-
);
|
|
3186
|
+
const recentBlockers = dl.db.prepare("SELECT title FROM blockers WHERE created_at >= datetime('now', '-1 day')").all();
|
|
3187
|
+
const existingBlockerKeys24h = new Set(recentBlockers.map(b => sha1(normalizeTextForKey(b.title))));
|
|
3039
3188
|
|
|
3040
3189
|
const now = new Date().toISOString();
|
|
3041
3190
|
const applyMode = String(payload.mode || 'all').trim();
|
|
@@ -3067,71 +3216,72 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3067
3216
|
|
|
3068
3217
|
const validTaskCats = new Set(['DO_NOW', 'SCHEDULE', 'DELEGATE', 'IGNORE']);
|
|
3069
3218
|
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3219
|
+
const insertTask = dl.db.prepare(`INSERT INTO tasks (id, project_slug, description, category, status, metadata) VALUES (?, ?, ?, ?, ?, ?)`);
|
|
3220
|
+
const insertBlocker = dl.db.prepare(`INSERT INTO blockers (id, project_slug, title, severity, status, owner, next_action, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
3221
|
+
|
|
3222
|
+
// Execute everything inside a transaction for the apply process
|
|
3223
|
+
const applyTx = dl.db.transaction((actionsToApply) => {
|
|
3224
|
+
for (const a of actionsToApply) {
|
|
3225
|
+
if (!a || typeof a !== 'object') continue;
|
|
3226
|
+
const type = String(a.type || '').trim();
|
|
3227
|
+
|
|
3228
|
+
if (type === 'create_task') {
|
|
3229
|
+
if (applyMode !== 'all' && applyMode !== 'tasks') continue;
|
|
3230
|
+
const description = normalizeWhitespace(a.description);
|
|
3231
|
+
if (!description) continue;
|
|
3232
|
+
const projectSlug = String(a.projectSlug || '').trim() || inferProjectSlug(description, slugMap);
|
|
3233
|
+
const streamSlug = String(a.streamSlug || '').trim();
|
|
3234
|
+
const key = sha1(normalizeTextForKey((projectSlug ? projectSlug + ' ' : '') + description));
|
|
3235
|
+
if (existingTaskKeys24h.has(key)) { applied.tasksSkipped++; continue; }
|
|
3236
|
+
const category = validTaskCats.has(String(a.category || '').trim()) ? String(a.category).trim() : 'DO_NOW';
|
|
3237
|
+
const priority = normPriority(a.priority);
|
|
3238
|
+
|
|
3239
|
+
const id = makeId('t');
|
|
3240
|
+
const metadata = JSON.stringify({ priority, streamSlug });
|
|
3241
|
+
insertTask.run(id, projectSlug || null, description, category, 'PENDING', metadata);
|
|
3242
|
+
|
|
3243
|
+
applied.tasks++;
|
|
3244
|
+
existingTaskKeys24h.add(key); // prevent duplicates within same batch
|
|
3245
|
+
continue;
|
|
3246
|
+
}
|
|
3073
3247
|
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
applied.tasks++;
|
|
3094
|
-
continue;
|
|
3095
|
-
}
|
|
3248
|
+
if (type === 'create_blocker') {
|
|
3249
|
+
if (applyMode !== 'all' && applyMode !== 'blockers') continue;
|
|
3250
|
+
const title = normalizeWhitespace(a.title);
|
|
3251
|
+
const notes = normalizeWhitespace(a.notes);
|
|
3252
|
+
if (!title) continue;
|
|
3253
|
+
const projectSlug = String(a.projectSlug || '').trim() || inferProjectSlug(title + ' ' + notes, slugMap);
|
|
3254
|
+
const streamSlug = String(a.streamSlug || '').trim();
|
|
3255
|
+
const key = sha1(normalizeTextForKey((projectSlug ? projectSlug + ' ' : '') + title));
|
|
3256
|
+
if (existingBlockerKeys24h.has(key)) { applied.blockersSkipped++; continue; }
|
|
3257
|
+
const severity = normSeverity(a.severity);
|
|
3258
|
+
|
|
3259
|
+
const id = makeId('b');
|
|
3260
|
+
const metadata = JSON.stringify({ streamSlug, description: notes || title });
|
|
3261
|
+
insertBlocker.run(id, projectSlug || null, title, severity, 'OPEN', null, null, metadata);
|
|
3262
|
+
|
|
3263
|
+
applied.blockers++;
|
|
3264
|
+
existingBlockerKeys24h.add(key); // prevent duplicates within same batch
|
|
3265
|
+
continue;
|
|
3266
|
+
}
|
|
3096
3267
|
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
if (existingBlockerKeys24h.has(key)) { applied.blockersSkipped++; continue; }
|
|
3103
|
-
const notes = normalizeWhitespace(a.notes);
|
|
3104
|
-
if (!title) continue;
|
|
3105
|
-
const severity = normSeverity(a.severity);
|
|
3106
|
-
const blocker = {
|
|
3107
|
-
id: makeId('b'),
|
|
3108
|
-
title,
|
|
3109
|
-
description: notes || title,
|
|
3110
|
-
createdAt: now,
|
|
3111
|
-
status: 'OPEN',
|
|
3112
|
-
severity,
|
|
3113
|
-
};
|
|
3114
|
-
if (projectSlug) blocker.projectSlug = projectSlug;
|
|
3115
|
-
blockerLog.blockers.push(blocker);
|
|
3116
|
-
applied.blockers++;
|
|
3117
|
-
continue;
|
|
3118
|
-
}
|
|
3268
|
+
if (type === 'suggest_report') {
|
|
3269
|
+
const name = String(a.name || '').trim();
|
|
3270
|
+
if (name) applied.reportsSuggested.push(name);
|
|
3271
|
+
continue;
|
|
3272
|
+
}
|
|
3119
3273
|
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3274
|
+
if (type === 'oracle_query') {
|
|
3275
|
+
const query = String(a.query || '').trim();
|
|
3276
|
+
if (query) applied.oracleQueries.push(query);
|
|
3277
|
+
continue;
|
|
3278
|
+
}
|
|
3124
3279
|
}
|
|
3280
|
+
});
|
|
3125
3281
|
|
|
3126
|
-
|
|
3127
|
-
const query = String(a.query || '').trim();
|
|
3128
|
-
if (query) applied.oracleQueries.push(query);
|
|
3129
|
-
continue;
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3282
|
+
applyTx(actions);
|
|
3132
3283
|
|
|
3133
3284
|
// Auto-suggest reports when planner didn't include any
|
|
3134
|
-
// (keeps UX consistent: if you created a blocker, at least suggest blockers)
|
|
3135
3285
|
if (!applied.reportsSuggested.length) {
|
|
3136
3286
|
const sug = [];
|
|
3137
3287
|
sug.push('daily');
|
|
@@ -3143,10 +3293,6 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3143
3293
|
applied.reportsSuggested = Array.from(new Set(applied.reportsSuggested.map((s) => String(s).trim()).filter(Boolean)));
|
|
3144
3294
|
}
|
|
3145
3295
|
|
|
3146
|
-
// Persist
|
|
3147
|
-
writeJson(taskFile, taskLog);
|
|
3148
|
-
writeJson(blockerFile, blockerLog);
|
|
3149
|
-
|
|
3150
3296
|
return safeJson(res, 200, { ok: true, applied });
|
|
3151
3297
|
}
|
|
3152
3298
|
|
|
@@ -3237,28 +3383,51 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3237
3383
|
const query = String(payload.query || '').trim();
|
|
3238
3384
|
if (!query) return safeJson(res, 400, { error: 'Missing query' });
|
|
3239
3385
|
|
|
3240
|
-
const
|
|
3241
|
-
const
|
|
3242
|
-
const
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3386
|
+
const workspaceRulesBase = path.join(workspaceDir, '.agent', 'rules', 'freya');
|
|
3387
|
+
const packagedRulesBase = path.join(__dirname, '..', '.agent', 'rules', 'freya');
|
|
3388
|
+
const rulesBase = exists(workspaceRulesBase) ? workspaceRulesBase : packagedRulesBase;
|
|
3389
|
+
|
|
3390
|
+
const files = [
|
|
3391
|
+
path.join(rulesBase, 'freya.mdc'),
|
|
3392
|
+
path.join(rulesBase, 'agents', 'master.mdc'),
|
|
3393
|
+
path.join(rulesBase, 'agents', 'oracle.mdc')
|
|
3394
|
+
].filter(exists);
|
|
3395
|
+
|
|
3396
|
+
const rulesText = files.map((p) => {
|
|
3397
|
+
const rel = path.relative(workspaceDir, p).replace(/\\/g, '/');
|
|
3398
|
+
return `\n\n---\nFILE: ${rel}\n---\n` + fs.readFileSync(p, 'utf8');
|
|
3399
|
+
}).join('');
|
|
3400
|
+
|
|
3401
|
+
// V2 RAG Context
|
|
3402
|
+
const dm = new DataManager(workspaceDir, path.join(workspaceDir, 'logs'));
|
|
3403
|
+
const ragResults = await dm.semanticSearch(query, 12);
|
|
3404
|
+
let ragContext = '';
|
|
3405
|
+
if (ragResults.length > 0) {
|
|
3406
|
+
ragContext = '\n\n[MEMÓRIA DE LONGO PRAZO RECUPERADA (RAG VIA SQLITE)]\n';
|
|
3407
|
+
for (const r of ragResults) {
|
|
3408
|
+
ragContext += `\n---\nFONTE: ${r.reference_type} -> ID: ${r.reference_id} (Score: ${r.score.toFixed(3)})\nCONTEÚDO:\n${r.text_chunk}\n`;
|
|
3409
|
+
}
|
|
3257
3410
|
}
|
|
3258
3411
|
|
|
3259
|
-
const
|
|
3260
|
-
|
|
3261
|
-
|
|
3412
|
+
const prompt = `Você é o agente Oracle do sistema F.R.E.Y.A.\n\nSiga estritamente os arquivos de regras abaixo.\nResponda de forma analítica e consultiva.\n${ragContext}\n\nREGRAS:${rulesText}\n\nCONSULTA DO USUÁRIO:\n${query}\n`;
|
|
3413
|
+
|
|
3414
|
+
const cmd = process.env.COPILOT_CMD || 'copilot';
|
|
3415
|
+
|
|
3416
|
+
try {
|
|
3417
|
+
// Removed --allow-all-tools and --add-dir to force reliance on RAG context
|
|
3418
|
+
const r = await run(cmd, ['-s', '--no-color', '--stream', 'off', '-p', prompt], workspaceDir);
|
|
3419
|
+
const out = (r.stdout + r.stderr).trim();
|
|
3420
|
+
if (r.code !== 0) {
|
|
3421
|
+
return safeJson(res, 200, { ok: false, answer: 'Falha na busca do agente Oracle:\n' + (out || 'Exit code != 0'), sessionId });
|
|
3422
|
+
}
|
|
3423
|
+
return safeJson(res, 200, { ok: true, answer: out, sessionId });
|
|
3424
|
+
} catch (e) {
|
|
3425
|
+
return safeJson(res, 200, {
|
|
3426
|
+
ok: false,
|
|
3427
|
+
answer: `Agente indisponível (cmd: ${cmd}).\nDetalhes:\n` + (e.message || String(e)),
|
|
3428
|
+
sessionId
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3262
3431
|
}
|
|
3263
3432
|
|
|
3264
3433
|
// Chat persistence (per session)
|
|
@@ -3361,52 +3530,48 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3361
3530
|
const cat = payload.category ? String(payload.category).trim() : null;
|
|
3362
3531
|
const status = payload.status ? String(payload.status).trim() : null;
|
|
3363
3532
|
|
|
3364
|
-
|
|
3365
|
-
const
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3533
|
+
let query = 'SELECT * FROM tasks WHERE 1=1';
|
|
3534
|
+
const params = [];
|
|
3535
|
+
|
|
3536
|
+
if (cat) { query += ' AND category = ?'; params.push(cat); }
|
|
3537
|
+
if (status) { query += ' AND status = ?'; params.push(status); }
|
|
3538
|
+
|
|
3539
|
+
query += ` ORDER BY
|
|
3540
|
+
CASE JSON_EXTRACT(metadata, '$.priority')
|
|
3541
|
+
WHEN 'high' THEN 0 WHEN 'medium' THEN 1 WHEN 'low' THEN 2 ELSE 3
|
|
3542
|
+
END ASC,
|
|
3543
|
+
created_at DESC
|
|
3544
|
+
LIMIT ?`;
|
|
3545
|
+
params.push(limit);
|
|
3546
|
+
|
|
3547
|
+
const rawTasks = dl.db.prepare(query).all(...params);
|
|
3548
|
+
const tasks = rawTasks.map(t => {
|
|
3549
|
+
const meta = t.metadata ? JSON.parse(t.metadata) : {};
|
|
3550
|
+
return {
|
|
3551
|
+
id: t.id,
|
|
3552
|
+
description: t.description,
|
|
3553
|
+
category: t.category,
|
|
3554
|
+
status: t.status,
|
|
3555
|
+
createdAt: t.created_at,
|
|
3556
|
+
completedAt: t.completed_at,
|
|
3557
|
+
projectSlug: t.project_slug,
|
|
3558
|
+
priority: meta.priority,
|
|
3559
|
+
streamSlug: meta.streamSlug
|
|
3560
|
+
};
|
|
3561
|
+
});
|
|
3562
|
+
|
|
3563
|
+
return safeJson(res, 200, { ok: true, tasks });
|
|
3386
3564
|
}
|
|
3387
3565
|
|
|
3388
3566
|
if (req.url === '/api/tasks/complete') {
|
|
3389
3567
|
const id = String(payload.id || '').trim();
|
|
3390
3568
|
if (!id) return safeJson(res, 400, { error: 'Missing id' });
|
|
3391
3569
|
|
|
3392
|
-
const
|
|
3393
|
-
const
|
|
3394
|
-
const tasks = Array.isArray(doc.tasks) ? doc.tasks : [];
|
|
3395
|
-
|
|
3396
|
-
const now = isoNow();
|
|
3397
|
-
let updated = null;
|
|
3398
|
-
for (const t of tasks) {
|
|
3399
|
-
if (t && t.id === id) {
|
|
3400
|
-
t.status = 'COMPLETED';
|
|
3401
|
-
t.completedAt = now;
|
|
3402
|
-
updated = t;
|
|
3403
|
-
break;
|
|
3404
|
-
}
|
|
3405
|
-
}
|
|
3570
|
+
const now = new Date().toISOString();
|
|
3571
|
+
const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ? WHERE id = ?`).run(now, id);
|
|
3406
3572
|
|
|
3407
|
-
if (
|
|
3408
|
-
|
|
3409
|
-
return safeJson(res, 200, { ok: true, task: updated });
|
|
3573
|
+
if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
|
|
3574
|
+
return safeJson(res, 200, { ok: true, task: { id, status: 'COMPLETED', completedAt: now } });
|
|
3410
3575
|
}
|
|
3411
3576
|
|
|
3412
3577
|
|
|
@@ -3415,22 +3580,26 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3415
3580
|
if (!id) return safeJson(res, 400, { error: 'Missing id' });
|
|
3416
3581
|
const patch = payload.patch && typeof payload.patch === 'object' ? payload.patch : {};
|
|
3417
3582
|
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
const tasks = Array.isArray(doc.tasks) ? doc.tasks : [];
|
|
3583
|
+
let queryUpdates = [];
|
|
3584
|
+
let params = [];
|
|
3421
3585
|
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
if (typeof patch.projectSlug === 'string') t.projectSlug = patch.projectSlug.trim() || undefined;
|
|
3426
|
-
if (typeof patch.category === 'string') t.category = patch.category.trim();
|
|
3427
|
-
updated = t;
|
|
3428
|
-
break;
|
|
3429
|
-
}
|
|
3586
|
+
if (typeof patch.projectSlug === 'string') {
|
|
3587
|
+
queryUpdates.push('project_slug = ?');
|
|
3588
|
+
params.push(patch.projectSlug.trim() || null);
|
|
3430
3589
|
}
|
|
3431
|
-
if (
|
|
3432
|
-
|
|
3433
|
-
|
|
3590
|
+
if (typeof patch.category === 'string') {
|
|
3591
|
+
queryUpdates.push('category = ?');
|
|
3592
|
+
params.push(patch.category.trim());
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
if (queryUpdates.length === 0) return safeJson(res, 200, { ok: true, task: { id } });
|
|
3596
|
+
|
|
3597
|
+
params.push(id);
|
|
3598
|
+
const updateQuery = `UPDATE tasks SET ${queryUpdates.join(', ')} WHERE id = ?`;
|
|
3599
|
+
const info = dl.db.prepare(updateQuery).run(...params);
|
|
3600
|
+
|
|
3601
|
+
if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
|
|
3602
|
+
return safeJson(res, 200, { ok: true, task: { id, ...patch } });
|
|
3434
3603
|
}
|
|
3435
3604
|
|
|
3436
3605
|
if (req.url === '/api/health/checklist') {
|
|
@@ -3442,13 +3611,11 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3442
3611
|
return safeJson(res, 200, { ok: false, needsInit: true, error: 'Workspace not initialized', items: [] });
|
|
3443
3612
|
}
|
|
3444
3613
|
|
|
3445
|
-
const
|
|
3446
|
-
const
|
|
3447
|
-
const tasksDoc = readJsonOrNull(tasksFile) || { schemaVersion: 1, tasks: [] };
|
|
3448
|
-
const blockersDoc = readJsonOrNull(blockersFile) || { schemaVersion: 1, blockers: [] };
|
|
3614
|
+
const pendingTasksRes = dl.db.prepare('SELECT count(*) as count FROM tasks WHERE status = "PENDING" AND category = "DO_NOW"').get();
|
|
3615
|
+
const openBlockersRes = dl.db.prepare('SELECT count(*) as count FROM blockers WHERE status = "OPEN"').get();
|
|
3449
3616
|
|
|
3450
|
-
const pendingTasks =
|
|
3451
|
-
const openBlockers =
|
|
3617
|
+
const pendingTasks = pendingTasksRes ? pendingTasksRes.count : 0;
|
|
3618
|
+
const openBlockers = openBlockersRes ? openBlockersRes.count : 0;
|
|
3452
3619
|
|
|
3453
3620
|
const today = isoDate();
|
|
3454
3621
|
const reports = listReports(workspaceDir);
|
|
@@ -3457,7 +3624,7 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3457
3624
|
const items = [
|
|
3458
3625
|
{ label: 'Blockers abertos', status: openBlockers > 0 ? 'warn' : 'ok', detail: `${openBlockers} aberto(s)` },
|
|
3459
3626
|
{ label: 'Tarefas DO_NOW pendentes', status: pendingTasks > 0 ? 'warn' : 'ok', detail: `${pendingTasks} pendente(s)` },
|
|
3460
|
-
{ label: '
|
|
3627
|
+
{ label: 'Relatórios de hoje', status: reportsToday > 0 ? 'ok' : 'warn', detail: `${reportsToday} gerado(s)` }
|
|
3461
3628
|
];
|
|
3462
3629
|
|
|
3463
3630
|
return safeJson(res, 200, { ok: true, items, stats: { pendingTasks, openBlockers, reportsToday, reportsTotal: reports.length } });
|
|
@@ -3507,11 +3674,18 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3507
3674
|
}
|
|
3508
3675
|
|
|
3509
3676
|
if (req.url === '/api/blockers/summary') {
|
|
3510
|
-
const
|
|
3511
|
-
|
|
3512
|
-
|
|
3677
|
+
const open = dl.db.prepare(`
|
|
3678
|
+
SELECT * FROM blockers
|
|
3679
|
+
WHERE status IN ('OPEN', 'MITIGATING')
|
|
3680
|
+
`).all().map(b => ({
|
|
3681
|
+
id: b.id,
|
|
3682
|
+
title: b.title,
|
|
3683
|
+
severity: b.severity,
|
|
3684
|
+
status: b.status,
|
|
3685
|
+
projectSlug: b.project_slug,
|
|
3686
|
+
createdAt: b.created_at
|
|
3687
|
+
}));
|
|
3513
3688
|
|
|
3514
|
-
const open = blockers.filter((b) => b && String(b.status || '').trim() === 'OPEN');
|
|
3515
3689
|
const sevCount = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0, OTHER: 0 };
|
|
3516
3690
|
const now = Date.now();
|
|
3517
3691
|
let older7 = 0;
|
|
@@ -3530,7 +3704,7 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3530
3704
|
.slice(0, 5)
|
|
3531
3705
|
.map((b) => ({
|
|
3532
3706
|
id: b.id,
|
|
3533
|
-
title: b.title ||
|
|
3707
|
+
title: b.title || 'Sem titulo',
|
|
3534
3708
|
severity: b.severity || 'UNKNOWN',
|
|
3535
3709
|
projectSlug: b.projectSlug || null
|
|
3536
3710
|
}));
|
|
@@ -3549,34 +3723,40 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3549
3723
|
const limit = Math.max(1, Math.min(50, Number(payload.limit || 10)));
|
|
3550
3724
|
const status = payload.status ? String(payload.status).trim() : 'OPEN';
|
|
3551
3725
|
|
|
3552
|
-
|
|
3553
|
-
const
|
|
3554
|
-
const blockers = Array.isArray(doc.blockers) ? doc.blockers.slice() : [];
|
|
3555
|
-
|
|
3556
|
-
const sevRank = (s) => {
|
|
3557
|
-
const v = String(s || '').toUpperCase();
|
|
3558
|
-
if (v === 'CRITICAL') return 0;
|
|
3559
|
-
if (v === 'HIGH') return 1;
|
|
3560
|
-
if (v === 'MEDIUM') return 2;
|
|
3561
|
-
if (v === 'LOW') return 3;
|
|
3562
|
-
return 9;
|
|
3563
|
-
};
|
|
3726
|
+
let query = 'SELECT * FROM blockers WHERE 1=1';
|
|
3727
|
+
const params = [];
|
|
3564
3728
|
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3729
|
+
if (status) {
|
|
3730
|
+
query += ' AND status = ?';
|
|
3731
|
+
params.push(status);
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
query += ` ORDER BY
|
|
3735
|
+
CASE severity
|
|
3736
|
+
WHEN 'CRITICAL' THEN 0 WHEN 'HIGH' THEN 1 WHEN 'MEDIUM' THEN 2 WHEN 'LOW' THEN 3 ELSE 9
|
|
3737
|
+
END ASC,
|
|
3738
|
+
created_at ASC
|
|
3739
|
+
LIMIT ?`;
|
|
3740
|
+
params.push(limit);
|
|
3741
|
+
|
|
3742
|
+
const rawBlockers = dl.db.prepare(query).all(...params);
|
|
3743
|
+
const blockers = rawBlockers.map(b => {
|
|
3744
|
+
const meta = b.metadata ? JSON.parse(b.metadata) : {};
|
|
3745
|
+
return {
|
|
3746
|
+
id: b.id,
|
|
3747
|
+
title: b.title,
|
|
3748
|
+
severity: b.severity,
|
|
3749
|
+
status: b.status,
|
|
3750
|
+
createdAt: b.created_at,
|
|
3751
|
+
resolvedAt: b.resolved_at,
|
|
3752
|
+
projectSlug: b.project_slug,
|
|
3753
|
+
owner: b.owner,
|
|
3754
|
+
nextAction: b.next_action,
|
|
3755
|
+
streamSlug: meta.streamSlug
|
|
3756
|
+
};
|
|
3757
|
+
});
|
|
3758
|
+
|
|
3759
|
+
return safeJson(res, 200, { ok: true, blockers });
|
|
3580
3760
|
}
|
|
3581
3761
|
|
|
3582
3762
|
if (req.url === '/api/blockers/update') {
|
|
@@ -3584,21 +3764,22 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3584
3764
|
if (!id) return safeJson(res, 400, { error: 'Missing id' });
|
|
3585
3765
|
const patch = payload.patch && typeof payload.patch === 'object' ? payload.patch : {};
|
|
3586
3766
|
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
const blockers = Array.isArray(doc.blockers) ? doc.blockers : [];
|
|
3767
|
+
let queryUpdates = [];
|
|
3768
|
+
let params = [];
|
|
3590
3769
|
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
if (typeof patch.projectSlug === 'string') b.projectSlug = patch.projectSlug.trim() || undefined;
|
|
3595
|
-
updated = b;
|
|
3596
|
-
break;
|
|
3597
|
-
}
|
|
3770
|
+
if (typeof patch.projectSlug === 'string') {
|
|
3771
|
+
queryUpdates.push('project_slug = ?');
|
|
3772
|
+
params.push(patch.projectSlug.trim() || null);
|
|
3598
3773
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3774
|
+
|
|
3775
|
+
if (queryUpdates.length === 0) return safeJson(res, 200, { ok: true, blocker: { id } });
|
|
3776
|
+
|
|
3777
|
+
params.push(id);
|
|
3778
|
+
const updateQuery = `UPDATE blockers SET ${queryUpdates.join(', ')} WHERE id = ?`;
|
|
3779
|
+
const info = dl.db.prepare(updateQuery).run(...params);
|
|
3780
|
+
|
|
3781
|
+
if (info.changes === 0) return safeJson(res, 404, { error: 'Blocker not found' });
|
|
3782
|
+
return safeJson(res, 200, { ok: true, blocker: { id, ...patch } });
|
|
3602
3783
|
}
|
|
3603
3784
|
|
|
3604
3785
|
if (req.url === '/api/report') {
|
|
@@ -3689,4 +3870,150 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3689
3870
|
if (open) openBrowser(url);
|
|
3690
3871
|
}
|
|
3691
3872
|
|
|
3873
|
+
function buildSettingsHtml(safeDefault, appVersion) {
|
|
3874
|
+
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
3875
|
+
return `<!doctype html>
|
|
3876
|
+
<html>
|
|
3877
|
+
<head>
|
|
3878
|
+
<meta charset="utf-8" />
|
|
3879
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
3880
|
+
<title>FREYA Settings</title>
|
|
3881
|
+
<link rel="stylesheet" href="/app.css" />
|
|
3882
|
+
</head>
|
|
3883
|
+
<body data-page="settings">
|
|
3884
|
+
<div class="app">
|
|
3885
|
+
<div class="frame">
|
|
3886
|
+
<div class="shell">
|
|
3887
|
+
|
|
3888
|
+
<aside class="rail">
|
|
3889
|
+
<div class="railTop">
|
|
3890
|
+
<div class="railLogo">F</div>
|
|
3891
|
+
</div>
|
|
3892
|
+
<div class="railNav">
|
|
3893
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
3894
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
3895
|
+
</button>
|
|
3896
|
+
<button class="railBtn" id="railReports" type="button" title="Relatórios">
|
|
3897
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
3898
|
+
</button>
|
|
3899
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
3900
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
3901
|
+
</button>
|
|
3902
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
3903
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
3904
|
+
</button>
|
|
3905
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
3906
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
3907
|
+
</button>
|
|
3908
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
3909
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
3910
|
+
</button>
|
|
3911
|
+
<button class="railBtn" id="railDocs" type="button" title="Documentação">
|
|
3912
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
|
3913
|
+
</button>
|
|
3914
|
+
</div>
|
|
3915
|
+
<div class="railBottom">
|
|
3916
|
+
<button id="railSettings" class="railBtn active" title="Configurações">
|
|
3917
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
|
|
3918
|
+
</button>
|
|
3919
|
+
</div>
|
|
3920
|
+
</aside>
|
|
3921
|
+
|
|
3922
|
+
<main class="center" id="settingsPage">
|
|
3923
|
+
<div class="topbar">
|
|
3924
|
+
<div class="brandLine">
|
|
3925
|
+
<span class="spark"></span>
|
|
3926
|
+
<div class="brandStack">
|
|
3927
|
+
<div class="brand">FREYA</div>
|
|
3928
|
+
<div class="brandSub">Configurações Avançadas</div>
|
|
3929
|
+
</div>
|
|
3930
|
+
</div>
|
|
3931
|
+
<div class="topActions">
|
|
3932
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
3933
|
+
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
3934
|
+
<button class="btn icon" type="button" onclick="window.toggleTheme()" title="Alternar tema claro/escuro">
|
|
3935
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-sun"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
|
|
3936
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-moon" style="display:none"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
|
|
3937
|
+
</button>
|
|
3938
|
+
</div>
|
|
3939
|
+
</div>
|
|
3940
|
+
<section class="utilityGrid" style="margin-bottom: 24px;">
|
|
3941
|
+
<div class="utilityCard">
|
|
3942
|
+
<div class="utilityHead">Área de trabalho local</div>
|
|
3943
|
+
<div class="sidePath" id="sidePath">${safeDefault}</div>
|
|
3944
|
+
<div class="row" style="grid-template-columns: 1fr auto">
|
|
3945
|
+
<input id="dir" placeholder="./freya" value="${safeDefault}" />
|
|
3946
|
+
<button class="btn small" type="button" onclick="window.pickDir()">Procurar</button>
|
|
3947
|
+
</div>
|
|
3948
|
+
<div class="stack" style="margin-top:10px">
|
|
3949
|
+
<button class="btn" type="button" onclick="window.doUpdate()">Sincronizar workspace</button>
|
|
3950
|
+
<button class="btn" type="button" onclick="window.doMigrate()">Migrar dados base</button>
|
|
3951
|
+
</div>
|
|
3952
|
+
<div style="height:10px"></div>
|
|
3953
|
+
<div class="help"><b>Sincronizar workspace</b>: Atualiza scripts/templates da suite FREYA sem afetar <code>data</code>.</div>
|
|
3954
|
+
<div class="help"><b>Migrar dados</b>: Se alguma versão quebrar compatibilidade, ele recria o log.</div>
|
|
3955
|
+
</div>
|
|
3956
|
+
|
|
3957
|
+
<div class="utilityCard">
|
|
3958
|
+
<div class="utilityHead">Interface & Sistema</div>
|
|
3959
|
+
<div style="display:flex; flex-direction:column; gap:8px;">
|
|
3960
|
+
<label style="display:flex; align-items:center; gap:10px; user-select:none; margin: 6px 0 12px 0">
|
|
3961
|
+
<input id="prettyPublish" type="checkbox" checked style="width:auto" onchange="window.togglePrettyPublish()" />
|
|
3962
|
+
Publicação bonita (cards/embeds em RTF)
|
|
3963
|
+
</label>
|
|
3964
|
+
<div class="stack" style="margin-top:10px">
|
|
3965
|
+
<button class="btn" type="button" onclick="window.rebuildIndex()">Reconstruir Índice de Busca (Copilot)</button>
|
|
3966
|
+
<button class="btn" type="button" onclick="window.exportObsidian()">Exportar Notas (Obsidian Markdown)</button>
|
|
3967
|
+
</div>
|
|
3968
|
+
<div class="help">Usa Copilot/RagIndex para buscas conversacionais.</div>
|
|
3969
|
+
</div>
|
|
3970
|
+
</div>
|
|
3971
|
+
</section>
|
|
3972
|
+
|
|
3973
|
+
<div class="devGrid">
|
|
3974
|
+
<div class="panel">
|
|
3975
|
+
<div class="panelHead"><b>Configuração de Notificações</b></div>
|
|
3976
|
+
<div class="panelBody">
|
|
3977
|
+
<label>Discord webhook URL</label>
|
|
3978
|
+
<input id="discord" placeholder="https://discord.com/api/webhooks/..." />
|
|
3979
|
+
<div style="height:10px"></div>
|
|
3980
|
+
|
|
3981
|
+
<label>Teams webhook URL</label>
|
|
3982
|
+
<input id="teams" placeholder="https://..." />
|
|
3983
|
+
|
|
3984
|
+
<div class="stack" style="margin-top:20px;">
|
|
3985
|
+
<button class="btn primary" type="button" onclick="window.saveSettings()">Salvar Configurações</button>
|
|
3986
|
+
</div>
|
|
3987
|
+
</div>
|
|
3988
|
+
</div>
|
|
3989
|
+
|
|
3990
|
+
<div class="panel">
|
|
3991
|
+
<div class="panelHead"><b>Mapeamento Smart de Projetos</b></div>
|
|
3992
|
+
<div class="panelBody">
|
|
3993
|
+
<label>Regras de inferência (JSON)</label>
|
|
3994
|
+
<textarea id="slugRules" rows="8" placeholder="{ \"rules\": [ { \"contains\": \"fideliza\", \"slug\": \"vivo/fidelizacao\" } ] }"></textarea>
|
|
3995
|
+
<div class="help">Usado pra inferir <code>projectSlug</code>.</div>
|
|
3996
|
+
<div class="stack" style="margin-top:10px">
|
|
3997
|
+
<button class="btn" type="button" onclick="window.reloadSlugRules()">Recarregar regras</button>
|
|
3998
|
+
<button class="btn primary" type="button" onclick="window.saveSlugRules()">Salvar regras</button>
|
|
3999
|
+
</div>
|
|
4000
|
+
</div>
|
|
4001
|
+
</div>
|
|
4002
|
+
</div>
|
|
4003
|
+
|
|
4004
|
+
</div>
|
|
4005
|
+
</main>
|
|
4006
|
+
</div>
|
|
4007
|
+
</div>
|
|
4008
|
+
</div>
|
|
4009
|
+
|
|
4010
|
+
<script>
|
|
4011
|
+
window.__FREYA_DEFAULT_DIR = "${safeDefault}";
|
|
4012
|
+
</script>
|
|
4013
|
+
<script src="/app.js"></script>
|
|
4014
|
+
</body>
|
|
4015
|
+
</html>`;
|
|
4016
|
+
}
|
|
4017
|
+
|
|
3692
4018
|
module.exports = { cmdWeb };
|
|
4019
|
+
|