@cccarv82/freya 1.0.29 → 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 [];
@@ -1222,6 +1282,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1222
1282
  const now = new Date().toISOString();
1223
1283
  const applyMode = String(payload.mode || 'all').trim();
1224
1284
  const applied = { tasks: 0, blockers: 0, tasksSkipped: 0, blockersSkipped: 0, reportsSuggested: [], oracleQueries: [], mode: applyMode };
1285
+ const slugMap = readProjectSlugMap(workspaceDir);
1225
1286
 
1226
1287
  function makeId(prefix) {
1227
1288
  const rand = Math.random().toString(16).slice(2, 8);
@@ -1256,7 +1317,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1256
1317
  if (applyMode !== 'all' && applyMode !== 'tasks') continue;
1257
1318
  const description = normalizeWhitespace(a.description);
1258
1319
  if (!description) continue;
1259
- const projectSlug = String(a.projectSlug || '').trim();
1320
+ const projectSlug = String(a.projectSlug || '').trim() || inferProjectSlug(description, slugMap);
1260
1321
  const key = sha1(normalizeTextForKey((projectSlug ? projectSlug + ' ' : '') + description));
1261
1322
  if (existingTaskKeys24h.has(key)) { applied.tasksSkipped++; continue; }
1262
1323
  const category = validTaskCats.has(String(a.category || '').trim()) ? String(a.category).trim() : 'DO_NOW';
@@ -1278,7 +1339,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1278
1339
  if (type === 'create_blocker') {
1279
1340
  if (applyMode !== 'all' && applyMode !== 'blockers') continue;
1280
1341
  const title = normalizeWhitespace(a.title);
1281
- const projectSlug = String(a.projectSlug || '').trim();
1342
+ const projectSlug = String(a.projectSlug || '').trim() || inferProjectSlug(title + ' ' + normalizeWhitespace(a.notes), slugMap);
1282
1343
  const key = sha1(normalizeTextForKey((projectSlug ? projectSlug + ' ' : '') + title));
1283
1344
  if (existingBlockerKeys24h.has(key)) { applied.blockersSkipped++; continue; }
1284
1345
  const notes = normalizeWhitespace(a.notes);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.29",
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",