@heretek-ai/epistemic-swarm 1.0.2 → 1.0.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/dist/tui.js +802 -0
- package/package.json +10 -5
- package/src/panels.tsx +12 -6
package/dist/tui.js
ADDED
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/tui.tsx
|
|
3
|
+
import { userInfo } from "os";
|
|
4
|
+
import { Plugin } from "@opencode/plugin/tui";
|
|
5
|
+
|
|
6
|
+
// src/panels.tsx
|
|
7
|
+
import { memo as _$memo } from "@opentui/solid";
|
|
8
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
9
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
10
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
11
|
+
import { insert as _$insert } from "@opentui/solid";
|
|
12
|
+
import { setProp as _$setProp } from "@opentui/solid";
|
|
13
|
+
import { createElement as _$createElement } from "@opentui/solid";
|
|
14
|
+
import { createResource, createSignal, For, onCleanup, Show } from "solid-js";
|
|
15
|
+
var PANEL_REFRESH_MS = 2000;
|
|
16
|
+
function usePanelState(props, method) {
|
|
17
|
+
const [version, setVersion] = createSignal(0);
|
|
18
|
+
const bump = () => setVersion((value) => value + 1);
|
|
19
|
+
const unsubscribe = props.subscribe(bump);
|
|
20
|
+
const timer = setInterval(bump, PANEL_REFRESH_MS);
|
|
21
|
+
onCleanup(() => {
|
|
22
|
+
unsubscribe();
|
|
23
|
+
clearInterval(timer);
|
|
24
|
+
});
|
|
25
|
+
const [state] = createResource(version, () => props.call(method).then((data) => ({
|
|
26
|
+
data
|
|
27
|
+
}), (error) => ({
|
|
28
|
+
error: String(error?.data?.reason ?? error?.message ?? error)
|
|
29
|
+
})));
|
|
30
|
+
return {
|
|
31
|
+
data: () => state.latest?.data,
|
|
32
|
+
error: () => state.latest?.error
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
var Frame = (props) => (() => {
|
|
36
|
+
var _el$ = _$createElement("box"), _el$2 = _$createElement("text");
|
|
37
|
+
_$insertNode(_el$, _el$2);
|
|
38
|
+
_$setProp(_el$, "flexDirection", "column");
|
|
39
|
+
_$setProp(_el$, "padding", 1);
|
|
40
|
+
_$setProp(_el$, "gap", 1);
|
|
41
|
+
_$insert(_el$2, () => props.title);
|
|
42
|
+
_$insert(_el$, () => props.children, null);
|
|
43
|
+
return _el$;
|
|
44
|
+
})();
|
|
45
|
+
var Failed = (props) => _$createComponent(Show, {
|
|
46
|
+
get when() {
|
|
47
|
+
return props.error();
|
|
48
|
+
},
|
|
49
|
+
get children() {
|
|
50
|
+
var _el$3 = _$createElement("text"), _el$4 = _$createTextNode(`Could not load: `);
|
|
51
|
+
_$insertNode(_el$3, _el$4);
|
|
52
|
+
_$insert(_el$3, () => props.error(), null);
|
|
53
|
+
return _el$3;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
var FactoryPanel = (props) => {
|
|
57
|
+
const state = usePanelState(props, "factoryState");
|
|
58
|
+
return _$createComponent(Frame, {
|
|
59
|
+
title: "Factory dashboard",
|
|
60
|
+
get children() {
|
|
61
|
+
return [_$createComponent(Failed, {
|
|
62
|
+
get error() {
|
|
63
|
+
return state.error;
|
|
64
|
+
}
|
|
65
|
+
}), _$createComponent(Show, {
|
|
66
|
+
get when() {
|
|
67
|
+
return state.data();
|
|
68
|
+
},
|
|
69
|
+
get fallback() {
|
|
70
|
+
return (() => {
|
|
71
|
+
var _el$5 = _$createElement("text");
|
|
72
|
+
_$insert(_el$5, () => state.error() ? "" : "Loading factory state\u2026");
|
|
73
|
+
return _el$5;
|
|
74
|
+
})();
|
|
75
|
+
},
|
|
76
|
+
children: (data) => [(() => {
|
|
77
|
+
var _el$6 = _$createElement("text"), _el$7 = _$createTextNode(`Stage: `);
|
|
78
|
+
_$insertNode(_el$6, _el$7);
|
|
79
|
+
_$insert(_el$6, () => data().stage, null);
|
|
80
|
+
_$insert(_el$6, (() => {
|
|
81
|
+
var _c$ = _$memo(() => !!data().runId);
|
|
82
|
+
return () => _c$() ? ` \xB7 ${data().runId}` : "";
|
|
83
|
+
})(), null);
|
|
84
|
+
return _el$6;
|
|
85
|
+
})(), (() => {
|
|
86
|
+
var _el$8 = _$createElement("text"), _el$9 = _$createTextNode(`Spend: $`);
|
|
87
|
+
_$insertNode(_el$8, _el$9);
|
|
88
|
+
_$insert(_el$8, () => data().spend.usd.toFixed(2), null);
|
|
89
|
+
_$insert(_el$8, (() => {
|
|
90
|
+
var _c$2 = _$memo(() => !!data().spend.ceilingUSD);
|
|
91
|
+
return () => _c$2() ? ` / $${data().spend.ceilingUSD}` : "";
|
|
92
|
+
})(), null);
|
|
93
|
+
_$insert(_el$8, () => data().spend.estimated ? " (estimated)" : "", null);
|
|
94
|
+
return _el$8;
|
|
95
|
+
})(), _$createComponent(Show, {
|
|
96
|
+
get when() {
|
|
97
|
+
return data().halt;
|
|
98
|
+
},
|
|
99
|
+
get children() {
|
|
100
|
+
var _el$0 = _$createElement("text"), _el$1 = _$createTextNode(`HALTED: `);
|
|
101
|
+
_$insertNode(_el$0, _el$1);
|
|
102
|
+
_$insert(_el$0, () => data().halt, null);
|
|
103
|
+
return _el$0;
|
|
104
|
+
}
|
|
105
|
+
}), _$createComponent(Show, {
|
|
106
|
+
get when() {
|
|
107
|
+
return data().release;
|
|
108
|
+
},
|
|
109
|
+
get children() {
|
|
110
|
+
var _el$10 = _$createElement("text"), _el$11 = _$createTextNode(`PR: `);
|
|
111
|
+
_$insertNode(_el$10, _el$11);
|
|
112
|
+
_$insert(_el$10, () => data().release, null);
|
|
113
|
+
return _el$10;
|
|
114
|
+
}
|
|
115
|
+
}), _$createComponent(Show, {
|
|
116
|
+
get when() {
|
|
117
|
+
return data().pending.length;
|
|
118
|
+
},
|
|
119
|
+
get children() {
|
|
120
|
+
var _el$12 = _$createElement("text"), _el$13 = _$createTextNode(`Waiting on human: `);
|
|
121
|
+
_$insertNode(_el$12, _el$13);
|
|
122
|
+
_$insert(_el$12, () => data().pending.join(", "), null);
|
|
123
|
+
return _el$12;
|
|
124
|
+
}
|
|
125
|
+
}), (() => {
|
|
126
|
+
var _el$14 = _$createElement("text");
|
|
127
|
+
_$insertNode(_el$14, _$createTextNode(`Phases:`));
|
|
128
|
+
return _el$14;
|
|
129
|
+
})(), _$createComponent(For, {
|
|
130
|
+
get each() {
|
|
131
|
+
return data().phases;
|
|
132
|
+
},
|
|
133
|
+
children: (phase) => (() => {
|
|
134
|
+
var _el$16 = _$createElement("text"), _el$17 = _$createTextNode(` \xB7 `), _el$18 = _$createTextNode(` \xB7 failures `);
|
|
135
|
+
_$insertNode(_el$16, _el$17);
|
|
136
|
+
_$insertNode(_el$16, _el$18);
|
|
137
|
+
_$insert(_el$16, () => phase.id === data().activePhase ? "\u25B6 " : " ", _el$17);
|
|
138
|
+
_$insert(_el$16, () => phase.id, _el$17);
|
|
139
|
+
_$insert(_el$16, () => phase.status, _el$18);
|
|
140
|
+
_$insert(_el$16, () => phase.failures, null);
|
|
141
|
+
return _el$16;
|
|
142
|
+
})()
|
|
143
|
+
})]
|
|
144
|
+
})];
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
var LspPanel = (props) => {
|
|
149
|
+
const state = usePanelState(props, "lspState");
|
|
150
|
+
return _$createComponent(Frame, {
|
|
151
|
+
title: "Language servers",
|
|
152
|
+
get children() {
|
|
153
|
+
return [_$createComponent(Failed, {
|
|
154
|
+
get error() {
|
|
155
|
+
return state.error;
|
|
156
|
+
}
|
|
157
|
+
}), _$createComponent(Show, {
|
|
158
|
+
get when() {
|
|
159
|
+
return state.data();
|
|
160
|
+
},
|
|
161
|
+
get fallback() {
|
|
162
|
+
return (() => {
|
|
163
|
+
var _el$19 = _$createElement("text");
|
|
164
|
+
_$insert(_el$19, () => state.error() ? "" : "Loading language servers\u2026");
|
|
165
|
+
return _el$19;
|
|
166
|
+
})();
|
|
167
|
+
},
|
|
168
|
+
children: (data) => [(() => {
|
|
169
|
+
var _el$20 = _$createElement("text");
|
|
170
|
+
_$insert(_el$20, () => data().enabled ? "LSP is on." : "LSP is disabled (lsp: false).");
|
|
171
|
+
return _el$20;
|
|
172
|
+
})(), _$createComponent(For, {
|
|
173
|
+
get each() {
|
|
174
|
+
return data().servers;
|
|
175
|
+
},
|
|
176
|
+
children: (server) => (() => {
|
|
177
|
+
var _el$21 = _$createElement("text"), _el$22 = _$createTextNode(` `), _el$23 = _$createTextNode(` [`), _el$24 = _$createTextNode(`]`);
|
|
178
|
+
_$insertNode(_el$21, _el$22);
|
|
179
|
+
_$insertNode(_el$21, _el$23);
|
|
180
|
+
_$insertNode(_el$21, _el$24);
|
|
181
|
+
_$insert(_el$21, () => server.available ? "\u25CF" : "\u25CB", _el$22);
|
|
182
|
+
_$insert(_el$21, () => server.id, _el$23);
|
|
183
|
+
_$insert(_el$21, () => server.extensions.join(" "), _el$24);
|
|
184
|
+
_$insert(_el$21, (() => {
|
|
185
|
+
var _c$3 = _$memo(() => !!server.running);
|
|
186
|
+
return () => _c$3() ? ` \xB7 ${server.running} running` : "";
|
|
187
|
+
})(), null);
|
|
188
|
+
_$insert(_el$21, () => server.available ? "" : " \u2014 not installed", null);
|
|
189
|
+
return _el$21;
|
|
190
|
+
})()
|
|
191
|
+
}), _$createComponent(For, {
|
|
192
|
+
get each() {
|
|
193
|
+
return data().diagnostics;
|
|
194
|
+
},
|
|
195
|
+
children: (diagnostic) => (() => {
|
|
196
|
+
var _el$25 = _$createElement("text"), _el$26 = _$createTextNode(`! `);
|
|
197
|
+
_$insertNode(_el$25, _el$26);
|
|
198
|
+
_$insert(_el$25, diagnostic, null);
|
|
199
|
+
return _el$25;
|
|
200
|
+
})()
|
|
201
|
+
})]
|
|
202
|
+
})];
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
var HooksPanel = (props) => {
|
|
207
|
+
const state = usePanelState(props, "hooksState");
|
|
208
|
+
return _$createComponent(Frame, {
|
|
209
|
+
title: "Hook bridge",
|
|
210
|
+
get children() {
|
|
211
|
+
return [_$createComponent(Failed, {
|
|
212
|
+
get error() {
|
|
213
|
+
return state.error;
|
|
214
|
+
}
|
|
215
|
+
}), _$createComponent(Show, {
|
|
216
|
+
get when() {
|
|
217
|
+
return state.data();
|
|
218
|
+
},
|
|
219
|
+
get fallback() {
|
|
220
|
+
return (() => {
|
|
221
|
+
var _el$27 = _$createElement("text");
|
|
222
|
+
_$insert(_el$27, () => state.error() ? "" : "Loading hooks\u2026");
|
|
223
|
+
return _el$27;
|
|
224
|
+
})();
|
|
225
|
+
},
|
|
226
|
+
children: (data) => [(() => {
|
|
227
|
+
var _el$28 = _$createElement("text"), _el$29 = _$createTextNode(` handler(s) \xB7 `), _el$30 = _$createTextNode(` from the project \xB7 `);
|
|
228
|
+
_$insertNode(_el$28, _el$29);
|
|
229
|
+
_$insertNode(_el$28, _el$30);
|
|
230
|
+
_$insert(_el$28, () => data().handlers, _el$29);
|
|
231
|
+
_$insert(_el$28, () => data().projectHandlers, _el$30);
|
|
232
|
+
_$insert(_el$28, (() => {
|
|
233
|
+
var _c$4 = _$memo(() => data().projectHandlers === 0);
|
|
234
|
+
return () => _c$4() ? "nothing to trust" : data().trusted ? "trusted" : "NOT trusted";
|
|
235
|
+
})(), null);
|
|
236
|
+
return _el$28;
|
|
237
|
+
})(), _$createComponent(For, {
|
|
238
|
+
get each() {
|
|
239
|
+
return data().projectLines;
|
|
240
|
+
},
|
|
241
|
+
children: (line) => (() => {
|
|
242
|
+
var _el$34 = _$createElement("text");
|
|
243
|
+
_$insert(_el$34, line);
|
|
244
|
+
return _el$34;
|
|
245
|
+
})()
|
|
246
|
+
}), _$createComponent(Show, {
|
|
247
|
+
get when() {
|
|
248
|
+
return data().loss.length;
|
|
249
|
+
},
|
|
250
|
+
get children() {
|
|
251
|
+
return [(() => {
|
|
252
|
+
var _el$32 = _$createElement("text");
|
|
253
|
+
_$insertNode(_el$32, _$createTextNode(`Capability loss (advisory rows):`));
|
|
254
|
+
return _el$32;
|
|
255
|
+
})(), _$createComponent(For, {
|
|
256
|
+
get each() {
|
|
257
|
+
return data().loss;
|
|
258
|
+
},
|
|
259
|
+
children: (item) => (() => {
|
|
260
|
+
var _el$35 = _$createElement("text"), _el$36 = _$createTextNode(`! `), _el$37 = _$createTextNode(` \xB7 `), _el$38 = _$createTextNode(` \u2014 `), _el$39 = _$createTextNode(`: `);
|
|
261
|
+
_$insertNode(_el$35, _el$36);
|
|
262
|
+
_$insertNode(_el$35, _el$37);
|
|
263
|
+
_$insertNode(_el$35, _el$38);
|
|
264
|
+
_$insertNode(_el$35, _el$39);
|
|
265
|
+
_$insert(_el$35, () => item.event, _el$37);
|
|
266
|
+
_$insert(_el$35, () => item.handler, _el$38);
|
|
267
|
+
_$insert(_el$35, () => item.support, _el$39);
|
|
268
|
+
_$insert(_el$35, () => item.reason, null);
|
|
269
|
+
return _el$35;
|
|
270
|
+
})()
|
|
271
|
+
})];
|
|
272
|
+
}
|
|
273
|
+
}), _$createComponent(For, {
|
|
274
|
+
get each() {
|
|
275
|
+
return data().diagnostics;
|
|
276
|
+
},
|
|
277
|
+
children: (item) => (() => {
|
|
278
|
+
var _el$40 = _$createElement("text"), _el$41 = _$createTextNode(`! `);
|
|
279
|
+
_$insertNode(_el$40, _el$41);
|
|
280
|
+
_$insert(_el$40, item, null);
|
|
281
|
+
return _el$40;
|
|
282
|
+
})()
|
|
283
|
+
}), _$createComponent(For, {
|
|
284
|
+
get each() {
|
|
285
|
+
return data().recent;
|
|
286
|
+
},
|
|
287
|
+
children: (item) => (() => {
|
|
288
|
+
var _el$42 = _$createElement("text"), _el$43 = _$createTextNode(`recent error: `);
|
|
289
|
+
_$insertNode(_el$42, _el$43);
|
|
290
|
+
_$insert(_el$42, item, null);
|
|
291
|
+
return _el$42;
|
|
292
|
+
})()
|
|
293
|
+
})]
|
|
294
|
+
})];
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
var BrainstormPanel = (props) => {
|
|
299
|
+
const state = usePanelState(props, "brainstormState");
|
|
300
|
+
return _$createComponent(Frame, {
|
|
301
|
+
title: "Brainstorm board",
|
|
302
|
+
get children() {
|
|
303
|
+
return [_$createComponent(Failed, {
|
|
304
|
+
get error() {
|
|
305
|
+
return state.error;
|
|
306
|
+
}
|
|
307
|
+
}), _$createComponent(Show, {
|
|
308
|
+
get when() {
|
|
309
|
+
return state.data();
|
|
310
|
+
},
|
|
311
|
+
get fallback() {
|
|
312
|
+
return (() => {
|
|
313
|
+
var _el$44 = _$createElement("text");
|
|
314
|
+
_$insert(_el$44, () => state.error() ? "" : "Loading brainstorm\u2026");
|
|
315
|
+
return _el$44;
|
|
316
|
+
})();
|
|
317
|
+
},
|
|
318
|
+
children: (data) => _$createComponent(Show, {
|
|
319
|
+
get when() {
|
|
320
|
+
return data().active;
|
|
321
|
+
},
|
|
322
|
+
get fallback() {
|
|
323
|
+
return (() => {
|
|
324
|
+
var _el$59 = _$createElement("text");
|
|
325
|
+
_$insertNode(_el$59, _$createTextNode(`No brainstorm in .factory/brainstorm yet. Run /brainstorm.`));
|
|
326
|
+
return _el$59;
|
|
327
|
+
})();
|
|
328
|
+
},
|
|
329
|
+
get children() {
|
|
330
|
+
return [(() => {
|
|
331
|
+
var _el$45 = _$createElement("text"), _el$46 = _$createTextNode(`Brief: `);
|
|
332
|
+
_$insertNode(_el$45, _el$46);
|
|
333
|
+
_$insert(_el$45, () => data().brief, null);
|
|
334
|
+
return _el$45;
|
|
335
|
+
})(), (() => {
|
|
336
|
+
var _el$47 = _$createElement("text"), _el$48 = _$createTextNode(` idea(s) \xB7 `), _el$49 = _$createTextNode(` duplicate(s) \xB7 `), _el$50 = _$createTextNode(` scored \xB7 `);
|
|
337
|
+
_$insertNode(_el$47, _el$48);
|
|
338
|
+
_$insertNode(_el$47, _el$49);
|
|
339
|
+
_$insertNode(_el$47, _el$50);
|
|
340
|
+
_$insert(_el$47, () => data().ideas, _el$48);
|
|
341
|
+
_$insert(_el$47, () => data().duplicates, _el$49);
|
|
342
|
+
_$insert(_el$47, () => data().scored, _el$50);
|
|
343
|
+
_$insert(_el$47, () => data().complete ? "complete" : "in progress", null);
|
|
344
|
+
return _el$47;
|
|
345
|
+
})(), (() => {
|
|
346
|
+
var _el$52 = _$createElement("text"), _el$53 = _$createTextNode(`Coverage: `);
|
|
347
|
+
_$insertNode(_el$52, _el$53);
|
|
348
|
+
_$insert(_el$52, () => data().lenses.map((lens) => `${lens} ${data().coverage[lens] ?? 0}`).join(" \xB7 "), null);
|
|
349
|
+
return _el$52;
|
|
350
|
+
})(), _$createComponent(Show, {
|
|
351
|
+
get when() {
|
|
352
|
+
return data().gaps.length;
|
|
353
|
+
},
|
|
354
|
+
get children() {
|
|
355
|
+
var _el$55 = _$createElement("text"), _el$56 = _$createTextNode(`Gaps: `);
|
|
356
|
+
_$insertNode(_el$55, _el$56);
|
|
357
|
+
_$insert(_el$55, () => data().gaps.join(", "), null);
|
|
358
|
+
return _el$55;
|
|
359
|
+
}
|
|
360
|
+
}), _$createComponent(Show, {
|
|
361
|
+
get when() {
|
|
362
|
+
return data().shortlist.length;
|
|
363
|
+
},
|
|
364
|
+
get children() {
|
|
365
|
+
return [(() => {
|
|
366
|
+
var _el$57 = _$createElement("text");
|
|
367
|
+
_$insertNode(_el$57, _$createTextNode(`Shortlist:`));
|
|
368
|
+
return _el$57;
|
|
369
|
+
})(), _$createComponent(For, {
|
|
370
|
+
get each() {
|
|
371
|
+
return data().shortlist;
|
|
372
|
+
},
|
|
373
|
+
children: (entry) => (() => {
|
|
374
|
+
var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(` `), _el$63 = _$createTextNode(` (`), _el$64 = _$createTextNode(`/20) \u2014 `);
|
|
375
|
+
_$insertNode(_el$61, _el$62);
|
|
376
|
+
_$insertNode(_el$61, _el$63);
|
|
377
|
+
_$insertNode(_el$61, _el$64);
|
|
378
|
+
_$insert(_el$61, () => entry.outlier ? "\u25C7" : "\u25C6", _el$62);
|
|
379
|
+
_$insert(_el$61, () => entry.id, _el$63);
|
|
380
|
+
_$insert(_el$61, () => entry.total, _el$64);
|
|
381
|
+
_$insert(_el$61, () => entry.title, null);
|
|
382
|
+
_$insert(_el$61, () => entry.outlier ? " (forced outlier)" : "", null);
|
|
383
|
+
return _el$61;
|
|
384
|
+
})()
|
|
385
|
+
})];
|
|
386
|
+
}
|
|
387
|
+
})];
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
})];
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
var PANEL_COMPONENTS = {
|
|
395
|
+
factory: FactoryPanel,
|
|
396
|
+
lsp: LspPanel,
|
|
397
|
+
hooks: HooksPanel,
|
|
398
|
+
brainstorm: BrainstormPanel
|
|
399
|
+
};
|
|
400
|
+
var PANELS = Object.entries(PANEL_COMPONENTS).map(([name, Panel]) => ({
|
|
401
|
+
name,
|
|
402
|
+
render: (props) => _$createComponent(Panel, {
|
|
403
|
+
get call() {
|
|
404
|
+
return props.call;
|
|
405
|
+
},
|
|
406
|
+
get subscribe() {
|
|
407
|
+
return props.subscribe;
|
|
408
|
+
}
|
|
409
|
+
})
|
|
410
|
+
}));
|
|
411
|
+
function registerPanels(context, call, subscribe) {
|
|
412
|
+
const slotFn = typeof context.slot === "function" ? context.slot : context.ui?.slot;
|
|
413
|
+
if (typeof slotFn !== "function")
|
|
414
|
+
return () => {};
|
|
415
|
+
const disposers = [];
|
|
416
|
+
for (const panel of PANELS) {
|
|
417
|
+
const render = (input) => input.name === panel.name ? panel.render({
|
|
418
|
+
call,
|
|
419
|
+
subscribe
|
|
420
|
+
}) : null;
|
|
421
|
+
const dispose = slotFn.length <= 1 ? slotFn({
|
|
422
|
+
append: "session.panel",
|
|
423
|
+
render
|
|
424
|
+
}) : slotFn("session.panel", render);
|
|
425
|
+
if (typeof dispose === "function")
|
|
426
|
+
disposers.push(dispose);
|
|
427
|
+
}
|
|
428
|
+
return () => {
|
|
429
|
+
for (const dispose of disposers)
|
|
430
|
+
dispose();
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
var PANEL_NAMES = PANELS.map((panel) => panel.name);
|
|
434
|
+
|
|
435
|
+
// ../../node_modules/.bun/@opencode+schema@2.0.24/node_modules/@opencode/schema/dist/rpc.js
|
|
436
|
+
function define(definition) {
|
|
437
|
+
const reserved = Object.values(definition.methods).flatMap((method) => Object.keys(method.errors ?? {})).find((name) => name.startsWith("rpc."));
|
|
438
|
+
if (reserved)
|
|
439
|
+
throw new Error(`RPC error names starting with "rpc." are reserved: ${reserved}`);
|
|
440
|
+
return definition;
|
|
441
|
+
}
|
|
442
|
+
// src/rpc-def.ts
|
|
443
|
+
var obj = (properties, required = []) => ({
|
|
444
|
+
type: "object",
|
|
445
|
+
properties,
|
|
446
|
+
required,
|
|
447
|
+
additionalProperties: false
|
|
448
|
+
});
|
|
449
|
+
var str = { type: "string" };
|
|
450
|
+
var strArray = { type: "array", items: str };
|
|
451
|
+
var Preview = obj({ ok: { type: "boolean" }, title: str, lines: strArray, problems: strArray, token: str }, [
|
|
452
|
+
"ok",
|
|
453
|
+
"title",
|
|
454
|
+
"lines",
|
|
455
|
+
"problems"
|
|
456
|
+
]);
|
|
457
|
+
var EsRpc = define({
|
|
458
|
+
id: "epistemic-swarm",
|
|
459
|
+
methods: {
|
|
460
|
+
status: {
|
|
461
|
+
input: obj({}),
|
|
462
|
+
output: obj({ summary: str, stage: str, pending: strArray }, ["summary", "stage", "pending"])
|
|
463
|
+
},
|
|
464
|
+
previewApproval: {
|
|
465
|
+
input: obj({ stage: { type: "string", enum: ["frontier", "spec"] } }, ["stage"]),
|
|
466
|
+
output: Preview
|
|
467
|
+
},
|
|
468
|
+
approve: {
|
|
469
|
+
input: obj({ stage: { type: "string", enum: ["frontier", "spec"] }, user: str, token: str }, [
|
|
470
|
+
"stage",
|
|
471
|
+
"user",
|
|
472
|
+
"token"
|
|
473
|
+
]),
|
|
474
|
+
output: obj({ message: str }, ["message"]),
|
|
475
|
+
errors: { refused: obj({ reason: str }, ["reason"]) }
|
|
476
|
+
},
|
|
477
|
+
previewTrust: { input: obj({}), output: Preview },
|
|
478
|
+
trust: {
|
|
479
|
+
input: obj({ user: str, token: str }, ["user", "token"]),
|
|
480
|
+
output: obj({ message: str }, ["message"]),
|
|
481
|
+
errors: { refused: obj({ reason: str }, ["reason"]) }
|
|
482
|
+
},
|
|
483
|
+
previewResume: { input: obj({}), output: Preview },
|
|
484
|
+
previewLspInstall: { input: obj({ id: str }, ["id"]), output: Preview },
|
|
485
|
+
lspInstall: {
|
|
486
|
+
input: obj({ id: str, user: str, token: str }, ["id", "user", "token"]),
|
|
487
|
+
output: obj({ message: str }, ["message"]),
|
|
488
|
+
errors: { refused: obj({ reason: str }, ["reason"]) }
|
|
489
|
+
},
|
|
490
|
+
resume: {
|
|
491
|
+
input: obj({
|
|
492
|
+
user: str,
|
|
493
|
+
token: str,
|
|
494
|
+
acceptControlDrift: { type: "boolean" },
|
|
495
|
+
raiseCeilingUSD: { type: "number" },
|
|
496
|
+
extendRuntime: { type: "boolean" }
|
|
497
|
+
}, ["user", "token"]),
|
|
498
|
+
output: obj({ message: str }, ["message"]),
|
|
499
|
+
errors: { refused: obj({ reason: str }, ["reason"]) }
|
|
500
|
+
},
|
|
501
|
+
factoryState: {
|
|
502
|
+
input: obj({}),
|
|
503
|
+
output: obj({
|
|
504
|
+
stage: str,
|
|
505
|
+
runId: str,
|
|
506
|
+
halt: str,
|
|
507
|
+
release: str,
|
|
508
|
+
activePhase: str,
|
|
509
|
+
spend: obj({ usd: { type: "number" }, estimated: { type: "boolean" }, ceilingUSD: { type: "number" } }, [
|
|
510
|
+
"usd",
|
|
511
|
+
"estimated"
|
|
512
|
+
]),
|
|
513
|
+
phases: { type: "array" },
|
|
514
|
+
pending: strArray
|
|
515
|
+
}, ["stage", "spend", "phases", "pending"])
|
|
516
|
+
},
|
|
517
|
+
lspState: {
|
|
518
|
+
input: obj({}),
|
|
519
|
+
output: obj({
|
|
520
|
+
enabled: { type: "boolean" },
|
|
521
|
+
servers: { type: "array" },
|
|
522
|
+
diagnostics: strArray
|
|
523
|
+
}, ["enabled", "servers", "diagnostics"])
|
|
524
|
+
},
|
|
525
|
+
hooksState: {
|
|
526
|
+
input: obj({}),
|
|
527
|
+
output: obj({
|
|
528
|
+
handlers: { type: "number" },
|
|
529
|
+
projectHandlers: { type: "number" },
|
|
530
|
+
trusted: { type: "boolean" },
|
|
531
|
+
projectLines: strArray,
|
|
532
|
+
diagnostics: strArray,
|
|
533
|
+
recent: strArray,
|
|
534
|
+
loss: { type: "array" }
|
|
535
|
+
}, ["handlers", "projectHandlers", "trusted", "projectLines", "diagnostics", "recent", "loss"])
|
|
536
|
+
},
|
|
537
|
+
brainstormState: {
|
|
538
|
+
input: obj({}),
|
|
539
|
+
output: obj({
|
|
540
|
+
active: { type: "boolean" },
|
|
541
|
+
brief: str,
|
|
542
|
+
lenses: strArray,
|
|
543
|
+
ideas: { type: "number" },
|
|
544
|
+
duplicates: { type: "number" },
|
|
545
|
+
scored: { type: "number" },
|
|
546
|
+
coverage: { type: "object" },
|
|
547
|
+
shortlist: { type: "array" },
|
|
548
|
+
gaps: strArray,
|
|
549
|
+
complete: { type: "boolean" }
|
|
550
|
+
}, ["active"])
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
events: {
|
|
554
|
+
changed: { schema: obj({ stage: str, summary: str }, ["stage", "summary"]) }
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// src/tui.tsx
|
|
559
|
+
var tui_default = Plugin.define({
|
|
560
|
+
id: "epistemic-swarm.tui",
|
|
561
|
+
setup(context) {
|
|
562
|
+
const rpc = context.client.rpc(EsRpc);
|
|
563
|
+
const location = () => context.location ?? context.data.location.default();
|
|
564
|
+
const call = (method, input = {}) => rpc[method](input, {
|
|
565
|
+
location: location()
|
|
566
|
+
});
|
|
567
|
+
const stopPanels = registerPanels(context, call, (listener) => rpc.events.on("changed", () => listener()));
|
|
568
|
+
const user = userInfo().username;
|
|
569
|
+
const toast = (message, variant = "info") => context.ui.toast.show({
|
|
570
|
+
title: "Epistemic Swarm",
|
|
571
|
+
message,
|
|
572
|
+
variant
|
|
573
|
+
});
|
|
574
|
+
const confirm = async (preview, label) => {
|
|
575
|
+
if (!preview.ok || !preview.token) {
|
|
576
|
+
await context.ui.dialog.alert({
|
|
577
|
+
title: preview.title,
|
|
578
|
+
message: [...preview.problems, ...preview.lines].join(`
|
|
579
|
+
`)
|
|
580
|
+
});
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const ok = await context.ui.dialog.confirm({
|
|
584
|
+
title: preview.title,
|
|
585
|
+
message: preview.lines.join(`
|
|
586
|
+
`),
|
|
587
|
+
label: {
|
|
588
|
+
confirm: label,
|
|
589
|
+
cancel: "Cancel"
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
return ok ? preview.token : undefined;
|
|
593
|
+
};
|
|
594
|
+
const guarded = (fn) => async (input) => {
|
|
595
|
+
try {
|
|
596
|
+
await fn(input);
|
|
597
|
+
} catch (error) {
|
|
598
|
+
toast(error?.data?.reason ?? error?.message ?? String(error), "error");
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
const approve = guarded(async (input) => {
|
|
602
|
+
const status = await call("status");
|
|
603
|
+
const stage = input?.trim() || (status.pending.length === 1 ? status.pending[0] : await context.ui.dialog.select({
|
|
604
|
+
title: "Approve which checkpoint?",
|
|
605
|
+
options: [{
|
|
606
|
+
title: "Frontier (design tree + spend ceiling)",
|
|
607
|
+
value: "frontier"
|
|
608
|
+
}, {
|
|
609
|
+
title: "Spec (roadmap + every GOAL.md)",
|
|
610
|
+
value: "spec"
|
|
611
|
+
}]
|
|
612
|
+
}));
|
|
613
|
+
if (stage !== "frontier" && stage !== "spec")
|
|
614
|
+
return;
|
|
615
|
+
const token = await confirm(await call("previewApproval", {
|
|
616
|
+
stage
|
|
617
|
+
}), "Approve");
|
|
618
|
+
if (!token)
|
|
619
|
+
return;
|
|
620
|
+
toast((await call("approve", {
|
|
621
|
+
stage,
|
|
622
|
+
user,
|
|
623
|
+
token
|
|
624
|
+
})).message, "success");
|
|
625
|
+
});
|
|
626
|
+
const trust = guarded(async () => {
|
|
627
|
+
const token = await confirm(await call("previewTrust"), "Trust");
|
|
628
|
+
if (!token)
|
|
629
|
+
return;
|
|
630
|
+
toast((await call("trust", {
|
|
631
|
+
user,
|
|
632
|
+
token
|
|
633
|
+
})).message, "success");
|
|
634
|
+
});
|
|
635
|
+
const resume = guarded(async () => {
|
|
636
|
+
const preview = await call("previewResume");
|
|
637
|
+
const token = await confirm(preview, "Resume");
|
|
638
|
+
if (!token)
|
|
639
|
+
return;
|
|
640
|
+
const drift = preview.lines.some((line) => line.startsWith("Control-file drift"));
|
|
641
|
+
let raiseCeilingUSD;
|
|
642
|
+
if (/spend ceiling/i.test(preview.lines[0] ?? "")) {
|
|
643
|
+
const raised = await context.ui.dialog.prompt({
|
|
644
|
+
title: "New spend ceiling (USD)",
|
|
645
|
+
placeholder: "e.g. 50"
|
|
646
|
+
});
|
|
647
|
+
raiseCeilingUSD = raised ? Number(raised) : undefined;
|
|
648
|
+
}
|
|
649
|
+
const extendRuntime = /runtime cap/i.test(preview.lines[0] ?? "");
|
|
650
|
+
const result = await call("resume", {
|
|
651
|
+
user,
|
|
652
|
+
token,
|
|
653
|
+
...drift ? {
|
|
654
|
+
acceptControlDrift: true
|
|
655
|
+
} : {},
|
|
656
|
+
...raiseCeilingUSD ? {
|
|
657
|
+
raiseCeilingUSD
|
|
658
|
+
} : {},
|
|
659
|
+
...extendRuntime ? {
|
|
660
|
+
extendRuntime: true
|
|
661
|
+
} : {}
|
|
662
|
+
});
|
|
663
|
+
toast(result.message, "success");
|
|
664
|
+
});
|
|
665
|
+
const lspInstall = guarded(async (input) => {
|
|
666
|
+
const id = input?.trim() || await context.ui.dialog.prompt({
|
|
667
|
+
title: "Install which language server?",
|
|
668
|
+
placeholder: "typescript, pyright"
|
|
669
|
+
});
|
|
670
|
+
if (!id)
|
|
671
|
+
return;
|
|
672
|
+
const token = await confirm(await call("previewLspInstall", {
|
|
673
|
+
id
|
|
674
|
+
}), "Install");
|
|
675
|
+
if (!token)
|
|
676
|
+
return;
|
|
677
|
+
toast((await call("lspInstall", {
|
|
678
|
+
id,
|
|
679
|
+
user,
|
|
680
|
+
token
|
|
681
|
+
})).message, "success");
|
|
682
|
+
});
|
|
683
|
+
const status = guarded(async () => {
|
|
684
|
+
const result = await call("status");
|
|
685
|
+
await context.ui.dialog.alert({
|
|
686
|
+
title: "Factory",
|
|
687
|
+
message: result.summary.replace(/<\/?factory-state>/g, "").trim()
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
context.keymap.layer(() => ({
|
|
691
|
+
mode: "global",
|
|
692
|
+
commands: [{
|
|
693
|
+
id: "es.approve",
|
|
694
|
+
title: "Epistemic Swarm: approve a checkpoint",
|
|
695
|
+
group: "Epistemic Swarm",
|
|
696
|
+
palette: true,
|
|
697
|
+
slash: {
|
|
698
|
+
name: "es-approve",
|
|
699
|
+
arguments: true
|
|
700
|
+
},
|
|
701
|
+
run: approve
|
|
702
|
+
}, {
|
|
703
|
+
id: "es.trust",
|
|
704
|
+
title: "Epistemic Swarm: trust gate commands",
|
|
705
|
+
group: "Epistemic Swarm",
|
|
706
|
+
palette: true,
|
|
707
|
+
slash: {
|
|
708
|
+
name: "es-trust"
|
|
709
|
+
},
|
|
710
|
+
run: trust
|
|
711
|
+
}, {
|
|
712
|
+
id: "es.resume",
|
|
713
|
+
title: "Epistemic Swarm: resume a halted factory",
|
|
714
|
+
group: "Epistemic Swarm",
|
|
715
|
+
palette: true,
|
|
716
|
+
slash: {
|
|
717
|
+
name: "es-resume"
|
|
718
|
+
},
|
|
719
|
+
run: resume
|
|
720
|
+
}, {
|
|
721
|
+
id: "es.status",
|
|
722
|
+
title: "Epistemic Swarm: factory status",
|
|
723
|
+
group: "Epistemic Swarm",
|
|
724
|
+
palette: true,
|
|
725
|
+
slash: {
|
|
726
|
+
name: "es-status"
|
|
727
|
+
},
|
|
728
|
+
run: status
|
|
729
|
+
}, {
|
|
730
|
+
id: "es.lsp.install",
|
|
731
|
+
title: "Epistemic Swarm: install a language server",
|
|
732
|
+
group: "Epistemic Swarm",
|
|
733
|
+
palette: true,
|
|
734
|
+
slash: {
|
|
735
|
+
name: "es-lsp-install",
|
|
736
|
+
arguments: true
|
|
737
|
+
},
|
|
738
|
+
run: lspInstall
|
|
739
|
+
}, {
|
|
740
|
+
id: "es.panel.factory",
|
|
741
|
+
title: "Epistemic Swarm: factory dashboard",
|
|
742
|
+
group: "Epistemic Swarm",
|
|
743
|
+
palette: true,
|
|
744
|
+
slash: {
|
|
745
|
+
name: "es-factory"
|
|
746
|
+
},
|
|
747
|
+
run: () => {
|
|
748
|
+
context.ui.panel.open("factory", {
|
|
749
|
+
presentation: "fullscreen"
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}, {
|
|
753
|
+
id: "es.panel.lsp",
|
|
754
|
+
title: "Epistemic Swarm: language server panel",
|
|
755
|
+
group: "Epistemic Swarm",
|
|
756
|
+
palette: true,
|
|
757
|
+
slash: {
|
|
758
|
+
name: "es-lsp-panel"
|
|
759
|
+
},
|
|
760
|
+
run: () => {
|
|
761
|
+
context.ui.panel.open("lsp", {
|
|
762
|
+
presentation: "fullscreen"
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}, {
|
|
766
|
+
id: "es.panel.hooks",
|
|
767
|
+
title: "Epistemic Swarm: hook inspector",
|
|
768
|
+
group: "Epistemic Swarm",
|
|
769
|
+
palette: true,
|
|
770
|
+
slash: {
|
|
771
|
+
name: "es-hooks"
|
|
772
|
+
},
|
|
773
|
+
run: () => {
|
|
774
|
+
context.ui.panel.open("hooks", {
|
|
775
|
+
presentation: "fullscreen"
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
}, {
|
|
779
|
+
id: "es.panel.brainstorm",
|
|
780
|
+
title: "Epistemic Swarm: brainstorm board",
|
|
781
|
+
group: "Epistemic Swarm",
|
|
782
|
+
palette: true,
|
|
783
|
+
slash: {
|
|
784
|
+
name: "es-brainstorm"
|
|
785
|
+
},
|
|
786
|
+
run: () => {
|
|
787
|
+
context.ui.panel.open("brainstorm", {
|
|
788
|
+
presentation: "fullscreen"
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
}]
|
|
792
|
+
}));
|
|
793
|
+
const stop = rpc.events.on("changed", (event) => toast(`Factory \u2192 ${event.data.stage}`));
|
|
794
|
+
return () => {
|
|
795
|
+
stopPanels();
|
|
796
|
+
stop();
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
export {
|
|
801
|
+
tui_default as default
|
|
802
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heretek-ai/epistemic-swarm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Epistemic Swarm: an AI build factory for OpenCode v2 — grill, research, spec, build, QA and release, gated mechanically.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"./tui": {
|
|
22
22
|
"types": "./tui.tsx",
|
|
23
|
-
"default": "./tui.
|
|
23
|
+
"default": "./dist/tui.js"
|
|
24
24
|
},
|
|
25
25
|
"./rpc": {
|
|
26
26
|
"types": "./rpc.ts",
|
|
@@ -32,21 +32,26 @@
|
|
|
32
32
|
"server.ts",
|
|
33
33
|
"tui.tsx",
|
|
34
34
|
"rpc.ts",
|
|
35
|
-
"src"
|
|
35
|
+
"src",
|
|
36
|
+
"dist"
|
|
36
37
|
],
|
|
37
38
|
"scripts": {
|
|
38
|
-
"build": "
|
|
39
|
+
"build": "bun script/build-tui.ts",
|
|
40
|
+
"prepublishOnly": "bun run build",
|
|
39
41
|
"test": "bun test"
|
|
40
42
|
},
|
|
41
43
|
"dependencies": {
|
|
42
|
-
"@heretek-ai/es-core": "1.0.
|
|
44
|
+
"@heretek-ai/es-core": "1.0.4",
|
|
43
45
|
"@opencode/plugin": "2.0.24"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
48
|
+
"@babel/core": "7.28.0",
|
|
49
|
+
"@babel/preset-typescript": "7.27.1",
|
|
46
50
|
"@heretek-ai/es-testkit": "1.0.0",
|
|
47
51
|
"@opencode/sdk": "2.0.24",
|
|
48
52
|
"@opentui/core": "0.5.14",
|
|
49
53
|
"@opentui/solid": "0.5.14",
|
|
54
|
+
"babel-preset-solid": "1.9.12",
|
|
50
55
|
"solid-js": "1.9.15"
|
|
51
56
|
},
|
|
52
57
|
"engines": {
|
package/src/panels.tsx
CHANGED
|
@@ -206,12 +206,18 @@ const PANELS = Object.entries(PANEL_COMPONENTS).map(([name, Panel]) => ({
|
|
|
206
206
|
|
|
207
207
|
/** Claim the session.panel slot once per panel; each claim filters by name. */
|
|
208
208
|
export function registerPanels(context: any, call: PanelCall, subscribe: PanelSubscribe): () => void {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
209
|
+
// Slot entry point moved across host releases: opencode 2.0.x exposes it
|
|
210
|
+
// as context.ui.slot (arity 1 object form or arity 2 name/render form),
|
|
211
|
+
// later hosts as bare context.slot. Without either, panels stay
|
|
212
|
+
// unregistered but setup must not throw (commands keep working).
|
|
213
|
+
const slotFn = typeof context.slot === "function" ? context.slot : context.ui?.slot
|
|
214
|
+
if (typeof slotFn !== "function") return () => {}
|
|
215
|
+
const disposers: Array<() => void> = []
|
|
216
|
+
for (const panel of PANELS) {
|
|
217
|
+
const render = (input: { name: string }) => (input.name === panel.name ? panel.render({ call, subscribe }) : null)
|
|
218
|
+
const dispose = slotFn.length <= 1 ? slotFn({ append: "session.panel", render }) : slotFn("session.panel", render)
|
|
219
|
+
if (typeof dispose === "function") disposers.push(dispose)
|
|
220
|
+
}
|
|
215
221
|
return () => {
|
|
216
222
|
for (const dispose of disposers) dispose()
|
|
217
223
|
}
|