@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
- // Only create if missing; never overwrite user content.
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
- if (!exists(taskLog)) {
967
- fs.writeFileSync(taskLog, JSON.stringify({
983
+ const taskExisting = readJsonOrNull(taskLog);
984
+ if (!exists(taskLog) || looksLikeDevSeed(taskExisting)) {
985
+ writeJson(taskLog, {
968
986
  schemaVersion: 1,
969
987
  tasks: [
970
- { id: 't-demo-1', description: 'Preparar update executivo', category: 'DO_NOW', status: 'PENDING', createdAt: isoNow(), priority: 'high' },
971
- { id: 't-demo-2', description: 'Revisar PR de integração Teams', category: 'SCHEDULE', status: 'PENDING', createdAt: isoNow(), priority: 'medium' },
972
- { id: 't-demo-3', description: 'Rodar retro e registrar aprendizados', category: 'DO_NOW', status: 'COMPLETED', createdAt: isoNow(), completedAt: isoNow(), priority: 'low' }
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
- }, null, 2) + '\n', 'utf8');
998
+ });
975
999
  }
976
1000
 
977
1001
  const careerLog = path.join(workspaceDir, 'data', 'career', 'career-log.json');
978
- if (!exists(careerLog)) {
979
- fs.writeFileSync(careerLog, JSON.stringify({
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: isoDate(), type: 'Achievement', description: 'Publicou o CLI @cccarv82/freya com init/web', tags: ['shipping', 'tooling'], source: 'dev-seed' },
983
- { id: 'c-demo-2', date: isoDate(), type: 'Feedback', description: 'Feedback: UX do painel web está “muito promissor”', tags: ['product'], source: 'dev-seed' }
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
- }, null, 2) + '\n', 'utf8');
1012
+ });
986
1013
  }
987
1014
 
988
1015
  const blockerLog = path.join(workspaceDir, 'data', 'blockers', 'blocker-log.json');
989
- if (!exists(blockerLog)) {
990
- fs.writeFileSync(blockerLog, JSON.stringify({
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: 'Webhook do Teams falhando em ambientes com 2FA', description: 'Ajustar token / payload', createdAt: isoNow(), status: 'OPEN', severity: 'HIGH', nextAction: 'Validar payload e limites' },
994
- { id: 'b-demo-2', title: 'Definir template de status report por cliente', description: 'Padronizar headings', createdAt: isoNow(), status: 'MITIGATING', severity: 'MEDIUM' }
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
- }, null, 2) + '\n', 'utf8');
1026
+ });
997
1027
  }
998
1028
 
999
- const projectStatus = path.join(workspaceDir, 'data', 'Clients', 'acme', 'rocket', 'status.json');
1000
- if (!exists(projectStatus)) {
1001
- fs.writeFileSync(projectStatus, JSON.stringify({
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: isoNow(),
1038
+ lastUpdated: daysAgoIso(0),
1007
1039
  active: true,
1008
1040
  history: [
1009
- { date: isoNow(), type: 'Status', content: 'Launched stage 1', source: 'dev-seed' },
1010
- { date: isoNow(), type: 'Risk', content: 'Potential delay on vendor dependency', source: 'dev-seed' }
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
- }, null, 2) + '\n', 'utf8');
1047
+ });
1013
1048
  }
1014
1049
 
1015
- const dailyLog = path.join(workspaceDir, 'logs', 'daily', `${isoDate()}.md`);
1016
- if (!exists(dailyLog)) {
1017
- fs.writeFileSync(dailyLog, `# Daily Log ${isoDate()}\n\n## [09:15] Raw Input\nReunião com a Acme. Tudo verde, mas preciso alinhar com fornecedor.\n\n## [16:40] Raw Input\nTerminei o relatório SM e publiquei no Discord.\n`, 'utf8');
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: { taskLog, careerLog, blockerLog, projectStatus, dailyLog }
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: null
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",
@@ -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 = `report-${period}-${dateStr}.md`;
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