@cccarv82/freya 1.0.28 → 1.0.30

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-ui.js CHANGED
@@ -347,7 +347,9 @@
347
347
  const pri = (t.priority || '').toUpperCase();
348
348
  row.innerHTML = '<div style="display:flex; justify-content:space-between; gap:10px; align-items:center">'
349
349
  + '<div style="min-width:0"><div style="font-weight:700">' + escapeHtml(t.description || '') + '</div>'
350
- + '<div style="opacity:.7; font-size:11px; margin-top:4px">' + escapeHtml(String(t.category || '')) + (pri ? (' · ' + escapeHtml(pri)) : '') + '</div></div>'
350
+ + '<div style="opacity:.7; font-size:11px; margin-top:4px">' + escapeHtml(String(t.category || ''))
351
+ + (t.projectSlug ? (' · <span style="font-family:var(--mono); opacity:.9">[' + escapeHtml(String(t.projectSlug)) + ']</span>') : '')
352
+ + (pri ? (' · ' + escapeHtml(pri)) : '') + '</div></div>'
351
353
  + '<button class="btn small" type="button">Complete</button>'
352
354
  + '</div>';
353
355
  const btn = row.querySelector('button');
@@ -383,7 +385,9 @@
383
385
  const sev = String(b.severity || '').toUpperCase();
384
386
  row.innerHTML = '<div style="display:flex; justify-content:space-between; gap:10px; align-items:center">'
385
387
  + '<div style="min-width:0"><div style="font-weight:800">' + escapeHtml(sev) + '</div>'
386
- + '<div style="margin-top:4px">' + escapeHtml(b.title || '') + '</div>'
388
+ + '<div style="margin-top:4px">' + escapeHtml(b.title || '')
389
+ + (b.projectSlug ? (' <span style="font-family:var(--mono); opacity:.8">[' + escapeHtml(String(b.projectSlug)) + ']</span>') : '')
390
+ + '</div>'
387
391
  + '</div>'
388
392
  + '<div style="opacity:.7; font-size:11px; white-space:nowrap">' + escapeHtml(fmtWhen(new Date(b.createdAt || Date.now()).getTime())) + '</div>'
389
393
  + '</div>';
package/cli/web.js CHANGED
@@ -91,6 +91,66 @@ function writeSettings(workspaceDir, settings) {
91
91
  return out;
92
92
  }
93
93
 
94
+ function projectSlugMapPath(workspaceDir) {
95
+ return path.join(workspaceDir, 'data', 'settings', 'project-slug-map.json');
96
+ }
97
+
98
+ function readProjectSlugMap(workspaceDir) {
99
+ const p = projectSlugMapPath(workspaceDir);
100
+ try {
101
+ if (!exists(p)) {
102
+ ensureDir(path.dirname(p));
103
+ const defaults = {
104
+ schemaVersion: 1,
105
+ updatedAt: new Date().toISOString(),
106
+ rules: [
107
+ { contains: 'fideliza', slug: 'vivo/fidelizacao' },
108
+ { contains: 'bnpl', slug: 'vivo/bnpl' },
109
+ { contains: 'dpgc', slug: 'vivo/bnpl/dpgc' },
110
+ { contains: 'vivo+', slug: 'vivo/vivoplus' }
111
+ ]
112
+ };
113
+ fs.writeFileSync(p, JSON.stringify(defaults, null, 2) + '\n', 'utf8');
114
+ return defaults;
115
+ }
116
+ const json = JSON.parse(fs.readFileSync(p, 'utf8'));
117
+ if (!json || !Array.isArray(json.rules)) return { schemaVersion: 1, rules: [] };
118
+ return json;
119
+ } catch {
120
+ return { schemaVersion: 1, rules: [] };
121
+ }
122
+ }
123
+
124
+ function inferProjectSlug(text, map) {
125
+ const t = String(text || '').toLowerCase();
126
+ if (!t.trim()) return '';
127
+
128
+ let base = '';
129
+ const rules = (map && Array.isArray(map.rules)) ? map.rules : [];
130
+ for (const r of rules) {
131
+ if (!r) continue;
132
+ const needle = String(r.contains || '').toLowerCase().trim();
133
+ const slug = String(r.slug || '').trim();
134
+ if (!needle || !slug) continue;
135
+ if (t.includes(needle)) { base = slug; break; }
136
+ }
137
+
138
+ // CHG tags
139
+ const chg = (t.match(/\bchg\s*0*\d{4,}\b/i) || [])[0];
140
+ const chgNorm = chg ? chg.toLowerCase().replace(/\s+/g, '') : '';
141
+
142
+ // If no base but looks like Vivo context, at least prefix vivo
143
+ if (!base && (t.includes('vivo') || t.includes('vivo+'))) base = 'vivo';
144
+
145
+ if (base && chgNorm) {
146
+ // keep numeric id
147
+ const id = chgNorm.replace(/[^0-9]/g, '');
148
+ if (id) base = base.replace(/\/+$/g, '') + '/chg' + id;
149
+ }
150
+
151
+ return base;
152
+ }
153
+
94
154
  function listReports(workspaceDir) {
95
155
  const dir = path.join(workspaceDir, 'docs', 'reports');
96
156
  if (!exists(dir)) return [];
@@ -1000,8 +1060,8 @@ async function cmdWeb({ port, dir, open, dev }) {
1000
1060
  const schema = {
1001
1061
  actions: [
1002
1062
  { type: 'append_daily_log', text: '<string>' },
1003
- { type: 'create_task', description: '<string>', priority: 'HIGH|MEDIUM|LOW', category: 'DO_NOW|SCHEDULE|DELEGATE|IGNORE' },
1004
- { type: 'create_blocker', title: '<string>', severity: 'CRITICAL|HIGH|MEDIUM|LOW', notes: '<string>' },
1063
+ { type: 'create_task', description: '<string>', priority: 'HIGH|MEDIUM|LOW', category: 'DO_NOW|SCHEDULE|DELEGATE|IGNORE', projectSlug: '<string optional>' },
1064
+ { type: 'create_blocker', title: '<string>', severity: 'CRITICAL|HIGH|MEDIUM|LOW', notes: '<string>', projectSlug: '<string optional>' },
1005
1065
  { type: 'suggest_report', name: 'daily|status|sm-weekly|blockers' },
1006
1066
  { type: 'oracle_query', query: '<string>' }
1007
1067
  ]
@@ -1082,7 +1142,8 @@ async function cmdWeb({ port, dir, open, dev }) {
1082
1142
  const priorityRaw = String(a.priority || '').trim().toLowerCase();
1083
1143
  const priority = (priorityRaw === 'high' || priorityRaw === 'medium' || priorityRaw === 'low') ? priorityRaw : undefined;
1084
1144
  if (!description) { preview.errors.push('Task missing description'); continue; }
1085
- preview.tasks.push({ description, category: validTaskCats.has(category) ? category : 'DO_NOW', priority });
1145
+ const projectSlug = String(a.projectSlug || '').trim();
1146
+ preview.tasks.push({ description, category: validTaskCats.has(category) ? category : 'DO_NOW', priority, projectSlug: projectSlug || undefined });
1086
1147
  continue;
1087
1148
  }
1088
1149
 
@@ -1098,7 +1159,8 @@ async function cmdWeb({ port, dir, open, dev }) {
1098
1159
  else severity = 'MEDIUM';
1099
1160
  }
1100
1161
  if (!title) { preview.errors.push('Blocker missing title'); continue; }
1101
- preview.blockers.push({ title, notes, severity });
1162
+ const projectSlug = String(a.projectSlug || '').trim();
1163
+ preview.blockers.push({ title, notes, severity, projectSlug: projectSlug || undefined });
1102
1164
  continue;
1103
1165
  }
1104
1166
 
@@ -1220,6 +1282,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1220
1282
  const now = new Date().toISOString();
1221
1283
  const applyMode = String(payload.mode || 'all').trim();
1222
1284
  const applied = { tasks: 0, blockers: 0, tasksSkipped: 0, blockersSkipped: 0, reportsSuggested: [], oracleQueries: [], mode: applyMode };
1285
+ const slugMap = readProjectSlugMap(workspaceDir);
1223
1286
 
1224
1287
  function makeId(prefix) {
1225
1288
  const rand = Math.random().toString(16).slice(2, 8);
@@ -1254,7 +1317,8 @@ async function cmdWeb({ port, dir, open, dev }) {
1254
1317
  if (applyMode !== 'all' && applyMode !== 'tasks') continue;
1255
1318
  const description = normalizeWhitespace(a.description);
1256
1319
  if (!description) continue;
1257
- const key = sha1(normalizeTextForKey(description));
1320
+ const projectSlug = String(a.projectSlug || '').trim() || inferProjectSlug(description, slugMap);
1321
+ const key = sha1(normalizeTextForKey((projectSlug ? projectSlug + ' ' : '') + description));
1258
1322
  if (existingTaskKeys24h.has(key)) { applied.tasksSkipped++; continue; }
1259
1323
  const category = validTaskCats.has(String(a.category || '').trim()) ? String(a.category).trim() : 'DO_NOW';
1260
1324
  const priority = normPriority(a.priority);
@@ -1265,6 +1329,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1265
1329
  status: 'PENDING',
1266
1330
  createdAt: now,
1267
1331
  };
1332
+ if (projectSlug) task.projectSlug = projectSlug;
1268
1333
  if (priority) task.priority = priority;
1269
1334
  taskLog.tasks.push(task);
1270
1335
  applied.tasks++;
@@ -1274,7 +1339,8 @@ async function cmdWeb({ port, dir, open, dev }) {
1274
1339
  if (type === 'create_blocker') {
1275
1340
  if (applyMode !== 'all' && applyMode !== 'blockers') continue;
1276
1341
  const title = normalizeWhitespace(a.title);
1277
- const key = sha1(normalizeTextForKey(title));
1342
+ const projectSlug = String(a.projectSlug || '').trim() || inferProjectSlug(title + ' ' + normalizeWhitespace(a.notes), slugMap);
1343
+ const key = sha1(normalizeTextForKey((projectSlug ? projectSlug + ' ' : '') + title));
1278
1344
  if (existingBlockerKeys24h.has(key)) { applied.blockersSkipped++; continue; }
1279
1345
  const notes = normalizeWhitespace(a.notes);
1280
1346
  if (!title) continue;
@@ -1287,6 +1353,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1287
1353
  status: 'OPEN',
1288
1354
  severity,
1289
1355
  };
1356
+ if (projectSlug) blocker.projectSlug = projectSlug;
1290
1357
  blockerLog.blockers.push(blocker);
1291
1358
  applied.blockers++;
1292
1359
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",