@worca/ui 0.5.0 → 0.7.0
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/main.bundle.js +304 -300
- package/app/main.bundle.js.map +4 -4
- package/app/styles.css +10 -14
- package/package.json +1 -1
- package/server/versions.js +68 -10
- package/server/ws-message-router.js +97 -60
package/app/styles.css
CHANGED
|
@@ -1504,7 +1504,8 @@ sl-details.log-history-panel::part(content) {
|
|
|
1504
1504
|
font-size: 14px;
|
|
1505
1505
|
font-weight: 600;
|
|
1506
1506
|
color: var(--fg);
|
|
1507
|
-
text-transform:
|
|
1507
|
+
text-transform: uppercase;
|
|
1508
|
+
letter-spacing: 0.03em;
|
|
1508
1509
|
}
|
|
1509
1510
|
|
|
1510
1511
|
.settings-card-body {
|
|
@@ -1547,8 +1548,9 @@ sl-details.log-history-panel::part(content) {
|
|
|
1547
1548
|
|
|
1548
1549
|
.settings-switch-row {
|
|
1549
1550
|
display: flex;
|
|
1550
|
-
|
|
1551
|
-
|
|
1551
|
+
flex-direction: column;
|
|
1552
|
+
align-items: flex-start;
|
|
1553
|
+
gap: 4px;
|
|
1552
1554
|
padding: 6px 0;
|
|
1553
1555
|
}
|
|
1554
1556
|
|
|
@@ -1576,19 +1578,10 @@ sl-details.log-history-panel::part(content) {
|
|
|
1576
1578
|
align-items: center;
|
|
1577
1579
|
gap: 4px;
|
|
1578
1580
|
padding: 16px 0;
|
|
1579
|
-
|
|
1581
|
+
flex-wrap: wrap;
|
|
1580
1582
|
}
|
|
1581
1583
|
|
|
1582
1584
|
.pipeline-stage-node {
|
|
1583
|
-
display: flex;
|
|
1584
|
-
flex-direction: column;
|
|
1585
|
-
align-items: center;
|
|
1586
|
-
gap: 4px;
|
|
1587
|
-
padding: 10px 14px;
|
|
1588
|
-
border: 1px solid var(--border-subtle);
|
|
1589
|
-
border-radius: var(--radius);
|
|
1590
|
-
background: var(--bg-secondary);
|
|
1591
|
-
min-width: 140px;
|
|
1592
1585
|
transition: opacity 0.2s, border-color 0.2s, background 0.2s;
|
|
1593
1586
|
}
|
|
1594
1587
|
|
|
@@ -4015,7 +4008,7 @@ sl-details.learnings-panel::part(content) {
|
|
|
4015
4008
|
.version-row { display: flex; align-items: center; padding: 4px 0; gap: 8px; }
|
|
4016
4009
|
.version-row-label { font-size: 12px; color: var(--muted); white-space: nowrap; min-width: 64px; }
|
|
4017
4010
|
.version-row-value { font-size: 13px; font-weight: 500; color: var(--fg); font-family: var(--sl-font-mono); white-space: nowrap; margin-left: auto; display: flex; align-items: center; gap: 6px; }
|
|
4018
|
-
.settings-card-header > sl-badge { margin-left: auto; }
|
|
4011
|
+
.settings-card-header > sl-badge, .settings-card-header > sl-switch { margin-left: auto; }
|
|
4019
4012
|
.version-title-exact { text-transform: none; }
|
|
4020
4013
|
.version-copy-btn {
|
|
4021
4014
|
background: none; border: 1px solid var(--border); border-radius: 4px;
|
|
@@ -4025,6 +4018,9 @@ sl-details.learnings-panel::part(content) {
|
|
|
4025
4018
|
}
|
|
4026
4019
|
.version-copy-btn:hover { background: var(--bg-tertiary); color: var(--fg); }
|
|
4027
4020
|
.version-copy-icon { display: inline-flex; align-items: center; min-width: 12px; }
|
|
4021
|
+
.install-path-row { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding: 8px 12px; background: var(--bg-secondary); border-radius: var(--radius); border: 1px solid var(--border-subtle); }
|
|
4022
|
+
.install-path-label { font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
4023
|
+
.install-path-value { font-size: 12px; color: var(--fg); font-family: var(--sl-font-mono); word-break: break-all; }
|
|
4028
4024
|
.version-refresh { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
|
|
4029
4025
|
.version-refresh-hint { font-size: 11px; color: var(--muted); }
|
|
4030
4026
|
.project-worca-version { font-size: 11px; color: var(--muted); font-family: var(--sl-font-mono); margin-top: 2px; }
|
package/package.json
CHANGED
package/server/versions.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// server/versions.js — version fetching + caching for worca-cc and @worca/ui
|
|
2
2
|
import { execFileSync } from 'node:child_process';
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { readPreferences } from './preferences.js';
|
|
6
7
|
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const INSTALL_DIR = dirname(__dirname); // worca-ui root
|
|
10
|
+
|
|
7
11
|
/** Cache: { data, timestamp } */
|
|
8
12
|
let _cache = null;
|
|
9
13
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
@@ -66,18 +70,19 @@ export async function fetchNpmVersions(packageName) {
|
|
|
66
70
|
}
|
|
67
71
|
if (!res.ok) return nullResult;
|
|
68
72
|
const data = await res.json();
|
|
69
|
-
const
|
|
73
|
+
const distLatest = data['dist-tags']?.latest || null;
|
|
70
74
|
let latestRc = data['dist-tags']?.rc || null;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
|
|
76
|
+
// Scan all versions for the highest stable and highest RC
|
|
77
|
+
let bestStable = null;
|
|
78
|
+
let bestRc = null;
|
|
79
|
+
let bestRcNum = -1;
|
|
80
|
+
if (data.versions) {
|
|
75
81
|
for (const ver of Object.keys(data.versions)) {
|
|
76
82
|
const rcMatch = ver.match(/^(.+)-rc\.(\d+)$/);
|
|
77
83
|
if (rcMatch) {
|
|
78
84
|
const rcNum = parseInt(rcMatch[2], 10);
|
|
79
85
|
const base = rcMatch[1];
|
|
80
|
-
// Compare base+rcNum to find the highest RC
|
|
81
86
|
if (
|
|
82
87
|
!bestRc ||
|
|
83
88
|
compareVersions(base, bestRc.base) > 0 ||
|
|
@@ -86,10 +91,21 @@ export async function fetchNpmVersions(packageName) {
|
|
|
86
91
|
bestRc = { base, full: ver };
|
|
87
92
|
bestRcNum = rcNum;
|
|
88
93
|
}
|
|
94
|
+
} else if (!ver.includes('-')) {
|
|
95
|
+
// Stable version (no pre-release suffix)
|
|
96
|
+
if (!bestStable || compareVersions(ver, bestStable) > 0) {
|
|
97
|
+
bestStable = ver;
|
|
98
|
+
}
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
|
-
if (bestRc) latestRc = bestRc.full;
|
|
92
101
|
}
|
|
102
|
+
if (!latestRc && bestRc) latestRc = bestRc.full;
|
|
103
|
+
|
|
104
|
+
// Use dist-tags latest only if it's a stable version, otherwise fall back
|
|
105
|
+
// to the highest stable version found in the registry
|
|
106
|
+
const isRc = distLatest?.includes('-');
|
|
107
|
+
const latest = isRc ? bestStable || distLatest : distLatest;
|
|
108
|
+
|
|
93
109
|
return { latest, latestRc };
|
|
94
110
|
} catch {
|
|
95
111
|
return nullResult;
|
|
@@ -144,13 +160,48 @@ export async function fetchPyPIVersions(packageName) {
|
|
|
144
160
|
}
|
|
145
161
|
}
|
|
146
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Get git describe info for a repo: commits ahead of tag + dirty state.
|
|
165
|
+
* @param {string} repoPath
|
|
166
|
+
* @param {string} tagPrefix - e.g. 'worca-cc-v' or 'worca-ui-v'
|
|
167
|
+
* @returns {{ ahead: number, dirty: boolean } | null}
|
|
168
|
+
*/
|
|
169
|
+
function getGitDevStatus(repoPath, tagPrefix) {
|
|
170
|
+
try {
|
|
171
|
+
const desc = execFileSync(
|
|
172
|
+
'git',
|
|
173
|
+
['describe', '--tags', '--long', '--match', `${tagPrefix}*`],
|
|
174
|
+
{ cwd: repoPath, encoding: 'utf8', timeout: 3000 },
|
|
175
|
+
).trim();
|
|
176
|
+
// Format: tagPrefix0.10.0-3-gabcdef
|
|
177
|
+
const m = desc.match(/-(\d+)-g[0-9a-f]+$/);
|
|
178
|
+
const ahead = m ? parseInt(m[1], 10) : 0;
|
|
179
|
+
|
|
180
|
+
const status = execFileSync('git', ['status', '--porcelain'], {
|
|
181
|
+
cwd: repoPath,
|
|
182
|
+
encoding: 'utf8',
|
|
183
|
+
timeout: 3000,
|
|
184
|
+
}).trim();
|
|
185
|
+
const dirty = status.length > 0;
|
|
186
|
+
|
|
187
|
+
return { ahead, dirty };
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
147
193
|
/**
|
|
148
194
|
* Read versions from local dev path.
|
|
149
195
|
* @param {string} sourceRepo - path to local worca-cc repo
|
|
150
|
-
* @returns {{ worcaCc: string|null, worcaUi: string|null }}
|
|
196
|
+
* @returns {{ worcaCc: string|null, worcaUi: string|null, worcaCcDev: object|null, worcaUiDev: object|null }}
|
|
151
197
|
*/
|
|
152
198
|
export function getDevPathVersions(sourceRepo) {
|
|
153
|
-
const result = {
|
|
199
|
+
const result = {
|
|
200
|
+
worcaCc: null,
|
|
201
|
+
worcaUi: null,
|
|
202
|
+
worcaCcDev: null,
|
|
203
|
+
worcaUiDev: null,
|
|
204
|
+
};
|
|
154
205
|
if (!sourceRepo) return result;
|
|
155
206
|
try {
|
|
156
207
|
const pyproject = readFileSync(join(sourceRepo, 'pyproject.toml'), 'utf8');
|
|
@@ -167,6 +218,10 @@ export function getDevPathVersions(sourceRepo) {
|
|
|
167
218
|
} catch {
|
|
168
219
|
// package.json not found or unreadable
|
|
169
220
|
}
|
|
221
|
+
|
|
222
|
+
result.worcaCcDev = getGitDevStatus(sourceRepo, 'worca-cc-v');
|
|
223
|
+
result.worcaUiDev = getGitDevStatus(sourceRepo, 'worca-ui-v');
|
|
224
|
+
|
|
170
225
|
return result;
|
|
171
226
|
}
|
|
172
227
|
|
|
@@ -246,9 +301,12 @@ export async function getVersionInfo({ prefsPath, worcaVersion, force } = {}) {
|
|
|
246
301
|
path: sourceRepo,
|
|
247
302
|
worcaCc: devVersions.worcaCc,
|
|
248
303
|
worcaUi: devVersions.worcaUi,
|
|
304
|
+
worcaCcDev: devVersions.worcaCcDev,
|
|
305
|
+
worcaUiDev: devVersions.worcaUiDev,
|
|
249
306
|
}
|
|
250
307
|
: null,
|
|
251
308
|
activeWorcaCc: devVersions?.worcaCc || installedCc,
|
|
309
|
+
installDir: INSTALL_DIR,
|
|
252
310
|
cachedAt: new Date().toISOString(),
|
|
253
311
|
};
|
|
254
312
|
|
|
@@ -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
|
),
|