@h-rig/supervisor-plugin 0.0.6-alpha.135 → 0.0.6-alpha.137
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/src/cli.d.ts +29 -0
- package/dist/src/cli.js +209 -0
- package/dist/src/closureStage.d.ts +15 -1
- package/dist/src/closureStage.js +37 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +221 -0
- package/dist/src/plugin.d.ts +1 -0
- package/dist/src/plugin.js +250 -0
- package/package.json +4 -3
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RuntimeCliContext } from "@rig/core";
|
|
2
|
+
export declare const SUPERVISOR_LOOP_CLI_ID = "supervisor.loop";
|
|
3
|
+
export declare const SUPERVISOR_UNBLOCK_CLI_ID = "supervisor.unblock";
|
|
4
|
+
type CommandOutcome = {
|
|
5
|
+
readonly ok: boolean;
|
|
6
|
+
readonly group: string;
|
|
7
|
+
readonly command: string;
|
|
8
|
+
readonly details?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export declare function executeLoop(context: RuntimeCliContext, args: readonly string[]): Promise<CommandOutcome>;
|
|
11
|
+
export declare function executeUnblock(context: RuntimeCliContext, args: readonly string[]): Promise<CommandOutcome>;
|
|
12
|
+
export declare const supervisorCliCommands: readonly [{
|
|
13
|
+
readonly id: "supervisor.loop";
|
|
14
|
+
readonly family: "loop";
|
|
15
|
+
readonly command: "rig loop [--max-tasks <n>] [--concurrency <n>] [--dry-run]";
|
|
16
|
+
readonly description: "Run the autonomous supervisor loop over ready tasks.";
|
|
17
|
+
readonly usage: "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]";
|
|
18
|
+
readonly projectRequired: true;
|
|
19
|
+
readonly run: typeof executeLoop;
|
|
20
|
+
}, {
|
|
21
|
+
readonly id: "supervisor.unblock";
|
|
22
|
+
readonly family: "unblock";
|
|
23
|
+
readonly command: "rig unblock [task-id] [--dry-run]";
|
|
24
|
+
readonly description: "Plan or dispatch work that unblocks a blocked task.";
|
|
25
|
+
readonly usage: "rig unblock [task-id] [--dry-run] [--json]";
|
|
26
|
+
readonly projectRequired: true;
|
|
27
|
+
readonly run: typeof executeUnblock;
|
|
28
|
+
}];
|
|
29
|
+
export {};
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/supervisor-plugin/src/cli.ts
|
|
3
|
+
import {
|
|
4
|
+
dispatchRun,
|
|
5
|
+
listRuns,
|
|
6
|
+
listTasks,
|
|
7
|
+
runSupervisorLoop,
|
|
8
|
+
unblockTask
|
|
9
|
+
} from "@rig/client";
|
|
10
|
+
|
|
11
|
+
// packages/supervisor-plugin/src/awaiter.ts
|
|
12
|
+
var TERMINAL_RUN_STATUSES = {
|
|
13
|
+
created: false,
|
|
14
|
+
queued: false,
|
|
15
|
+
preparing: false,
|
|
16
|
+
running: false,
|
|
17
|
+
"waiting-approval": false,
|
|
18
|
+
"waiting-user-input": false,
|
|
19
|
+
paused: false,
|
|
20
|
+
validating: false,
|
|
21
|
+
reviewing: false,
|
|
22
|
+
"closing-out": false,
|
|
23
|
+
"needs-attention": true,
|
|
24
|
+
completed: true,
|
|
25
|
+
failed: true,
|
|
26
|
+
stopped: true
|
|
27
|
+
};
|
|
28
|
+
async function awaitTerminalRun(options) {
|
|
29
|
+
const startedAt = Date.now();
|
|
30
|
+
const pollMs = Math.max(0, Math.floor(options.pollMs ?? 5000));
|
|
31
|
+
while (true) {
|
|
32
|
+
const snapshot = await options.readStatus(options.runId);
|
|
33
|
+
if (snapshot && TERMINAL_RUN_STATUSES[snapshot.status]) {
|
|
34
|
+
return {
|
|
35
|
+
runId: options.runId,
|
|
36
|
+
status: snapshot.status,
|
|
37
|
+
failed: snapshot.status !== "completed"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (options.timeoutMs !== undefined && Date.now() - startedAt >= options.timeoutMs) {
|
|
41
|
+
return { runId: options.runId, status: "needs-attention", failed: true };
|
|
42
|
+
}
|
|
43
|
+
await options.waitForChange(pollMs);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// packages/supervisor-plugin/src/cli.ts
|
|
48
|
+
var SUPERVISOR_LOOP_CLI_ID = "supervisor.loop";
|
|
49
|
+
var SUPERVISOR_UNBLOCK_CLI_ID = "supervisor.unblock";
|
|
50
|
+
function printJson(value) {
|
|
51
|
+
console.log(JSON.stringify(value, null, 2));
|
|
52
|
+
}
|
|
53
|
+
function takeFlag(args, flag) {
|
|
54
|
+
const rest = [...args];
|
|
55
|
+
const index = rest.indexOf(flag);
|
|
56
|
+
if (index < 0)
|
|
57
|
+
return { value: false, rest };
|
|
58
|
+
rest.splice(index, 1);
|
|
59
|
+
return { value: true, rest };
|
|
60
|
+
}
|
|
61
|
+
function takeOption(args, flag) {
|
|
62
|
+
const rest = [...args];
|
|
63
|
+
const index = rest.indexOf(flag);
|
|
64
|
+
if (index < 0)
|
|
65
|
+
return { rest };
|
|
66
|
+
const value = rest[index + 1];
|
|
67
|
+
if (!value || value.startsWith("-"))
|
|
68
|
+
throw new Error(`${flag} requires a value.`);
|
|
69
|
+
rest.splice(index, 2);
|
|
70
|
+
return { value, rest };
|
|
71
|
+
}
|
|
72
|
+
function requireNoExtraArgs(args, usage) {
|
|
73
|
+
if (args.length > 0)
|
|
74
|
+
throw new Error(`Unexpected argument: ${args[0]}
|
|
75
|
+
Usage: ${usage}`);
|
|
76
|
+
}
|
|
77
|
+
function parsePositiveInt(value, flag, fallback) {
|
|
78
|
+
if (value === undefined)
|
|
79
|
+
return fallback;
|
|
80
|
+
const parsed = Number.parseInt(value, 10);
|
|
81
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || String(parsed) !== value.trim()) {
|
|
82
|
+
throw new Error(`${flag} must be a positive integer.`);
|
|
83
|
+
}
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
function parseCsv(value) {
|
|
87
|
+
return value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [];
|
|
88
|
+
}
|
|
89
|
+
function asRunStatus(value) {
|
|
90
|
+
switch (value) {
|
|
91
|
+
case "created":
|
|
92
|
+
case "queued":
|
|
93
|
+
case "preparing":
|
|
94
|
+
case "running":
|
|
95
|
+
case "waiting-approval":
|
|
96
|
+
case "waiting-user-input":
|
|
97
|
+
case "paused":
|
|
98
|
+
case "validating":
|
|
99
|
+
case "reviewing":
|
|
100
|
+
case "closing-out":
|
|
101
|
+
case "needs-attention":
|
|
102
|
+
case "completed":
|
|
103
|
+
case "failed":
|
|
104
|
+
case "stopped":
|
|
105
|
+
return value;
|
|
106
|
+
default:
|
|
107
|
+
return "needs-attention";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function delay(ms) {
|
|
111
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
112
|
+
setTimeout(resolve, ms);
|
|
113
|
+
return promise;
|
|
114
|
+
}
|
|
115
|
+
function supervisorDeps(timeoutMs) {
|
|
116
|
+
return {
|
|
117
|
+
listTasks,
|
|
118
|
+
listRuns,
|
|
119
|
+
dispatchRun,
|
|
120
|
+
awaitRunTerminal: async (projectRoot, runId) => {
|
|
121
|
+
const awaitedRunId = runId;
|
|
122
|
+
return awaitTerminalRun({
|
|
123
|
+
runId: awaitedRunId,
|
|
124
|
+
timeoutMs,
|
|
125
|
+
readStatus: async (id) => {
|
|
126
|
+
const run = (await listRuns(projectRoot)).find((candidate) => candidate.runId === id);
|
|
127
|
+
return run ? { runId: id, status: asRunStatus(run.status), ...run.errorSummary ? { diagnostic: run.errorSummary } : {} } : null;
|
|
128
|
+
},
|
|
129
|
+
waitForChange: delay
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function formatSupervisorResult(result) {
|
|
135
|
+
return [
|
|
136
|
+
`ok: ${result.ok}`,
|
|
137
|
+
`dry-run: ${result.dryRun}`,
|
|
138
|
+
`planned: ${result.plannedOrder.length > 0 ? result.plannedOrder.join(", ") : "none"}`,
|
|
139
|
+
`processed: ${result.projection.processed}`,
|
|
140
|
+
`succeeded: ${result.projection.succeeded}`,
|
|
141
|
+
`failed: ${result.projection.failed}`,
|
|
142
|
+
result.projection.idleReason ? `idle: ${result.projection.idleReason}` : null
|
|
143
|
+
].filter((line) => line !== null).join(`
|
|
144
|
+
`);
|
|
145
|
+
}
|
|
146
|
+
async function executeLoop(context, args) {
|
|
147
|
+
const dry = takeFlag(args, "--dry-run");
|
|
148
|
+
const json = takeFlag(dry.rest, "--json");
|
|
149
|
+
const maxTasks = takeOption(json.rest, "--max-tasks");
|
|
150
|
+
const concurrency = takeOption(maxTasks.rest, "--concurrency");
|
|
151
|
+
const stopWhen = takeOption(concurrency.rest, "--stop-when");
|
|
152
|
+
const timeout = takeOption(stopWhen.rest, "--timeout-ms");
|
|
153
|
+
requireNoExtraArgs(timeout.rest, "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]");
|
|
154
|
+
const result = await runSupervisorLoop(context.projectRoot, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 30 * 60 * 1000)), {
|
|
155
|
+
maxTasks: parsePositiveInt(maxTasks.value, "--max-tasks", 1),
|
|
156
|
+
concurrency: parsePositiveInt(concurrency.value, "--concurrency", 1),
|
|
157
|
+
dryRun: dry.value || context.dryRun
|
|
158
|
+
});
|
|
159
|
+
const details = { ...result, stopWhen: parseCsv(stopWhen.value) };
|
|
160
|
+
if (context.outputMode === "text") {
|
|
161
|
+
if (json.value)
|
|
162
|
+
printJson(details);
|
|
163
|
+
else
|
|
164
|
+
console.log(formatSupervisorResult(result));
|
|
165
|
+
}
|
|
166
|
+
return { ok: result.ok, group: "loop", command: "run", details };
|
|
167
|
+
}
|
|
168
|
+
async function executeUnblock(context, args) {
|
|
169
|
+
const dry = takeFlag(args, "--dry-run");
|
|
170
|
+
const json = takeFlag(dry.rest, "--json");
|
|
171
|
+
const timeout = takeOption(json.rest, "--timeout-ms");
|
|
172
|
+
const taskId = timeout.rest[0]?.startsWith("-") ? undefined : timeout.rest[0];
|
|
173
|
+
requireNoExtraArgs(taskId ? timeout.rest.slice(1) : timeout.rest, "rig unblock [task-id] [--dry-run] [--json]");
|
|
174
|
+
const result = await unblockTask(context.projectRoot, taskId ?? null, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 30 * 60 * 1000)), { dryRun: dry.value || context.dryRun });
|
|
175
|
+
if (context.outputMode === "text") {
|
|
176
|
+
if (json.value)
|
|
177
|
+
printJson(result);
|
|
178
|
+
else
|
|
179
|
+
console.log(formatSupervisorResult(result));
|
|
180
|
+
}
|
|
181
|
+
return { ok: result.ok, group: "unblock", command: "run", details: result };
|
|
182
|
+
}
|
|
183
|
+
var supervisorCliCommands = [
|
|
184
|
+
{
|
|
185
|
+
id: SUPERVISOR_LOOP_CLI_ID,
|
|
186
|
+
family: "loop",
|
|
187
|
+
command: "rig loop [--max-tasks <n>] [--concurrency <n>] [--dry-run]",
|
|
188
|
+
description: "Run the autonomous supervisor loop over ready tasks.",
|
|
189
|
+
usage: "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]",
|
|
190
|
+
projectRequired: true,
|
|
191
|
+
run: executeLoop
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: SUPERVISOR_UNBLOCK_CLI_ID,
|
|
195
|
+
family: "unblock",
|
|
196
|
+
command: "rig unblock [task-id] [--dry-run]",
|
|
197
|
+
description: "Plan or dispatch work that unblocks a blocked task.",
|
|
198
|
+
usage: "rig unblock [task-id] [--dry-run] [--json]",
|
|
199
|
+
projectRequired: true,
|
|
200
|
+
run: executeUnblock
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
export {
|
|
204
|
+
supervisorCliCommands,
|
|
205
|
+
executeUnblock,
|
|
206
|
+
executeLoop,
|
|
207
|
+
SUPERVISOR_UNBLOCK_CLI_ID,
|
|
208
|
+
SUPERVISOR_LOOP_CLI_ID
|
|
209
|
+
};
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import type { StageContext, StageMutation, StageResult, TaskClosureSummary } from "@rig/contracts";
|
|
1
|
+
import type { StageContext, StageMutation, StageResult, StageRun, TaskClosureSummary } from "@rig/contracts";
|
|
2
2
|
export declare const SUPERVISOR_CLOSURE_STAGE_ID = "supervisor-closure-observer";
|
|
3
3
|
export interface ClosureSummaryPort {
|
|
4
4
|
summarize(ctx: StageContext): Promise<TaskClosureSummary | null> | TaskClosureSummary | null;
|
|
5
5
|
record?(summary: TaskClosureSummary): Promise<void> | void;
|
|
6
6
|
}
|
|
7
7
|
export declare function createClosureStage(port: ClosureSummaryPort): (ctx: StageContext) => Promise<StageResult>;
|
|
8
|
+
/** Live closeout state threaded into `ctx.metadata.closeoutState`. */
|
|
9
|
+
export interface ClosureCloseoutState {
|
|
10
|
+
readonly prUrl?: string | null;
|
|
11
|
+
readonly mergeGate?: "passed" | "blocked" | "skipped" | null;
|
|
12
|
+
readonly merged?: boolean;
|
|
13
|
+
readonly unblockedTaskIds?: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function createSupervisorClosureStage(): StageRun;
|
|
16
|
+
/**
|
|
17
|
+
* Default closure stage: appends a `supervisor.outcome` event carrying the
|
|
18
|
+
* {@link TaskClosureSummary} to `<projectRoot>/.rig/supervisor.jsonl`, so the
|
|
19
|
+
* supervisor projection's `closures` are populated by real closeouts.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createDefaultSupervisorClosureStage(): StageRun;
|
|
8
22
|
export declare const supervisorClosureStageMutation: StageMutation;
|
package/dist/src/closureStage.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __require = import.meta.require;
|
|
3
|
+
|
|
2
4
|
// packages/supervisor-plugin/src/closureStage.ts
|
|
3
5
|
var SUPERVISOR_CLOSURE_STAGE_ID = "supervisor-closure-observer";
|
|
4
6
|
function createClosureStage(port) {
|
|
@@ -11,6 +13,39 @@ function createClosureStage(port) {
|
|
|
11
13
|
return { kind: "continue", ctx: { ...ctx, metadata } };
|
|
12
14
|
};
|
|
13
15
|
}
|
|
16
|
+
function summarizeClosure(ctx) {
|
|
17
|
+
const state = (ctx.metadata ?? {}).closeoutState ?? null;
|
|
18
|
+
const taskId = typeof ctx.taskId === "string" ? ctx.taskId : null;
|
|
19
|
+
if (!state || !taskId)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
taskId,
|
|
23
|
+
runId: ctx.runId,
|
|
24
|
+
prUrl: state.prUrl ?? null,
|
|
25
|
+
mergeGate: state.mergeGate ?? null,
|
|
26
|
+
acceptanceChecked: [],
|
|
27
|
+
unblockedTaskIds: state.unblockedTaskIds ?? []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createSupervisorClosureStage() {
|
|
31
|
+
return createClosureStage({ summarize: summarizeClosure });
|
|
32
|
+
}
|
|
33
|
+
function createDefaultSupervisorClosureStage() {
|
|
34
|
+
return async (ctx) => {
|
|
35
|
+
const summary = summarizeClosure(ctx);
|
|
36
|
+
const projectRoot = (ctx.metadata ?? {}).projectRoot;
|
|
37
|
+
if (summary && typeof projectRoot === "string") {
|
|
38
|
+
const { appendFileSync, mkdirSync } = await import("fs");
|
|
39
|
+
const { join } = await import("path");
|
|
40
|
+
try {
|
|
41
|
+
mkdirSync(join(projectRoot, ".rig"), { recursive: true });
|
|
42
|
+
appendFileSync(join(projectRoot, ".rig", "supervisor.jsonl"), `${JSON.stringify({ kind: "supervisor.outcome", at: new Date().toISOString(), taskId: summary.taskId, runId: summary.runId, status: summary.mergeGate === "passed" ? "completed" : "needs-attention", failed: false, unblockedTaskIds: summary.unblockedTaskIds, closure: summary })}
|
|
43
|
+
`);
|
|
44
|
+
} catch {}
|
|
45
|
+
}
|
|
46
|
+
return { kind: "continue", ctx };
|
|
47
|
+
};
|
|
48
|
+
}
|
|
14
49
|
var supervisorClosureStageMutation = {
|
|
15
50
|
op: "insert",
|
|
16
51
|
contributedBy: "@rig/supervisor-plugin",
|
|
@@ -25,6 +60,8 @@ var supervisorClosureStageMutation = {
|
|
|
25
60
|
};
|
|
26
61
|
export {
|
|
27
62
|
supervisorClosureStageMutation,
|
|
63
|
+
createSupervisorClosureStage,
|
|
64
|
+
createDefaultSupervisorClosureStage,
|
|
28
65
|
createClosureStage,
|
|
29
66
|
SUPERVISOR_CLOSURE_STAGE_ID
|
|
30
67
|
};
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __require = import.meta.require;
|
|
3
|
+
|
|
2
4
|
// packages/supervisor-plugin/src/awaiter.ts
|
|
3
5
|
var TERMINAL_RUN_STATUSES = {
|
|
4
6
|
created: false,
|
|
@@ -46,6 +48,39 @@ function createClosureStage(port) {
|
|
|
46
48
|
return { kind: "continue", ctx: { ...ctx, metadata } };
|
|
47
49
|
};
|
|
48
50
|
}
|
|
51
|
+
function summarizeClosure(ctx) {
|
|
52
|
+
const state = (ctx.metadata ?? {}).closeoutState ?? null;
|
|
53
|
+
const taskId = typeof ctx.taskId === "string" ? ctx.taskId : null;
|
|
54
|
+
if (!state || !taskId)
|
|
55
|
+
return null;
|
|
56
|
+
return {
|
|
57
|
+
taskId,
|
|
58
|
+
runId: ctx.runId,
|
|
59
|
+
prUrl: state.prUrl ?? null,
|
|
60
|
+
mergeGate: state.mergeGate ?? null,
|
|
61
|
+
acceptanceChecked: [],
|
|
62
|
+
unblockedTaskIds: state.unblockedTaskIds ?? []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function createSupervisorClosureStage() {
|
|
66
|
+
return createClosureStage({ summarize: summarizeClosure });
|
|
67
|
+
}
|
|
68
|
+
function createDefaultSupervisorClosureStage() {
|
|
69
|
+
return async (ctx) => {
|
|
70
|
+
const summary = summarizeClosure(ctx);
|
|
71
|
+
const projectRoot = (ctx.metadata ?? {}).projectRoot;
|
|
72
|
+
if (summary && typeof projectRoot === "string") {
|
|
73
|
+
const { appendFileSync, mkdirSync } = await import("fs");
|
|
74
|
+
const { join } = await import("path");
|
|
75
|
+
try {
|
|
76
|
+
mkdirSync(join(projectRoot, ".rig"), { recursive: true });
|
|
77
|
+
appendFileSync(join(projectRoot, ".rig", "supervisor.jsonl"), `${JSON.stringify({ kind: "supervisor.outcome", at: new Date().toISOString(), taskId: summary.taskId, runId: summary.runId, status: summary.mergeGate === "passed" ? "completed" : "needs-attention", failed: false, unblockedTaskIds: summary.unblockedTaskIds, closure: summary })}
|
|
78
|
+
`);
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
return { kind: "continue", ctx };
|
|
82
|
+
};
|
|
83
|
+
}
|
|
49
84
|
var supervisorClosureStageMutation = {
|
|
50
85
|
op: "insert",
|
|
51
86
|
contributedBy: "@rig/supervisor-plugin",
|
|
@@ -58,6 +93,169 @@ var supervisorClosureStageMutation = {
|
|
|
58
93
|
protected: false
|
|
59
94
|
}
|
|
60
95
|
};
|
|
96
|
+
// packages/supervisor-plugin/src/cli.ts
|
|
97
|
+
import {
|
|
98
|
+
dispatchRun,
|
|
99
|
+
listRuns,
|
|
100
|
+
listTasks,
|
|
101
|
+
runSupervisorLoop,
|
|
102
|
+
unblockTask
|
|
103
|
+
} from "@rig/client";
|
|
104
|
+
var SUPERVISOR_LOOP_CLI_ID = "supervisor.loop";
|
|
105
|
+
var SUPERVISOR_UNBLOCK_CLI_ID = "supervisor.unblock";
|
|
106
|
+
function printJson(value) {
|
|
107
|
+
console.log(JSON.stringify(value, null, 2));
|
|
108
|
+
}
|
|
109
|
+
function takeFlag(args, flag) {
|
|
110
|
+
const rest = [...args];
|
|
111
|
+
const index = rest.indexOf(flag);
|
|
112
|
+
if (index < 0)
|
|
113
|
+
return { value: false, rest };
|
|
114
|
+
rest.splice(index, 1);
|
|
115
|
+
return { value: true, rest };
|
|
116
|
+
}
|
|
117
|
+
function takeOption(args, flag) {
|
|
118
|
+
const rest = [...args];
|
|
119
|
+
const index = rest.indexOf(flag);
|
|
120
|
+
if (index < 0)
|
|
121
|
+
return { rest };
|
|
122
|
+
const value = rest[index + 1];
|
|
123
|
+
if (!value || value.startsWith("-"))
|
|
124
|
+
throw new Error(`${flag} requires a value.`);
|
|
125
|
+
rest.splice(index, 2);
|
|
126
|
+
return { value, rest };
|
|
127
|
+
}
|
|
128
|
+
function requireNoExtraArgs(args, usage) {
|
|
129
|
+
if (args.length > 0)
|
|
130
|
+
throw new Error(`Unexpected argument: ${args[0]}
|
|
131
|
+
Usage: ${usage}`);
|
|
132
|
+
}
|
|
133
|
+
function parsePositiveInt(value, flag, fallback) {
|
|
134
|
+
if (value === undefined)
|
|
135
|
+
return fallback;
|
|
136
|
+
const parsed = Number.parseInt(value, 10);
|
|
137
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || String(parsed) !== value.trim()) {
|
|
138
|
+
throw new Error(`${flag} must be a positive integer.`);
|
|
139
|
+
}
|
|
140
|
+
return parsed;
|
|
141
|
+
}
|
|
142
|
+
function parseCsv(value) {
|
|
143
|
+
return value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [];
|
|
144
|
+
}
|
|
145
|
+
function asRunStatus(value) {
|
|
146
|
+
switch (value) {
|
|
147
|
+
case "created":
|
|
148
|
+
case "queued":
|
|
149
|
+
case "preparing":
|
|
150
|
+
case "running":
|
|
151
|
+
case "waiting-approval":
|
|
152
|
+
case "waiting-user-input":
|
|
153
|
+
case "paused":
|
|
154
|
+
case "validating":
|
|
155
|
+
case "reviewing":
|
|
156
|
+
case "closing-out":
|
|
157
|
+
case "needs-attention":
|
|
158
|
+
case "completed":
|
|
159
|
+
case "failed":
|
|
160
|
+
case "stopped":
|
|
161
|
+
return value;
|
|
162
|
+
default:
|
|
163
|
+
return "needs-attention";
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function delay(ms) {
|
|
167
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
168
|
+
setTimeout(resolve, ms);
|
|
169
|
+
return promise;
|
|
170
|
+
}
|
|
171
|
+
function supervisorDeps(timeoutMs) {
|
|
172
|
+
return {
|
|
173
|
+
listTasks,
|
|
174
|
+
listRuns,
|
|
175
|
+
dispatchRun,
|
|
176
|
+
awaitRunTerminal: async (projectRoot, runId) => {
|
|
177
|
+
const awaitedRunId = runId;
|
|
178
|
+
return awaitTerminalRun({
|
|
179
|
+
runId: awaitedRunId,
|
|
180
|
+
timeoutMs,
|
|
181
|
+
readStatus: async (id) => {
|
|
182
|
+
const run = (await listRuns(projectRoot)).find((candidate) => candidate.runId === id);
|
|
183
|
+
return run ? { runId: id, status: asRunStatus(run.status), ...run.errorSummary ? { diagnostic: run.errorSummary } : {} } : null;
|
|
184
|
+
},
|
|
185
|
+
waitForChange: delay
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function formatSupervisorResult(result) {
|
|
191
|
+
return [
|
|
192
|
+
`ok: ${result.ok}`,
|
|
193
|
+
`dry-run: ${result.dryRun}`,
|
|
194
|
+
`planned: ${result.plannedOrder.length > 0 ? result.plannedOrder.join(", ") : "none"}`,
|
|
195
|
+
`processed: ${result.projection.processed}`,
|
|
196
|
+
`succeeded: ${result.projection.succeeded}`,
|
|
197
|
+
`failed: ${result.projection.failed}`,
|
|
198
|
+
result.projection.idleReason ? `idle: ${result.projection.idleReason}` : null
|
|
199
|
+
].filter((line) => line !== null).join(`
|
|
200
|
+
`);
|
|
201
|
+
}
|
|
202
|
+
async function executeLoop(context, args) {
|
|
203
|
+
const dry = takeFlag(args, "--dry-run");
|
|
204
|
+
const json = takeFlag(dry.rest, "--json");
|
|
205
|
+
const maxTasks = takeOption(json.rest, "--max-tasks");
|
|
206
|
+
const concurrency = takeOption(maxTasks.rest, "--concurrency");
|
|
207
|
+
const stopWhen = takeOption(concurrency.rest, "--stop-when");
|
|
208
|
+
const timeout = takeOption(stopWhen.rest, "--timeout-ms");
|
|
209
|
+
requireNoExtraArgs(timeout.rest, "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]");
|
|
210
|
+
const result = await runSupervisorLoop(context.projectRoot, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 30 * 60 * 1000)), {
|
|
211
|
+
maxTasks: parsePositiveInt(maxTasks.value, "--max-tasks", 1),
|
|
212
|
+
concurrency: parsePositiveInt(concurrency.value, "--concurrency", 1),
|
|
213
|
+
dryRun: dry.value || context.dryRun
|
|
214
|
+
});
|
|
215
|
+
const details = { ...result, stopWhen: parseCsv(stopWhen.value) };
|
|
216
|
+
if (context.outputMode === "text") {
|
|
217
|
+
if (json.value)
|
|
218
|
+
printJson(details);
|
|
219
|
+
else
|
|
220
|
+
console.log(formatSupervisorResult(result));
|
|
221
|
+
}
|
|
222
|
+
return { ok: result.ok, group: "loop", command: "run", details };
|
|
223
|
+
}
|
|
224
|
+
async function executeUnblock(context, args) {
|
|
225
|
+
const dry = takeFlag(args, "--dry-run");
|
|
226
|
+
const json = takeFlag(dry.rest, "--json");
|
|
227
|
+
const timeout = takeOption(json.rest, "--timeout-ms");
|
|
228
|
+
const taskId = timeout.rest[0]?.startsWith("-") ? undefined : timeout.rest[0];
|
|
229
|
+
requireNoExtraArgs(taskId ? timeout.rest.slice(1) : timeout.rest, "rig unblock [task-id] [--dry-run] [--json]");
|
|
230
|
+
const result = await unblockTask(context.projectRoot, taskId ?? null, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 30 * 60 * 1000)), { dryRun: dry.value || context.dryRun });
|
|
231
|
+
if (context.outputMode === "text") {
|
|
232
|
+
if (json.value)
|
|
233
|
+
printJson(result);
|
|
234
|
+
else
|
|
235
|
+
console.log(formatSupervisorResult(result));
|
|
236
|
+
}
|
|
237
|
+
return { ok: result.ok, group: "unblock", command: "run", details: result };
|
|
238
|
+
}
|
|
239
|
+
var supervisorCliCommands = [
|
|
240
|
+
{
|
|
241
|
+
id: SUPERVISOR_LOOP_CLI_ID,
|
|
242
|
+
family: "loop",
|
|
243
|
+
command: "rig loop [--max-tasks <n>] [--concurrency <n>] [--dry-run]",
|
|
244
|
+
description: "Run the autonomous supervisor loop over ready tasks.",
|
|
245
|
+
usage: "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]",
|
|
246
|
+
projectRequired: true,
|
|
247
|
+
run: executeLoop
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: SUPERVISOR_UNBLOCK_CLI_ID,
|
|
251
|
+
family: "unblock",
|
|
252
|
+
command: "rig unblock [task-id] [--dry-run]",
|
|
253
|
+
description: "Plan or dispatch work that unblocks a blocked task.",
|
|
254
|
+
usage: "rig unblock [task-id] [--dry-run] [--json]",
|
|
255
|
+
projectRequired: true,
|
|
256
|
+
run: executeUnblock
|
|
257
|
+
}
|
|
258
|
+
];
|
|
61
259
|
// packages/supervisor-plugin/src/journal.ts
|
|
62
260
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
63
261
|
import { dirname } from "path";
|
|
@@ -124,9 +322,24 @@ var supervisorPlugin = definePlugin({
|
|
|
124
322
|
provides: [],
|
|
125
323
|
requires: ["journal", "transport"],
|
|
126
324
|
contributes: {
|
|
325
|
+
capabilities: [
|
|
326
|
+
{ id: "supervisor.loop", title: "Supervisor loop", commandId: SUPERVISOR_LOOP_CLI_ID },
|
|
327
|
+
{ id: "supervisor.unblock", title: "Supervisor unblock", commandId: SUPERVISOR_UNBLOCK_CLI_ID }
|
|
328
|
+
],
|
|
329
|
+
cliCommands: supervisorCliCommands.map(({ run: _run, ...metadata }) => metadata),
|
|
127
330
|
stageMutations: [supervisorClosureStageMutation]
|
|
128
331
|
}
|
|
332
|
+
}, {
|
|
333
|
+
stages: { [SUPERVISOR_CLOSURE_STAGE_ID]: createDefaultSupervisorClosureStage() },
|
|
334
|
+
featureCapabilities: [
|
|
335
|
+
{ id: "supervisor.loop", title: "Supervisor loop", commandId: SUPERVISOR_LOOP_CLI_ID },
|
|
336
|
+
{ id: "supervisor.unblock", title: "Supervisor unblock", commandId: SUPERVISOR_UNBLOCK_CLI_ID }
|
|
337
|
+
],
|
|
338
|
+
cliCommands: supervisorCliCommands
|
|
129
339
|
});
|
|
340
|
+
function createSupervisorPlugin() {
|
|
341
|
+
return supervisorPlugin;
|
|
342
|
+
}
|
|
130
343
|
// packages/supervisor-plugin/src/supervisor.ts
|
|
131
344
|
import {
|
|
132
345
|
computeTaskDependencyBadges,
|
|
@@ -298,13 +511,21 @@ async function runSupervisor(ctx, options = {}) {
|
|
|
298
511
|
export {
|
|
299
512
|
supervisorPlugin,
|
|
300
513
|
supervisorClosureStageMutation,
|
|
514
|
+
supervisorCliCommands,
|
|
301
515
|
runSupervisor,
|
|
302
516
|
parseSupervisorJournal,
|
|
517
|
+
executeUnblock,
|
|
518
|
+
executeLoop,
|
|
519
|
+
createSupervisorPlugin,
|
|
303
520
|
createSupervisorJournal,
|
|
521
|
+
createSupervisorClosureStage,
|
|
304
522
|
createInMemorySupervisorJournalStore,
|
|
305
523
|
createFileSupervisorJournal,
|
|
524
|
+
createDefaultSupervisorClosureStage,
|
|
306
525
|
createClosureStage,
|
|
307
526
|
awaitTerminalRun,
|
|
527
|
+
SUPERVISOR_UNBLOCK_CLI_ID,
|
|
308
528
|
SUPERVISOR_PLUGIN_NAME,
|
|
529
|
+
SUPERVISOR_LOOP_CLI_ID,
|
|
309
530
|
SUPERVISOR_CLOSURE_STAGE_ID
|
|
310
531
|
};
|
package/dist/src/plugin.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare const SUPERVISOR_PLUGIN_NAME = "@rig/supervisor-plugin";
|
|
2
2
|
export declare const supervisorPlugin: import("@rig/core").RigPluginWithRuntime;
|
|
3
|
+
export declare function createSupervisorPlugin(): import("@rig/core").RigPluginWithRuntime;
|
|
3
4
|
export default supervisorPlugin;
|
package/dist/src/plugin.js
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __require = import.meta.require;
|
|
3
|
+
|
|
2
4
|
// packages/supervisor-plugin/src/plugin.ts
|
|
3
5
|
import { definePlugin } from "@rig/core";
|
|
4
6
|
|
|
5
7
|
// packages/supervisor-plugin/src/closureStage.ts
|
|
6
8
|
var SUPERVISOR_CLOSURE_STAGE_ID = "supervisor-closure-observer";
|
|
9
|
+
function summarizeClosure(ctx) {
|
|
10
|
+
const state = (ctx.metadata ?? {}).closeoutState ?? null;
|
|
11
|
+
const taskId = typeof ctx.taskId === "string" ? ctx.taskId : null;
|
|
12
|
+
if (!state || !taskId)
|
|
13
|
+
return null;
|
|
14
|
+
return {
|
|
15
|
+
taskId,
|
|
16
|
+
runId: ctx.runId,
|
|
17
|
+
prUrl: state.prUrl ?? null,
|
|
18
|
+
mergeGate: state.mergeGate ?? null,
|
|
19
|
+
acceptanceChecked: [],
|
|
20
|
+
unblockedTaskIds: state.unblockedTaskIds ?? []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createDefaultSupervisorClosureStage() {
|
|
24
|
+
return async (ctx) => {
|
|
25
|
+
const summary = summarizeClosure(ctx);
|
|
26
|
+
const projectRoot = (ctx.metadata ?? {}).projectRoot;
|
|
27
|
+
if (summary && typeof projectRoot === "string") {
|
|
28
|
+
const { appendFileSync, mkdirSync } = await import("fs");
|
|
29
|
+
const { join } = await import("path");
|
|
30
|
+
try {
|
|
31
|
+
mkdirSync(join(projectRoot, ".rig"), { recursive: true });
|
|
32
|
+
appendFileSync(join(projectRoot, ".rig", "supervisor.jsonl"), `${JSON.stringify({ kind: "supervisor.outcome", at: new Date().toISOString(), taskId: summary.taskId, runId: summary.runId, status: summary.mergeGate === "passed" ? "completed" : "needs-attention", failed: false, unblockedTaskIds: summary.unblockedTaskIds, closure: summary })}
|
|
33
|
+
`);
|
|
34
|
+
} catch {}
|
|
35
|
+
}
|
|
36
|
+
return { kind: "continue", ctx };
|
|
37
|
+
};
|
|
38
|
+
}
|
|
7
39
|
var supervisorClosureStageMutation = {
|
|
8
40
|
op: "insert",
|
|
9
41
|
contributedBy: "@rig/supervisor-plugin",
|
|
@@ -17,6 +49,208 @@ var supervisorClosureStageMutation = {
|
|
|
17
49
|
}
|
|
18
50
|
};
|
|
19
51
|
|
|
52
|
+
// packages/supervisor-plugin/src/cli.ts
|
|
53
|
+
import {
|
|
54
|
+
dispatchRun,
|
|
55
|
+
listRuns,
|
|
56
|
+
listTasks,
|
|
57
|
+
runSupervisorLoop,
|
|
58
|
+
unblockTask
|
|
59
|
+
} from "@rig/client";
|
|
60
|
+
|
|
61
|
+
// packages/supervisor-plugin/src/awaiter.ts
|
|
62
|
+
var TERMINAL_RUN_STATUSES = {
|
|
63
|
+
created: false,
|
|
64
|
+
queued: false,
|
|
65
|
+
preparing: false,
|
|
66
|
+
running: false,
|
|
67
|
+
"waiting-approval": false,
|
|
68
|
+
"waiting-user-input": false,
|
|
69
|
+
paused: false,
|
|
70
|
+
validating: false,
|
|
71
|
+
reviewing: false,
|
|
72
|
+
"closing-out": false,
|
|
73
|
+
"needs-attention": true,
|
|
74
|
+
completed: true,
|
|
75
|
+
failed: true,
|
|
76
|
+
stopped: true
|
|
77
|
+
};
|
|
78
|
+
async function awaitTerminalRun(options) {
|
|
79
|
+
const startedAt = Date.now();
|
|
80
|
+
const pollMs = Math.max(0, Math.floor(options.pollMs ?? 5000));
|
|
81
|
+
while (true) {
|
|
82
|
+
const snapshot = await options.readStatus(options.runId);
|
|
83
|
+
if (snapshot && TERMINAL_RUN_STATUSES[snapshot.status]) {
|
|
84
|
+
return {
|
|
85
|
+
runId: options.runId,
|
|
86
|
+
status: snapshot.status,
|
|
87
|
+
failed: snapshot.status !== "completed"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (options.timeoutMs !== undefined && Date.now() - startedAt >= options.timeoutMs) {
|
|
91
|
+
return { runId: options.runId, status: "needs-attention", failed: true };
|
|
92
|
+
}
|
|
93
|
+
await options.waitForChange(pollMs);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// packages/supervisor-plugin/src/cli.ts
|
|
98
|
+
var SUPERVISOR_LOOP_CLI_ID = "supervisor.loop";
|
|
99
|
+
var SUPERVISOR_UNBLOCK_CLI_ID = "supervisor.unblock";
|
|
100
|
+
function printJson(value) {
|
|
101
|
+
console.log(JSON.stringify(value, null, 2));
|
|
102
|
+
}
|
|
103
|
+
function takeFlag(args, flag) {
|
|
104
|
+
const rest = [...args];
|
|
105
|
+
const index = rest.indexOf(flag);
|
|
106
|
+
if (index < 0)
|
|
107
|
+
return { value: false, rest };
|
|
108
|
+
rest.splice(index, 1);
|
|
109
|
+
return { value: true, rest };
|
|
110
|
+
}
|
|
111
|
+
function takeOption(args, flag) {
|
|
112
|
+
const rest = [...args];
|
|
113
|
+
const index = rest.indexOf(flag);
|
|
114
|
+
if (index < 0)
|
|
115
|
+
return { rest };
|
|
116
|
+
const value = rest[index + 1];
|
|
117
|
+
if (!value || value.startsWith("-"))
|
|
118
|
+
throw new Error(`${flag} requires a value.`);
|
|
119
|
+
rest.splice(index, 2);
|
|
120
|
+
return { value, rest };
|
|
121
|
+
}
|
|
122
|
+
function requireNoExtraArgs(args, usage) {
|
|
123
|
+
if (args.length > 0)
|
|
124
|
+
throw new Error(`Unexpected argument: ${args[0]}
|
|
125
|
+
Usage: ${usage}`);
|
|
126
|
+
}
|
|
127
|
+
function parsePositiveInt(value, flag, fallback) {
|
|
128
|
+
if (value === undefined)
|
|
129
|
+
return fallback;
|
|
130
|
+
const parsed = Number.parseInt(value, 10);
|
|
131
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || String(parsed) !== value.trim()) {
|
|
132
|
+
throw new Error(`${flag} must be a positive integer.`);
|
|
133
|
+
}
|
|
134
|
+
return parsed;
|
|
135
|
+
}
|
|
136
|
+
function parseCsv(value) {
|
|
137
|
+
return value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [];
|
|
138
|
+
}
|
|
139
|
+
function asRunStatus(value) {
|
|
140
|
+
switch (value) {
|
|
141
|
+
case "created":
|
|
142
|
+
case "queued":
|
|
143
|
+
case "preparing":
|
|
144
|
+
case "running":
|
|
145
|
+
case "waiting-approval":
|
|
146
|
+
case "waiting-user-input":
|
|
147
|
+
case "paused":
|
|
148
|
+
case "validating":
|
|
149
|
+
case "reviewing":
|
|
150
|
+
case "closing-out":
|
|
151
|
+
case "needs-attention":
|
|
152
|
+
case "completed":
|
|
153
|
+
case "failed":
|
|
154
|
+
case "stopped":
|
|
155
|
+
return value;
|
|
156
|
+
default:
|
|
157
|
+
return "needs-attention";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function delay(ms) {
|
|
161
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
162
|
+
setTimeout(resolve, ms);
|
|
163
|
+
return promise;
|
|
164
|
+
}
|
|
165
|
+
function supervisorDeps(timeoutMs) {
|
|
166
|
+
return {
|
|
167
|
+
listTasks,
|
|
168
|
+
listRuns,
|
|
169
|
+
dispatchRun,
|
|
170
|
+
awaitRunTerminal: async (projectRoot, runId) => {
|
|
171
|
+
const awaitedRunId = runId;
|
|
172
|
+
return awaitTerminalRun({
|
|
173
|
+
runId: awaitedRunId,
|
|
174
|
+
timeoutMs,
|
|
175
|
+
readStatus: async (id) => {
|
|
176
|
+
const run = (await listRuns(projectRoot)).find((candidate) => candidate.runId === id);
|
|
177
|
+
return run ? { runId: id, status: asRunStatus(run.status), ...run.errorSummary ? { diagnostic: run.errorSummary } : {} } : null;
|
|
178
|
+
},
|
|
179
|
+
waitForChange: delay
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function formatSupervisorResult(result) {
|
|
185
|
+
return [
|
|
186
|
+
`ok: ${result.ok}`,
|
|
187
|
+
`dry-run: ${result.dryRun}`,
|
|
188
|
+
`planned: ${result.plannedOrder.length > 0 ? result.plannedOrder.join(", ") : "none"}`,
|
|
189
|
+
`processed: ${result.projection.processed}`,
|
|
190
|
+
`succeeded: ${result.projection.succeeded}`,
|
|
191
|
+
`failed: ${result.projection.failed}`,
|
|
192
|
+
result.projection.idleReason ? `idle: ${result.projection.idleReason}` : null
|
|
193
|
+
].filter((line) => line !== null).join(`
|
|
194
|
+
`);
|
|
195
|
+
}
|
|
196
|
+
async function executeLoop(context, args) {
|
|
197
|
+
const dry = takeFlag(args, "--dry-run");
|
|
198
|
+
const json = takeFlag(dry.rest, "--json");
|
|
199
|
+
const maxTasks = takeOption(json.rest, "--max-tasks");
|
|
200
|
+
const concurrency = takeOption(maxTasks.rest, "--concurrency");
|
|
201
|
+
const stopWhen = takeOption(concurrency.rest, "--stop-when");
|
|
202
|
+
const timeout = takeOption(stopWhen.rest, "--timeout-ms");
|
|
203
|
+
requireNoExtraArgs(timeout.rest, "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]");
|
|
204
|
+
const result = await runSupervisorLoop(context.projectRoot, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 30 * 60 * 1000)), {
|
|
205
|
+
maxTasks: parsePositiveInt(maxTasks.value, "--max-tasks", 1),
|
|
206
|
+
concurrency: parsePositiveInt(concurrency.value, "--concurrency", 1),
|
|
207
|
+
dryRun: dry.value || context.dryRun
|
|
208
|
+
});
|
|
209
|
+
const details = { ...result, stopWhen: parseCsv(stopWhen.value) };
|
|
210
|
+
if (context.outputMode === "text") {
|
|
211
|
+
if (json.value)
|
|
212
|
+
printJson(details);
|
|
213
|
+
else
|
|
214
|
+
console.log(formatSupervisorResult(result));
|
|
215
|
+
}
|
|
216
|
+
return { ok: result.ok, group: "loop", command: "run", details };
|
|
217
|
+
}
|
|
218
|
+
async function executeUnblock(context, args) {
|
|
219
|
+
const dry = takeFlag(args, "--dry-run");
|
|
220
|
+
const json = takeFlag(dry.rest, "--json");
|
|
221
|
+
const timeout = takeOption(json.rest, "--timeout-ms");
|
|
222
|
+
const taskId = timeout.rest[0]?.startsWith("-") ? undefined : timeout.rest[0];
|
|
223
|
+
requireNoExtraArgs(taskId ? timeout.rest.slice(1) : timeout.rest, "rig unblock [task-id] [--dry-run] [--json]");
|
|
224
|
+
const result = await unblockTask(context.projectRoot, taskId ?? null, supervisorDeps(parsePositiveInt(timeout.value, "--timeout-ms", 30 * 60 * 1000)), { dryRun: dry.value || context.dryRun });
|
|
225
|
+
if (context.outputMode === "text") {
|
|
226
|
+
if (json.value)
|
|
227
|
+
printJson(result);
|
|
228
|
+
else
|
|
229
|
+
console.log(formatSupervisorResult(result));
|
|
230
|
+
}
|
|
231
|
+
return { ok: result.ok, group: "unblock", command: "run", details: result };
|
|
232
|
+
}
|
|
233
|
+
var supervisorCliCommands = [
|
|
234
|
+
{
|
|
235
|
+
id: SUPERVISOR_LOOP_CLI_ID,
|
|
236
|
+
family: "loop",
|
|
237
|
+
command: "rig loop [--max-tasks <n>] [--concurrency <n>] [--dry-run]",
|
|
238
|
+
description: "Run the autonomous supervisor loop over ready tasks.",
|
|
239
|
+
usage: "rig loop [--max-tasks <n>] [--concurrency <n>] [--stop-when <csv>] [--dry-run] [--json]",
|
|
240
|
+
projectRequired: true,
|
|
241
|
+
run: executeLoop
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: SUPERVISOR_UNBLOCK_CLI_ID,
|
|
245
|
+
family: "unblock",
|
|
246
|
+
command: "rig unblock [task-id] [--dry-run]",
|
|
247
|
+
description: "Plan or dispatch work that unblocks a blocked task.",
|
|
248
|
+
usage: "rig unblock [task-id] [--dry-run] [--json]",
|
|
249
|
+
projectRequired: true,
|
|
250
|
+
run: executeUnblock
|
|
251
|
+
}
|
|
252
|
+
];
|
|
253
|
+
|
|
20
254
|
// packages/supervisor-plugin/src/plugin.ts
|
|
21
255
|
var SUPERVISOR_PLUGIN_NAME = "@rig/supervisor-plugin";
|
|
22
256
|
var supervisorPlugin = definePlugin({
|
|
@@ -25,12 +259,28 @@ var supervisorPlugin = definePlugin({
|
|
|
25
259
|
provides: [],
|
|
26
260
|
requires: ["journal", "transport"],
|
|
27
261
|
contributes: {
|
|
262
|
+
capabilities: [
|
|
263
|
+
{ id: "supervisor.loop", title: "Supervisor loop", commandId: SUPERVISOR_LOOP_CLI_ID },
|
|
264
|
+
{ id: "supervisor.unblock", title: "Supervisor unblock", commandId: SUPERVISOR_UNBLOCK_CLI_ID }
|
|
265
|
+
],
|
|
266
|
+
cliCommands: supervisorCliCommands.map(({ run: _run, ...metadata }) => metadata),
|
|
28
267
|
stageMutations: [supervisorClosureStageMutation]
|
|
29
268
|
}
|
|
269
|
+
}, {
|
|
270
|
+
stages: { [SUPERVISOR_CLOSURE_STAGE_ID]: createDefaultSupervisorClosureStage() },
|
|
271
|
+
featureCapabilities: [
|
|
272
|
+
{ id: "supervisor.loop", title: "Supervisor loop", commandId: SUPERVISOR_LOOP_CLI_ID },
|
|
273
|
+
{ id: "supervisor.unblock", title: "Supervisor unblock", commandId: SUPERVISOR_UNBLOCK_CLI_ID }
|
|
274
|
+
],
|
|
275
|
+
cliCommands: supervisorCliCommands
|
|
30
276
|
});
|
|
277
|
+
function createSupervisorPlugin() {
|
|
278
|
+
return supervisorPlugin;
|
|
279
|
+
}
|
|
31
280
|
var plugin_default = supervisorPlugin;
|
|
32
281
|
export {
|
|
33
282
|
supervisorPlugin,
|
|
34
283
|
plugin_default as default,
|
|
284
|
+
createSupervisorPlugin,
|
|
35
285
|
SUPERVISOR_PLUGIN_NAME
|
|
36
286
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/supervisor-plugin",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.137",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "First-party autonomous supervisor loop plugin for Rig.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -41,8 +41,9 @@
|
|
|
41
41
|
"module": "./dist/src/index.js",
|
|
42
42
|
"types": "./dist/src/index.d.ts",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@rig/
|
|
45
|
-
"@rig/
|
|
44
|
+
"@rig/client": "npm:@h-rig/client@0.0.6-alpha.137",
|
|
45
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.137",
|
|
46
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.137",
|
|
46
47
|
"effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@8881a9b"
|
|
47
48
|
}
|
|
48
49
|
}
|