@h-rig/core 0.0.6-alpha.12 → 0.0.6-alpha.121

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.
@@ -0,0 +1,18 @@
1
+ import { RigConfig } from "@rig/contracts";
2
+ /**
3
+ * Author-facing config input: `plugins` and `workspace` are optional and
4
+ * default to a plugin-less worktree setup, so the minimal documented config
5
+ * (project + taskSource) works out of the box.
6
+ */
7
+ export type RigConfigInput = Omit<RigConfig, "plugins" | "workspace"> & {
8
+ plugins?: RigConfig["plugins"];
9
+ workspace?: RigConfig["workspace"];
10
+ };
11
+ /** Fill optional top-level sections before Schema decode (which requires them). */
12
+ export declare function applyConfigDefaults(raw: unknown): unknown;
13
+ /**
14
+ * Validate a Rig config and decode through Schema, preserving each plugin's
15
+ * `__runtime` channel. Schema decode strips unknown fields, so we capture
16
+ * the runtime objects before decoding and re-attach them after.
17
+ */
18
+ export declare function defineConfig(cfg: RigConfigInput): RigConfig;
@@ -2,6 +2,23 @@
2
2
  // packages/core/src/define-config.ts
3
3
  import { Schema } from "effect";
4
4
  import { RigConfig } from "@rig/contracts";
5
+ function normalizeWorkspaceConfig(raw) {
6
+ const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : { mainRepo: "." };
7
+ workspace.checkout = workspace.checkout ?? workspace.isolation ?? "worktree";
8
+ workspace.isolation = workspace.isolation ?? workspace.checkout;
9
+ workspace.sandbox = workspace.sandbox ?? "enforce";
10
+ return workspace;
11
+ }
12
+ function applyConfigDefaults(raw) {
13
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
14
+ return raw;
15
+ const record = raw;
16
+ return {
17
+ ...record,
18
+ plugins: Array.isArray(record.plugins) ? record.plugins : [],
19
+ workspace: normalizeWorkspaceConfig(record.workspace)
20
+ };
21
+ }
5
22
  function defineConfig(cfg) {
6
23
  const runtimeByName = new Map;
7
24
  const plugins = cfg.plugins ?? [];
@@ -10,7 +27,7 @@ function defineConfig(cfg) {
10
27
  runtimeByName.set(plugin.name, plugin.__runtime);
11
28
  }
12
29
  }
13
- const decoded = Schema.decodeUnknownSync(RigConfig)(cfg);
30
+ const decoded = Schema.decodeUnknownSync(RigConfig)(applyConfigDefaults(cfg));
14
31
  const decodedPlugins = decoded.plugins.map((p) => {
15
32
  const runtime = runtimeByName.get(p.name);
16
33
  if (!runtime)
@@ -20,5 +37,6 @@ function defineConfig(cfg) {
20
37
  return { ...decoded, plugins: decodedPlugins };
21
38
  }
22
39
  export {
23
- defineConfig
40
+ defineConfig,
41
+ applyConfigDefaults
24
42
  };
@@ -0,0 +1,9 @@
1
+ import { RigPlugin } from "@rig/contracts";
2
+ import type { RigPluginRuntime, RigPluginWithRuntime } from "./plugin-runtime";
3
+ /**
4
+ * Define a Rig plugin. The first argument is the schema-validated metadata
5
+ * (id strings, version, contributions). The optional second argument carries
6
+ * executable bits (validator `run` functions, etc.) that Schema cannot
7
+ * represent — they travel alongside the validated metadata as `__runtime`.
8
+ */
9
+ export declare function definePlugin(meta: RigPlugin, runtime?: RigPluginRuntime): RigPluginWithRuntime;
@@ -43,6 +43,23 @@ function definePlugin(meta, runtime) {
43
43
  }
44
44
  }
45
45
  }
46
+ const declaredHooks = new Map((validated.contributes?.hooks ?? []).map((h) => [h.id, h]));
47
+ for (const hookId of Object.keys(runtime.hooks ?? {})) {
48
+ const metadata = declaredHooks.get(hookId);
49
+ if (!metadata) {
50
+ throw new Error(`definePlugin(${validated.name}): typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
51
+ }
52
+ if (metadata.command) {
53
+ throw new Error(`definePlugin(${validated.name}): hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
54
+ }
55
+ }
56
+ if (runtime.hooks) {
57
+ for (const h of declaredHooks.values()) {
58
+ if (!runtime.hooks[h.id] && !h.command) {
59
+ throw new Error(`definePlugin(${validated.name}): hook metadata "${h.id}" has no implementation (typed function or command)`);
60
+ }
61
+ }
62
+ }
46
63
  return { ...validated, __runtime: runtime };
47
64
  }
48
65
  export {
@@ -0,0 +1,12 @@
1
+ import { EngineEvent, EngineReadModel, QueueEntry, RunSummary, TaskSummary } from "@rig/contracts";
2
+ type EngineApplyStatus = "applied" | "ignored" | "requires-resync";
3
+ export type ApplyStatus = EngineApplyStatus;
4
+ export interface EngineEventApplyResult {
5
+ readonly status: EngineApplyStatus;
6
+ readonly snapshot: EngineReadModel;
7
+ }
8
+ export declare function mapTaskStatusFromRunStatus(status: RunSummary["status"], fallback: TaskSummary["status"]): TaskSummary["status"];
9
+ export declare function applyEngineEvent(snapshot: EngineReadModel, event: EngineEvent): EngineEventApplyResult;
10
+ export declare function applyEngineEvents(snapshot: EngineReadModel, events: ReadonlyArray<EngineEvent>): EngineEventApplyResult;
11
+ export declare function pruneQueueEntries(snapshot: EngineReadModel): QueueEntry[];
12
+ export {};
@@ -149,7 +149,7 @@ function mapLegacySessionStatusToRunStatus(status) {
149
149
  case "error":
150
150
  return "failed";
151
151
  case "stopped":
152
- return "cancelled";
152
+ return "stopped";
153
153
  default:
154
154
  return "created";
155
155
  }
@@ -185,12 +185,15 @@ function mapTaskStatusFromRunStatus(status, fallback) {
185
185
  case "paused":
186
186
  return "in_progress";
187
187
  case "reviewing":
188
+ case "closing-out":
188
189
  return "under_review";
190
+ case "needs-attention":
191
+ return "blocked";
189
192
  case "completed":
190
193
  return "completed";
191
194
  case "failed":
192
195
  return "ready";
193
- case "cancelled":
196
+ case "stopped":
194
197
  return "cancelled";
195
198
  }
196
199
  }
@@ -1040,7 +1043,7 @@ function applyEngineEvent(snapshot, event) {
1040
1043
  ...base,
1041
1044
  runs: patchById(base.runs, payload.runId, (run) => ({
1042
1045
  ...run,
1043
- status: "cancelled",
1046
+ status: "stopped",
1044
1047
  updatedAt: payload.createdAt,
1045
1048
  completedAt: payload.createdAt
1046
1049
  }))
@@ -0,0 +1,12 @@
1
+ export declare const RIG_CORE_PACKAGE = "@rig/core";
2
+ export { definePlugin } from "./define-plugin";
3
+ export { defineConfig } from "./define-config";
4
+ export { createPluginHost } from "./plugin-host";
5
+ export type { PluginHost } from "./plugin-host";
6
+ export { buildRigInitConfigSource, type RigInitConfigInput, } from "./rig-init-builder";
7
+ export type { RegisteredValidator, RigPluginRuntime, RigPluginWithRuntime, TaskSourceFactoryContext, TaskSourceFactoryEntry, ValidatorContext, ValidatorResult, } from "./plugin-runtime";
8
+ export { applyEngineEvent, applyEngineEvents, applyEngineEvent as applyRigEvent, pruneQueueEntries, type ApplyStatus, type EngineEventApplyResult, type EngineEventApplyResult as RigEventApplyResult, } from "./engineReadModelReducer";
9
+ export { pickDefaultWorkspaceId, projectRunStatusForTaskGrouping, projectTaskStatusForGrouping, projectTaskStatusWithSessions, normalizeTaskAssigneeFilter, readTaskAssigneeLogins, selectAdhocRuns, selectAdhocRunsForWorkspace, selectApprovalsForRun, selectApprovalsForWorkspace, selectGraphsForWorkspace, selectPendingApprovals, selectPrimaryWorkspace, selectQueueForWorkspace, selectRun, selectRunsByTask, selectRunsForTask, selectRunsForWorkspace, selectTask, selectTasksByStatus, selectTasksByWorkspace, selectTasksForWorkspace, selectTasksGroupedByStatus, selectTasksAssignedTo, selectTasksAssignedToMe, selectUserInputsForRun, selectUserInputsForWorkspace, selectWorkspace, selectWorkspaces, type RigTaskSessionProjection, type RigTaskStatusGroup, type RigTaskStatusGroupingInput, } from "./rigSelectors";
10
+ export { buildTaskReferenceIndex, computeTaskBlockingDepths, computeTaskDependencyBadges, isTaskTerminalStatus, readTaskDependencyRefs, readTaskMetadataStringList, readTaskSourceIssueId, resolveTaskReference, selectNextReadyTaskByPriority, type TaskDependencyBadge, type TaskDependencyBadgeKind, type TaskDependencyBadgeSummary, type TaskDependencyProjection, } from "./taskGraph";
11
+ export { extractTaskCode, extractTaskGroupKey, stripTaskCode, } from "./taskGraphCodes";
12
+ export { buildTaskGraphLayout, type TaskGraphEdge, type TaskGraphLane, type TaskGraphLayout, type TaskGraphNode, type TaskGraphStage, } from "./taskGraphLayout";
package/dist/src/index.js CHANGED
@@ -43,11 +43,45 @@ function definePlugin(meta, runtime) {
43
43
  }
44
44
  }
45
45
  }
46
+ const declaredHooks = new Map((validated.contributes?.hooks ?? []).map((h) => [h.id, h]));
47
+ for (const hookId of Object.keys(runtime.hooks ?? {})) {
48
+ const metadata = declaredHooks.get(hookId);
49
+ if (!metadata) {
50
+ throw new Error(`definePlugin(${validated.name}): typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
51
+ }
52
+ if (metadata.command) {
53
+ throw new Error(`definePlugin(${validated.name}): hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
54
+ }
55
+ }
56
+ if (runtime.hooks) {
57
+ for (const h of declaredHooks.values()) {
58
+ if (!runtime.hooks[h.id] && !h.command) {
59
+ throw new Error(`definePlugin(${validated.name}): hook metadata "${h.id}" has no implementation (typed function or command)`);
60
+ }
61
+ }
62
+ }
46
63
  return { ...validated, __runtime: runtime };
47
64
  }
48
65
  // packages/core/src/define-config.ts
49
66
  import { Schema as Schema2 } from "effect";
50
67
  import { RigConfig } from "@rig/contracts";
68
+ function normalizeWorkspaceConfig(raw) {
69
+ const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : { mainRepo: "." };
70
+ workspace.checkout = workspace.checkout ?? workspace.isolation ?? "worktree";
71
+ workspace.isolation = workspace.isolation ?? workspace.checkout;
72
+ workspace.sandbox = workspace.sandbox ?? "enforce";
73
+ return workspace;
74
+ }
75
+ function applyConfigDefaults(raw) {
76
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
77
+ return raw;
78
+ const record = raw;
79
+ return {
80
+ ...record,
81
+ plugins: Array.isArray(record.plugins) ? record.plugins : [],
82
+ workspace: normalizeWorkspaceConfig(record.workspace)
83
+ };
84
+ }
51
85
  function defineConfig(cfg) {
52
86
  const runtimeByName = new Map;
53
87
  const plugins = cfg.plugins ?? [];
@@ -56,7 +90,7 @@ function defineConfig(cfg) {
56
90
  runtimeByName.set(plugin.name, plugin.__runtime);
57
91
  }
58
92
  }
59
- const decoded = Schema2.decodeUnknownSync(RigConfig)(cfg);
93
+ const decoded = Schema2.decodeUnknownSync(RigConfig)(applyConfigDefaults(cfg));
60
94
  const decodedPlugins = decoded.plugins.map((p) => {
61
95
  const runtime = runtimeByName.get(p.name);
62
96
  if (!runtime)
@@ -124,6 +158,24 @@ function assertRuntimeMatchesMetadata(plugin) {
124
158
  }
125
159
  }
126
160
  }
161
+ const declaredHooks = new Map((plugin.contributes?.hooks ?? []).map((hook) => [hook.id, hook]));
162
+ const runtimeHooks = plugin.__runtime?.hooks;
163
+ for (const hookId of Object.keys(runtimeHooks ?? {})) {
164
+ const metadata = declaredHooks.get(hookId);
165
+ if (!metadata) {
166
+ throw new Error(`plugin "${plugin.name}" typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
167
+ }
168
+ if (metadata.command) {
169
+ throw new Error(`plugin "${plugin.name}" hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
170
+ }
171
+ }
172
+ if (runtimeHooks) {
173
+ for (const hook of declaredHooks.values()) {
174
+ if (!runtimeHooks[hook.id] && !hook.command) {
175
+ throw new Error(`plugin "${plugin.name}" hook metadata "${hook.id}" has no implementation (typed function or command)`);
176
+ }
177
+ }
178
+ }
127
179
  }
128
180
  function createPluginHost(plugins) {
129
181
  assertUniquePluginNames(plugins);
@@ -257,7 +309,8 @@ function buildRigInitConfigSource(input) {
257
309
  lines.push(` },`);
258
310
  }
259
311
  lines.push(` workspace: { mainRepo: ".", isolation: "worktree" },`);
260
- lines.push(` runtime: { harness: "pi", mode: "yolo" },`);
312
+ const sshTarget = input.sshTarget?.trim();
313
+ lines.push(sshTarget ? ` runtime: { harness: "pi", mode: "yolo", server: { sshTarget: ${JSON.stringify(sshTarget)} } },` : ` runtime: { harness: "pi", mode: "yolo" }, // server.sshTarget unset = local placement`);
261
314
  lines.push(` planning: { mode: "auto" },`);
262
315
  lines.push(` github: {`);
263
316
  lines.push(` issueUpdates: "lifecycle",`);
@@ -422,7 +475,7 @@ function mapLegacySessionStatusToRunStatus(status) {
422
475
  case "error":
423
476
  return "failed";
424
477
  case "stopped":
425
- return "cancelled";
478
+ return "stopped";
426
479
  default:
427
480
  return "created";
428
481
  }
@@ -458,12 +511,15 @@ function mapTaskStatusFromRunStatus(status, fallback) {
458
511
  case "paused":
459
512
  return "in_progress";
460
513
  case "reviewing":
514
+ case "closing-out":
461
515
  return "under_review";
516
+ case "needs-attention":
517
+ return "blocked";
462
518
  case "completed":
463
519
  return "completed";
464
520
  case "failed":
465
521
  return "ready";
466
- case "cancelled":
522
+ case "stopped":
467
523
  return "cancelled";
468
524
  }
469
525
  }
@@ -1313,7 +1369,7 @@ function applyEngineEvent(snapshot, event) {
1313
1369
  ...base,
1314
1370
  runs: patchById(base.runs, payload.runId, (run) => ({
1315
1371
  ...run,
1316
- status: "cancelled",
1372
+ status: "stopped",
1317
1373
  updatedAt: payload.createdAt,
1318
1374
  completedAt: payload.createdAt
1319
1375
  }))
@@ -2085,10 +2141,40 @@ function projectTaskStatusForGrouping(status) {
2085
2141
  case "in_progress":
2086
2142
  case "under_review":
2087
2143
  return "running";
2144
+ case null:
2145
+ case undefined:
2146
+ case "":
2147
+ return "unknown";
2088
2148
  default:
2089
2149
  return status;
2090
2150
  }
2091
2151
  }
2152
+ function projectRunStatusForTaskGrouping(status) {
2153
+ switch (status) {
2154
+ case "created":
2155
+ case "queued":
2156
+ case "preparing":
2157
+ return "queued";
2158
+ case "running":
2159
+ case "waiting-approval":
2160
+ case "waiting-user-input":
2161
+ case "paused":
2162
+ case "validating":
2163
+ case "reviewing":
2164
+ case "closing-out":
2165
+ return "running";
2166
+ case "needs-attention":
2167
+ return "blocked";
2168
+ case "failed":
2169
+ case "stopped":
2170
+ return "ready";
2171
+ case "completed":
2172
+ return "completed";
2173
+ case null:
2174
+ case undefined:
2175
+ return null;
2176
+ }
2177
+ }
2092
2178
  var STATUS_PRIORITY = [
2093
2179
  "running",
2094
2180
  "blocked",
@@ -2100,11 +2186,31 @@ var STATUS_PRIORITY = [
2100
2186
  "cancelled",
2101
2187
  "completed"
2102
2188
  ];
2103
- function selectTasksGroupedByStatus(snapshot, workspaceId) {
2104
- const tasks = selectTasksByWorkspace(snapshot, workspaceId);
2189
+ function taskIdFromSession(session) {
2190
+ return session.taskId ?? session.record.taskId ?? null;
2191
+ }
2192
+ function latestSessionByTask(sessions) {
2193
+ const byTask = new Map;
2194
+ for (const session of sessions) {
2195
+ const taskId = taskIdFromSession(session);
2196
+ if (!taskId)
2197
+ continue;
2198
+ const existing = byTask.get(taskId);
2199
+ if (!existing || (session.lastEventAt ?? "").localeCompare(existing.lastEventAt ?? "") >= 0) {
2200
+ byTask.set(taskId, session);
2201
+ }
2202
+ }
2203
+ return byTask;
2204
+ }
2205
+ function projectTaskStatusWithSessions(task, sessionsByTask = new Map) {
2206
+ const sessionStatus = projectRunStatusForTaskGrouping(sessionsByTask.get(task.id)?.status);
2207
+ return sessionStatus ?? projectTaskStatusForGrouping(task.status);
2208
+ }
2209
+ function groupTasksByProjectedStatus(tasks, sessions = []) {
2210
+ const sessionsByTask = latestSessionByTask(sessions);
2105
2211
  const byStatus = new Map;
2106
2212
  for (const task of tasks) {
2107
- const projectedStatus = projectTaskStatusForGrouping(task.status);
2213
+ const projectedStatus = projectTaskStatusWithSessions(task, sessionsByTask);
2108
2214
  const group = byStatus.get(projectedStatus);
2109
2215
  if (group) {
2110
2216
  group.push(task);
@@ -2128,6 +2234,69 @@ function selectTasksGroupedByStatus(snapshot, workspaceId) {
2128
2234
  }
2129
2235
  return result;
2130
2236
  }
2237
+ function isProjectionGroupingInput(value) {
2238
+ return Boolean(value && typeof value === "object" && !("snapshotSequence" in value) && Array.isArray(value.tasks));
2239
+ }
2240
+ function selectTasksGroupedByStatus(input, workspaceId) {
2241
+ if (isProjectionGroupingInput(input)) {
2242
+ const filteredTasks = input.workspaceId ? input.tasks.filter((task) => {
2243
+ const workspaceTask = task;
2244
+ return workspaceTask.workspaceId === input.workspaceId;
2245
+ }) : input.tasks;
2246
+ return groupTasksByProjectedStatus(filteredTasks, input.sessions);
2247
+ }
2248
+ const tasks = selectTasksByWorkspace(input, workspaceId ?? null);
2249
+ return groupTasksByProjectedStatus(tasks, []);
2250
+ }
2251
+ function isObjectRecord(value) {
2252
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2253
+ }
2254
+ function normalizeLogin(value) {
2255
+ return value.trim().replace(/^@+/, "").toLowerCase();
2256
+ }
2257
+ function assigneeLoginsFromValue(value) {
2258
+ if (!Array.isArray(value))
2259
+ return [];
2260
+ return value.flatMap((entry) => {
2261
+ if (typeof entry === "string" && entry.trim())
2262
+ return [normalizeLogin(entry)];
2263
+ if (isObjectRecord(entry) && typeof entry.login === "string" && entry.login.trim()) {
2264
+ return [normalizeLogin(entry.login)];
2265
+ }
2266
+ return [];
2267
+ });
2268
+ }
2269
+ function normalizeTaskAssigneeFilter(assignee, currentUserLogin) {
2270
+ const trimmed = assignee?.trim();
2271
+ if (!trimmed)
2272
+ return null;
2273
+ if (trimmed === "@me" || trimmed.toLowerCase() === "me") {
2274
+ return currentUserLogin?.trim() ? normalizeLogin(currentUserLogin) : null;
2275
+ }
2276
+ return normalizeLogin(trimmed);
2277
+ }
2278
+ function readTaskAssigneeLogins(task) {
2279
+ const taskRecord = task;
2280
+ const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
2281
+ const raw = isObjectRecord(metadata?.raw) ? metadata.raw : null;
2282
+ return Array.from(new Set([
2283
+ ...assigneeLoginsFromValue(taskRecord.assignees),
2284
+ ...assigneeLoginsFromValue(metadata?.assignees),
2285
+ ...assigneeLoginsFromValue(raw?.assignees)
2286
+ ]));
2287
+ }
2288
+ function taskMatchesAssigneeFilter(task, assignee, options = {}) {
2289
+ const normalized = normalizeTaskAssigneeFilter(assignee, options.currentUserLogin);
2290
+ if (!normalized)
2291
+ return false;
2292
+ return readTaskAssigneeLogins(task).includes(normalized);
2293
+ }
2294
+ function selectTasksAssignedTo(tasks, assignee, options = {}) {
2295
+ return tasks.filter((task) => taskMatchesAssigneeFilter(task, assignee, options));
2296
+ }
2297
+ function selectTasksAssignedToMe(tasks, currentUserLogin) {
2298
+ return selectTasksAssignedTo(tasks, "@me", currentUserLogin === undefined ? {} : { currentUserLogin });
2299
+ }
2131
2300
  function selectRun(snapshot, runId) {
2132
2301
  if (!runId)
2133
2302
  return null;
@@ -2189,17 +2358,45 @@ function selectUserInputsForWorkspace(snapshot, workspaceId) {
2189
2358
  return (snapshot.userInputs ?? []).filter((request) => runIds.has(request.runId));
2190
2359
  }
2191
2360
  // packages/core/src/taskGraph.ts
2192
- function isObjectRecord(value) {
2361
+ function isObjectRecord2(value) {
2193
2362
  return typeof value === "object" && value !== null && !Array.isArray(value);
2194
2363
  }
2195
- function readTaskMetadataStringList(task, key) {
2196
- const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
2197
- const value = metadata?.[key];
2364
+ function readStringList(value) {
2198
2365
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
2199
2366
  }
2367
+ function unique(values) {
2368
+ return Array.from(new Set(values));
2369
+ }
2370
+ function readTaskMetadataStringList(task, key) {
2371
+ const taskRecord = task;
2372
+ const topLevel = readStringList(taskRecord[key]);
2373
+ if (topLevel.length > 0)
2374
+ return topLevel;
2375
+ const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
2376
+ const metadataList = readStringList(metadata?.[key]);
2377
+ if (metadataList.length > 0)
2378
+ return metadataList;
2379
+ if (key === "dependencies") {
2380
+ return readStringList(metadata?.deps);
2381
+ }
2382
+ return [];
2383
+ }
2384
+ function readTaskDependencyRefs(task) {
2385
+ return unique([
2386
+ ...readTaskMetadataStringList(task, "dependencies"),
2387
+ ...readTaskMetadataStringList(task, "parentChildDeps")
2388
+ ]);
2389
+ }
2200
2390
  function readTaskSourceIssueId(task) {
2201
- const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
2202
- return typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0 ? metadata.sourceIssueId : null;
2391
+ if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
2392
+ return task.sourceIssueId;
2393
+ }
2394
+ const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
2395
+ if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
2396
+ return metadata.sourceIssueId;
2397
+ }
2398
+ const rigMetadata = isObjectRecord2(metadata?._rig) ? metadata._rig : null;
2399
+ return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
2203
2400
  }
2204
2401
  function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
2205
2402
  if (tasksById.has(ref))
@@ -2229,11 +2426,7 @@ function computeTaskBlockingDepths(tasks) {
2229
2426
  if (!task)
2230
2427
  return 0;
2231
2428
  stack.add(taskId);
2232
- const refs = [
2233
- ...readTaskMetadataStringList(task, "dependencies"),
2234
- ...readTaskMetadataStringList(task, "parentChildDeps")
2235
- ];
2236
- const blockers = refs.map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
2429
+ const blockers = readTaskDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
2237
2430
  const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
2238
2431
  stack.delete(taskId);
2239
2432
  memo.set(taskId, depth);
@@ -2244,6 +2437,134 @@ function computeTaskBlockingDepths(tasks) {
2244
2437
  }
2245
2438
  return memo;
2246
2439
  }
2440
+ function isTaskTerminalStatus(status) {
2441
+ switch (status) {
2442
+ case "closed":
2443
+ case "completed":
2444
+ case "done":
2445
+ case "cancelled":
2446
+ case "canceled":
2447
+ return true;
2448
+ default:
2449
+ return false;
2450
+ }
2451
+ }
2452
+ function isTaskBlockedStatus(status) {
2453
+ return status === "blocked";
2454
+ }
2455
+ function isTaskRunnableStatus(status) {
2456
+ if (status === null || status === undefined || status === "")
2457
+ return true;
2458
+ if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
2459
+ return false;
2460
+ switch (status) {
2461
+ case "ready":
2462
+ case "open":
2463
+ case "failed":
2464
+ return true;
2465
+ default:
2466
+ return false;
2467
+ }
2468
+ }
2469
+ function priorityValue(task) {
2470
+ return typeof task.priority === "number" && Number.isFinite(task.priority) ? task.priority : Number.MAX_SAFE_INTEGER;
2471
+ }
2472
+ function computeTaskDependencyBadges(tasks) {
2473
+ const index = buildTaskReferenceIndex(tasks);
2474
+ const blockingDepths = computeTaskBlockingDepths(tasks);
2475
+ const dependencyIdsByTask = new Map;
2476
+ const unresolvedRefsByTask = new Map;
2477
+ const blocksByTask = new Map;
2478
+ for (const task of tasks) {
2479
+ const dependencyIds = [];
2480
+ const unresolvedRefs = [];
2481
+ for (const ref of readTaskDependencyRefs(task)) {
2482
+ const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
2483
+ if (dependencyId && dependencyId !== task.id) {
2484
+ dependencyIds.push(dependencyId);
2485
+ const blocks = blocksByTask.get(dependencyId);
2486
+ if (blocks) {
2487
+ blocks.push(task.id);
2488
+ } else {
2489
+ blocksByTask.set(dependencyId, [task.id]);
2490
+ }
2491
+ } else {
2492
+ unresolvedRefs.push(ref);
2493
+ }
2494
+ }
2495
+ dependencyIdsByTask.set(task.id, unique(dependencyIds));
2496
+ unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
2497
+ }
2498
+ const summaries = new Map;
2499
+ for (const task of tasks) {
2500
+ const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
2501
+ const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
2502
+ const blockedBy = dependencyIds.filter((dependencyId) => {
2503
+ const dependency = index.tasksById.get(dependencyId);
2504
+ return dependency ? !isTaskTerminalStatus(dependency.status) : false;
2505
+ });
2506
+ const blocks = unique(blocksByTask.get(task.id) ?? []);
2507
+ const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
2508
+ const ready = isTaskRunnableStatus(task.status) && !blocked;
2509
+ const badges = [];
2510
+ if (blocked) {
2511
+ badges.push({
2512
+ kind: "blocked",
2513
+ label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
2514
+ description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
2515
+ ...blockedBy.length > 0 ? { count: blockedBy.length } : {},
2516
+ taskIds: blockedBy
2517
+ });
2518
+ } else if (ready) {
2519
+ badges.push({
2520
+ kind: "ready",
2521
+ label: "ready",
2522
+ description: "No open dependencies block this task."
2523
+ });
2524
+ }
2525
+ if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
2526
+ badges.push({
2527
+ kind: "dependency",
2528
+ label: `deps ${dependencyIds.length}/${blocks.length}`,
2529
+ description: [
2530
+ dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
2531
+ blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
2532
+ unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
2533
+ ].filter((part) => part !== null).join(" "),
2534
+ count: dependencyIds.length + blocks.length,
2535
+ taskIds: unique([...dependencyIds, ...blocks])
2536
+ });
2537
+ }
2538
+ summaries.set(task.id, {
2539
+ taskId: task.id,
2540
+ blockingDepth: blockingDepths.get(task.id) ?? 0,
2541
+ dependencyIds,
2542
+ unresolvedDependencyRefs,
2543
+ blockedBy,
2544
+ blocks,
2545
+ blocked,
2546
+ ready,
2547
+ dependencyCount: dependencyIds.length,
2548
+ dependentCount: blocks.length,
2549
+ badges
2550
+ });
2551
+ }
2552
+ return summaries;
2553
+ }
2554
+ function selectNextReadyTaskByPriority(tasks, options = {}) {
2555
+ const excluded = new Set(options.excludeTaskIds ?? []);
2556
+ const badges = computeTaskDependencyBadges(tasks);
2557
+ const candidates = tasks.filter((task) => !excluded.has(task.id)).filter((task) => options.filter?.(task) ?? true).filter((task) => badges.get(task.id)?.ready === true).toSorted((left, right) => {
2558
+ const priorityDelta = priorityValue(left) - priorityValue(right);
2559
+ if (priorityDelta !== 0)
2560
+ return priorityDelta;
2561
+ const createdDelta = (left.createdAt ?? "").localeCompare(right.createdAt ?? "");
2562
+ if (createdDelta !== 0)
2563
+ return createdDelta;
2564
+ return left.id.localeCompare(right.id);
2565
+ });
2566
+ return candidates[0] ?? null;
2567
+ }
2247
2568
  // packages/core/src/taskGraphCodes.ts
2248
2569
  var TASK_CODE_RE = /^\[([A-Z0-9]+(?:-[A-Z0-9]+)*)\]\s*/;
2249
2570
  function extractTaskCode(title) {
@@ -2283,11 +2604,11 @@ var PALETTE = [
2283
2604
  { bg: "#132c35", border: "#1783a6", edge: "#53c4e5" },
2284
2605
  { bg: "#26310f", border: "#6d9a19", edge: "#a7da42" }
2285
2606
  ];
2286
- function isObjectRecord2(value) {
2607
+ function isObjectRecord3(value) {
2287
2608
  return typeof value === "object" && value !== null && !Array.isArray(value);
2288
2609
  }
2289
2610
  function readIssueType(task) {
2290
- const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
2611
+ const metadata = isObjectRecord3(task.metadata) ? task.metadata : null;
2291
2612
  return typeof metadata?.issueType === "string" ? metadata.issueType : null;
2292
2613
  }
2293
2614
  function isGraphTask(task) {
@@ -2582,6 +2903,8 @@ export {
2582
2903
  selectTasksForWorkspace,
2583
2904
  selectTasksByWorkspace,
2584
2905
  selectTasksByStatus,
2906
+ selectTasksAssignedToMe,
2907
+ selectTasksAssignedTo,
2585
2908
  selectTask,
2586
2909
  selectRunsForWorkspace,
2587
2910
  selectRunsForTask,
@@ -2590,6 +2913,7 @@ export {
2590
2913
  selectQueueForWorkspace,
2591
2914
  selectPrimaryWorkspace,
2592
2915
  selectPendingApprovals,
2916
+ selectNextReadyTaskByPriority,
2593
2917
  selectGraphsForWorkspace,
2594
2918
  selectApprovalsForWorkspace,
2595
2919
  selectApprovalsForRun,
@@ -2598,13 +2922,21 @@ export {
2598
2922
  resolveTaskReference,
2599
2923
  readTaskSourceIssueId,
2600
2924
  readTaskMetadataStringList,
2925
+ readTaskDependencyRefs,
2926
+ readTaskAssigneeLogins,
2601
2927
  pruneQueueEntries,
2928
+ projectTaskStatusWithSessions,
2929
+ projectTaskStatusForGrouping,
2930
+ projectRunStatusForTaskGrouping,
2602
2931
  pickDefaultWorkspaceId,
2932
+ normalizeTaskAssigneeFilter,
2933
+ isTaskTerminalStatus,
2603
2934
  extractTaskGroupKey,
2604
2935
  extractTaskCode,
2605
2936
  definePlugin,
2606
2937
  defineConfig,
2607
2938
  createPluginHost,
2939
+ computeTaskDependencyBadges,
2608
2940
  computeTaskBlockingDepths,
2609
2941
  buildTaskReferenceIndex,
2610
2942
  buildTaskGraphLayout,
@@ -0,0 +1,2 @@
1
+ import { RigConfig } from "@rig/contracts";
2
+ export declare function loadConfig(cwd: string): Promise<RigConfig>;