@cccarv82/freya 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/cli/web.js +129 -32
  2. package/package.json +1 -1
package/cli/web.js CHANGED
@@ -6,11 +6,13 @@ const path = require('path');
6
6
  const { spawn } = require('child_process');
7
7
 
8
8
  function guessNpmCmd() {
9
- return process.platform === 'win32' ? 'npm.cmd' : 'npm';
9
+ // We'll execute via cmd.exe on Windows for reliability.
10
+ return process.platform === 'win32' ? 'npm' : 'npm';
10
11
  }
11
12
 
12
13
  function guessNpxCmd() {
13
- return process.platform === 'win32' ? 'npx.cmd' : 'npx';
14
+ // We'll execute via cmd.exe on Windows for reliability.
15
+ return process.platform === 'win32' ? 'npx' : 'npx';
14
16
  }
15
17
 
16
18
  function guessNpxYesFlag() {
@@ -99,8 +101,15 @@ function readBody(req) {
99
101
  function run(cmd, args, cwd) {
100
102
  return new Promise((resolve) => {
101
103
  let child;
104
+
102
105
  try {
103
- child = spawn(cmd, args, { cwd, shell: false, env: process.env });
106
+ // On Windows, reliably execute CLI tools through cmd.exe.
107
+ if (process.platform === 'win32' && (cmd === 'npx' || cmd === 'npm')) {
108
+ const comspec = process.env.ComSpec || 'cmd.exe';
109
+ child = spawn(comspec, ['/d', '/s', '/c', cmd, ...args], { cwd, shell: false, env: process.env });
110
+ } else {
111
+ child = spawn(cmd, args, { cwd, shell: false, env: process.env });
112
+ }
104
113
  } catch (e) {
105
114
  return resolve({ code: 1, stdout: '', stderr: e.message || String(e) });
106
115
  }
@@ -115,7 +124,7 @@ function run(cmd, args, cwd) {
115
124
  stderr += d.toString();
116
125
  });
117
126
 
118
- // Prevent unhandled error event (e.g., ENOENT on Windows when cmd not found)
127
+ // Prevent unhandled error event (e.g., ENOENT/EINVAL)
119
128
  child.on('error', (e) => {
120
129
  stderr += `\n${e.message || String(e)}`;
121
130
  resolve({ code: 1, stdout, stderr });
@@ -945,72 +954,160 @@ function isoNow() {
945
954
  return new Date().toISOString();
946
955
  }
947
956
 
957
+ function daysAgoIso(n) {
958
+ return new Date(Date.now() - n * 24 * 60 * 60 * 1000).toISOString();
959
+ }
960
+
961
+ function readJsonOrNull(p) {
962
+ try {
963
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
964
+ } catch {
965
+ return null;
966
+ }
967
+ }
968
+
969
+ function writeJson(p, obj) {
970
+ ensureDir(path.dirname(p));
971
+ fs.writeFileSync(p, JSON.stringify(obj, null, 2) + '\n', 'utf8');
972
+ }
973
+
974
+ function looksLikeDevSeed(json) {
975
+ if (!json || typeof json !== 'object') return false;
976
+ // Heuristic: any id ends with demo marker or source is dev-seed
977
+ const dump = JSON.stringify(json);
978
+ return dump.includes('dev-seed') || dump.includes('t-demo-') || dump.includes('b-demo-') || dump.includes('c-demo-');
979
+ }
980
+
948
981
  function seedDevWorkspace(workspaceDir) {
949
- // Only create if missing; never overwrite user content.
982
+ // Safe by default:
983
+ // - create missing demo files
984
+ // - if file exists and looks like prior dev-seed, upgrade it to richer demo
950
985
  ensureDir(path.join(workspaceDir, 'data', 'tasks'));
951
986
  ensureDir(path.join(workspaceDir, 'data', 'career'));
952
987
  ensureDir(path.join(workspaceDir, 'data', 'blockers'));
953
988
  ensureDir(path.join(workspaceDir, 'data', 'Clients', 'acme', 'rocket'));
989
+ ensureDir(path.join(workspaceDir, 'data', 'Clients', 'vivo', '5g'));
954
990
  ensureDir(path.join(workspaceDir, 'logs', 'daily'));
955
991
 
956
992
  const taskLog = path.join(workspaceDir, 'data', 'tasks', 'task-log.json');
957
- if (!exists(taskLog)) {
958
- fs.writeFileSync(taskLog, JSON.stringify({
993
+ const taskExisting = readJsonOrNull(taskLog);
994
+ if (!exists(taskLog) || looksLikeDevSeed(taskExisting)) {
995
+ writeJson(taskLog, {
959
996
  schemaVersion: 1,
960
997
  tasks: [
961
- { id: 't-demo-1', description: 'Preparar update executivo', category: 'DO_NOW', status: 'PENDING', createdAt: isoNow(), priority: 'high' },
962
- { id: 't-demo-2', description: 'Revisar PR de integração Teams', category: 'SCHEDULE', status: 'PENDING', createdAt: isoNow(), priority: 'medium' },
963
- { id: 't-demo-3', description: 'Rodar retro e registrar aprendizados', category: 'DO_NOW', status: 'COMPLETED', createdAt: isoNow(), completedAt: isoNow(), priority: 'low' }
998
+ // Completed in last 7 days
999
+ { 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' },
1000
+ { 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' },
1001
+ { 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' },
1002
+ // Pending
1003
+ { id: 't-demo-now-1', description: 'Refinar UI/UX do painel web (layout + preview)', category: 'DO_NOW', status: 'PENDING', createdAt: daysAgoIso(1), priority: 'high' },
1004
+ { id: 't-demo-now-2', description: 'Melhorar publish no Teams (cards + chunks)', category: 'DO_NOW', status: 'PENDING', createdAt: daysAgoIso(1), priority: 'medium' },
1005
+ { id: 't-demo-schedule-1', description: 'Criar modo "freya web" com preview Markdown', category: 'SCHEDULE', status: 'PENDING', createdAt: daysAgoIso(0), priority: 'medium' },
1006
+ { id: 't-demo-delegate-1', description: 'Pedir feedback de 3 usuários beta (UX)', category: 'DELEGATE', status: 'PENDING', createdAt: daysAgoIso(0), priority: 'low' }
964
1007
  ]
965
- }, null, 2) + '\n', 'utf8');
1008
+ });
966
1009
  }
967
1010
 
968
1011
  const careerLog = path.join(workspaceDir, 'data', 'career', 'career-log.json');
969
- if (!exists(careerLog)) {
970
- fs.writeFileSync(careerLog, JSON.stringify({
1012
+ const careerExisting = readJsonOrNull(careerLog);
1013
+ if (!exists(careerLog) || looksLikeDevSeed(careerExisting)) {
1014
+ writeJson(careerLog, {
971
1015
  schemaVersion: 1,
972
1016
  entries: [
973
- { id: 'c-demo-1', date: isoDate(), type: 'Achievement', description: 'Publicou o CLI @cccarv82/freya com init/web', tags: ['shipping', 'tooling'], source: 'dev-seed' },
974
- { id: 'c-demo-2', date: isoDate(), type: 'Feedback', description: 'Feedback: UX do painel web está “muito promissor”', tags: ['product'], source: 'dev-seed' }
1017
+ { 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' },
1018
+ { 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' },
1019
+ { 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' },
1020
+ { 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' }
975
1021
  ]
976
- }, null, 2) + '\n', 'utf8');
1022
+ });
977
1023
  }
978
1024
 
979
1025
  const blockerLog = path.join(workspaceDir, 'data', 'blockers', 'blocker-log.json');
980
- if (!exists(blockerLog)) {
981
- fs.writeFileSync(blockerLog, JSON.stringify({
1026
+ const blockerExisting = readJsonOrNull(blockerLog);
1027
+ if (!exists(blockerLog) || looksLikeDevSeed(blockerExisting)) {
1028
+ writeJson(blockerLog, {
982
1029
  schemaVersion: 1,
983
1030
  blockers: [
984
- { 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' },
985
- { id: 'b-demo-2', title: 'Definir template de status report por cliente', description: 'Padronizar headings', createdAt: isoNow(), status: 'MITIGATING', severity: 'MEDIUM' }
1031
+ { 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' },
1032
+ { 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' },
1033
+ { 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' },
1034
+ { 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' }
986
1035
  ]
987
- }, null, 2) + '\n', 'utf8');
1036
+ });
988
1037
  }
989
1038
 
990
- const projectStatus = path.join(workspaceDir, 'data', 'Clients', 'acme', 'rocket', 'status.json');
991
- if (!exists(projectStatus)) {
992
- fs.writeFileSync(projectStatus, JSON.stringify({
1039
+ // Project statuses
1040
+ const projectStatus1 = path.join(workspaceDir, 'data', 'Clients', 'acme', 'rocket', 'status.json');
1041
+ const ps1 = readJsonOrNull(projectStatus1);
1042
+ if (!exists(projectStatus1) || looksLikeDevSeed(ps1)) {
1043
+ writeJson(projectStatus1, {
993
1044
  schemaVersion: 1,
994
1045
  client: 'Acme',
995
1046
  project: 'Rocket',
996
1047
  currentStatus: 'Green — progressing as planned',
997
- lastUpdated: isoNow(),
1048
+ lastUpdated: daysAgoIso(0),
998
1049
  active: true,
999
1050
  history: [
1000
- { date: isoNow(), type: 'Status', content: 'Launched stage 1', source: 'dev-seed' },
1001
- { date: isoNow(), type: 'Risk', content: 'Potential delay on vendor dependency', source: 'dev-seed' }
1051
+ { date: daysAgoIso(6), type: 'Decision', content: 'Adotar publish via tags vX.Y.Z no GitHub', source: 'dev-seed' },
1052
+ { date: daysAgoIso(4), type: 'Status', content: 'Painel web MVP subiu localmente (freya web)', source: 'dev-seed' },
1053
+ { date: daysAgoIso(2), type: 'Risk', content: 'Windows spawn issues ao chamar npx (corrigir)', source: 'dev-seed' },
1054
+ { date: daysAgoIso(1), type: 'Status', content: 'Correções de compatibilidade Windows + auto-seed', source: 'dev-seed' },
1055
+ { date: daysAgoIso(0), type: 'Status', content: 'UI redesign inspirado em apps modernos (tema claro + toggle)', source: 'dev-seed' }
1002
1056
  ]
1003
- }, null, 2) + '\n', 'utf8');
1057
+ });
1004
1058
  }
1005
1059
 
1006
- const dailyLog = path.join(workspaceDir, 'logs', 'daily', `${isoDate()}.md`);
1007
- if (!exists(dailyLog)) {
1008
- 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');
1060
+ const projectStatus2 = path.join(workspaceDir, 'data', 'Clients', 'vivo', '5g', 'status.json');
1061
+ const ps2 = readJsonOrNull(projectStatus2);
1062
+ if (!exists(projectStatus2) || looksLikeDevSeed(ps2)) {
1063
+ writeJson(projectStatus2, {
1064
+ schemaVersion: 1,
1065
+ client: 'Vivo',
1066
+ project: '5G',
1067
+ currentStatus: 'Amber — dependency on vendor payload format',
1068
+ lastUpdated: daysAgoIso(1),
1069
+ active: true,
1070
+ history: [
1071
+ { date: daysAgoIso(5), type: 'Status', content: 'Integração inicial concluída; aguardando webhook do Teams', source: 'dev-seed' },
1072
+ { date: daysAgoIso(3), type: 'Blocker', content: 'Falha intermitente no webhook em ambiente com 2FA', source: 'dev-seed' },
1073
+ { date: daysAgoIso(1), type: 'Decision', content: 'Implementar chunking e fallback de publish', source: 'dev-seed' }
1074
+ ]
1075
+ });
1076
+ }
1077
+
1078
+ // Daily logs: create today and yesterday if missing
1079
+ const today = isoDate();
1080
+ const yesterday = isoDate(new Date(Date.now() - 24 * 60 * 60 * 1000));
1081
+
1082
+ const daily1 = path.join(workspaceDir, 'logs', 'daily', `${yesterday}.md`);
1083
+ if (!exists(daily1)) {
1084
+ fs.writeFileSync(
1085
+ daily1,
1086
+ `# 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`,
1087
+ 'utf8'
1088
+ );
1089
+ }
1090
+
1091
+ const daily2 = path.join(workspaceDir, 'logs', 'daily', `${today}.md`);
1092
+ if (!exists(daily2)) {
1093
+ fs.writeFileSync(
1094
+ daily2,
1095
+ `# 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`,
1096
+ 'utf8'
1097
+ );
1009
1098
  }
1010
1099
 
1011
1100
  return {
1012
1101
  seeded: true,
1013
- paths: { taskLog, careerLog, blockerLog, projectStatus, dailyLog }
1102
+ paths: {
1103
+ taskLog,
1104
+ careerLog,
1105
+ blockerLog,
1106
+ projectStatus1,
1107
+ projectStatus2,
1108
+ daily1,
1109
+ daily2
1110
+ }
1014
1111
  };
1015
1112
  }
1016
1113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",