@eventmodelers/cli 2.0.34 → 2.0.35
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventmodelers/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.35",
|
|
4
4
|
"description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,23 +34,45 @@
|
|
|
34
34
|
.em-badge button:hover { color: #bfdbfe; }
|
|
35
35
|
.em-outline-toggle { display: inline-flex; gap: 5px; align-items: center; font: 12px system-ui; color: #9ca3af; cursor: pointer; }
|
|
36
36
|
.em-part-label { font: 600 11px system-ui; color: #6b7280; margin-bottom: 6px; text-transform: uppercase; letter-spacing: .06em; }
|
|
37
|
-
.em-panel {
|
|
38
|
-
|
|
37
|
+
.em-panel { --p-bg: #0e1522; --p-surface: #161f30; --p-raised: #1d283c; --p-line: #232f45; --p-text: #e7eaf0; --p-muted: #8a95a9; --p-accent: #f97316;
|
|
38
|
+
background: var(--p-bg); color: var(--p-text); padding: 0 16px 24px; overflow-y: auto; max-height: 100vh; position: sticky; top: 0; font: 13px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
|
39
|
+
.em-panel::-webkit-scrollbar { width: 8px; } .em-panel::-webkit-scrollbar-thumb { background: var(--p-line); border-radius: 4px; }
|
|
40
|
+
.em-panel h2, .em-auto h2 { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: #8a95a9; margin: 0; }
|
|
41
|
+
.em-sect { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 18px 0 12px; min-height: 28px; }
|
|
42
|
+
.em-sect .em-sub { color: var(--p-muted); font-size: 12px; margin-top: 2px; text-transform: none; letter-spacing: 0; font-weight: 400; }
|
|
43
|
+
.em-empty { color: var(--p-muted); background: var(--p-surface); border: 1px dashed var(--p-line); border-radius: 10px; padding: 18px; text-align: center; }
|
|
39
44
|
/* The panel's tabs are real tabs: a strip with a rule under it, the open tab joined to the content below (same colour, no rule under it) with an orange top edge. */
|
|
40
45
|
.em-panel.em-collapsed { padding: 8px 4px; overflow: hidden; }
|
|
41
|
-
.em-toggle { background: #
|
|
42
|
-
.em-toggle:hover { background: #
|
|
46
|
+
.em-toggle { background: #161f30; color: #8a95a9; border: 1px solid #232f45; border-radius: 7px; width: 28px; height: 28px; cursor: pointer; font: 14px system-ui; flex: none; }
|
|
47
|
+
.em-toggle:hover { background: #1d283c; color: #e7eaf0; }
|
|
43
48
|
.em-tabs .em-toggle { width: 28px; height: 28px; padding: 0; margin-bottom: 0; border-radius: 6px; color: #e5e7eb; }
|
|
44
49
|
.em-collapsed .em-toggle { display: block; margin: 0 auto 8px; }
|
|
45
50
|
.em-collapsed .em-vlabel { writing-mode: vertical-rl; color: #9ca3af; font: 12px system-ui; letter-spacing: .06em; text-transform: uppercase; text-align: center; cursor: pointer; width: 100%; }
|
|
46
|
-
.em-tabs { display: flex; gap:
|
|
47
|
-
.em-tabs button { background:
|
|
48
|
-
.em-tabs button:hover { color:
|
|
49
|
-
.em-tabs button.em-active {
|
|
50
|
-
.em-btn { background: #
|
|
51
|
-
.em-btn
|
|
52
|
-
.em-
|
|
53
|
-
.em-
|
|
51
|
+
.em-tabs { display: flex; align-items: center; gap: 4px; border-bottom: 1px solid var(--p-line); margin: 0 -16px; padding: 10px 16px 0; position: sticky; top: 0; background: var(--p-bg); z-index: 5; }
|
|
52
|
+
.em-tabs button:not(.em-toggle) { background: none; color: var(--p-muted); border: 0; border-bottom: 2px solid transparent; border-radius: 0; padding: 8px 12px 10px; margin-bottom: -1px; cursor: pointer; font: 500 13px -apple-system, BlinkMacSystemFont, system-ui, sans-serif; text-transform: capitalize; transition: color .15s, border-color .15s; }
|
|
53
|
+
.em-tabs button:not(.em-toggle):hover { color: var(--p-text); }
|
|
54
|
+
.em-tabs button.em-active { color: #fff; border-bottom-color: var(--p-accent); }
|
|
55
|
+
.em-btn { background: var(--p-raised, #1d283c); color: #e7eaf0; border: 1px solid #2c3a55; border-radius: 7px; padding: 5px 11px; cursor: pointer; font: 500 12px -apple-system, BlinkMacSystemFont, system-ui, sans-serif; transition: background .15s, border-color .15s; }
|
|
56
|
+
.em-btn:hover { background: #26344e; border-color: #3a4b6b; }
|
|
57
|
+
.em-btn.em-on { background: #f97316; border-color: #f97316; color: #1a0f05; }
|
|
58
|
+
.em-btn.em-ghost { background: transparent; border-color: transparent; color: #8a95a9; padding: 5px 8px; } .em-btn.em-ghost:hover { background: #1d283c; color: #e7eaf0; }
|
|
59
|
+
.em-btn.em-icon { width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; }
|
|
60
|
+
.em-ev { background: var(--p-surface, #161f30); border: 1px solid var(--p-line, #232f45); border-left: 3px solid #f97316; border-radius: 10px; padding: 10px 12px; margin: 8px 0; font-size: 12px; color: #e7eaf0; }
|
|
61
|
+
.em-ev b { color: #fdba74; font-weight: 600; }
|
|
62
|
+
.em-ev-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; cursor: pointer; }
|
|
63
|
+
.em-ev-pos { font: 600 11px ui-monospace, SFMono-Regular, Menlo, monospace; color: #8a95a9; background: #0e1522; border-radius: 5px; padding: 1px 6px; }
|
|
64
|
+
.em-ev-type { font-weight: 600; font-size: 13px; color: #fdba74; }
|
|
65
|
+
.em-ev-from { color: #8a95a9; font-size: 12px; }
|
|
66
|
+
.em-ev-time { margin-left: auto; color: #6b7690; font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
67
|
+
.em-kv { display: grid; grid-template-columns: minmax(70px, max-content) 1fr; gap: 3px 14px; margin: 8px 0 2px; padding-top: 8px; border-top: 1px solid var(--p-line, #232f45); }
|
|
68
|
+
.em-kv dt { color: #8a95a9; margin: 0; } .em-kv dd { margin: 0; word-break: break-word; color: #e7eaf0; } .em-kv dd.em-none { color: #4f5b73; }
|
|
69
|
+
.em-kv dd a { color: #e7eaf0; text-decoration: none; border-bottom: 1px dotted #4f5b73; } .em-kv dd a:hover { color: #fdba74; border-color: #fdba74; }
|
|
70
|
+
.em-ev-foot { margin-top: 10px; }
|
|
71
|
+
.em-pill { display: inline-flex; align-items: center; gap: 4px; font: 600 11px -apple-system, system-ui, sans-serif; padding: 1px 8px; border-radius: 999px; background: #1d283c; color: #8a95a9; }
|
|
72
|
+
.em-pill.em-pass { background: rgba(34,197,94,.14); color: #4ade80; } .em-pill.em-fail { background: rgba(239,68,68,.15); color: #f87171; }
|
|
73
|
+
.em-spec-title { font-weight: 600; font-size: 13px; color: #e7eaf0; line-height: 1.35; }
|
|
74
|
+
.em-spec-meta { color: #8a95a9; font-size: 12px; margin-top: 2px; }
|
|
75
|
+
.em-chev { display: inline-block; width: 14px; color: #6b7690; font-size: 10px; }
|
|
54
76
|
.em-ev pre { margin: 4px 0 0; padding: 0; background: transparent; white-space: pre-wrap; word-break: break-word; color: #d1d5db; font-size: 11px; } /* background set explicitly: a screen framework (Bulma) styles bare <pre> light, which left this text unreadable */
|
|
55
77
|
.em-slider { display: flex; align-items: center; gap: 8px; margin: 8px 0; font-size: 12px; color: #9ca3af; } .em-slider .em-slider-text { min-width: 120px; text-align: right; }
|
|
56
78
|
/* `.em-panel input` (below) makes every panel input a full-width text box: the slider and the checkbox undo that, with a selector that beats it. */
|
|
@@ -59,8 +81,10 @@
|
|
|
59
81
|
.em-hl { outline: 3px solid #f97316 !important; outline-offset: 2px; box-shadow: 0 0 0 6px rgba(249, 115, 22, 0.25) !important; }
|
|
60
82
|
.em-ev.em-cmd { border-left-color: #3b82f6; } .em-ev.em-cmd b { color: #93c5fd; }
|
|
61
83
|
.em-ev.em-fail { border-left-color: #ef4444; } .em-ev.em-pass { border-left-color: #22c55e; }
|
|
84
|
+
.em-ev.em-pass:hover, .em-ev.em-fail:hover { border-color: #2c3a55; }
|
|
62
85
|
.em-ev.em-out { border-left-color: #14b8a6; }
|
|
63
|
-
.em-panel input, .em-panel textarea { width: 100%; box-sizing: border-box; margin: 2px 0 6px; padding:
|
|
86
|
+
.em-panel input, .em-panel textarea { width: 100%; box-sizing: border-box; margin: 2px 0 6px; padding: 6px 9px; border-radius: 7px; border: 1px solid #2c3a55; background: #0e1522; color: #fff; font: inherit; }
|
|
87
|
+
.em-panel input:focus, .em-panel textarea:focus { outline: none; border-color: #f97316; }
|
|
64
88
|
.em-auto { max-width: 760px; }
|
|
65
89
|
.em-auto .em-ev { font-size: 13px; }
|
|
66
90
|
.em-auto h2 { color: #6b7280; }
|
|
@@ -309,7 +333,8 @@ function EventBrowser({ events }) {
|
|
|
309
333
|
<button className="em-btn" onClick={() => setFollow(null)}>×</button>
|
|
310
334
|
</div>
|
|
311
335
|
)}
|
|
312
|
-
<
|
|
336
|
+
<div className="em-sect" style={{ margin: '4px 0 0' }}><span className="em-pill">{shown.length === events.length ? `${events.length} event${events.length === 1 ? '' : 's'}` : `${shown.length} of ${events.length} events`}{withIx ? ` · ${recorded.length} interactions` : ''}</span></div>
|
|
337
|
+
{rows.length === 0 && <div className="em-empty">No events yet. Use the prototype and they appear here.</div>}
|
|
313
338
|
{[...rows].reverse().map((row) => {
|
|
314
339
|
if (row.kind === 'interaction') {
|
|
315
340
|
return (
|
|
@@ -326,35 +351,41 @@ function EventBrowser({ events }) {
|
|
|
326
351
|
const future = cursor != null && e.position > cursor;
|
|
327
352
|
return (
|
|
328
353
|
<div key={e.id} className="em-ev" style={future ? { opacity: 0.4 } : null}>
|
|
329
|
-
<div
|
|
330
|
-
<
|
|
331
|
-
|
|
332
|
-
{e.metadata.
|
|
333
|
-
{e.metadata.
|
|
334
|
-
<span
|
|
335
|
-
|
|
336
|
-
<div style={{ marginTop: 4 }}>
|
|
337
|
-
{Object.entries(e.data || {}).map(([k, v]) => (
|
|
338
|
-
<div key={k}>
|
|
339
|
-
<span style={{ color: '#9ca3af' }}>{k}: </span>
|
|
340
|
-
{v != null && typeof v === 'object' ? (
|
|
341
|
-
<code>{JSON.stringify(v)}</code>
|
|
342
|
-
) : (
|
|
343
|
-
<a href="#" title="Show every event carrying this value" style={{ color: '#e5e7eb' }}
|
|
344
|
-
onClick={(ev) => { ev.preventDefault(); setFollow({ field: k, value: v }); }}>
|
|
345
|
-
{String(v)}
|
|
346
|
-
</a>
|
|
347
|
-
)}
|
|
348
|
-
</div>
|
|
349
|
-
))}
|
|
354
|
+
<div className="em-ev-head" onClick={() => setOpen(isOpen ? null : e.id)}>
|
|
355
|
+
<span className="em-ev-pos">#{e.position}</span>
|
|
356
|
+
<span className="em-ev-type">{e.type}</span>
|
|
357
|
+
{e.metadata.command && <span className="em-ev-from">← {e.metadata.command}</span>}
|
|
358
|
+
{e.metadata.automation && <span className="em-pill">automation</span>}
|
|
359
|
+
{e.metadata.simulated && <span className="em-pill">simulated</span>}
|
|
360
|
+
<span className="em-ev-time">{new Date(e.metadata.at).toLocaleTimeString()}</span>
|
|
350
361
|
</div>
|
|
362
|
+
{Object.keys(e.data || {}).length > 0 && (
|
|
363
|
+
<dl className="em-kv">
|
|
364
|
+
{Object.entries(e.data || {}).flatMap(([k, v]) => {
|
|
365
|
+
const none = v === '' || v == null || v === undefined || v === 'undefined';
|
|
366
|
+
return [
|
|
367
|
+
<dt key={k + 't'}>{k}</dt>,
|
|
368
|
+
<dd key={k + 'd'} className={none ? 'em-none' : ''}>
|
|
369
|
+
{none ? '—' : v != null && typeof v === 'object' ? (
|
|
370
|
+
<code>{JSON.stringify(v)}</code>
|
|
371
|
+
) : (
|
|
372
|
+
<a href="#" title="Show every event carrying this value"
|
|
373
|
+
onClick={(ev) => { ev.preventDefault(); setFollow({ field: k, value: v }); }}>
|
|
374
|
+
{String(v)}
|
|
375
|
+
</a>
|
|
376
|
+
)}
|
|
377
|
+
</dd>,
|
|
378
|
+
];
|
|
379
|
+
})}
|
|
380
|
+
</dl>
|
|
381
|
+
)}
|
|
351
382
|
{isOpen && (
|
|
352
383
|
<pre>{JSON.stringify(
|
|
353
384
|
Object.assign({ id: e.id, at: e.metadata.at }, e.metadata.command && { command: e.metadata.command },
|
|
354
385
|
e.metadata.actor && { actor: e.metadata.actor }, e.metadata.automation && { automation: e.metadata.automation },
|
|
355
386
|
e.metadata.simulated && { simulated: true }), null, 1)}</pre>
|
|
356
387
|
)}
|
|
357
|
-
<button className="em-btn"
|
|
388
|
+
<div className="em-ev-foot"><button className="em-btn" onClick={() => rt.viewAsOf(e.position)}>View as of here</button></div>
|
|
358
389
|
</div>
|
|
359
390
|
);
|
|
360
391
|
})}
|
|
@@ -467,8 +498,7 @@ function Panel({ slices, open: panelOpen, onToggle }) {
|
|
|
467
498
|
|
|
468
499
|
{tab === 'events' && (
|
|
469
500
|
<>
|
|
470
|
-
<h2>Event log</h2>
|
|
471
|
-
<button className="em-btn" onClick={() => rt.reset()}>Reset</button>
|
|
501
|
+
<div className="em-sect"><h2>Event log</h2><button className="em-btn" onClick={() => rt.reset()}>Reset</button></div>
|
|
472
502
|
<EventBrowser events={events} />
|
|
473
503
|
</>
|
|
474
504
|
)}
|
|
@@ -483,27 +513,31 @@ function Panel({ slices, open: panelOpen, onToggle }) {
|
|
|
483
513
|
|
|
484
514
|
{tab === 'specs' && (
|
|
485
515
|
<>
|
|
486
|
-
<
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
{allSpecs ? 'All screens' : 'This screen'}
|
|
490
|
-
|
|
491
|
-
</
|
|
492
|
-
|
|
516
|
+
<div className="em-sect">
|
|
517
|
+
<div>
|
|
518
|
+
<h2>Scenarios</h2>
|
|
519
|
+
{slices && <div className="em-sub">{allSpecs ? 'All screens' : 'This screen'} · {shownSpecs.length} of {specResults.length}</div>}
|
|
520
|
+
</div>
|
|
521
|
+
{slices && <button className="em-btn" onClick={() => setAllSpecs(!allSpecs)}>{allSpecs ? 'This screen only' : 'Show all'}</button>}
|
|
522
|
+
</div>
|
|
523
|
+
{shownSpecs.length === 0 && <div className="em-empty">No scenarios for this screen.</div>}
|
|
493
524
|
{shownSpecs.map(([r, i]) => (
|
|
494
|
-
<div key={i} className={'em-ev ' + (r.pass ? 'em-pass' : 'em-fail')} style={{ display: 'flex', alignItems: '
|
|
525
|
+
<div key={i} className={'em-ev ' + (r.pass ? 'em-pass' : 'em-fail')} style={{ display: 'flex', alignItems: 'flex-start', gap: 6 }}>
|
|
495
526
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
496
|
-
<div style={{ cursor: 'pointer' }} onClick={() => setOpen({ ...open, [i]: !open[i] })} title="Show or hide the scenario as text">
|
|
497
|
-
<span style={{
|
|
498
|
-
<
|
|
527
|
+
<div style={{ cursor: 'pointer', display: 'flex', gap: 6 }} onClick={() => setOpen({ ...open, [i]: !open[i] })} title="Show or hide the scenario as text">
|
|
528
|
+
<span className="em-chev" style={{ marginTop: 3 }}>{open[i] ? '▼' : '▶'}</span>
|
|
529
|
+
<div style={{ minWidth: 0 }}>
|
|
530
|
+
<div className="em-spec-title">{r.spec.title}</div>
|
|
531
|
+
<div className="em-spec-meta"><span className={'em-pill ' + (r.pass ? 'em-pass' : 'em-fail')}>{r.pass ? '✓ Passing' : '✗ Failing'}</span> <span style={{ marginLeft: 4 }}>{r.spec.slice}</span></div>
|
|
532
|
+
</div>
|
|
499
533
|
</div>
|
|
500
534
|
{open[i] && <SpecView spec={r.spec} />}
|
|
501
535
|
{!r.pass && <pre>{r.detail}</pre>}
|
|
502
536
|
</div>
|
|
503
|
-
<button className={'em-btn' + (armed === i ? ' em-on' : '')} style={{ flex: 'none' }} onClick={() => playGiven(r.spec, i)}
|
|
537
|
+
<button className={'em-btn em-icon' + (armed === i ? ' em-on' : '')} style={{ flex: 'none' }} onClick={() => playGiven(r.spec, i)}
|
|
504
538
|
title="Play the given events: the screens show this scenario's starting state">▶</button>
|
|
505
539
|
{r.spec.sliceId && (
|
|
506
|
-
<button className="em-btn" style={{ flex: 'none' }} onClick={() => EMRuntime.zoomToSlice(r.spec.sliceId)}
|
|
540
|
+
<button className="em-btn em-icon" style={{ flex: 'none' }} onClick={() => EMRuntime.zoomToSlice(r.spec.sliceId)}
|
|
507
541
|
title="Show this scenario's slice on the board"><span aria-hidden>◎</span></button>
|
|
508
542
|
)}
|
|
509
543
|
</div>
|