@worca/ui 0.3.1-rc.5 → 0.5.0-rc.1
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/app/favicon-global.svg +5 -0
- package/app/favicon-project.svg +5 -0
- package/app/index.html +1 -0
- package/app/main.bundle.js +650 -615
- package/app/main.bundle.js.map +3 -3
- package/app/styles.css +24 -5
- package/package.json +2 -1
- package/server/app.js +10 -0
- package/server/ws-beads-watcher.js +1 -1
- package/server/ws-message-router.js +103 -60
package/app/styles.css
CHANGED
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
--status-resuming: #3b82f6;
|
|
26
26
|
--status-skipped: #94a3b8;
|
|
27
27
|
/* legacy */
|
|
28
|
-
--status-in-progress: #
|
|
28
|
+
--status-in-progress: #3b82f6;
|
|
29
29
|
--status-error: #ef4444;
|
|
30
|
-
--status-blocked: #
|
|
30
|
+
--status-blocked: #f59e0b;
|
|
31
31
|
|
|
32
32
|
/* Log viewer */
|
|
33
33
|
--log-bg: #0f172a;
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
/* Shoelace token mapping */
|
|
48
48
|
--sl-color-primary-600: var(--accent);
|
|
49
49
|
--sl-color-success-600: var(--status-completed);
|
|
50
|
-
--sl-color-warning-600: var(--status-
|
|
50
|
+
--sl-color-warning-600: var(--status-blocked);
|
|
51
51
|
--sl-color-danger-600: var(--status-error);
|
|
52
52
|
--sl-color-neutral-600: var(--status-pending);
|
|
53
53
|
--sl-border-radius-medium: var(--radius);
|
|
@@ -577,11 +577,11 @@ h1, h2, h3, h4, h5, h6 {
|
|
|
577
577
|
}
|
|
578
578
|
|
|
579
579
|
.timing-bar-thinking {
|
|
580
|
-
background: var(--status-blocked, #
|
|
580
|
+
background: var(--status-blocked, #f59e0b);
|
|
581
581
|
}
|
|
582
582
|
|
|
583
583
|
.timing-bar-tools {
|
|
584
|
-
background: var(--status-in-progress, #
|
|
584
|
+
background: var(--status-in-progress, #3b82f6);
|
|
585
585
|
}
|
|
586
586
|
|
|
587
587
|
.timing-bar-rest {
|
|
@@ -2323,6 +2323,21 @@ sl-option.template-grouped::part(suffix) {
|
|
|
2323
2323
|
padding: 0 0 4px;
|
|
2324
2324
|
}
|
|
2325
2325
|
|
|
2326
|
+
/* Fix contrast on selected/current option description */
|
|
2327
|
+
sl-option.template-grouped[aria-selected="true"]::part(suffix),
|
|
2328
|
+
sl-option.template-grouped:focus::part(suffix) {
|
|
2329
|
+
color: inherit;
|
|
2330
|
+
opacity: 0.85;
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
/* Selected template description below dropdown */
|
|
2334
|
+
.template-description {
|
|
2335
|
+
font-size: 12px;
|
|
2336
|
+
color: var(--muted);
|
|
2337
|
+
margin-top: 6px;
|
|
2338
|
+
line-height: 1.5;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2326
2341
|
/* Plan file autocomplete */
|
|
2327
2342
|
.plan-autocomplete {
|
|
2328
2343
|
position: relative;
|
|
@@ -2598,6 +2613,10 @@ sl-option.template-grouped::part(suffix) {
|
|
|
2598
2613
|
border-style: dashed;
|
|
2599
2614
|
}
|
|
2600
2615
|
|
|
2616
|
+
.beads-kanban-card--in_progress {
|
|
2617
|
+
border-color: var(--status-in-progress);
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2601
2620
|
.beads-kanban-card-header {
|
|
2602
2621
|
display: flex;
|
|
2603
2622
|
align-items: center;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@worca/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-rc.1",
|
|
4
4
|
"description": "Pipeline monitoring UI for worca-cc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sinisha Djukic",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"server/*.js",
|
|
27
27
|
"!server/*.test.js",
|
|
28
28
|
"!server/test/",
|
|
29
|
+
"app/favicon-*.svg",
|
|
29
30
|
"app/index.html",
|
|
30
31
|
"app/main.bundle.js",
|
|
31
32
|
"app/main.bundle.js.map",
|
package/server/app.js
CHANGED
|
@@ -427,6 +427,16 @@ export function createApp(options = {}) {
|
|
|
427
427
|
);
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
// ─── Dynamic favicon ──────────────────────────────────────────────────
|
|
431
|
+
// Serve mode-specific favicon before express.static so it takes precedence.
|
|
432
|
+
app.get('/favicon.svg', (_req, res) => {
|
|
433
|
+
const faviconFile = projectRoot
|
|
434
|
+
? 'favicon-project.svg'
|
|
435
|
+
: 'favicon-global.svg';
|
|
436
|
+
res.setHeader('Content-Type', 'image/svg+xml');
|
|
437
|
+
res.sendFile(faviconFile, { root: appDir });
|
|
438
|
+
});
|
|
439
|
+
|
|
430
440
|
app.use(express.static(appDir));
|
|
431
441
|
app.get('/{*splat}', (_req, res) => {
|
|
432
442
|
res.sendFile('index.html', { root: appDir });
|
|
@@ -8,7 +8,7 @@ import { existsSync, watch } from 'node:fs';
|
|
|
8
8
|
import { join, resolve } from 'node:path';
|
|
9
9
|
import { listIssues } from './beads-reader.js';
|
|
10
10
|
|
|
11
|
-
const BEADS_DEBOUNCE_MS =
|
|
11
|
+
const BEADS_DEBOUNCE_MS = 500;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @param {{ worcaDir: string, broadcaster: { broadcast: Function }, projectId?: string }} deps
|
|
@@ -35,24 +35,6 @@ import {
|
|
|
35
35
|
import { readSettings } from './settings-reader.js';
|
|
36
36
|
import { discoverRuns } from './watcher.js';
|
|
37
37
|
|
|
38
|
-
// Legacy fallback prefixes — only used when status.json lacks a stored prompt
|
|
39
|
-
const STAGE_PROMPT_PREFIX = {
|
|
40
|
-
plan: 'Create a detailed implementation plan for the following work request. Write the plan to the designated plan file.\n\nWork request: ',
|
|
41
|
-
coordinate:
|
|
42
|
-
'Decompose the following work request into Beads tasks with dependencies. Do NOT implement anything — only create tasks using `bd create`.\n\nWork request: ',
|
|
43
|
-
implement:
|
|
44
|
-
'Implement the code changes described in the work request. Follow the plan and complete the tasks assigned to you.\n\nWork request: ',
|
|
45
|
-
test: 'Review and test the implementation for the following work request. Run tests and report results. Do NOT modify code.\n\nWork request: ',
|
|
46
|
-
review:
|
|
47
|
-
'Review the code changes for the following work request. Check for correctness, style, and adherence to the plan. Do NOT modify code.\n\nWork request: ',
|
|
48
|
-
pr: 'Create a pull request for the following work request. Summarize the changes and ensure the commit history is clean.\n\nWork request: ',
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
function _buildStagePrompt(stage, rawPrompt) {
|
|
52
|
-
const prefix = STAGE_PROMPT_PREFIX[stage];
|
|
53
|
-
return prefix ? prefix + rawPrompt : rawPrompt;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
38
|
/**
|
|
57
39
|
* @param {{
|
|
58
40
|
* watcherSets: Map<string, import('./watcher-set.js').WatcherSet>,
|
|
@@ -174,6 +156,7 @@ export function createMessageRouter({
|
|
|
174
156
|
return;
|
|
175
157
|
}
|
|
176
158
|
const agentName = run.stages?.[stage]?.agent || stage;
|
|
159
|
+
const effectiveRunId = run.run_id || runId;
|
|
177
160
|
|
|
178
161
|
const iterations = run.stages?.[stage]?.iterations || [];
|
|
179
162
|
const iterationPrompts = iterations.map((iter, idx) => {
|
|
@@ -181,62 +164,116 @@ export function createMessageRouter({
|
|
|
181
164
|
return { iteration: iter.number ?? idx, prompt };
|
|
182
165
|
});
|
|
183
166
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let promptSource;
|
|
187
|
-
if (storedPrompt) {
|
|
188
|
-
fallbackPrompt = storedPrompt;
|
|
189
|
-
promptSource = 'actual';
|
|
190
|
-
} else {
|
|
191
|
-
const rawPrompt =
|
|
192
|
-
run.work_request?.description || run.work_request?.title || '';
|
|
193
|
-
fallbackPrompt = _buildStagePrompt(stage, rawPrompt);
|
|
194
|
-
promptSource = 'reconstructed';
|
|
195
|
-
}
|
|
196
|
-
|
|
167
|
+
// User message (-p) — stored in status.json
|
|
168
|
+
const userPrompt = run.stages?.[stage]?.prompt || null;
|
|
197
169
|
const hasIterationPrompts = iterationPrompts.some(
|
|
198
170
|
(ip) => ip.prompt != null,
|
|
199
171
|
);
|
|
200
|
-
if (!hasIterationPrompts) {
|
|
172
|
+
if (!hasIterationPrompts && userPrompt) {
|
|
201
173
|
for (const ip of iterationPrompts) {
|
|
202
|
-
ip.prompt =
|
|
174
|
+
ip.prompt = userPrompt;
|
|
203
175
|
}
|
|
204
176
|
}
|
|
205
177
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
178
|
+
// Resolved agent prompt — the full document the agent actually received.
|
|
179
|
+
// Prefer per-iteration resolved files (W-037+), fall back to the
|
|
180
|
+
// unresolved agent template for pre-W-037 runs.
|
|
181
|
+
let resolvedPrompt = null;
|
|
182
|
+
const resolvedIterationPrompts = [];
|
|
183
|
+
|
|
184
|
+
// Collect per-iteration resolved files
|
|
185
|
+
// Filename format: {stage}-{agent}-iter-{N}.md (W-037+)
|
|
186
|
+
// Fallback: {agent}-iter-{N}.md (early W-037 runs)
|
|
187
|
+
for (const iter of iterations) {
|
|
188
|
+
const iterNum = iter.number ?? 0;
|
|
189
|
+
const resolvedCandidates = [
|
|
190
|
+
join(
|
|
191
|
+
proj.worcaDir,
|
|
192
|
+
'runs',
|
|
193
|
+
effectiveRunId,
|
|
194
|
+
'agents',
|
|
195
|
+
'resolved',
|
|
196
|
+
`${stage}-${agentName}-iter-${iterNum}.md`,
|
|
197
|
+
),
|
|
198
|
+
join(
|
|
199
|
+
proj.worcaDir,
|
|
200
|
+
'results',
|
|
201
|
+
effectiveRunId,
|
|
202
|
+
'agents',
|
|
203
|
+
'resolved',
|
|
204
|
+
`${stage}-${agentName}-iter-${iterNum}.md`,
|
|
205
|
+
),
|
|
206
|
+
// Fallback for early W-037 runs without stage prefix
|
|
207
|
+
join(
|
|
208
|
+
proj.worcaDir,
|
|
209
|
+
'runs',
|
|
210
|
+
effectiveRunId,
|
|
211
|
+
'agents',
|
|
212
|
+
'resolved',
|
|
213
|
+
`${agentName}-iter-${iterNum}.md`,
|
|
214
|
+
),
|
|
215
|
+
join(
|
|
216
|
+
proj.worcaDir,
|
|
217
|
+
'results',
|
|
218
|
+
effectiveRunId,
|
|
219
|
+
'agents',
|
|
220
|
+
'resolved',
|
|
221
|
+
`${agentName}-iter-${iterNum}.md`,
|
|
222
|
+
),
|
|
223
|
+
];
|
|
224
|
+
let content = null;
|
|
225
|
+
for (const p of resolvedCandidates) {
|
|
226
|
+
if (existsSync(p)) {
|
|
227
|
+
try {
|
|
228
|
+
content = readFileSync(p, 'utf8');
|
|
229
|
+
} catch {
|
|
230
|
+
/* ignore */
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
resolvedIterationPrompts.push({ iteration: iterNum, prompt: content });
|
|
236
|
+
if (!resolvedPrompt && content) resolvedPrompt = content;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Fall back to unresolved agent template (pre-W-037 runs)
|
|
240
|
+
if (!resolvedPrompt) {
|
|
241
|
+
const templateCandidates = [
|
|
242
|
+
join(
|
|
243
|
+
proj.worcaDir,
|
|
244
|
+
'runs',
|
|
245
|
+
effectiveRunId,
|
|
246
|
+
'agents',
|
|
247
|
+
`${agentName}.md`,
|
|
248
|
+
),
|
|
249
|
+
join(
|
|
250
|
+
proj.worcaDir,
|
|
251
|
+
'results',
|
|
252
|
+
effectiveRunId,
|
|
253
|
+
'agents',
|
|
254
|
+
`${agentName}.md`,
|
|
255
|
+
),
|
|
256
|
+
];
|
|
257
|
+
for (const p of templateCandidates) {
|
|
258
|
+
if (existsSync(p)) {
|
|
259
|
+
try {
|
|
260
|
+
resolvedPrompt = readFileSync(p, 'utf8');
|
|
261
|
+
} catch {
|
|
262
|
+
/* ignore */
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
229
265
|
}
|
|
230
|
-
break;
|
|
231
266
|
}
|
|
232
267
|
}
|
|
268
|
+
|
|
233
269
|
ws.send(
|
|
234
270
|
JSON.stringify(
|
|
235
271
|
makeOk(req, {
|
|
236
|
-
agentInstructions,
|
|
237
|
-
userPrompt
|
|
272
|
+
agentInstructions: resolvedPrompt,
|
|
273
|
+
userPrompt,
|
|
238
274
|
iterationPrompts,
|
|
239
|
-
|
|
275
|
+
resolvedIterationPrompts,
|
|
276
|
+
promptSource: userPrompt ? 'actual' : 'none',
|
|
240
277
|
agent: agentName,
|
|
241
278
|
}),
|
|
242
279
|
),
|
|
@@ -614,6 +651,12 @@ export function createMessageRouter({
|
|
|
614
651
|
return;
|
|
615
652
|
}
|
|
616
653
|
const issues = await listIssuesByLabel(beadsDbPath, `run:${runId}`);
|
|
654
|
+
console.log(
|
|
655
|
+
'[list-beads-by-run] runId=%s count=%d statuses=%o',
|
|
656
|
+
runId,
|
|
657
|
+
issues.length,
|
|
658
|
+
issues.map((i) => i.status),
|
|
659
|
+
);
|
|
617
660
|
ws.send(JSON.stringify(makeOk(req, { issues, runId })));
|
|
618
661
|
return;
|
|
619
662
|
}
|