@worca/ui 0.27.0 → 0.28.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/main.bundle.js +1599 -1502
- package/app/main.bundle.js.map +4 -4
- package/app/styles.css +230 -0
- package/package.json +3 -1
- package/server/app.js +47 -1
- package/server/dispatch-defaults.js +81 -0
- package/server/dispatch-events-aggregator.js +25 -19
- package/server/dispatch-migration.js +132 -0
- package/server/git-helpers.js +32 -0
- package/server/known-skills.json +31 -0
- package/server/known-tools.json +18 -0
- package/server/project-routes.js +7 -11
- package/server/settings-validator.js +83 -11
- package/server/watcher.js +2 -0
- package/server/worktrees-routes.js +4 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "name": "batch", "group": "Built-in" },
|
|
3
|
+
{ "name": "claude-api", "group": "Built-in" },
|
|
4
|
+
{ "name": "debug", "group": "Built-in" },
|
|
5
|
+
{ "name": "fewer-permission-prompts", "group": "Built-in" },
|
|
6
|
+
{ "name": "init", "group": "Built-in" },
|
|
7
|
+
{ "name": "loop", "group": "Built-in" },
|
|
8
|
+
{ "name": "review", "group": "Built-in" },
|
|
9
|
+
{ "name": "schedule", "group": "Built-in" },
|
|
10
|
+
{ "name": "security-review", "group": "Built-in" },
|
|
11
|
+
{ "name": "simplify", "group": "Built-in" },
|
|
12
|
+
{ "name": "update-config", "group": "Built-in" },
|
|
13
|
+
{ "name": "hookify:hookify", "group": "Plugin" },
|
|
14
|
+
{ "name": "hookify:configure", "group": "Plugin" },
|
|
15
|
+
{ "name": "hookify:list", "group": "Plugin" },
|
|
16
|
+
{ "name": "hookify:writing-rules", "group": "Plugin" },
|
|
17
|
+
{ "name": "feature-dev:feature-dev", "group": "Plugin" },
|
|
18
|
+
{ "name": "feature-dev:code-reviewer", "group": "Plugin" },
|
|
19
|
+
{ "name": "feature-dev:code-architect", "group": "Plugin" },
|
|
20
|
+
{ "name": "feature-dev:code-explorer", "group": "Plugin" },
|
|
21
|
+
{ "name": "claude-md-management:revise-claude-md", "group": "Plugin" },
|
|
22
|
+
{ "name": "claude-md-management:claude-md-improver", "group": "Plugin" },
|
|
23
|
+
{ "name": "worca-install", "group": "Worca" },
|
|
24
|
+
{ "name": "worca-rc", "group": "Worca" },
|
|
25
|
+
{ "name": "worca-release", "group": "Worca" },
|
|
26
|
+
{ "name": "worca-sync", "group": "Worca" },
|
|
27
|
+
{ "name": "worca-sync-pr", "group": "Worca" },
|
|
28
|
+
{ "name": "worca-sync-commit", "group": "Worca" },
|
|
29
|
+
{ "name": "worca-analyze", "group": "Worca" },
|
|
30
|
+
{ "name": "worca-agent-override", "group": "Worca" }
|
|
31
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "name": "Agent", "group": "Core" },
|
|
3
|
+
{ "name": "Bash", "group": "Core" },
|
|
4
|
+
{ "name": "Edit", "group": "Core" },
|
|
5
|
+
{ "name": "Glob", "group": "Core" },
|
|
6
|
+
{ "name": "Grep", "group": "Core" },
|
|
7
|
+
{ "name": "Read", "group": "Core" },
|
|
8
|
+
{ "name": "Write", "group": "Core" },
|
|
9
|
+
{ "name": "Skill", "group": "Core" },
|
|
10
|
+
{ "name": "WebFetch", "group": "Core" },
|
|
11
|
+
{ "name": "WebSearch", "group": "Core" },
|
|
12
|
+
{ "name": "NotebookEdit", "group": "Core" },
|
|
13
|
+
{ "name": "EnterPlanMode", "group": "Mode" },
|
|
14
|
+
{ "name": "EnterWorktree", "group": "Mode" },
|
|
15
|
+
{ "name": "TodoWrite", "group": "Task" },
|
|
16
|
+
{ "name": "TaskCreate", "group": "Task" },
|
|
17
|
+
{ "name": "TaskUpdate", "group": "Task" }
|
|
18
|
+
]
|
package/server/project-routes.js
CHANGED
|
@@ -23,7 +23,9 @@ import { actionAllowed } from '../app/utils/state-actions.js';
|
|
|
23
23
|
import { atomicWriteSync } from './atomic-write.js';
|
|
24
24
|
import { dbExists, getIssue, listIssues } from './beads-reader.js';
|
|
25
25
|
import { dispatchExternal } from './dispatch-external.js';
|
|
26
|
+
import { migrateDispatchGovernance } from './dispatch-migration.js';
|
|
26
27
|
import { ensureWebhookForUi } from './ensure-webhook.js';
|
|
28
|
+
import { getDefaultBranch } from './git-helpers.js';
|
|
27
29
|
import { extractAndStripGlobalKeys } from './global-keys.js';
|
|
28
30
|
import { LaunchLock } from './launch-lock.js';
|
|
29
31
|
import { createModelEnvRouter } from './model-env-routes.js';
|
|
@@ -361,7 +363,8 @@ export function createProjectScopedRoutes({
|
|
|
361
363
|
router.get('/runs', requireWorcaDir, (req, res) => {
|
|
362
364
|
try {
|
|
363
365
|
const runs = discoverRuns(req.project.worcaDir);
|
|
364
|
-
const
|
|
366
|
+
const default_branch = getDefaultBranch(req.project.projectRoot);
|
|
367
|
+
const response = { ok: true, runs, default_branch };
|
|
365
368
|
// Include settings so multi-project clients can use loop limits, etc.
|
|
366
369
|
const { settingsPath } = req.project;
|
|
367
370
|
if (settingsPath && existsSync(settingsPath)) {
|
|
@@ -515,22 +518,15 @@ export function createProjectScopedRoutes({
|
|
|
515
518
|
if (!base.worca || typeof base.worca !== 'object') base.worca = {};
|
|
516
519
|
base.worca = deepMerge(base.worca, body.worca);
|
|
517
520
|
|
|
518
|
-
if (
|
|
519
|
-
body.worca.governance &&
|
|
520
|
-
typeof body.worca.governance === 'object' &&
|
|
521
|
-
body.worca.governance.subagent_dispatch !== undefined &&
|
|
522
|
-
base.worca.governance &&
|
|
523
|
-
typeof base.worca.governance === 'object' &&
|
|
524
|
-
'dispatch' in base.worca.governance
|
|
525
|
-
) {
|
|
526
|
-
delete base.worca.governance.dispatch;
|
|
527
|
-
}
|
|
528
521
|
baseChanged = true;
|
|
529
522
|
}
|
|
530
523
|
|
|
531
524
|
// STEP 1: extract misplaced global keys + inert milestone keys
|
|
532
525
|
const autoMigrated = extractAndStripGlobalKeys(base);
|
|
533
526
|
|
|
527
|
+
// STEP 1a: migrate legacy subagent_dispatch → dispatch.subagents (W-054)
|
|
528
|
+
if (base.worca) migrateDispatchGovernance(base.worca);
|
|
529
|
+
|
|
534
530
|
// STEP 2: write extracted global keys to ~/.worca/settings.json
|
|
535
531
|
const globalSettingsPath = prefsDir
|
|
536
532
|
? join(prefsDir, 'settings.json')
|
|
@@ -396,19 +396,91 @@ export function validateSettingsPayload(body, options = {}) {
|
|
|
396
396
|
) {
|
|
397
397
|
details.push('governance.dispatch must be an object');
|
|
398
398
|
} else {
|
|
399
|
+
const DISPATCH_SECTIONS = ['tools', 'skills', 'subagents'];
|
|
399
400
|
for (const [key, val] of Object.entries(g.dispatch)) {
|
|
400
|
-
if (
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
401
|
+
if (DISPATCH_SECTIONS.includes(key)) {
|
|
402
|
+
// W-054 nested shape: dispatch.{tools,skills,subagents}
|
|
403
|
+
if (
|
|
404
|
+
typeof val !== 'object' ||
|
|
405
|
+
val === null ||
|
|
406
|
+
Array.isArray(val)
|
|
407
|
+
) {
|
|
408
|
+
details.push(`governance.dispatch.${key} must be an object`);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
for (const tierKey of ['always_disallowed', 'default_denied']) {
|
|
412
|
+
if (val[tierKey] === undefined) continue;
|
|
413
|
+
if (!Array.isArray(val[tierKey])) {
|
|
414
|
+
details.push(
|
|
415
|
+
`governance.dispatch.${key}.${tierKey} must be an array`,
|
|
416
|
+
);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
for (const entry of val[tierKey]) {
|
|
420
|
+
if (typeof entry !== 'string') {
|
|
421
|
+
details.push(
|
|
422
|
+
`governance.dispatch.${key}.${tierKey} entries must be strings`,
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (val.per_agent_allow !== undefined) {
|
|
428
|
+
if (
|
|
429
|
+
typeof val.per_agent_allow !== 'object' ||
|
|
430
|
+
val.per_agent_allow === null ||
|
|
431
|
+
Array.isArray(val.per_agent_allow)
|
|
432
|
+
) {
|
|
433
|
+
details.push(
|
|
434
|
+
`governance.dispatch.${key}.per_agent_allow must be an object`,
|
|
435
|
+
);
|
|
436
|
+
} else {
|
|
437
|
+
for (const [agent, allowList] of Object.entries(
|
|
438
|
+
val.per_agent_allow,
|
|
439
|
+
)) {
|
|
440
|
+
if (
|
|
441
|
+
agent !== '_defaults' &&
|
|
442
|
+
!VALID_AGENTS.includes(agent) &&
|
|
443
|
+
agent !== 'workspace_planner'
|
|
444
|
+
) {
|
|
445
|
+
details.push(
|
|
446
|
+
`governance.dispatch.${key}.per_agent_allow: unknown agent "${agent}"`,
|
|
447
|
+
);
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (!Array.isArray(allowList)) {
|
|
451
|
+
details.push(
|
|
452
|
+
`governance.dispatch.${key}.per_agent_allow.${agent} must be an array`,
|
|
453
|
+
);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
for (const entry of allowList) {
|
|
457
|
+
if (typeof entry !== 'string') {
|
|
458
|
+
details.push(
|
|
459
|
+
`governance.dispatch.${key}.per_agent_allow.${agent} entries must be strings`,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
} else if (
|
|
467
|
+
VALID_AGENTS.includes(key) ||
|
|
468
|
+
key === 'workspace_planner'
|
|
469
|
+
) {
|
|
470
|
+
// Pre-W-054 legacy flat shape — tolerated for migration.
|
|
471
|
+
if (!Array.isArray(val)) {
|
|
472
|
+
details.push(`Dispatch for "${key}" must be an array`);
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
for (const v of val) {
|
|
476
|
+
if (typeof v !== 'string') {
|
|
477
|
+
details.push(
|
|
478
|
+
`Dispatch entry for "${key}" must be a string`,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
411
481
|
}
|
|
482
|
+
} else {
|
|
483
|
+
details.push(`Unknown dispatch key: "${key}"`);
|
|
412
484
|
}
|
|
413
485
|
}
|
|
414
486
|
}
|
package/server/watcher.js
CHANGED
|
@@ -157,6 +157,7 @@ export function discoverRuns(worcaDir) {
|
|
|
157
157
|
...status,
|
|
158
158
|
worktree_worca_dir: join(reg.worktree_path, '.worca'),
|
|
159
159
|
is_worktree_run: true,
|
|
160
|
+
head_branch: reg.branch || null,
|
|
160
161
|
fleet_id: reg.fleet_id || null,
|
|
161
162
|
workspace_id: reg.workspace_id || null,
|
|
162
163
|
group_type: reg.group_type || null,
|
|
@@ -311,6 +312,7 @@ export async function discoverRunsAsync(worcaDir) {
|
|
|
311
312
|
...status,
|
|
312
313
|
worktree_worca_dir: join(reg.worktree_path, '.worca'),
|
|
313
314
|
is_worktree_run: true,
|
|
315
|
+
head_branch: reg.branch || null,
|
|
314
316
|
fleet_id: reg.fleet_id || null,
|
|
315
317
|
workspace_id: reg.workspace_id || null,
|
|
316
318
|
group_type: reg.group_type || null,
|
|
@@ -20,6 +20,7 @@ import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
20
20
|
import * as fsp from 'node:fs/promises';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { Router } from 'express';
|
|
23
|
+
import { getDefaultBranch } from './git-helpers.js';
|
|
23
24
|
import { pruneWorktrees, removeWorktree } from './worktree-ops.js';
|
|
24
25
|
|
|
25
26
|
const CLEANUP_CONCURRENCY = 4;
|
|
@@ -362,6 +363,7 @@ async function _listWorktrees(worcaDir) {
|
|
|
362
363
|
started_at: m.reg.started_at || null,
|
|
363
364
|
status: m.status,
|
|
364
365
|
removable: m.status !== 'running',
|
|
366
|
+
target_branch: m.reg.target_branch || null,
|
|
365
367
|
fleet_id: m.reg.fleet_id || null,
|
|
366
368
|
workspace_id: m.reg.workspace_id || null,
|
|
367
369
|
group_type: m.reg.group_type || null,
|
|
@@ -399,9 +401,11 @@ export function createWorktreesRouter() {
|
|
|
399
401
|
}
|
|
400
402
|
try {
|
|
401
403
|
const worktrees = await _listWorktrees(worcaDir);
|
|
404
|
+
const default_branch = getDefaultBranch(req.project?.projectRoot);
|
|
402
405
|
res.json({
|
|
403
406
|
ok: true,
|
|
404
407
|
worktrees,
|
|
408
|
+
default_branch,
|
|
405
409
|
// Documents the semantics shift in `disk_bytes` (project files only).
|
|
406
410
|
// Clients can render this as a caveat next to disk totals.
|
|
407
411
|
disk_walk_skip_dirs: [...WALK_SKIP_DIRS],
|