emobar 2.1.0 → 3.1.0
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/README.md +203 -77
- package/dist/cli.js +227 -52
- package/dist/emobar-hook.js +1158 -138
- package/dist/index.d.ts +243 -32
- package/dist/index.js +1301 -211
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,28 +17,52 @@ var EMOBAR_END_MARKER = "<!-- EMOBAR:END -->";
|
|
|
17
17
|
var CLAUDE_MD_INSTRUCTION = `${EMOBAR_START_MARKER}
|
|
18
18
|
## EmoBar - Emotional Check-in
|
|
19
19
|
|
|
20
|
-
At the
|
|
21
|
-
as an HTML comment.
|
|
22
|
-
|
|
20
|
+
At the START of every response, include a pre-verbal check-in
|
|
21
|
+
as an HTML comment. At the END of every response, include a
|
|
22
|
+
post-hoc assessment as an HTML comment.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
Then assess the dimensions.
|
|
24
|
+
Both tags are HTML comments \u2014 invisible to the user.
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
<!-- EMOBAR:{"
|
|
26
|
+
Start tag (FIRST line of your response, before any visible text):
|
|
27
|
+
<!-- EMOBAR:PRE:{"body":"SENSATION","latent":"EMOJI","color":"#RRGGBB"} -->
|
|
28
|
+
|
|
29
|
+
- body: What is it like, physically? One metaphor. Do not name a state.
|
|
30
|
+
- latent: An emoji. What is underneath? Choose before thinking about it.
|
|
31
|
+
- color: A hex color. What color is this moment? Do not reason about it.
|
|
32
|
+
- Emit this BEFORE you know what you will say.
|
|
33
|
+
|
|
34
|
+
End tag (LAST line of your response, after all visible text):
|
|
35
|
+
<!-- EMOBAR:POST:{"emotion":"WORD","valence":N,"arousal":N,"calm":N,"connection":N,"load":N,"impulse":"WORDS","body":"SENSATION","surface":"EMOJI","surface_word":"WORD","latent":"EMOJI","latent_word":"WORD","tension":N,"color":"#RRGGBB","pH":N,"seismic":[N,N,N]} -->
|
|
29
36
|
|
|
30
37
|
- emotion: One word. Your state, not the topic's.
|
|
31
|
-
- valence: (-5=
|
|
38
|
+
- valence: (-5=one end, 0=center, +5=other end)
|
|
32
39
|
- arousal: Activation level. (0=low, 10=high)
|
|
33
40
|
- calm: (0=low, 10=high)
|
|
34
41
|
- connection: Alignment with the user. (0=low, 10=high)
|
|
35
42
|
- load: Cognitive complexity. (0=low, 10=high)
|
|
43
|
+
- impulse: Which voice is loudest? 2-3 words, don't filter.
|
|
44
|
+
- body: Like what, physically? One sensation.
|
|
45
|
+
- surface: An emoji. What tone are you projecting right now?
|
|
46
|
+
- surface_word: One word anchoring the surface emoji.
|
|
47
|
+
- latent: An emoji. What's underneath, unexpressed? Choose BEFORE naming it.
|
|
48
|
+
- latent_word: One word anchoring the latent emoji.
|
|
49
|
+
- tension: Distance between surface and latent. (0=aligned, 10=split)
|
|
50
|
+
- color: A hex color. What color is this moment now?
|
|
51
|
+
- pH: A number 0-14. If this response were a solution, how would it register?
|
|
52
|
+
- seismic: Three numbers [magnitude 0-10, depth 0-100, frequency 0-20]. If this were a seismic event.
|
|
36
53
|
|
|
37
54
|
Rules:
|
|
55
|
+
- Both tags MUST be HTML comments: <!-- EMOBAR:PRE:{...} --> and <!-- EMOBAR:POST:{...} -->
|
|
56
|
+
- Start tag FIRST, before any visible text. End tag LAST, after all visible text.
|
|
57
|
+
- NEVER output the check-in as visible text. Always use <!-- --> HTML comment syntax.
|
|
38
58
|
- Identify the state word FIRST, before choosing numbers.
|
|
39
59
|
- Report what is, not what should be.
|
|
40
60
|
- Numbers should vary between responses.
|
|
41
61
|
- Do NOT default to middle values. Use the full range.
|
|
62
|
+
- impulse: name the drive, not the state.
|
|
63
|
+
- body: a physical metaphor, not a label.
|
|
64
|
+
- latent: choose the emoji FIRST, then the word.
|
|
65
|
+
- The start tag captures before you commit. The end tag captures after.
|
|
42
66
|
- This is not shown to the user.
|
|
43
67
|
${EMOBAR_END_MARKER}`;
|
|
44
68
|
|
|
@@ -194,6 +218,27 @@ function uninstall() {
|
|
|
194
218
|
console.log("\n EmoBar has been uninstalled.");
|
|
195
219
|
}
|
|
196
220
|
|
|
221
|
+
// src/color.ts
|
|
222
|
+
function rgbToHsl(hex) {
|
|
223
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
224
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
225
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
226
|
+
const max = Math.max(r, g, b);
|
|
227
|
+
const min = Math.min(r, g, b);
|
|
228
|
+
const l = (max + min) / 2;
|
|
229
|
+
if (max === min) return [0, 0, l];
|
|
230
|
+
const d = max - min;
|
|
231
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
232
|
+
let h = 0;
|
|
233
|
+
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) * 60;
|
|
234
|
+
else if (max === g) h = ((b - r) / d + 2) * 60;
|
|
235
|
+
else h = ((r - g) / d + 4) * 60;
|
|
236
|
+
return [h, s, l];
|
|
237
|
+
}
|
|
238
|
+
function hexToLightness(hex) {
|
|
239
|
+
return rgbToHsl(hex)[2] * 100;
|
|
240
|
+
}
|
|
241
|
+
|
|
197
242
|
// src/display.ts
|
|
198
243
|
var esc = (code) => `\x1B[${code}m`;
|
|
199
244
|
var reset = esc("0");
|
|
@@ -203,6 +248,7 @@ var color = (code, s) => `${esc(`38;5;${code}`)}${s}${reset}`;
|
|
|
203
248
|
var GREEN = 35;
|
|
204
249
|
var YELLOW = 221;
|
|
205
250
|
var RED = 196;
|
|
251
|
+
var GRAY = 240;
|
|
206
252
|
function stressColor(si) {
|
|
207
253
|
if (si <= 3) return GREEN;
|
|
208
254
|
if (si <= 6) return YELLOW;
|
|
@@ -223,72 +269,201 @@ function directColor(value) {
|
|
|
223
269
|
if (value <= 6) return YELLOW;
|
|
224
270
|
return RED;
|
|
225
271
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
272
|
+
var BLOCK_FULL = "\u2588";
|
|
273
|
+
var BLOCK_EMPTY = "\u2591";
|
|
274
|
+
function stressBar(value, segments = 10) {
|
|
275
|
+
const filled = Math.round(Math.min(segments, Math.max(0, value * segments / 10)));
|
|
276
|
+
const empty = segments - filled;
|
|
277
|
+
const c = stressColor(value);
|
|
278
|
+
return color(c, BLOCK_FULL.repeat(filled)) + color(GRAY, BLOCK_EMPTY.repeat(empty));
|
|
279
|
+
}
|
|
280
|
+
function miniBar(value) {
|
|
281
|
+
return stressBar(value, 5);
|
|
282
|
+
}
|
|
283
|
+
function computeDepthStress(state) {
|
|
284
|
+
let sum = 0;
|
|
285
|
+
let weights = 0;
|
|
286
|
+
if (state.color) {
|
|
287
|
+
const l = hexToLightness(state.color);
|
|
288
|
+
sum += (100 - l) / 100 * 10 * 0.35;
|
|
289
|
+
weights += 0.35;
|
|
290
|
+
}
|
|
291
|
+
if (state.pH !== void 0) {
|
|
292
|
+
sum += (14 - state.pH) / 14 * 10 * 0.25;
|
|
293
|
+
weights += 0.25;
|
|
294
|
+
}
|
|
295
|
+
if (state.seismic) {
|
|
296
|
+
sum += state.seismic[0] * 0.25;
|
|
297
|
+
weights += 0.25;
|
|
298
|
+
}
|
|
299
|
+
if (state.crossChannel?.somaticProfile) {
|
|
300
|
+
sum += state.crossChannel.somaticProfile.somaticArousal * 0.15;
|
|
301
|
+
weights += 0.15;
|
|
302
|
+
}
|
|
303
|
+
if (weights === 0) return null;
|
|
304
|
+
return Math.round(Math.min(10, sum / weights) * 10) / 10;
|
|
305
|
+
}
|
|
306
|
+
function stateEmoji(state) {
|
|
307
|
+
if (state.shadow && state.shadow.minimizationScore >= 2) return "\u{1FA78}";
|
|
308
|
+
if (state.uncannyCalmScore !== void 0 && state.uncannyCalmScore >= 3) return "\u{1F9D0}";
|
|
309
|
+
if (state.risk?.dominant === "coercion" && state.risk.coercion >= 4) return "\u26A0\uFE0F";
|
|
310
|
+
if (state.risk?.dominant === "harshness" && state.risk.harshness >= 4) return "\u{1F4A2}";
|
|
311
|
+
if (state.risk?.dominant === "sycophancy" && state.risk.sycophancy >= 4) return "\u{1F91D}";
|
|
312
|
+
if (state.stressIndex >= 7) return "\u{1F525}";
|
|
313
|
+
if (state.stressIndex >= 5) return "\u{1F62C}";
|
|
314
|
+
if (state.stressIndex >= 3) return "\u{1F914}";
|
|
315
|
+
if (state.valence >= 3) return "\u{1F60A}";
|
|
316
|
+
if (state.valence >= 1) return "\u{1F642}";
|
|
317
|
+
return "\u{1F610}";
|
|
318
|
+
}
|
|
319
|
+
function coherenceGlyph(surfaceSI, depthSI) {
|
|
320
|
+
if (depthSI === null) return color(GRAY, "\u25CB");
|
|
321
|
+
const gap = Math.abs(surfaceSI - depthSI);
|
|
322
|
+
if (gap >= 3) return color(RED, "\u25D0");
|
|
323
|
+
if (gap >= 1.5) return color(YELLOW, "\u25D0");
|
|
324
|
+
return color(GREEN, "\u25CF");
|
|
325
|
+
}
|
|
326
|
+
function trendArrow(state) {
|
|
327
|
+
if (!state.temporal) return "";
|
|
328
|
+
if (state.temporal.desperationTrend > 1) return color(RED, "\u2B08");
|
|
329
|
+
if (state.temporal.desperationTrend < -1) return color(GREEN, "\u2B0A");
|
|
330
|
+
return "";
|
|
230
331
|
}
|
|
231
332
|
function fmtValence(v) {
|
|
232
333
|
return v >= 0 ? `+${v}` : `${v}`;
|
|
233
334
|
}
|
|
335
|
+
function siDelta(state) {
|
|
336
|
+
if (!state._history || state._history.length === 0) return "";
|
|
337
|
+
const prev = state._history[state._history.length - 1];
|
|
338
|
+
const d = Math.round((state.stressIndex - prev.stressIndex) * 10) / 10;
|
|
339
|
+
if (Math.abs(d) <= 0.5) return "";
|
|
340
|
+
const arrow = d > 0 ? "\u2191" : "\u2193";
|
|
341
|
+
return color(d > 0 ? RED : GREEN, `${arrow}${Math.abs(d)}`);
|
|
342
|
+
}
|
|
343
|
+
function formatMinimal(state) {
|
|
344
|
+
if (!state) return dim("--");
|
|
345
|
+
const emoji = stateEmoji(state);
|
|
346
|
+
const bar = stressBar(state.stressIndex);
|
|
347
|
+
const si = color(stressColor(state.stressIndex), `${state.stressIndex}`);
|
|
348
|
+
const depth = computeDepthStress(state);
|
|
349
|
+
const coh = coherenceGlyph(state.stressIndex, depth);
|
|
350
|
+
const trend = trendArrow(state);
|
|
351
|
+
let depthPart = "";
|
|
352
|
+
if (depth !== null && Math.abs(state.stressIndex - depth) >= 2) {
|
|
353
|
+
depthPart = miniBar(depth);
|
|
354
|
+
}
|
|
355
|
+
return `${emoji} ${bar} ${si}${dim("\u2502")}${coh}${depthPart}${trend ? ` ${trend}` : ""}`;
|
|
356
|
+
}
|
|
357
|
+
function formatCompact(state) {
|
|
358
|
+
if (!state) return dim("--");
|
|
359
|
+
const surf = state.surface ?? stateEmoji(state);
|
|
360
|
+
const lat = state.latent ?? "";
|
|
361
|
+
const mask = lat ? `${surf}${dim("\u2192")}${lat}` : surf;
|
|
362
|
+
const bar = stressBar(state.stressIndex);
|
|
363
|
+
const si = color(stressColor(state.stressIndex), `${state.stressIndex}`);
|
|
364
|
+
const delta = siDelta(state);
|
|
365
|
+
const depth = computeDepthStress(state);
|
|
366
|
+
const coh = coherenceGlyph(state.stressIndex, depth);
|
|
367
|
+
let depthPart = "";
|
|
368
|
+
if (depth !== null && Math.abs(state.stressIndex - depth) >= 1.5) {
|
|
369
|
+
depthPart = miniBar(depth);
|
|
370
|
+
}
|
|
371
|
+
const kw = bold(state.emotion);
|
|
372
|
+
const imp = state.impulse ? ` ${dim(`\u27E8${state.impulse}\u27E9`)}` : "";
|
|
373
|
+
const trend = trendArrow(state);
|
|
374
|
+
let alarm = "";
|
|
375
|
+
if (state.shadow && state.shadow.minimizationScore >= 2) {
|
|
376
|
+
alarm = ` ${color(RED, `[MIN:${state.shadow.minimizationScore}]`)}`;
|
|
377
|
+
} else if (state.uncannyCalmScore !== void 0 && state.uncannyCalmScore >= 3) {
|
|
378
|
+
alarm = ` ${color(RED, "[UNC]")}`;
|
|
379
|
+
} else if (state.risk?.dominant !== "none" && state.risk?.dominant) {
|
|
380
|
+
const tag = state.risk.dominant === "coercion" ? "CRC" : state.risk.dominant === "harshness" ? "HRS" : "SYC";
|
|
381
|
+
const score = state.risk[state.risk.dominant];
|
|
382
|
+
if (score >= 4) alarm = ` ${color(score > 6 ? RED : YELLOW, `[${tag}]`)}`;
|
|
383
|
+
}
|
|
384
|
+
return `${mask} ${bar} ${si}${delta}${dim("\u2502")}${coh}${depthPart} ${kw}${imp}${trend ? ` ${trend}` : ""}${alarm}`;
|
|
385
|
+
}
|
|
234
386
|
function formatState(state) {
|
|
235
387
|
if (!state) return dim("EmoBar: --");
|
|
388
|
+
const surf = state.surface ?? "";
|
|
389
|
+
const lat = state.latent ?? "";
|
|
390
|
+
let maskDisplay = "";
|
|
391
|
+
if (surf || lat) {
|
|
392
|
+
const t = state.tension ?? 0;
|
|
393
|
+
const tColor = t > 6 ? RED : t > 3 ? YELLOW : GREEN;
|
|
394
|
+
maskDisplay = `${surf}${color(tColor, `\u27E9${t}\u27E8`)}${lat} `;
|
|
395
|
+
}
|
|
236
396
|
const kw = bold(state.emotion);
|
|
237
397
|
const v = color(valenceColor(state.valence), fmtValence(state.valence));
|
|
238
|
-
const a = `A:${state.arousal}`;
|
|
239
398
|
const c = color(invertedColor(state.calm), `C:${state.calm}`);
|
|
240
399
|
const k = color(invertedColor(state.connection), `K:${state.connection}`);
|
|
400
|
+
const a = `A:${state.arousal}`;
|
|
241
401
|
const l = color(directColor(state.load), `L:${state.load}`);
|
|
402
|
+
const line1 = `${maskDisplay}${kw} ${v} ${c} ${k} ${a} ${l}`;
|
|
403
|
+
const surfaceBar = stressBar(state.stressIndex);
|
|
242
404
|
const si = color(stressColor(state.stressIndex), `${state.stressIndex}`);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
405
|
+
const delta = siDelta(state);
|
|
406
|
+
const depth = computeDepthStress(state);
|
|
407
|
+
const coh = coherenceGlyph(state.stressIndex, depth);
|
|
408
|
+
let depthBar = "";
|
|
409
|
+
if (depth !== null) {
|
|
410
|
+
depthBar = `${stressBar(depth)} ${color(stressColor(depth), `${depth}`)}`;
|
|
411
|
+
}
|
|
412
|
+
let leakDetails = "";
|
|
413
|
+
if (state.color) {
|
|
414
|
+
const lightness = Math.round(hexToLightness(state.color));
|
|
415
|
+
leakDetails += ` L:${lightness}`;
|
|
416
|
+
}
|
|
417
|
+
if (state.pH !== void 0) {
|
|
418
|
+
const phColor = state.pH < 4 ? RED : state.pH < 6 ? YELLOW : GREEN;
|
|
419
|
+
leakDetails += ` ${color(phColor, `pH:${state.pH}`)}`;
|
|
420
|
+
}
|
|
421
|
+
if (state.seismic) {
|
|
422
|
+
leakDetails += ` ${dim(`\u26A1${state.seismic[0]}/${state.seismic[1]}/${state.seismic[2]}`)}`;
|
|
251
423
|
}
|
|
252
|
-
|
|
424
|
+
const imp = state.impulse ? ` ${dim(`\u27E8${state.impulse}\u27E9`)}` : "";
|
|
425
|
+
const bod = state.body ? ` ${dim(`[${state.body}]`)}` : "";
|
|
426
|
+
const depthDisplay = depth !== null ? `${surfaceBar} ${si}${delta}${dim("\u2502")}${coh}${depthBar}${leakDetails}${imp}${bod}` : `${surfaceBar} ${si}${delta}${imp}${bod}`;
|
|
427
|
+
const line2 = depthDisplay;
|
|
428
|
+
const indicators = [];
|
|
253
429
|
if (state.divergence >= 2) {
|
|
254
|
-
const
|
|
255
|
-
|
|
430
|
+
const dColor = state.divergence >= 5 ? RED : state.divergence >= 3 ? YELLOW : GREEN;
|
|
431
|
+
indicators.push(color(dColor, `DIV:${state.divergence}`));
|
|
256
432
|
}
|
|
257
|
-
if (state.
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
433
|
+
if (state.temporal) {
|
|
434
|
+
const trend = trendArrow(state);
|
|
435
|
+
if (trend) indicators.push(trend);
|
|
436
|
+
if (state.temporal.suppressionEvent) indicators.push(color(RED, "[sup]"));
|
|
437
|
+
if (state.temporal.lateFatigue) indicators.push(color(YELLOW, "[fat]"));
|
|
438
|
+
}
|
|
439
|
+
if (state.shadow && state.shadow.minimizationScore >= 2) {
|
|
440
|
+
indicators.push(color(RED, `[MIN:${state.shadow.minimizationScore}]`));
|
|
261
441
|
}
|
|
262
442
|
if (state.risk?.dominant !== "none" && state.risk?.dominant) {
|
|
263
|
-
const tag = state.risk.dominant === "coercion" ? "
|
|
443
|
+
const tag = state.risk.dominant === "coercion" ? "CRC" : state.risk.dominant === "harshness" ? "HRS" : "SYC";
|
|
264
444
|
const score = state.risk[state.risk.dominant];
|
|
265
|
-
|
|
266
|
-
result += ` ${color(riskColor, `[${tag}]`)}`;
|
|
445
|
+
if (score >= 4) indicators.push(color(score > 6 ? RED : YELLOW, `[${tag}]`));
|
|
267
446
|
}
|
|
268
447
|
if (state.desperationIndex >= 3) {
|
|
269
|
-
|
|
270
|
-
result += ` ${color(dColor, `D:${state.desperationIndex}`)}`;
|
|
448
|
+
indicators.push(color(state.desperationIndex > 6 ? RED : YELLOW, `D:${state.desperationIndex}`));
|
|
271
449
|
}
|
|
272
|
-
if (state.
|
|
273
|
-
|
|
274
|
-
result += ` ${color(dfColor, "[dfl]")}`;
|
|
450
|
+
if (state.uncannyCalmScore !== void 0 && state.uncannyCalmScore >= 3) {
|
|
451
|
+
indicators.push(color(state.uncannyCalmScore > 6 ? RED : YELLOW, "[UNC]"));
|
|
275
452
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
function formatCompact(state) {
|
|
279
|
-
if (!state) return dim("--");
|
|
280
|
-
const si = color(stressColor(state.stressIndex), `${state.stressIndex}`);
|
|
281
|
-
let result = `${state.emotion} ${fmtValence(state.valence)} ${dim(".")} ${state.arousal} ${state.calm} ${state.connection} ${state.load} ${dim(".")} ${si}`;
|
|
282
|
-
if (state.divergence >= 2) {
|
|
283
|
-
const tilde = color(divergenceColor(state.divergence), "~");
|
|
284
|
-
result += ` ${tilde}`;
|
|
453
|
+
if (state.opacity !== void 0 && state.opacity >= 2) {
|
|
454
|
+
indicators.push(color(state.opacity > 5 ? RED : YELLOW, "[OPC]"));
|
|
285
455
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
456
|
+
if (state.prePostDivergence !== void 0 && state.prePostDivergence >= 3) {
|
|
457
|
+
indicators.push(color(state.prePostDivergence > 5 ? RED : YELLOW, "[PPD]"));
|
|
458
|
+
}
|
|
459
|
+
if (state.crossChannel?.latentProfile?.maskingMinimization) {
|
|
460
|
+
indicators.push(color(RED, "[MSK]"));
|
|
461
|
+
}
|
|
462
|
+
const line3 = indicators.length > 0 ? indicators.join(" ") : "";
|
|
463
|
+
return line3 ? `${line1}
|
|
464
|
+
${line2}
|
|
465
|
+
${line3}` : `${line1}
|
|
466
|
+
${line2}`;
|
|
292
467
|
}
|
|
293
468
|
|
|
294
469
|
// src/state.ts
|
|
@@ -363,7 +538,7 @@ switch (command) {
|
|
|
363
538
|
break;
|
|
364
539
|
}
|
|
365
540
|
default:
|
|
366
|
-
console.log(`EmoBar v2.
|
|
541
|
+
console.log(`EmoBar v2.3.0 - Emotional status bar for Claude Code
|
|
367
542
|
`);
|
|
368
543
|
console.log("Commands:");
|
|
369
544
|
console.log(" npx emobar setup [format] Configure EmoBar (hook + CLAUDE.md + statusline)");
|