@prplkane/roundtable 0.0.0-stage → 0.6.4
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/CHANGELOG.md +529 -0
- package/LICENSE +21 -0
- package/README.md +189 -3
- package/SECURITY.md +299 -0
- package/docs/workflow.md +1344 -0
- package/package.json +52 -6
- package/scripts/agent.mjs +40 -0
- package/scripts/export.mjs +20 -0
- package/scripts/mcp.mjs +175 -0
- package/scripts/run-debate.mjs +70 -0
- package/scripts/server.mjs +28 -0
- package/scripts/setup.mjs +182 -0
- package/skills/roundtable/SKILL.md +57 -0
- package/src/adapters/agy.js +7 -0
- package/src/adapters/catalogue.js +49 -0
- package/src/adapters/claude.js +6 -0
- package/src/adapters/codex.js +6 -0
- package/src/adapters/common.js +304 -0
- package/src/adapters/config.js +432 -0
- package/src/adapters/endpoint.js +178 -0
- package/src/adapters/errors.js +69 -0
- package/src/adapters/index.js +11 -0
- package/src/adapters/models.js +106 -0
- package/src/adapters/quota.js +305 -0
- package/src/adapters/spawn.js +178 -0
- package/src/agent-client.js +108 -0
- package/src/cli.js +44 -0
- package/src/contract.js +256 -0
- package/src/diagnostics.js +133 -0
- package/src/doctor.js +101 -0
- package/src/engine.js +902 -0
- package/src/estimate.js +331 -0
- package/src/export.js +307 -0
- package/src/fake.js +75 -0
- package/src/folder-picker.js +46 -0
- package/src/history.js +220 -0
- package/src/local-http.js +39 -0
- package/src/local-server.js +110 -0
- package/src/notes.js +16 -0
- package/src/personas.js +49 -0
- package/src/preflight.js +126 -0
- package/src/progress.js +30 -0
- package/src/prompts.js +278 -0
- package/src/research.js +113 -0
- package/src/review-view.js +157 -0
- package/src/seats.js +272 -0
- package/src/server.js +928 -0
- package/src/settings.js +27 -0
- package/src/setup-toml.js +124 -0
- package/src/snapshot.js +93 -0
- package/src/store.js +303 -0
- package/src/terminal.js +77 -0
- package/src/terms.js +52 -0
- package/src/ui/app.js +1967 -0
- package/src/ui/avatars.js +132 -0
- package/src/ui/ceremony.js +87 -0
- package/src/ui/design/app.css +714 -0
- package/src/ui/design/preset-dusk.css +91 -0
- package/src/ui/design/tokens.css +129 -0
- package/src/ui/diagnostics.js +61 -0
- package/src/ui/help.js +122 -0
- package/src/ui/icons.js +64 -0
- package/src/ui/index.html +272 -0
- package/src/ui/loader.js +73 -0
- package/src/ui/logo.svg +4 -0
- package/src/ui/scan.js +57 -0
- package/src/ui/scribe.js +64 -0
- package/src/ui/sound.js +80 -0
- package/src/validate.js +112 -0
- package/src/version.js +5 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Pixel avatars for the seat cards: one per mood, plus the chairman's gavel. 12x12, one colour
|
|
2
|
+
// (currentColor, so the card's vendor colour), drawn with crisp edges like the logo tile.
|
|
3
|
+
// The shape says how the seat argues; the colour says whose CLI it is.
|
|
4
|
+
|
|
5
|
+
const ART = {
|
|
6
|
+
grill: [ // a pointing hand, thumb up (a nod to Ace Attorney's "Objection!"): attack assumptions, demand evidence
|
|
7
|
+
'............',
|
|
8
|
+
'...XX.......',
|
|
9
|
+
'...XX.......',
|
|
10
|
+
'XX.XXXXXXXXX',
|
|
11
|
+
'XX.XXXXXXXXX',
|
|
12
|
+
'XX.XXXXX....',
|
|
13
|
+
'XX.XXXX.....',
|
|
14
|
+
'XX.XXXXX....',
|
|
15
|
+
'XX..XXX.....',
|
|
16
|
+
'............',
|
|
17
|
+
'............',
|
|
18
|
+
'............',
|
|
19
|
+
],
|
|
20
|
+
steelman: [ // a heater shield with a gem at its heart (a nod to The Rising of the Shield Hero): the strongest version of each option first
|
|
21
|
+
'XXXXXXXXXXXX',
|
|
22
|
+
'XXXXXXXXXXXX',
|
|
23
|
+
'XXXXX..XXXXX',
|
|
24
|
+
'XXXX.XX.XXXX',
|
|
25
|
+
'XXX.XXXX.XXX',
|
|
26
|
+
'XXXX.XX.XXXX',
|
|
27
|
+
'XXXXX..XXXXX',
|
|
28
|
+
'.XXXXXXXXXX.',
|
|
29
|
+
'.XXXXXXXXXX.',
|
|
30
|
+
'..XXXXXXXX..',
|
|
31
|
+
'...XXXXXX...',
|
|
32
|
+
'.....XX.....',
|
|
33
|
+
],
|
|
34
|
+
builder: [ // a hard hat (a nod to Bob the Builder): what it takes to ship
|
|
35
|
+
'............',
|
|
36
|
+
'............',
|
|
37
|
+
'............',
|
|
38
|
+
'....XXXX....',
|
|
39
|
+
'..XX.XX.XX..',
|
|
40
|
+
'.XXX.XX.XXX.',
|
|
41
|
+
'.XXX.XX.XXX.',
|
|
42
|
+
'.XXX.XX.XXX.',
|
|
43
|
+
'XXXXXXXXXXXX',
|
|
44
|
+
'XXXXXXXXXXXX',
|
|
45
|
+
'............',
|
|
46
|
+
'............',
|
|
47
|
+
],
|
|
48
|
+
contrarian: [ // a reverse card: the stance the others are least likely to take
|
|
49
|
+
'.XXXXXXXXXX.',
|
|
50
|
+
'.X........X.',
|
|
51
|
+
'.X.....X..X.',
|
|
52
|
+
'.X.XXXXXX.X.',
|
|
53
|
+
'.X.....X..X.',
|
|
54
|
+
'.X........X.',
|
|
55
|
+
'.X........X.',
|
|
56
|
+
'.X..X.....X.',
|
|
57
|
+
'.X.XXXXXX.X.',
|
|
58
|
+
'.X..X.....X.',
|
|
59
|
+
'.X........X.',
|
|
60
|
+
'.XXXXXXXXXX.',
|
|
61
|
+
],
|
|
62
|
+
custom: [ // a quill: the seat's own sentence
|
|
63
|
+
'..........XX',
|
|
64
|
+
'.........XXX',
|
|
65
|
+
'........XXX.',
|
|
66
|
+
'.......XXX..',
|
|
67
|
+
'......XXX...',
|
|
68
|
+
'.....XXX....',
|
|
69
|
+
'....XXX.....',
|
|
70
|
+
'...XXX......',
|
|
71
|
+
'...XX.......',
|
|
72
|
+
'..X.........',
|
|
73
|
+
'.X..........',
|
|
74
|
+
'X...........',
|
|
75
|
+
],
|
|
76
|
+
chairman: [ // a judge's gavel with a cross on its head (a nod to Higuruma's): writes the verdict; the human decides
|
|
77
|
+
'XXXXXXX.....',
|
|
78
|
+
'.XXXXX......',
|
|
79
|
+
'.XX.XX.....X',
|
|
80
|
+
'.X...XXXXXXX',
|
|
81
|
+
'.XX.XXXXXXXX',
|
|
82
|
+
'.XXXXX.....X',
|
|
83
|
+
'.XXXXX......',
|
|
84
|
+
'XXXXXXX.....',
|
|
85
|
+
'............',
|
|
86
|
+
'XXXXXXXX....',
|
|
87
|
+
'XXXXXXXX....',
|
|
88
|
+
'............',
|
|
89
|
+
],
|
|
90
|
+
// the searcher: an open eye, pointed at the corners, a glint in the iris. It is the court's
|
|
91
|
+
// investigator: it looks for evidence before the trial and reads; it never argues or votes.
|
|
92
|
+
// (An eye inside a magnifier's lens has no room at 12 pixels and reads as a snout.)
|
|
93
|
+
searcher: [
|
|
94
|
+
'............',
|
|
95
|
+
'............',
|
|
96
|
+
'....XXXX....',
|
|
97
|
+
'..XX....XX..',
|
|
98
|
+
'.X..XXXX..X.',
|
|
99
|
+
'X..XX.XXX..X',
|
|
100
|
+
'X..XXXXXX..X',
|
|
101
|
+
'.X..XXXX..X.',
|
|
102
|
+
'..XX....XX..',
|
|
103
|
+
'....XXXX....',
|
|
104
|
+
'............',
|
|
105
|
+
'............',
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Each run of filled cells in a row is one rect, so a pixel drawing stays small. The session
|
|
110
|
+
// ceremony draws its larger gavel with the same helper, so both read as one family.
|
|
111
|
+
export function pixelRects(rows, mark = 'X') {
|
|
112
|
+
const rects = [];
|
|
113
|
+
rows.forEach((row, y) => {
|
|
114
|
+
for (let x = 0; x < row.length; x++) {
|
|
115
|
+
if (row[x] !== mark) continue;
|
|
116
|
+
let w = 1;
|
|
117
|
+
while (row[x + w] === mark) w++;
|
|
118
|
+
rects.push(`<rect x="${x}" y="${y}" width="${w}" height="1"/>`);
|
|
119
|
+
x += w - 1;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return rects.join('');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The raw rows, for a drawing built from the same art elsewhere (the session gavel).
|
|
126
|
+
export const sprite = (name) => ART[name] || ART.grill;
|
|
127
|
+
|
|
128
|
+
// One <svg> per avatar.
|
|
129
|
+
export function avatar(name, rows = ART[name] || ART.grill) {
|
|
130
|
+
const rects = pixelRects(rows);
|
|
131
|
+
return `<svg viewBox="0 0 12 12" shape-rendering="crispEdges" fill="currentColor" aria-hidden="true">${rects}</svg>`;
|
|
132
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// The session cue: a pixel gavel that strikes once when a session opens and once when it ends,
|
|
2
|
+
// with a few chips, a 1px shake and a short synthesized sound. It never gates a request, changes
|
|
3
|
+
// review state or loads media. Replayed history never plays (ceremonyOutcome in review-view.js).
|
|
4
|
+
import { pixelRects, sprite } from '/ui/avatars.js';
|
|
5
|
+
import { playCue } from '/ui/sound.js';
|
|
6
|
+
|
|
7
|
+
// The chairman seat's own 12x12 gavel, so the pop-up and the card show one drawing. Rows 0-7
|
|
8
|
+
// are the gavel, rows 9-10 the block it strikes. Swing frames are redrawn on a grid twice as
|
|
9
|
+
// fine (nearest cell), not rotated by CSS, so every frame stays crisp. The pivot is the end of
|
|
10
|
+
// the handle; positive degrees lift the head, negative drop it onto the block.
|
|
11
|
+
const ART = sprite('chairman');
|
|
12
|
+
const GAVEL = ART.map((row, y) => (y <= 7 ? row : ''));
|
|
13
|
+
const BLOCK = ART.map((row, y) => (y >= 9 ? row : ''));
|
|
14
|
+
const FINE = 2, SIZE = 12 * FINE, PAD = 5, PIVOT = [11.5 * FINE, 4 * FINE]; // PAD: headroom for the lifted head
|
|
15
|
+
function rotated(rows, deg) {
|
|
16
|
+
const r = (deg * Math.PI) / 180, cos = Math.cos(r), sin = Math.sin(r), [px, py] = PIVOT;
|
|
17
|
+
return Array.from({ length: SIZE + PAD }, (_, i) => Array.from({ length: SIZE }, (_, x) => {
|
|
18
|
+
const y = i - PAD, dx = x + 0.5 - px, dy = y + 0.5 - py;
|
|
19
|
+
const sx = px + dx * cos + dy * sin, sy = py - dx * sin + dy * cos;
|
|
20
|
+
return rows[Math.floor(sy / FINE)]?.[Math.floor(sx / FINE)] === 'X' ? 'X' : '.';
|
|
21
|
+
}).join(''));
|
|
22
|
+
}
|
|
23
|
+
const scaled = (rows) => [...Array(PAD).fill(''), ...rows.flatMap((row) => { const r = [...row].map((c) => c.repeat(FINE)).join(''); return Array(FINE).fill(r); })];
|
|
24
|
+
const ANGLES = [0, 6, 12, -7, 3]; // rest, lifted, high, struck, bounce
|
|
25
|
+
|
|
26
|
+
// [ms after play, frame]. The strike lands on frame 0 at HIT_MS, where the sound's knock is.
|
|
27
|
+
const HIT_MS = 505;
|
|
28
|
+
const SHOW_MS = 2000;
|
|
29
|
+
const SWING = [[120, 1], [190, 2], [455, 1], [HIT_MS, 3], [600, 4], [680, 0]];
|
|
30
|
+
const TAP = [[150, 1], [HIT_MS, 3], [620, 0]];
|
|
31
|
+
|
|
32
|
+
const PHASES = {
|
|
33
|
+
open: { label: 'Session open', text: 'Independent replies first.', chips: [[-2, -2], [2, -3]] },
|
|
34
|
+
verdict: { label: 'Verdict ready', text: 'The verdict is ready to review.', chips: [[-3, -3], [-2, -5], [-4, -1], [2, -4], [3, -2], [1, -5]] },
|
|
35
|
+
cancelled: { label: 'Review stopped', text: 'Calls cancelled. Saved replies are kept.', chips: [] },
|
|
36
|
+
ended: { label: 'Session closed', text: 'Replies saved. No verdict was written.', chips: [[-3, -2], [-2, -4], [2, -3], [3, -1]] },
|
|
37
|
+
failed: { label: 'Session failed', text: 'It did not finish. The board shows what happened.', chips: [[-2, 2], [-1, 3], [2, 2]] },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function installCeremony() {
|
|
41
|
+
const notice = document.createElement('div');
|
|
42
|
+
notice.className = 'session-ceremony'; notice.hidden = true;
|
|
43
|
+
notice.setAttribute('aria-hidden', 'true'); // The board's status already announces the step.
|
|
44
|
+
notice.innerHTML = `<div class="ceremony-card">
|
|
45
|
+
<svg class="ceremony-art" viewBox="0 0 ${SIZE} ${SIZE + PAD}" shape-rendering="crispEdges" focusable="false" aria-hidden="true">
|
|
46
|
+
<g class="ceremony-block">${pixelRects(scaled(BLOCK))}</g>
|
|
47
|
+
<g class="ceremony-gavel">${ANGLES.map((deg, i) => `<g${i ? ' hidden' : ''}>${pixelRects(deg ? rotated(GAVEL, deg) : scaled(GAVEL))}</g>`).join('')}</g>
|
|
48
|
+
<g class="ceremony-chips"></g>
|
|
49
|
+
</svg>
|
|
50
|
+
<div class="ceremony-text"><strong></strong><span></span></div>
|
|
51
|
+
</div>`;
|
|
52
|
+
document.body.append(notice);
|
|
53
|
+
const chips = notice.querySelector('.ceremony-chips');
|
|
54
|
+
const frames = [...notice.querySelector('.ceremony-gavel').children];
|
|
55
|
+
const show = (n) => frames.forEach((g, i) => g.toggleAttribute('hidden', i !== n));
|
|
56
|
+
let timers = [];
|
|
57
|
+
|
|
58
|
+
function cancel() {
|
|
59
|
+
timers.forEach(clearTimeout); timers = [];
|
|
60
|
+
notice.hidden = true; notice.classList.remove('is-playing'); chips.replaceChildren(); show(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function play(kind) {
|
|
64
|
+
const phase = PHASES[kind];
|
|
65
|
+
if (!phase) return;
|
|
66
|
+
cancel();
|
|
67
|
+
playCue(kind, HIT_MS / 1000); // sound follows the Sound toggle, even in a background tab
|
|
68
|
+
if (document.hidden) return; // nobody would see it; the sound already told them
|
|
69
|
+
const still = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
70
|
+
notice.dataset.phase = kind;
|
|
71
|
+
notice.querySelector('strong').textContent = phase.label;
|
|
72
|
+
notice.querySelector('span').textContent = phase.text;
|
|
73
|
+
if (!still) {
|
|
74
|
+
// Chips leave from the block where the head lands and fly away from it, one art pixel each.
|
|
75
|
+
chips.innerHTML = phase.chips.map(([dx, dy]) => `<rect class="ceremony-chip" x="${dx < 0 ? 2 : 12}" y="${16 + PAD}" width="${FINE}" height="${FINE}" style="--dx:${dx}px;--dy:${dy}px"/>`).join('');
|
|
76
|
+
for (const [ms, n] of kind === 'open' ? TAP : SWING) timers.push(setTimeout(show, ms, n));
|
|
77
|
+
}
|
|
78
|
+
notice.hidden = false;
|
|
79
|
+
void notice.offsetWidth; // restart the animation if it was mid-play
|
|
80
|
+
notice.classList.add('is-playing');
|
|
81
|
+
timers.push(setTimeout(cancel, SHOW_MS));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
document.addEventListener('keydown', (event) => { if (event.key === 'Escape') cancel(); });
|
|
85
|
+
document.addEventListener('visibilitychange', () => { if (document.hidden) cancel(); });
|
|
86
|
+
return { play, cancel };
|
|
87
|
+
}
|