@cccarv82/freya 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/web.js
CHANGED
|
@@ -185,8 +185,9 @@ async function pickDirectoryNative() {
|
|
|
185
185
|
return null;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
function html() {
|
|
188
|
+
function html(defaultDir) {
|
|
189
189
|
// Aesthetic: “clean workstation” — light-first UI inspired by modern productivity apps.
|
|
190
|
+
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
190
191
|
return `<!doctype html>
|
|
191
192
|
<html>
|
|
192
193
|
<head>
|
|
@@ -632,13 +633,6 @@ function html() {
|
|
|
632
633
|
<div class="help">Dica: se você já tem uma workspace antiga, use <b>Update</b>. Por padrão, <b>data/</b> e <b>logs/</b> não são sobrescritos.</div>
|
|
633
634
|
</div>
|
|
634
635
|
|
|
635
|
-
<div class="sideBlock">
|
|
636
|
-
<h3>Publish</h3>
|
|
637
|
-
<button class="btn sideBtn" onclick="publish('discord')">Publish → Discord</button>
|
|
638
|
-
<button class="btn sideBtn" onclick="publish('teams')">Publish → Teams</button>
|
|
639
|
-
<div class="help">Configure os webhooks no painel principal. O publish envia o último relatório gerado.</div>
|
|
640
|
-
</div>
|
|
641
|
-
|
|
642
636
|
<div class="sideBlock">
|
|
643
637
|
<h3>Atalhos</h3>
|
|
644
638
|
<div class="help"><span class="k">--dev</span> cria dados de exemplo para testar rápido.</div>
|
|
@@ -695,13 +689,6 @@ function html() {
|
|
|
695
689
|
|
|
696
690
|
<div style="height:12px"></div>
|
|
697
691
|
|
|
698
|
-
<div class="stack">
|
|
699
|
-
<button class="btn primary" onclick="doInit()">Init</button>
|
|
700
|
-
<button class="btn" onclick="doUpdate()">Update</button>
|
|
701
|
-
<button class="btn" onclick="doHealth()">Health</button>
|
|
702
|
-
<button class="btn" onclick="doMigrate()">Migrate</button>
|
|
703
|
-
</div>
|
|
704
|
-
|
|
705
692
|
<div style="height:16px"></div>
|
|
706
693
|
|
|
707
694
|
<label>Discord webhook URL</label>
|
|
@@ -743,6 +730,7 @@ function html() {
|
|
|
743
730
|
</div>
|
|
744
731
|
|
|
745
732
|
<script>
|
|
733
|
+
window.__FREYA_DEFAULT_DIR = "${safeDefault}";
|
|
746
734
|
const $ = (id) => document.getElementById(id);
|
|
747
735
|
const state = { lastReportPath: null, lastText: '' };
|
|
748
736
|
|
|
@@ -798,10 +786,12 @@ function html() {
|
|
|
798
786
|
}
|
|
799
787
|
|
|
800
788
|
function loadLocal() {
|
|
801
|
-
$('dir').value = localStorage.getItem('freya.dir') || './freya';
|
|
789
|
+
$('dir').value = (window.__FREYA_DEFAULT_DIR && window.__FREYA_DEFAULT_DIR !== '__FREYA_DEFAULT_DIR__') ? window.__FREYA_DEFAULT_DIR : (localStorage.getItem('freya.dir') || './freya');
|
|
802
790
|
$('discord').value = localStorage.getItem('freya.discord') || '';
|
|
803
791
|
$('teams').value = localStorage.getItem('freya.teams') || '';
|
|
804
792
|
$('sidePath').textContent = $('dir').value || './freya';
|
|
793
|
+
// Always persist the current run's default dir to avoid stale values
|
|
794
|
+
localStorage.setItem('freya.dir', $('dir').value || './freya');
|
|
805
795
|
}
|
|
806
796
|
|
|
807
797
|
async function api(p, body) {
|
|
@@ -954,72 +944,160 @@ function isoNow() {
|
|
|
954
944
|
return new Date().toISOString();
|
|
955
945
|
}
|
|
956
946
|
|
|
947
|
+
function daysAgoIso(n) {
|
|
948
|
+
return new Date(Date.now() - n * 24 * 60 * 60 * 1000).toISOString();
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function readJsonOrNull(p) {
|
|
952
|
+
try {
|
|
953
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
954
|
+
} catch {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function writeJson(p, obj) {
|
|
960
|
+
ensureDir(path.dirname(p));
|
|
961
|
+
fs.writeFileSync(p, JSON.stringify(obj, null, 2) + '\n', 'utf8');
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function looksLikeDevSeed(json) {
|
|
965
|
+
if (!json || typeof json !== 'object') return false;
|
|
966
|
+
// Heuristic: any id ends with demo marker or source is dev-seed
|
|
967
|
+
const dump = JSON.stringify(json);
|
|
968
|
+
return dump.includes('dev-seed') || dump.includes('t-demo-') || dump.includes('b-demo-') || dump.includes('c-demo-');
|
|
969
|
+
}
|
|
970
|
+
|
|
957
971
|
function seedDevWorkspace(workspaceDir) {
|
|
958
|
-
//
|
|
972
|
+
// Safe by default:
|
|
973
|
+
// - create missing demo files
|
|
974
|
+
// - if file exists and looks like prior dev-seed, upgrade it to richer demo
|
|
959
975
|
ensureDir(path.join(workspaceDir, 'data', 'tasks'));
|
|
960
976
|
ensureDir(path.join(workspaceDir, 'data', 'career'));
|
|
961
977
|
ensureDir(path.join(workspaceDir, 'data', 'blockers'));
|
|
962
978
|
ensureDir(path.join(workspaceDir, 'data', 'Clients', 'acme', 'rocket'));
|
|
979
|
+
ensureDir(path.join(workspaceDir, 'data', 'Clients', 'vivo', '5g'));
|
|
963
980
|
ensureDir(path.join(workspaceDir, 'logs', 'daily'));
|
|
964
981
|
|
|
965
982
|
const taskLog = path.join(workspaceDir, 'data', 'tasks', 'task-log.json');
|
|
966
|
-
|
|
967
|
-
|
|
983
|
+
const taskExisting = readJsonOrNull(taskLog);
|
|
984
|
+
if (!exists(taskLog) || looksLikeDevSeed(taskExisting)) {
|
|
985
|
+
writeJson(taskLog, {
|
|
968
986
|
schemaVersion: 1,
|
|
969
987
|
tasks: [
|
|
970
|
-
|
|
971
|
-
{ id: 't-demo-
|
|
972
|
-
{ id: 't-demo-
|
|
988
|
+
// Completed in last 7 days
|
|
989
|
+
{ id: 't-demo-ship-1', description: 'Publicar pacote @cccarv82/freya no npm (CI)', category: 'DO_NOW', status: 'COMPLETED', createdAt: daysAgoIso(6), completedAt: daysAgoIso(5), priority: 'high', projectSlug: 'acme-rocket' },
|
|
990
|
+
{ id: 't-demo-ship-2', description: 'Adicionar UI web local (freya web)', category: 'DO_NOW', status: 'COMPLETED', createdAt: daysAgoIso(4), completedAt: daysAgoIso(3), priority: 'high', projectSlug: 'acme-rocket' },
|
|
991
|
+
{ id: 't-demo-ship-3', description: 'Corrigir publish via npm token/2FA', category: 'DO_NOW', status: 'COMPLETED', createdAt: daysAgoIso(3), completedAt: daysAgoIso(2), priority: 'medium' },
|
|
992
|
+
// Pending
|
|
993
|
+
{ id: 't-demo-now-1', description: 'Refinar UI/UX do painel web (layout + preview)', category: 'DO_NOW', status: 'PENDING', createdAt: daysAgoIso(1), priority: 'high' },
|
|
994
|
+
{ id: 't-demo-now-2', description: 'Melhorar publish no Teams (cards + chunks)', category: 'DO_NOW', status: 'PENDING', createdAt: daysAgoIso(1), priority: 'medium' },
|
|
995
|
+
{ id: 't-demo-schedule-1', description: 'Criar modo "freya web" com preview Markdown', category: 'SCHEDULE', status: 'PENDING', createdAt: daysAgoIso(0), priority: 'medium' },
|
|
996
|
+
{ id: 't-demo-delegate-1', description: 'Pedir feedback de 3 usuários beta (UX)', category: 'DELEGATE', status: 'PENDING', createdAt: daysAgoIso(0), priority: 'low' }
|
|
973
997
|
]
|
|
974
|
-
}
|
|
998
|
+
});
|
|
975
999
|
}
|
|
976
1000
|
|
|
977
1001
|
const careerLog = path.join(workspaceDir, 'data', 'career', 'career-log.json');
|
|
978
|
-
|
|
979
|
-
|
|
1002
|
+
const careerExisting = readJsonOrNull(careerLog);
|
|
1003
|
+
if (!exists(careerLog) || looksLikeDevSeed(careerExisting)) {
|
|
1004
|
+
writeJson(careerLog, {
|
|
980
1005
|
schemaVersion: 1,
|
|
981
1006
|
entries: [
|
|
982
|
-
{ id: 'c-demo-1', date:
|
|
983
|
-
{ id: 'c-demo-2', date:
|
|
1007
|
+
{ id: 'c-demo-1', date: daysAgoIso(6).slice(0, 10), type: 'Achievement', description: 'Estruturou pipeline de publicação npm com tags e NPM_TOKEN', tags: ['devops', 'release'], source: 'dev-seed' },
|
|
1008
|
+
{ id: 'c-demo-2', date: daysAgoIso(4).slice(0, 10), type: 'Feedback', description: 'Feedback: “Setup via npx ficou ridiculamente simples.”', tags: ['product', 'ux'], source: 'dev-seed' },
|
|
1009
|
+
{ id: 'c-demo-3', date: daysAgoIso(2).slice(0, 10), type: 'Achievement', description: 'Entregou modo web local com geração de relatórios e publish.', tags: ['shipping', 'frontend'], source: 'dev-seed' },
|
|
1010
|
+
{ id: 'c-demo-4', date: daysAgoIso(1).slice(0, 10), type: 'Goal', description: 'Validar o produto com 5 times e transformar em serviço B2B.', tags: ['business'], source: 'dev-seed' }
|
|
984
1011
|
]
|
|
985
|
-
}
|
|
1012
|
+
});
|
|
986
1013
|
}
|
|
987
1014
|
|
|
988
1015
|
const blockerLog = path.join(workspaceDir, 'data', 'blockers', 'blocker-log.json');
|
|
989
|
-
|
|
990
|
-
|
|
1016
|
+
const blockerExisting = readJsonOrNull(blockerLog);
|
|
1017
|
+
if (!exists(blockerLog) || looksLikeDevSeed(blockerExisting)) {
|
|
1018
|
+
writeJson(blockerLog, {
|
|
991
1019
|
schemaVersion: 1,
|
|
992
1020
|
blockers: [
|
|
993
|
-
{ id: 'b-demo-1', title: '
|
|
994
|
-
{ id: 'b-demo-
|
|
1021
|
+
{ id: 'b-demo-crit-1', title: 'Spawn EINVAL no Windows ao rodar npx via server', description: 'Ajustar execução via cmd.exe /c', createdAt: daysAgoIso(2), status: 'RESOLVED', severity: 'CRITICAL', resolvedAt: daysAgoIso(1), nextAction: 'Validar em ambiente real' },
|
|
1022
|
+
{ id: 'b-demo-high-1', title: 'Teams webhook truncando mensagens longas', description: 'Implementar chunking + cards', createdAt: daysAgoIso(1), status: 'OPEN', severity: 'HIGH', nextAction: 'Dividir em blocos e enviar sequencialmente' },
|
|
1023
|
+
{ id: 'b-demo-med-1', title: 'Preview Markdown no web (render)', description: 'Adicionar render de Markdown no front', createdAt: daysAgoIso(1), status: 'MITIGATING', severity: 'MEDIUM', nextAction: 'Render simples (headers/lists/code) sem deps' },
|
|
1024
|
+
{ id: 'b-demo-low-1', title: 'Polish: remover duplicações na UI', description: 'Consolidar ações na sidebar ou na página', createdAt: daysAgoIso(0), status: 'OPEN', severity: 'LOW' }
|
|
995
1025
|
]
|
|
996
|
-
}
|
|
1026
|
+
});
|
|
997
1027
|
}
|
|
998
1028
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1029
|
+
// Project statuses
|
|
1030
|
+
const projectStatus1 = path.join(workspaceDir, 'data', 'Clients', 'acme', 'rocket', 'status.json');
|
|
1031
|
+
const ps1 = readJsonOrNull(projectStatus1);
|
|
1032
|
+
if (!exists(projectStatus1) || looksLikeDevSeed(ps1)) {
|
|
1033
|
+
writeJson(projectStatus1, {
|
|
1002
1034
|
schemaVersion: 1,
|
|
1003
1035
|
client: 'Acme',
|
|
1004
1036
|
project: 'Rocket',
|
|
1005
1037
|
currentStatus: 'Green — progressing as planned',
|
|
1006
|
-
lastUpdated:
|
|
1038
|
+
lastUpdated: daysAgoIso(0),
|
|
1007
1039
|
active: true,
|
|
1008
1040
|
history: [
|
|
1009
|
-
{ date:
|
|
1010
|
-
{ date:
|
|
1041
|
+
{ date: daysAgoIso(6), type: 'Decision', content: 'Adotar publish via tags vX.Y.Z no GitHub', source: 'dev-seed' },
|
|
1042
|
+
{ date: daysAgoIso(4), type: 'Status', content: 'Painel web MVP subiu localmente (freya web)', source: 'dev-seed' },
|
|
1043
|
+
{ date: daysAgoIso(2), type: 'Risk', content: 'Windows spawn issues ao chamar npx (corrigir)', source: 'dev-seed' },
|
|
1044
|
+
{ date: daysAgoIso(1), type: 'Status', content: 'Correções de compatibilidade Windows + auto-seed', source: 'dev-seed' },
|
|
1045
|
+
{ date: daysAgoIso(0), type: 'Status', content: 'UI redesign inspirado em apps modernos (tema claro + toggle)', source: 'dev-seed' }
|
|
1011
1046
|
]
|
|
1012
|
-
}
|
|
1047
|
+
});
|
|
1013
1048
|
}
|
|
1014
1049
|
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1050
|
+
const projectStatus2 = path.join(workspaceDir, 'data', 'Clients', 'vivo', '5g', 'status.json');
|
|
1051
|
+
const ps2 = readJsonOrNull(projectStatus2);
|
|
1052
|
+
if (!exists(projectStatus2) || looksLikeDevSeed(ps2)) {
|
|
1053
|
+
writeJson(projectStatus2, {
|
|
1054
|
+
schemaVersion: 1,
|
|
1055
|
+
client: 'Vivo',
|
|
1056
|
+
project: '5G',
|
|
1057
|
+
currentStatus: 'Amber — dependency on vendor payload format',
|
|
1058
|
+
lastUpdated: daysAgoIso(1),
|
|
1059
|
+
active: true,
|
|
1060
|
+
history: [
|
|
1061
|
+
{ date: daysAgoIso(5), type: 'Status', content: 'Integração inicial concluída; aguardando webhook do Teams', source: 'dev-seed' },
|
|
1062
|
+
{ date: daysAgoIso(3), type: 'Blocker', content: 'Falha intermitente no webhook em ambiente com 2FA', source: 'dev-seed' },
|
|
1063
|
+
{ date: daysAgoIso(1), type: 'Decision', content: 'Implementar chunking e fallback de publish', source: 'dev-seed' }
|
|
1064
|
+
]
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Daily logs: create today and yesterday if missing
|
|
1069
|
+
const today = isoDate();
|
|
1070
|
+
const yesterday = isoDate(new Date(Date.now() - 24 * 60 * 60 * 1000));
|
|
1071
|
+
|
|
1072
|
+
const daily1 = path.join(workspaceDir, 'logs', 'daily', `${yesterday}.md`);
|
|
1073
|
+
if (!exists(daily1)) {
|
|
1074
|
+
fs.writeFileSync(
|
|
1075
|
+
daily1,
|
|
1076
|
+
`# Daily Log ${yesterday}\n\n## [09:10] Raw Input\nHoje preciso melhorar a UX do web e destravar publish no Teams.\n\n## [11:25] Raw Input\nEstou travado no payload do Teams; vou dividir mensagens em chunks.\n\n## [18:05] Raw Input\nTerminei a correção do Windows (spawn) e rodei testes.\n`,
|
|
1077
|
+
'utf8'
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const daily2 = path.join(workspaceDir, 'logs', 'daily', `${today}.md`);
|
|
1082
|
+
if (!exists(daily2)) {
|
|
1083
|
+
fs.writeFileSync(
|
|
1084
|
+
daily2,
|
|
1085
|
+
`# Daily Log ${today}\n\n## [09:05] Raw Input\nReunião com Acme: projeto Rocket verde, foco em polish do produto.\n\n## [14:20] Raw Input\nPreciso preparar update executivo e publicar no Discord.\n\n## [17:45] Raw Input\nFechei os blockers críticos e gerei relatório SM semanal.\n`,
|
|
1086
|
+
'utf8'
|
|
1087
|
+
);
|
|
1018
1088
|
}
|
|
1019
1089
|
|
|
1020
1090
|
return {
|
|
1021
1091
|
seeded: true,
|
|
1022
|
-
paths: {
|
|
1092
|
+
paths: {
|
|
1093
|
+
taskLog,
|
|
1094
|
+
careerLog,
|
|
1095
|
+
blockerLog,
|
|
1096
|
+
projectStatus1,
|
|
1097
|
+
projectStatus2,
|
|
1098
|
+
daily1,
|
|
1099
|
+
daily2
|
|
1100
|
+
}
|
|
1023
1101
|
};
|
|
1024
1102
|
}
|
|
1025
1103
|
|
|
@@ -1031,7 +1109,7 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1031
1109
|
if (!req.url) return safeJson(res, 404, { error: 'Not found' });
|
|
1032
1110
|
|
|
1033
1111
|
if (req.method === 'GET' && req.url === '/') {
|
|
1034
|
-
const body = html();
|
|
1112
|
+
const body = html(dir || './freya');
|
|
1035
1113
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
1036
1114
|
res.end(body);
|
|
1037
1115
|
return;
|
|
@@ -1090,7 +1168,7 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
1090
1168
|
blockers: 'blockers-',
|
|
1091
1169
|
'sm-weekly': 'sm-weekly-',
|
|
1092
1170
|
status: 'executive-',
|
|
1093
|
-
daily:
|
|
1171
|
+
daily: 'daily-'
|
|
1094
1172
|
};
|
|
1095
1173
|
const prefix = prefixMap[script] || null;
|
|
1096
1174
|
const reportPath = prefix ? newestFile(reportsDir, prefix) : null;
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ const { safeReadJson, quarantineCorruptedFile } = require('./lib/fs-utils');
|
|
|
6
6
|
|
|
7
7
|
const TASK_LOG_PATH = path.join(__dirname, '../data/tasks/task-log.json');
|
|
8
8
|
const BLOCKERS_LOG_PATH = path.join(__dirname, '../data/blockers/blocker-log.json');
|
|
9
|
+
const REPORT_DIR = path.join(__dirname, '../docs/reports');
|
|
9
10
|
|
|
10
11
|
// --- Helper Logic ---
|
|
11
12
|
const now = new Date();
|
|
@@ -88,6 +89,16 @@ function generateDailySummary() {
|
|
|
88
89
|
|
|
89
90
|
console.log(summary);
|
|
90
91
|
|
|
92
|
+
// Write report file for UI (optional, but helps preview/history)
|
|
93
|
+
try {
|
|
94
|
+
fs.mkdirSync(REPORT_DIR, { recursive: true });
|
|
95
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
96
|
+
const outPath = path.join(REPORT_DIR, `daily-${date}.md`);
|
|
97
|
+
fs.writeFileSync(outPath, `# Daily Summary — ${date}\n\n${summary}\n`, 'utf8');
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// non-fatal
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
} catch (err) {
|
|
92
103
|
console.error("Error generating daily:", err.message);
|
|
93
104
|
}
|
|
@@ -218,7 +218,7 @@ function generateReport(period) {
|
|
|
218
218
|
|
|
219
219
|
// Save
|
|
220
220
|
ensureDir(OUTPUT_DIR);
|
|
221
|
-
const filename = `
|
|
221
|
+
const filename = `executive-${period}-${dateStr}.md`;
|
|
222
222
|
const outputPath = path.join(OUTPUT_DIR, filename);
|
|
223
223
|
fs.writeFileSync(outputPath, md, 'utf8');
|
|
224
224
|
|