trantor 0.17.16 → 0.17.17
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/ui.html +47 -7
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + context-handoff for independent AI coding agents (Claude, Codex, Gemini, …)",
|
|
9
|
-
"version": "0.17.
|
|
9
|
+
"version": "0.17.17"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "trantor",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "The hub-world for AI agent crews. Say \"fire up the crew\" and Claude becomes the architect: a plan-aware Advisor routes the work (solo / cheap inline calls / live crew of Codex, Gemini, Kimi & DeepSeek in their own terminal windows), a Kanban/flow command center with a testing gate tracks it, and an economics brain (Scrooge) keeps the receipts. Includes the relay MCP, a SessionStart auto-discovery hook, and a PreCompact context-handoff so a fresh session can take over a full window instead of compacting.",
|
|
16
|
-
"version": "0.17.
|
|
16
|
+
"version": "0.17.17",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Sasha Bogojevic"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.17",
|
|
4
4
|
"description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + crew orchestration for independent AI coding agents (Claude, Codex, Gemini, Kimi, DeepSeek)",
|
|
5
5
|
"mcpServers": {
|
|
6
6
|
"relay": {
|
package/package.json
CHANGED
package/ui.html
CHANGED
|
@@ -198,6 +198,12 @@ main:not(.learn-open) .learn-body{display:none}
|
|
|
198
198
|
.pfcount{fill:var(--mut);font-size:10.5px;font-family:ui-sans-serif,system-ui}
|
|
199
199
|
.pfsub{fill:#9fb0c4;font-size:10.5px;font-family:ui-sans-serif,system-ui}
|
|
200
200
|
.pflowwrap .fhint{position:absolute;right:12px;bottom:5px}
|
|
201
|
+
/* mini-map scrubber: whole project compressed to a bar; click/drag to jump, .gmapview shows position */
|
|
202
|
+
.gmap{position:relative;height:18px;margin:10px 16px 4px;border-radius:6px;background:#0c1320;border:1px solid var(--line);overflow:hidden;cursor:pointer;touch-action:none}
|
|
203
|
+
.gmapseg{position:absolute;top:2px;bottom:2px;border-radius:2px;opacity:.5}
|
|
204
|
+
.gmapseg.done{background:var(--grn2)}.gmapseg.active{background:#f0b24b}.gmapseg.failed,.gmapseg.blocked{background:var(--red)}.gmapseg.planned{background:#46566f}
|
|
205
|
+
.gmap:hover .gmapseg{opacity:.7}
|
|
206
|
+
.gmapview{position:absolute;top:-1px;bottom:-1px;min-width:8px;background:rgba(255,255,255,.14);border:1.5px solid rgba(255,255,255,.6);border-radius:5px;pointer-events:none;transition:left .04s linear}
|
|
201
207
|
/* card detail modal — the full story of one card: status journey + the agent's own bus reports */
|
|
202
208
|
.cmodal{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;background:rgba(4,7,12,.62)}
|
|
203
209
|
.cmpanel{background:var(--panel);border:1px solid var(--line);border-radius:14px;width:min(760px,93vw);max-height:84vh;display:flex;flex-direction:column;box-shadow:0 20px 64px rgba(0,0,0,.55)}
|
|
@@ -400,7 +406,7 @@ function flowHTML(pt, proj){
|
|
|
400
406
|
};
|
|
401
407
|
const gedge = (x1,y1,x2,y2,done) => { const mx=(x1+x2)/2;
|
|
402
408
|
return `<path class="gedge${done?' done':''}" d="M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}"/>`; };
|
|
403
|
-
let x = 26, svg = '', bands = '', prevIntRight = null, pi = 0;
|
|
409
|
+
let x = 26, svg = '', bands = '', segs = [], prevIntRight = null, pi = 0;
|
|
404
410
|
for (const L of layouts){
|
|
405
411
|
const { ph, cards, byId, parentsOf, hasChild, maxDepth, cols } = L;
|
|
406
412
|
const planX = x, planY = Y0 - NH/2;
|
|
@@ -411,6 +417,7 @@ function flowHTML(pt, proj){
|
|
|
411
417
|
const bx = planX - 16, bw = (intX + NW + 16) - bx;
|
|
412
418
|
bands += `<rect class="pfband ${pi%2?'alt':''}" x="${bx}" y="2" width="${bw}" height="${totalH-4}" rx="10"/>`
|
|
413
419
|
+ `<rect class="pfbandtop ${ph.status}" x="${bx}" y="2" width="${bw}" height="${MT-12}" rx="10"/>`;
|
|
420
|
+
segs.push({ label: ph.label, status: ph.status, total: ph.total, x0: bx, cx: planX, x1: bx + bw });
|
|
414
421
|
// header: phase label + the GOAL (explicit) or derived THEME — "what this phase is about"
|
|
415
422
|
const desc = (ph.goal || ph.theme || '').slice(0, Math.max(28, Math.floor(bw/7)));
|
|
416
423
|
svg += `<text class="pflabel ${ph.status}" x="${planX}" y="${MT-38}">${esc(ph.label)}</text>`
|
|
@@ -439,17 +446,50 @@ function flowHTML(pt, proj){
|
|
|
439
446
|
const totalW = x + 10;
|
|
440
447
|
const defs = `<defs><marker id="garr" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L8,4 L0,8 z" fill="#46566f"/></marker></defs>`;
|
|
441
448
|
const notice = data.sparse ? `<div class="finfo">⚠ inferred phases — few cards carry explicit tags (P1/P5…), so they're grouped by time + title. Prefix cards (e.g. "P6 …") for sharper phases.</div>` : '';
|
|
442
|
-
|
|
449
|
+
// mini-map / scrubber: each phase a status-colored segment (% of total width). Click or drag to
|
|
450
|
+
// jump anywhere; the .gmapview overlay tracks where you are. data-total lets the JS map px↔ratio.
|
|
451
|
+
const pct = v => (100 * v / totalW).toFixed(3);
|
|
452
|
+
const mapsegs = segs.map(s =>
|
|
453
|
+
`<div class="gmapseg ${s.status}" style="left:${pct(s.x0)}%;width:${pct(s.x1 - s.x0)}%" data-cx="${s.cx}" title="${esc(s.label)} · ${s.status} · ${s.total} card${s.total>1?'s':''}"></div>`).join('');
|
|
454
|
+
const map = `<div class="gmap" data-proj="${esc(proj)}" data-total="${totalW}">${mapsegs}<div class="gmapview"></div></div>`;
|
|
455
|
+
return `<div class="pflowwrap gflow" data-proj="${esc(proj)}">${notice}${map}`
|
|
443
456
|
+ `<div class="gscroll" data-proj="${esc(proj)}"><svg width="${totalW}" height="${totalH}">${defs}${bands}${svg}</svg></div>`
|
|
444
|
-
+ `<div class="fhint">orchestrator → crew → integrate · phase by phase ·
|
|
457
|
+
+ `<div class="fhint">orchestrator → crew → integrate · phase by phase · click/drag the bar above to scrub →</div></div>`;
|
|
445
458
|
}
|
|
446
459
|
const GSCROLL = {};
|
|
447
460
|
function wireTimeline(el){
|
|
448
|
-
// FLOW v2 graph: click a card node
|
|
449
|
-
el.querySelectorAll('.
|
|
450
|
-
const proj =
|
|
461
|
+
// FLOW v2 graph: scroll-position scrubber (mini-map) + click a card node for its /card detail.
|
|
462
|
+
el.querySelectorAll('.gflow').forEach(wrap => {
|
|
463
|
+
const proj = wrap.dataset.proj;
|
|
464
|
+
const s = wrap.querySelector('.gscroll'), map = wrap.querySelector('.gmap'), view = map && map.querySelector('.gmapview');
|
|
465
|
+
if (!s) return;
|
|
451
466
|
if (GSCROLL[proj] != null) s.scrollLeft = GSCROLL[proj];
|
|
452
|
-
|
|
467
|
+
const syncView = () => { // reflect where we are onto the mini-map
|
|
468
|
+
if (!view) return;
|
|
469
|
+
const w = s.scrollWidth || 1;
|
|
470
|
+
view.style.left = (100 * s.scrollLeft / w) + '%';
|
|
471
|
+
view.style.width = Math.min(100, 100 * s.clientWidth / w) + '%';
|
|
472
|
+
};
|
|
473
|
+
s.onscroll = () => { GSCROLL[proj] = s.scrollLeft; syncView(); };
|
|
474
|
+
requestAnimationFrame(syncView);
|
|
475
|
+
if (map) {
|
|
476
|
+
const scrubTo = (clientX) => { // map a pointer x onto a scroll position (centered)
|
|
477
|
+
const r = map.getBoundingClientRect();
|
|
478
|
+
const ratio = Math.max(0, Math.min(1, (clientX - r.left) / r.width));
|
|
479
|
+
s.scrollLeft = ratio * s.scrollWidth - s.clientWidth / 2;
|
|
480
|
+
};
|
|
481
|
+
let dragging = false;
|
|
482
|
+
map.addEventListener('pointerdown', e => {
|
|
483
|
+
dragging = true;
|
|
484
|
+
try { map.setPointerCapture?.(e.pointerId); } catch {}
|
|
485
|
+
const seg = e.target.closest && e.target.closest('.gmapseg'); // click a phase segment → jump to that phase
|
|
486
|
+
if (seg && seg.dataset.cx != null) s.scrollLeft = +seg.dataset.cx - 40; else scrubTo(e.clientX);
|
|
487
|
+
e.preventDefault();
|
|
488
|
+
});
|
|
489
|
+
map.addEventListener('pointermove', e => { if (dragging) scrubTo(e.clientX); });
|
|
490
|
+
const end = () => { dragging = false; };
|
|
491
|
+
map.addEventListener('pointerup', end); map.addEventListener('pointercancel', end);
|
|
492
|
+
}
|
|
453
493
|
});
|
|
454
494
|
el.querySelectorAll('.gnode[data-id]').forEach(n => n.onclick = () => openCard(+n.dataset.id));
|
|
455
495
|
}
|