@ricsam/r5dctl 0.0.58 → 0.0.60
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/README.md +9 -0
- package/dist/cjs/cli.cjs +322 -189
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +317 -189
- package/dist/mjs/package.json +1 -1
- package/dist/types/cli.d.ts +97 -1
- package/package.json +2 -2
package/dist/cjs/package.json
CHANGED
package/dist/mjs/cli.mjs
CHANGED
|
@@ -78,6 +78,19 @@ const K8S_HELP_TEXT = [
|
|
|
78
78
|
"Show current and rolling CPU and memory usage for managed-vCluster workload containers.",
|
|
79
79
|
""
|
|
80
80
|
].join("\n");
|
|
81
|
+
const AGENT_HELP_TEXT = [
|
|
82
|
+
"Usage:",
|
|
83
|
+
' r5dctl -p <project> agent start --worker <label> --source worktree:<branch> <research|debug|test> "<prompt>"',
|
|
84
|
+
" r5dctl agent status <session-id>",
|
|
85
|
+
' r5dctl agent prompt --worker <label> <session-id> "<prompt>"',
|
|
86
|
+
" r5dctl agent merge --worker <label> <session-id>",
|
|
87
|
+
" r5dctl agent stop <session-id>",
|
|
88
|
+
' r5dctl agent finish-merge --worker <label> <resolver-session-id> --summary "<summary>"',
|
|
89
|
+
"",
|
|
90
|
+
"Create, inspect, resume, stop, and merge independent branch agents.",
|
|
91
|
+
"The source is the named branch worktree, including its committed and uncommitted files.",
|
|
92
|
+
""
|
|
93
|
+
].join("\n");
|
|
81
94
|
const SHARED_HELP_ENTRIES = [
|
|
82
95
|
{ section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
|
|
83
96
|
{ section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
|
|
@@ -201,18 +214,34 @@ const SHARED_HELP_ENTRIES = [
|
|
|
201
214
|
{ section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." },
|
|
202
215
|
{
|
|
203
216
|
section: "agents",
|
|
204
|
-
usage: '-p <project>
|
|
205
|
-
description: "
|
|
217
|
+
usage: '-p <project> agent start --worker <label> --source worktree:<branch> <research|debug|test> "<prompt>"',
|
|
218
|
+
description: "Create a branch from an explicit worktree source and start an independent agent."
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
section: "agents",
|
|
222
|
+
usage: "agent status <session-id>",
|
|
223
|
+
description: "Show branch-agent execution, publication, and merge status."
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
section: "agents",
|
|
227
|
+
usage: 'agent prompt --worker <label> <session-id> "<prompt>"',
|
|
228
|
+
description: "Queue a prompt or resume an idle branch agent on the selected worker."
|
|
206
229
|
},
|
|
207
|
-
{ section: "agents", usage: "agent-status <session-id>", description: "Show detached branch agent status." },
|
|
208
|
-
{ section: "agents", usage: 'send-prompt <session-id> "<prompt>"', description: "Queue or resume a detached branch agent." },
|
|
209
230
|
{
|
|
210
|
-
section: "
|
|
211
|
-
usage: "
|
|
212
|
-
description: "
|
|
231
|
+
section: "agents",
|
|
232
|
+
usage: "agent merge --worker <label> <session-id>",
|
|
233
|
+
description: "Merge a terminal branch agent into its recorded parent worktree."
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
section: "agents",
|
|
237
|
+
usage: "agent stop <session-id>",
|
|
238
|
+
description: "Stop a branch agent or merge resolver."
|
|
213
239
|
},
|
|
214
|
-
{
|
|
215
|
-
|
|
240
|
+
{
|
|
241
|
+
section: "agents",
|
|
242
|
+
usage: 'agent finish-merge --worker <label> <resolver-session-id> --summary "<summary>"',
|
|
243
|
+
description: "Publish and finalize a manually resolved merge."
|
|
244
|
+
}
|
|
216
245
|
];
|
|
217
246
|
const HELP_SECTION_ORDER = [
|
|
218
247
|
"auth",
|
|
@@ -224,8 +253,7 @@ const HELP_SECTION_ORDER = [
|
|
|
224
253
|
"kubernetes",
|
|
225
254
|
"processes",
|
|
226
255
|
"shell",
|
|
227
|
-
"agents"
|
|
228
|
-
"merges"
|
|
256
|
+
"agents"
|
|
229
257
|
];
|
|
230
258
|
const HELP_SECTION_TITLES = {
|
|
231
259
|
auth: "Auth",
|
|
@@ -237,8 +265,7 @@ const HELP_SECTION_TITLES = {
|
|
|
237
265
|
kubernetes: "Kubernetes",
|
|
238
266
|
processes: "Processes",
|
|
239
267
|
shell: "Shell",
|
|
240
|
-
agents: "
|
|
241
|
-
merges: "Merges"
|
|
268
|
+
agents: "Branch agents"
|
|
242
269
|
};
|
|
243
270
|
function getCliHelpText() {
|
|
244
271
|
const entries = [...CLI_ONLY_HELP_ENTRIES, ...SHARED_HELP_ENTRIES];
|
|
@@ -337,7 +364,7 @@ function renderCliOnlyCommandHelp(entry) {
|
|
|
337
364
|
function renderSharedCommandHelp(pathSegments) {
|
|
338
365
|
const entry = SHARED_HELP_ENTRIES.map((candidate) => ({
|
|
339
366
|
candidate,
|
|
340
|
-
usageSegments: candidate.usage.split(" ").map((segment) => segment.replace(/^\[/, "").replace(/\]$/, "")).filter((segment) => !segment.startsWith("-") && !segment.
|
|
367
|
+
usageSegments: candidate.usage.split(" ").map((segment) => segment.replace(/^\[/, "").replace(/\]$/, "")).filter((segment) => !segment.startsWith("-") && !segment.includes("<") && !segment.startsWith("["))
|
|
341
368
|
})).filter(
|
|
342
369
|
({ usageSegments }) => usageSegments.length <= pathSegments.length && usageSegments.every((segment, index) => segment === pathSegments[index])
|
|
343
370
|
).sort((left, right) => right.usageSegments.length - left.usageSegments.length)[0]?.candidate;
|
|
@@ -385,6 +412,108 @@ function parseOptionalFlagValue(args, flag, shortFlag) {
|
|
|
385
412
|
function hasBooleanFlag(args, flag) {
|
|
386
413
|
return args.includes(flag);
|
|
387
414
|
}
|
|
415
|
+
function parseAgentOperands(args, valueFlags, options = {}) {
|
|
416
|
+
const positionals = [];
|
|
417
|
+
const values = /* @__PURE__ */ new Map();
|
|
418
|
+
let parseOptions = true;
|
|
419
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
420
|
+
const arg = args[index];
|
|
421
|
+
if (parseOptions && arg === "--") {
|
|
422
|
+
parseOptions = false;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const inlineFlag = parseOptions ? [...valueFlags].find((flag) => arg.startsWith(`${flag}=`)) : void 0;
|
|
426
|
+
if (inlineFlag) {
|
|
427
|
+
if (values.has(inlineFlag)) throw new Error(`${inlineFlag} may only be provided once`);
|
|
428
|
+
const value = arg.slice(inlineFlag.length + 1).trim();
|
|
429
|
+
if (!value) throw new Error(`Missing value for ${inlineFlag}`);
|
|
430
|
+
values.set(inlineFlag, value);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (parseOptions && valueFlags.has(arg)) {
|
|
434
|
+
if (values.has(arg)) throw new Error(`${arg} may only be provided once`);
|
|
435
|
+
const value = args[index + 1];
|
|
436
|
+
if (!value || value === "--" || valueFlags.has(value)) throw new Error(`Missing value for ${arg}`);
|
|
437
|
+
values.set(arg, value.trim());
|
|
438
|
+
index += 1;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (parseOptions && arg.startsWith("-") && positionals.length < (options.allowFlagLikeValuesAfter ?? Number.POSITIVE_INFINITY)) {
|
|
442
|
+
throw new Error(`Unknown agent flag: ${arg}`);
|
|
443
|
+
}
|
|
444
|
+
positionals.push(arg);
|
|
445
|
+
}
|
|
446
|
+
return { positionals, values };
|
|
447
|
+
}
|
|
448
|
+
function requireAgentWorker(values) {
|
|
449
|
+
return requireValue(values.get("--worker")?.trim(), "--worker <label> is required");
|
|
450
|
+
}
|
|
451
|
+
function requireAgentSource(values) {
|
|
452
|
+
const raw = requireValue(values.get("--source")?.trim(), "--source worktree:<branch> is required for `agent start`");
|
|
453
|
+
const separator = raw.indexOf(":");
|
|
454
|
+
const type = raw.slice(0, separator);
|
|
455
|
+
const branchName = raw.slice(separator + 1).trim();
|
|
456
|
+
if (type !== "worktree" || separator < 1 || !branchName) {
|
|
457
|
+
throw new Error("Invalid --source. Expected worktree:<branch>");
|
|
458
|
+
}
|
|
459
|
+
return { type, branchName };
|
|
460
|
+
}
|
|
461
|
+
function rejectAgentGlobalScopes(options, allowProject) {
|
|
462
|
+
if (!allowProject && options.project) throw new Error("--project/-p is only supported for `agent start`");
|
|
463
|
+
if (options.branch) throw new Error("--branch/-b is not supported for `agent`; pass --source explicitly");
|
|
464
|
+
if (options.session) throw new Error("--session/-s is not supported for `agent`; pass the session id explicitly");
|
|
465
|
+
}
|
|
466
|
+
function parseBranchAgentCommand(options, args) {
|
|
467
|
+
const subcommand = requireValue(args[0], "Missing agent command");
|
|
468
|
+
const commandArgs = args.slice(1);
|
|
469
|
+
if (subcommand === "start") {
|
|
470
|
+
rejectAgentGlobalScopes(options, true);
|
|
471
|
+
const project = requireValue(options.project, "--project/-p is required for `agent start`");
|
|
472
|
+
const parsed = parseAgentOperands(commandArgs, /* @__PURE__ */ new Set(["--worker", "--source"]), { allowFlagLikeValuesAfter: 1 });
|
|
473
|
+
const agentType = requireValue(parsed.positionals[0], "Agent type is required for `agent start`");
|
|
474
|
+
if (!AGENT_TYPES.has(agentType)) {
|
|
475
|
+
throw new Error("Invalid agent type. Expected one of: research, debug, test");
|
|
476
|
+
}
|
|
477
|
+
const prompt = parsed.positionals.slice(1).join(" ").trim();
|
|
478
|
+
if (!prompt) throw new Error("Agent prompt is required for `agent start`");
|
|
479
|
+
return {
|
|
480
|
+
kind: "start",
|
|
481
|
+
project,
|
|
482
|
+
worker: requireAgentWorker(parsed.values),
|
|
483
|
+
source: requireAgentSource(parsed.values),
|
|
484
|
+
agentType,
|
|
485
|
+
prompt
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
rejectAgentGlobalScopes(options, false);
|
|
489
|
+
if (subcommand === "status" || subcommand === "stop") {
|
|
490
|
+
const parsed = parseAgentOperands(commandArgs, /* @__PURE__ */ new Set());
|
|
491
|
+
const sessionId = requireValue(parsed.positionals[0], `Session id is required for \`agent ${subcommand}\``);
|
|
492
|
+
if (parsed.positionals.length > 1) throw new Error(`Unexpected agent ${subcommand} argument: ${parsed.positionals[1]}`);
|
|
493
|
+
return { kind: subcommand, sessionId };
|
|
494
|
+
}
|
|
495
|
+
if (subcommand === "prompt") {
|
|
496
|
+
const parsed = parseAgentOperands(commandArgs, /* @__PURE__ */ new Set(["--worker"]), { allowFlagLikeValuesAfter: 1 });
|
|
497
|
+
const sessionId = requireValue(parsed.positionals[0], "Session id is required for `agent prompt`");
|
|
498
|
+
const prompt = parsed.positionals.slice(1).join(" ").trim();
|
|
499
|
+
if (!prompt) throw new Error("Prompt is required for `agent prompt`");
|
|
500
|
+
return { kind: "prompt", worker: requireAgentWorker(parsed.values), sessionId, prompt };
|
|
501
|
+
}
|
|
502
|
+
if (subcommand === "merge") {
|
|
503
|
+
const parsed = parseAgentOperands(commandArgs, /* @__PURE__ */ new Set(["--worker"]));
|
|
504
|
+
const sessionId = requireValue(parsed.positionals[0], "Session id is required for `agent merge`");
|
|
505
|
+
if (parsed.positionals.length > 1) throw new Error(`Unexpected agent merge argument: ${parsed.positionals[1]}`);
|
|
506
|
+
return { kind: "merge", worker: requireAgentWorker(parsed.values), sessionId };
|
|
507
|
+
}
|
|
508
|
+
if (subcommand === "finish-merge") {
|
|
509
|
+
const parsed = parseAgentOperands(commandArgs, /* @__PURE__ */ new Set(["--worker", "--summary"]));
|
|
510
|
+
const resolverSessionId = requireValue(parsed.positionals[0], "Resolver session id is required for `agent finish-merge`");
|
|
511
|
+
if (parsed.positionals.length > 1) throw new Error(`Unexpected agent finish-merge argument: ${parsed.positionals[1]}`);
|
|
512
|
+
const summary = requireValue(parsed.values.get("--summary")?.trim(), "--summary <text> is required for `agent finish-merge`");
|
|
513
|
+
return { kind: "finish-merge", worker: requireAgentWorker(parsed.values), resolverSessionId, summary };
|
|
514
|
+
}
|
|
515
|
+
throw new Error(`Unknown agent command: ${subcommand}`);
|
|
516
|
+
}
|
|
388
517
|
function assertAuthLoginArgs(args) {
|
|
389
518
|
const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
|
|
390
519
|
const booleanFlags = /* @__PURE__ */ new Set(["--no-open", "--no-qr"]);
|
|
@@ -1157,7 +1286,11 @@ function renderWorkspaceStatus(status) {
|
|
|
1157
1286
|
const lines = [
|
|
1158
1287
|
`Workspace HEAD: ${status.head ?? "(not initialized)"}`,
|
|
1159
1288
|
`Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
|
|
1160
|
-
`Remediation: ${status.incident ? `${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}
|
|
1289
|
+
`Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
|
|
1290
|
+
`Materialization: ${status.materialization ? `${status.materialization.state}; ${status.materialization.materializedHead ?? "(none)"} -> ${status.materialization.desiredHead ?? "(none)"}; lag ${status.materialization.lagMs}ms` : "not initialized"}`,
|
|
1291
|
+
...status.workers.map(
|
|
1292
|
+
(worker) => `Worker ${worker.workerLabel}: ${worker.ready ? "ready" : "bootstrapping"}; publication ${worker.publicationState}; head ${worker.localHead ?? "(none)"}; desired ${worker.desiredCanonicalHead ?? "(none)"}; generations ${worker.publishedGeneration}/${worker.dirtyGeneration}; pending checkouts ${worker.pendingCheckouts.length}; CAS retries ${worker.casRetries}; fan-out lag ${worker.fanoutLagMs ?? "unknown"}ms`
|
|
1293
|
+
)
|
|
1161
1294
|
];
|
|
1162
1295
|
return `${lines.join("\n")}
|
|
1163
1296
|
`;
|
|
@@ -1526,43 +1659,164 @@ async function setProjectEnvs(client, projectRef, args) {
|
|
|
1526
1659
|
}
|
|
1527
1660
|
return result;
|
|
1528
1661
|
}
|
|
1529
|
-
function
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
if (
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1662
|
+
function responseString(response, key) {
|
|
1663
|
+
const value = response[key];
|
|
1664
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1665
|
+
}
|
|
1666
|
+
function responseSource(response) {
|
|
1667
|
+
const source = response.source;
|
|
1668
|
+
if (!source || source.type !== "worktree" || !source.branchName) return void 0;
|
|
1669
|
+
return `${source.type}:${source.branchName}`;
|
|
1670
|
+
}
|
|
1671
|
+
function formatStatusValue(value) {
|
|
1672
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
1673
|
+
if (typeof value === "object" && value !== null) {
|
|
1674
|
+
const status = value.status;
|
|
1675
|
+
if (typeof status === "string" && status.length > 0) return status;
|
|
1676
|
+
return JSON.stringify(value);
|
|
1677
|
+
}
|
|
1678
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
1679
|
+
}
|
|
1680
|
+
function asResponseRecord(value) {
|
|
1681
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
1682
|
+
}
|
|
1683
|
+
function appendMergeDetails(lines, response) {
|
|
1684
|
+
const merge = asResponseRecord(response.merge);
|
|
1685
|
+
if (!merge) return;
|
|
1686
|
+
const mergeStatus = formatStatusValue(merge.status);
|
|
1687
|
+
lines.push("", `Merge: ${mergeStatus ?? "pending"}`);
|
|
1688
|
+
for (const [label, key] of [
|
|
1689
|
+
["Attempt", "attemptId"],
|
|
1690
|
+
["Source", "sourceBranch"],
|
|
1691
|
+
["Target", "targetBranch"],
|
|
1692
|
+
["Commit", "commitHash"],
|
|
1693
|
+
["Resolver agent", "resolverSessionId"],
|
|
1694
|
+
["Resolver branch", "resolverBranch"],
|
|
1695
|
+
["Resolver worktree", "folderPath"]
|
|
1696
|
+
]) {
|
|
1697
|
+
const value = responseString(merge, key);
|
|
1698
|
+
if (value) lines.push(`${label}: ${value}`);
|
|
1699
|
+
}
|
|
1700
|
+
const conflictedFiles = Array.isArray(merge.conflictedFiles) ? merge.conflictedFiles.filter((file) => typeof file === "string") : [];
|
|
1701
|
+
if (conflictedFiles.length > 0) {
|
|
1702
|
+
lines.push("", "Conflicts:", ...conflictedFiles.map((file) => `- ${file}`));
|
|
1703
|
+
}
|
|
1704
|
+
const message = responseString(merge, "message");
|
|
1705
|
+
if (message) lines.push("", message);
|
|
1706
|
+
const failureReason = responseString(merge, "failureReason");
|
|
1707
|
+
if (failureReason) lines.push(`Failure: ${failureReason}`);
|
|
1708
|
+
const resolverSessionId = responseString(merge, "resolverSessionId");
|
|
1709
|
+
const worker = responseString(merge, "worker") ?? responseString(response, "worker");
|
|
1710
|
+
if (resolverSessionId && worker) {
|
|
1711
|
+
lines.push(
|
|
1712
|
+
"",
|
|
1713
|
+
"Resolver takeover:",
|
|
1714
|
+
` r5dctl agent stop ${resolverSessionId}`,
|
|
1715
|
+
` r5dctl agent finish-merge --worker ${worker} ${resolverSessionId} --summary "<summary>"`
|
|
1716
|
+
);
|
|
1547
1717
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
1718
|
+
}
|
|
1719
|
+
function renderBranchAgentStart(agent) {
|
|
1720
|
+
const lines = [
|
|
1721
|
+
`Agent: ${responseString(agent, "sessionId") ?? "(unknown)"}`,
|
|
1722
|
+
`Status: ${formatStatusValue(agent.status) ?? "queued"}`,
|
|
1723
|
+
`Branch: ${responseString(agent, "branchName") ?? "(provisioning)"}`,
|
|
1724
|
+
`Source: ${responseSource(agent) ?? "(unknown)"}`,
|
|
1725
|
+
`Workspace head: ${responseString(agent, "workspaceHead") ?? "(pending)"}`,
|
|
1726
|
+
`Baseline: ${responseString(agent, "baselineCommit") ?? "(pending)"}`,
|
|
1727
|
+
`Worker: ${responseString(agent, "worker") ?? "(unassigned)"}`
|
|
1728
|
+
];
|
|
1729
|
+
const sessionUrl = responseString(agent, "sessionUrl");
|
|
1730
|
+
if (sessionUrl) lines.push(`Session: ${sessionUrl}`);
|
|
1731
|
+
const sessionId = responseString(agent, "sessionId");
|
|
1732
|
+
if (sessionId) lines.push("", `Next: r5dctl agent status ${sessionId}`);
|
|
1733
|
+
return `${lines.join("\n")}
|
|
1734
|
+
`;
|
|
1735
|
+
}
|
|
1736
|
+
function renderBranchAgentStatus(agent) {
|
|
1737
|
+
const status = formatStatusValue(agent.status) ?? "unknown";
|
|
1738
|
+
const lines = [`Agent: ${responseString(agent, "sessionId") ?? "(unknown)"}`, `Status: ${status}`];
|
|
1739
|
+
const runStatus = formatStatusValue(agent.runStatus);
|
|
1740
|
+
if (runStatus) lines.push(`Run: ${runStatus}`);
|
|
1741
|
+
const promptDisposition = responseString(agent, "promptDisposition");
|
|
1742
|
+
if (promptDisposition) lines.push(`Prompt: ${promptDisposition}`);
|
|
1743
|
+
const branch = responseString(agent, "branchName");
|
|
1744
|
+
if (branch) lines.push(`Branch: ${branch}`);
|
|
1745
|
+
const source = responseSource(agent);
|
|
1746
|
+
if (source) lines.push(`Source: ${source}`);
|
|
1747
|
+
for (const [label, key] of [
|
|
1748
|
+
["Workspace head", "workspaceHead"],
|
|
1749
|
+
["Baseline", "baselineCommit"],
|
|
1750
|
+
["Head", "headCommit"],
|
|
1751
|
+
["Worker", "worker"],
|
|
1752
|
+
["Session", "sessionUrl"]
|
|
1753
|
+
]) {
|
|
1754
|
+
const value = responseString(agent, key);
|
|
1755
|
+
if (value) lines.push(`${label}: ${value}`);
|
|
1756
|
+
}
|
|
1757
|
+
const error = responseString(agent, "error");
|
|
1758
|
+
if (error) lines.push(`Error: ${error}`);
|
|
1759
|
+
const headCommitError = responseString(agent, "headCommitError");
|
|
1760
|
+
if (headCommitError) lines.push(`Head error: ${headCommitError}`);
|
|
1761
|
+
const diffSummary = responseString(agent, "diffSummary");
|
|
1762
|
+
if (diffSummary) lines.push("", diffSummary);
|
|
1763
|
+
const summary = responseString(agent, "summary");
|
|
1764
|
+
if (summary) lines.push("", summary);
|
|
1765
|
+
appendMergeDetails(lines, agent);
|
|
1766
|
+
const sessionId = responseString(agent, "sessionId");
|
|
1767
|
+
if (sessionId && !asResponseRecord(agent.merge)) {
|
|
1768
|
+
if (["provisioning", "queued", "running"].includes(status)) {
|
|
1769
|
+
lines.push("", "Next:", ` r5dctl agent status ${sessionId}`, ` r5dctl agent stop ${sessionId}`);
|
|
1770
|
+
} else if (["completed", "failed", "stopped"].includes(status)) {
|
|
1771
|
+
const worker = responseString(agent, "worker") ?? "<worker>";
|
|
1772
|
+
lines.push("", "Next:", ` r5dctl agent merge --worker ${worker} ${sessionId}`);
|
|
1773
|
+
}
|
|
1550
1774
|
}
|
|
1551
1775
|
return `${lines.join("\n")}
|
|
1552
1776
|
`;
|
|
1553
1777
|
}
|
|
1554
|
-
function
|
|
1555
|
-
if (result.
|
|
1556
|
-
|
|
1557
|
-
|
|
1778
|
+
function renderBranchAgentMerge(result) {
|
|
1779
|
+
if (asResponseRecord(result.merge)) return renderBranchAgentStatus(result);
|
|
1780
|
+
const wrapped = {
|
|
1781
|
+
sessionId: result.sessionId,
|
|
1782
|
+
status: result.status,
|
|
1783
|
+
worker: result.worker,
|
|
1784
|
+
merge: result
|
|
1785
|
+
};
|
|
1786
|
+
const lines = [];
|
|
1787
|
+
appendMergeDetails(lines, wrapped);
|
|
1788
|
+
return `${lines.slice(lines[0] === "" ? 1 : 0).join("\n")}
|
|
1558
1789
|
`;
|
|
1790
|
+
}
|
|
1791
|
+
async function dispatchBranchAgentCommand(client, command) {
|
|
1792
|
+
if (command.kind === "start") {
|
|
1793
|
+
const data2 = await client.projects.agents.start(command.project, {
|
|
1794
|
+
source: command.source,
|
|
1795
|
+
agentType: command.agentType,
|
|
1796
|
+
prompt: command.prompt,
|
|
1797
|
+
worker: command.worker,
|
|
1798
|
+
requestId: randomUUID()
|
|
1799
|
+
});
|
|
1800
|
+
return { data: data2, human: renderBranchAgentStart(data2) };
|
|
1559
1801
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1802
|
+
if (command.kind === "status") {
|
|
1803
|
+
const data2 = await client.agents.status(command.sessionId);
|
|
1804
|
+
return { data: data2, human: renderBranchAgentStatus(data2) };
|
|
1805
|
+
}
|
|
1806
|
+
if (command.kind === "prompt") {
|
|
1807
|
+
const data2 = await client.agents.sendPrompt(command.sessionId, { prompt: command.prompt, worker: command.worker });
|
|
1808
|
+
return { data: data2, human: renderBranchAgentStatus(data2) };
|
|
1809
|
+
}
|
|
1810
|
+
if (command.kind === "merge") {
|
|
1811
|
+
const data2 = await client.agents.merge(command.sessionId, { worker: command.worker });
|
|
1812
|
+
return { data: data2, human: renderBranchAgentMerge(data2) };
|
|
1813
|
+
}
|
|
1814
|
+
if (command.kind === "stop") {
|
|
1815
|
+
const data2 = await client.agents.stop(command.sessionId);
|
|
1816
|
+
return { data: data2, human: renderBranchAgentStatus(data2) };
|
|
1817
|
+
}
|
|
1818
|
+
const data = await client.agents.finishMerge(command.resolverSessionId, { worker: command.worker, summary: command.summary });
|
|
1819
|
+
return { data, human: renderBranchAgentMerge(data) };
|
|
1566
1820
|
}
|
|
1567
1821
|
function parseProjectUpdateArgs(args) {
|
|
1568
1822
|
const input = {};
|
|
@@ -1862,65 +2116,6 @@ Sessions: ${result.sessions.length}
|
|
|
1862
2116
|
write(session, renderSessionDescription(session));
|
|
1863
2117
|
return;
|
|
1864
2118
|
}
|
|
1865
|
-
if (first === "start-agent") {
|
|
1866
|
-
const projectRef = requireValue(args[1], "Missing project reference");
|
|
1867
|
-
const sourceBranch = requireValue(args[2], "Missing source branch");
|
|
1868
|
-
const agentType = requireValue(args[3], "Missing agent type");
|
|
1869
|
-
if (!AGENT_TYPES.has(agentType)) {
|
|
1870
|
-
throw new Error("Invalid agent type. Expected one of: research, debug, test");
|
|
1871
|
-
}
|
|
1872
|
-
const prompt = args.slice(4).join(" ").trim();
|
|
1873
|
-
if (!prompt) {
|
|
1874
|
-
throw new Error("Agent prompt is required");
|
|
1875
|
-
}
|
|
1876
|
-
const agent = await client.projects.agents.start(projectRef, {
|
|
1877
|
-
sourceBranch,
|
|
1878
|
-
agentType,
|
|
1879
|
-
prompt
|
|
1880
|
-
});
|
|
1881
|
-
write(agent, renderAgentStart(agent));
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
if (first === "agent-status") {
|
|
1885
|
-
const agent = await client.agents.status(requireValue(args[1], "Missing session id"));
|
|
1886
|
-
write(agent, renderAgentStatus(agent));
|
|
1887
|
-
return;
|
|
1888
|
-
}
|
|
1889
|
-
if (first === "send-prompt") {
|
|
1890
|
-
const sessionId = requireValue(args[1], "Missing session id");
|
|
1891
|
-
const prompt = args.slice(2).join(" ").trim();
|
|
1892
|
-
if (!prompt) {
|
|
1893
|
-
throw new Error("Prompt is required");
|
|
1894
|
-
}
|
|
1895
|
-
const agent = await client.agents.sendPrompt(sessionId, { prompt });
|
|
1896
|
-
write(agent, renderAgentStatus(agent));
|
|
1897
|
-
return;
|
|
1898
|
-
}
|
|
1899
|
-
if (first === "merge-changes") {
|
|
1900
|
-
const result = await client.projects.mergeChanges(requireValue(args[1], "Missing project reference"), {
|
|
1901
|
-
targetBranch: requireValue(args[2], "Missing target branch"),
|
|
1902
|
-
sourceBranch: requireValue(args[3], "Missing source branch")
|
|
1903
|
-
});
|
|
1904
|
-
write(result, renderMergeResult(result));
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
if (first === "continue-merge") {
|
|
1908
|
-
const result = await client.projects.continueMerge(requireValue(args[1], "Missing project reference"), {
|
|
1909
|
-
targetBranch: requireValue(args[2], "Missing target branch")
|
|
1910
|
-
});
|
|
1911
|
-
write(result, `Merge committed: ${result.commitHash}
|
|
1912
|
-
${result.message}
|
|
1913
|
-
`);
|
|
1914
|
-
return;
|
|
1915
|
-
}
|
|
1916
|
-
if (first === "abort-merge") {
|
|
1917
|
-
const result = await client.projects.abortMerge(requireValue(args[1], "Missing project reference"), {
|
|
1918
|
-
targetBranch: requireValue(args[2], "Missing target branch")
|
|
1919
|
-
});
|
|
1920
|
-
write(result, `${result.message}
|
|
1921
|
-
`);
|
|
1922
|
-
return;
|
|
1923
|
-
}
|
|
1924
2119
|
if (first === "sessions" && second === "describe" || first === "describe" && second === "session") {
|
|
1925
2120
|
const session = await client.sessions.describe(requireValue(args[2], "Missing session id"));
|
|
1926
2121
|
write(session, renderSessionDescription(session));
|
|
@@ -2016,6 +2211,12 @@ function resolveCommandExecution(options, rest) {
|
|
|
2016
2211
|
text: K8S_HELP_TEXT
|
|
2017
2212
|
};
|
|
2018
2213
|
}
|
|
2214
|
+
if (command === "agent" && commandArgs.length === 0) {
|
|
2215
|
+
return {
|
|
2216
|
+
kind: "cli-help",
|
|
2217
|
+
text: AGENT_HELP_TEXT
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2019
2220
|
if (trailingHelp) {
|
|
2020
2221
|
const cliOnlyHelp = findCliOnlyCommandHelp(normalizedRest);
|
|
2021
2222
|
if (cliOnlyHelp) {
|
|
@@ -2420,91 +2621,8 @@ function resolveCommandExecution(options, rest) {
|
|
|
2420
2621
|
pluginArgs: ["prompt", sessionId, parsed.mode, parsed.model, parsed.message]
|
|
2421
2622
|
};
|
|
2422
2623
|
}
|
|
2423
|
-
if (command === "
|
|
2424
|
-
|
|
2425
|
-
throw new Error("--project/-p is required for `start-agent`");
|
|
2426
|
-
}
|
|
2427
|
-
const sourceBranch = commandArgs[0] ?? options.branch;
|
|
2428
|
-
if (!sourceBranch) {
|
|
2429
|
-
throw new Error("Source branch is required for `start-agent`");
|
|
2430
|
-
}
|
|
2431
|
-
const agentType = requireValue(commandArgs[1], "Agent type is required for `start-agent`");
|
|
2432
|
-
if (!AGENT_TYPES.has(agentType)) {
|
|
2433
|
-
throw new Error("Invalid agent type. Expected one of: research, debug, test");
|
|
2434
|
-
}
|
|
2435
|
-
const prompt = commandArgs.slice(2).join(" ").trim();
|
|
2436
|
-
if (!prompt) {
|
|
2437
|
-
throw new Error("Agent prompt is required");
|
|
2438
|
-
}
|
|
2439
|
-
return {
|
|
2440
|
-
kind: "plugin",
|
|
2441
|
-
pluginArgs: ["start-agent", options.project, sourceBranch, agentType, prompt]
|
|
2442
|
-
};
|
|
2443
|
-
}
|
|
2444
|
-
if (command === "agent-status") {
|
|
2445
|
-
const sessionId = commandArgs[0] ?? options.session;
|
|
2446
|
-
if (!sessionId) {
|
|
2447
|
-
throw new Error("Session id is required for `agent-status`");
|
|
2448
|
-
}
|
|
2449
|
-
return {
|
|
2450
|
-
kind: "plugin",
|
|
2451
|
-
pluginArgs: ["agent-status", sessionId]
|
|
2452
|
-
};
|
|
2453
|
-
}
|
|
2454
|
-
if (command === "send-prompt") {
|
|
2455
|
-
const sessionId = options.session ?? commandArgs[0];
|
|
2456
|
-
if (!sessionId) {
|
|
2457
|
-
throw new Error("Session id is required for `send-prompt`");
|
|
2458
|
-
}
|
|
2459
|
-
const promptArgs = options.session ? commandArgs : commandArgs.slice(1);
|
|
2460
|
-
const prompt = promptArgs.join(" ").trim();
|
|
2461
|
-
if (!prompt) {
|
|
2462
|
-
throw new Error("Prompt is required for `send-prompt`");
|
|
2463
|
-
}
|
|
2464
|
-
return {
|
|
2465
|
-
kind: "plugin",
|
|
2466
|
-
pluginArgs: ["send-prompt", sessionId, prompt]
|
|
2467
|
-
};
|
|
2468
|
-
}
|
|
2469
|
-
if (command === "merge-changes") {
|
|
2470
|
-
if (!options.project) {
|
|
2471
|
-
throw new Error("--project/-p is required for `merge-changes`");
|
|
2472
|
-
}
|
|
2473
|
-
const targetBranch = commandArgs[0] ?? options.branch;
|
|
2474
|
-
if (!targetBranch) {
|
|
2475
|
-
throw new Error("Target branch is required for `merge-changes`");
|
|
2476
|
-
}
|
|
2477
|
-
const sourceBranch = requireValue(commandArgs[1], "Sub-agent branch is required for `merge-changes`");
|
|
2478
|
-
return {
|
|
2479
|
-
kind: "plugin",
|
|
2480
|
-
pluginArgs: ["merge-changes", options.project, targetBranch, sourceBranch]
|
|
2481
|
-
};
|
|
2482
|
-
}
|
|
2483
|
-
if (command === "continue-merge") {
|
|
2484
|
-
if (!options.project) {
|
|
2485
|
-
throw new Error("--project/-p is required for `continue-merge`");
|
|
2486
|
-
}
|
|
2487
|
-
const targetBranch = commandArgs[0] ?? options.branch;
|
|
2488
|
-
if (!targetBranch) {
|
|
2489
|
-
throw new Error("Target branch is required for `continue-merge`");
|
|
2490
|
-
}
|
|
2491
|
-
return {
|
|
2492
|
-
kind: "plugin",
|
|
2493
|
-
pluginArgs: ["continue-merge", options.project, targetBranch]
|
|
2494
|
-
};
|
|
2495
|
-
}
|
|
2496
|
-
if (command === "abort-merge") {
|
|
2497
|
-
if (!options.project) {
|
|
2498
|
-
throw new Error("--project/-p is required for `abort-merge`");
|
|
2499
|
-
}
|
|
2500
|
-
const targetBranch = commandArgs[0] ?? options.branch;
|
|
2501
|
-
if (!targetBranch) {
|
|
2502
|
-
throw new Error("Target branch is required for `abort-merge`");
|
|
2503
|
-
}
|
|
2504
|
-
return {
|
|
2505
|
-
kind: "plugin",
|
|
2506
|
-
pluginArgs: ["abort-merge", options.project, targetBranch]
|
|
2507
|
-
};
|
|
2624
|
+
if (command === "agent") {
|
|
2625
|
+
return { kind: "agent", command: parseBranchAgentCommand(options, commandArgs) };
|
|
2508
2626
|
}
|
|
2509
2627
|
if (command === "answer-questions") {
|
|
2510
2628
|
const sessionId = options.session;
|
|
@@ -2554,6 +2672,11 @@ async function runCommand(argv) {
|
|
|
2554
2672
|
process.stdout.write(execution.text);
|
|
2555
2673
|
return 0;
|
|
2556
2674
|
}
|
|
2675
|
+
if (execution.kind === "agent") {
|
|
2676
|
+
const result = await dispatchBranchAgentCommand(client, execution.command);
|
|
2677
|
+
writeDataOutput(options.json, result.data, result.human);
|
|
2678
|
+
return 0;
|
|
2679
|
+
}
|
|
2557
2680
|
if (execution.kind === "shell") {
|
|
2558
2681
|
const project = await client.projects.describe(options.project);
|
|
2559
2682
|
return await runR5dctlShell({
|
|
@@ -2602,6 +2725,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2602
2725
|
export {
|
|
2603
2726
|
advanceTransientDevicePollBackoff,
|
|
2604
2727
|
collectK8sUsage,
|
|
2728
|
+
dispatchBranchAgentCommand,
|
|
2605
2729
|
formatK8sCpu,
|
|
2606
2730
|
formatK8sMemory,
|
|
2607
2731
|
formatProcessAge,
|
|
@@ -2613,6 +2737,7 @@ export {
|
|
|
2613
2737
|
handleAuthLogin,
|
|
2614
2738
|
main,
|
|
2615
2739
|
parseAnswerFlags,
|
|
2740
|
+
parseBranchAgentCommand,
|
|
2616
2741
|
parseConversationRenderArgs,
|
|
2617
2742
|
parseConversationWorkDetailArgs,
|
|
2618
2743
|
parseEnvFlags,
|
|
@@ -2626,6 +2751,9 @@ export {
|
|
|
2626
2751
|
parseSetEnvArgs,
|
|
2627
2752
|
parseShellArgs,
|
|
2628
2753
|
readDotenvFile,
|
|
2754
|
+
renderBranchAgentMerge,
|
|
2755
|
+
renderBranchAgentStart,
|
|
2756
|
+
renderBranchAgentStatus,
|
|
2629
2757
|
renderConversationNodeResponse,
|
|
2630
2758
|
renderConversationOverviewResponse,
|
|
2631
2759
|
renderConversationResponse,
|
package/dist/mjs/package.json
CHANGED