@ricsam/r5dctl 0.0.6 → 0.0.7

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/cjs/cli.cjs CHANGED
@@ -35,7 +35,7 @@ __export(cli_exports, {
35
35
  parseGlobalArgs: () => parseGlobalArgs,
36
36
  parsePromptArgs: () => parsePromptArgs,
37
37
  resolveCommandExecution: () => resolveCommandExecution,
38
- runBinctlCli: () => runBinctlCli
38
+ runR5dctlCli: () => runR5dctlCli
39
39
  });
40
40
  module.exports = __toCommonJS(cli_exports);
41
41
  var import_node_fs = __toESM(require("node:fs"), 1);
@@ -44,8 +44,20 @@ var import_node_path = __toESM(require("node:path"), 1);
44
44
  var import_node_child_process = require("node:child_process");
45
45
  var import_promises = require("node:timers/promises");
46
46
  var import_r5d_api = require("@ricsam/r5d-api");
47
- const CHAT_MODES = /* @__PURE__ */ new Set(["ask", "plan", "build", "agent", "explore", "review", "test", "research"]);
47
+ const CHAT_MODES = /* @__PURE__ */ new Set([
48
+ "ask",
49
+ "plan",
50
+ "build",
51
+ "agent",
52
+ "explore",
53
+ "review",
54
+ "test",
55
+ "research",
56
+ "security_review",
57
+ "large_diff_remediation"
58
+ ]);
48
59
  const MODEL_TIERS = /* @__PURE__ */ new Set(["low", "medium", "high", "max"]);
60
+ const AGENT_TYPES = /* @__PURE__ */ new Set(["research", "debug", "test"]);
49
61
  const CLI_GLOBAL_OPTION_HELP = [
50
62
  "--base-url <url>",
51
63
  "--json",
@@ -90,14 +102,22 @@ const SHARED_HELP_ENTRIES = [
90
102
  { section: "sessions", usage: "-s <session-id> conversation [--full] [--compact] [--tail <nodes>]", description: "Read a conversation transcript, pending questions, and required envs." },
91
103
  { section: "sessions", usage: '-s <session-id> prompt --mode <mode> --model <tier> "<message>"', description: "Send a prompt to a session." },
92
104
  { section: "sessions", usage: '-s <session-id> answer-questions -a1 "<answer>" -a2 "<answer>" ...', description: "Answer pending questions in order." },
93
- { section: "sessions", usage: "-s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value", description: "Apply required backend or frontend env values." }
105
+ { section: "sessions", usage: "-s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value", description: "Apply required backend or frontend env values." },
106
+ { section: "agents", usage: '-p <project> start-agent <src-branch> <agent-type> "<prompt>"', description: "Start a detached branch agent." },
107
+ { section: "agents", usage: "agent-status <session-id>", description: "Show detached branch agent status." },
108
+ { section: "agents", usage: 'send-prompt <session-id> "<prompt>"', description: "Queue or resume a detached branch agent." },
109
+ { section: "merges", usage: "-p <project> merge-changes <target-branch> <sub-agent-branch>", description: "Squash merge an agent branch into a target branch." },
110
+ { section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
111
+ { section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
94
112
  ];
95
- const HELP_SECTION_ORDER = ["auth", "projects", "branches", "sessions"];
113
+ const HELP_SECTION_ORDER = ["auth", "projects", "branches", "sessions", "agents", "merges"];
96
114
  const HELP_SECTION_TITLES = {
97
115
  auth: "Auth",
98
116
  projects: "Projects",
99
117
  branches: "Branches",
100
- sessions: "Sessions"
118
+ sessions: "Sessions",
119
+ agents: "Agents",
120
+ merges: "Merges"
101
121
  };
102
122
  function getCliHelpText() {
103
123
  const entries = [...CLI_ONLY_HELP_ENTRIES, ...SHARED_HELP_ENTRIES];
@@ -322,9 +342,9 @@ function writeConfig(configPath, config) {
322
342
  }
323
343
  function resolveClientOptions(options, config) {
324
344
  return {
325
- baseUrl: options.baseUrl ?? process.env.R5D_BASE_URL ?? process.env.BINCTL_BASE_URL ?? config.baseUrl,
326
- token: options.token ?? process.env.R5D_TOKEN ?? process.env.BINCTL_TOKEN ?? config.token,
327
- apiKey: options.apiKey ?? process.env.R5D_API_KEY ?? process.env.BINCTL_API_KEY ?? config.apiKey
345
+ baseUrl: options.baseUrl ?? process.env.R5D_BASE_URL ?? process.env.R5DCTL_BASE_URL ?? config.baseUrl,
346
+ token: options.token ?? process.env.R5D_TOKEN ?? process.env.R5DCTL_TOKEN ?? config.token,
347
+ apiKey: options.apiKey ?? process.env.R5D_API_KEY ?? process.env.R5DCTL_API_KEY ?? config.apiKey
328
348
  };
329
349
  }
330
350
  function parseAnswerFlags(args) {
@@ -459,7 +479,7 @@ function parsePromptArgs(args) {
459
479
  throw new Error("--mode is required for `prompt`");
460
480
  }
461
481
  if (!CHAT_MODES.has(modeRaw)) {
462
- throw new Error(`Invalid --mode '${modeRaw}'. Expected one of: ask, plan, build, agent, explore, review, test, research`);
482
+ throw new Error(`Invalid --mode '${modeRaw}'. Expected one of: ${Array.from(CHAT_MODES).join(", ")}`);
463
483
  }
464
484
  if (!modelRaw) {
465
485
  throw new Error("--model is required for `prompt`");
@@ -614,6 +634,49 @@ function renderSessionList(sessions) {
614
634
  function renderSessionDescription(session) {
615
635
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
616
636
  }
637
+ function renderAgentStart(agent) {
638
+ return [
639
+ `Agent: ${agent.sessionId}`,
640
+ `Status: ${agent.status}`,
641
+ `Branch: ${agent.branchName}`,
642
+ `Base: ${agent.baseBranch} @ ${agent.baseCommit}`
643
+ ].join("\n") + "\n";
644
+ }
645
+ function renderAgentStatus(agent) {
646
+ const lines = [
647
+ `Agent: ${agent.sessionId}`,
648
+ `Status: ${agent.status}`,
649
+ `Branch: ${agent.branchName}`,
650
+ `Head: ${agent.headCommit}`
651
+ ];
652
+ if (agent.baseBranch && agent.baseCommit) {
653
+ lines.push(`Base: ${agent.baseBranch} @ ${agent.baseCommit}`);
654
+ }
655
+ if (agent.error) {
656
+ lines.push(`Error: ${agent.error}`);
657
+ }
658
+ if (agent.diffSummary) {
659
+ lines.push("", agent.diffSummary);
660
+ }
661
+ if (agent.summary) {
662
+ lines.push("", agent.summary);
663
+ }
664
+ return `${lines.join("\n")}
665
+ `;
666
+ }
667
+ function renderMergeResult(result) {
668
+ if (result.status === "merged") {
669
+ return `Merged ${result.sourceBranch} into ${result.targetBranch}: ${result.commitHash}
670
+ ${result.message}
671
+ `;
672
+ }
673
+ const files = result.conflictedFiles.length > 0 ? `
674
+ Conflicts:
675
+ ${result.conflictedFiles.map((file) => `- ${file}`).join("\n")}
676
+ ` : "\n";
677
+ return `Merge has conflicts from ${result.sourceBranch} into ${result.targetBranch}.${files}${result.message}
678
+ `;
679
+ }
617
680
  function parseProjectUpdateArgs(args) {
618
681
  const input = {};
619
682
  for (let index = 0; index < args.length; index += 1) {
@@ -738,7 +801,7 @@ function renderConversationResponse(read, options = { full: false, compact: fals
738
801
  return `${lines.join("\n")}
739
802
  `;
740
803
  }
741
- async function executeBinctlCommand(client, json, args) {
804
+ async function executeR5dctlCommand(client, json, args) {
742
805
  const write = (data, human) => writeDataOutput(json, data, human);
743
806
  const [first, second, third] = args;
744
807
  if (first === "auth" && second === "status") {
@@ -814,6 +877,65 @@ Sessions: ${result.sessions.length}
814
877
  write(session, renderSessionDescription(session));
815
878
  return;
816
879
  }
880
+ if (first === "start-agent") {
881
+ const projectRef = requireValue(args[1], "Missing project reference");
882
+ const sourceBranch = requireValue(args[2], "Missing source branch");
883
+ const agentType = requireValue(args[3], "Missing agent type");
884
+ if (!AGENT_TYPES.has(agentType)) {
885
+ throw new Error("Invalid agent type. Expected one of: research, debug, test");
886
+ }
887
+ const prompt = args.slice(4).join(" ").trim();
888
+ if (!prompt) {
889
+ throw new Error("Agent prompt is required");
890
+ }
891
+ const agent = await client.projects.agents.start(projectRef, {
892
+ sourceBranch,
893
+ agentType,
894
+ prompt
895
+ });
896
+ write(agent, renderAgentStart(agent));
897
+ return;
898
+ }
899
+ if (first === "agent-status") {
900
+ const agent = await client.agents.status(requireValue(args[1], "Missing session id"));
901
+ write(agent, renderAgentStatus(agent));
902
+ return;
903
+ }
904
+ if (first === "send-prompt") {
905
+ const sessionId = requireValue(args[1], "Missing session id");
906
+ const prompt = args.slice(2).join(" ").trim();
907
+ if (!prompt) {
908
+ throw new Error("Prompt is required");
909
+ }
910
+ const agent = await client.agents.sendPrompt(sessionId, { prompt });
911
+ write(agent, renderAgentStatus(agent));
912
+ return;
913
+ }
914
+ if (first === "merge-changes") {
915
+ const result = await client.projects.mergeChanges(requireValue(args[1], "Missing project reference"), {
916
+ targetBranch: requireValue(args[2], "Missing target branch"),
917
+ sourceBranch: requireValue(args[3], "Missing source branch")
918
+ });
919
+ write(result, renderMergeResult(result));
920
+ return;
921
+ }
922
+ if (first === "continue-merge") {
923
+ const result = await client.projects.continueMerge(requireValue(args[1], "Missing project reference"), {
924
+ targetBranch: requireValue(args[2], "Missing target branch")
925
+ });
926
+ write(result, `Merge committed: ${result.commitHash}
927
+ ${result.message}
928
+ `);
929
+ return;
930
+ }
931
+ if (first === "abort-merge") {
932
+ const result = await client.projects.abortMerge(requireValue(args[1], "Missing project reference"), {
933
+ targetBranch: requireValue(args[2], "Missing target branch")
934
+ });
935
+ write(result, `${result.message}
936
+ `);
937
+ return;
938
+ }
817
939
  if (first === "sessions" && second === "describe" || first === "describe" && second === "session") {
818
940
  const session = await client.sessions.describe(requireValue(args[2], "Missing session id"));
819
941
  write(session, renderSessionDescription(session));
@@ -1106,6 +1228,92 @@ function resolveCommandExecution(options, rest) {
1106
1228
  pluginArgs: ["prompt", sessionId, parsed.mode, parsed.model, parsed.message]
1107
1229
  };
1108
1230
  }
1231
+ if (command === "start-agent") {
1232
+ if (!options.project) {
1233
+ throw new Error("--project/-p is required for `start-agent`");
1234
+ }
1235
+ const sourceBranch = commandArgs[0] ?? options.branch;
1236
+ if (!sourceBranch) {
1237
+ throw new Error("Source branch is required for `start-agent`");
1238
+ }
1239
+ const agentType = requireValue(commandArgs[1], "Agent type is required for `start-agent`");
1240
+ if (!AGENT_TYPES.has(agentType)) {
1241
+ throw new Error("Invalid agent type. Expected one of: research, debug, test");
1242
+ }
1243
+ const prompt = commandArgs.slice(2).join(" ").trim();
1244
+ if (!prompt) {
1245
+ throw new Error("Agent prompt is required");
1246
+ }
1247
+ return {
1248
+ kind: "plugin",
1249
+ pluginArgs: ["start-agent", options.project, sourceBranch, agentType, prompt]
1250
+ };
1251
+ }
1252
+ if (command === "agent-status") {
1253
+ const sessionId = commandArgs[0] ?? options.session;
1254
+ if (!sessionId) {
1255
+ throw new Error("Session id is required for `agent-status`");
1256
+ }
1257
+ return {
1258
+ kind: "plugin",
1259
+ pluginArgs: ["agent-status", sessionId]
1260
+ };
1261
+ }
1262
+ if (command === "send-prompt") {
1263
+ const sessionId = options.session ?? commandArgs[0];
1264
+ if (!sessionId) {
1265
+ throw new Error("Session id is required for `send-prompt`");
1266
+ }
1267
+ const promptArgs = options.session ? commandArgs : commandArgs.slice(1);
1268
+ const prompt = promptArgs.join(" ").trim();
1269
+ if (!prompt) {
1270
+ throw new Error("Prompt is required for `send-prompt`");
1271
+ }
1272
+ return {
1273
+ kind: "plugin",
1274
+ pluginArgs: ["send-prompt", sessionId, prompt]
1275
+ };
1276
+ }
1277
+ if (command === "merge-changes") {
1278
+ if (!options.project) {
1279
+ throw new Error("--project/-p is required for `merge-changes`");
1280
+ }
1281
+ const targetBranch = commandArgs[0] ?? options.branch;
1282
+ if (!targetBranch) {
1283
+ throw new Error("Target branch is required for `merge-changes`");
1284
+ }
1285
+ const sourceBranch = requireValue(commandArgs[1], "Sub-agent branch is required for `merge-changes`");
1286
+ return {
1287
+ kind: "plugin",
1288
+ pluginArgs: ["merge-changes", options.project, targetBranch, sourceBranch]
1289
+ };
1290
+ }
1291
+ if (command === "continue-merge") {
1292
+ if (!options.project) {
1293
+ throw new Error("--project/-p is required for `continue-merge`");
1294
+ }
1295
+ const targetBranch = commandArgs[0] ?? options.branch;
1296
+ if (!targetBranch) {
1297
+ throw new Error("Target branch is required for `continue-merge`");
1298
+ }
1299
+ return {
1300
+ kind: "plugin",
1301
+ pluginArgs: ["continue-merge", options.project, targetBranch]
1302
+ };
1303
+ }
1304
+ if (command === "abort-merge") {
1305
+ if (!options.project) {
1306
+ throw new Error("--project/-p is required for `abort-merge`");
1307
+ }
1308
+ const targetBranch = commandArgs[0] ?? options.branch;
1309
+ if (!targetBranch) {
1310
+ throw new Error("Target branch is required for `abort-merge`");
1311
+ }
1312
+ return {
1313
+ kind: "plugin",
1314
+ pluginArgs: ["abort-merge", options.project, targetBranch]
1315
+ };
1316
+ }
1109
1317
  if (command === "answer-questions") {
1110
1318
  const sessionId = options.session;
1111
1319
  if (!sessionId) {
@@ -1135,7 +1343,7 @@ async function runCommand(argv) {
1135
1343
  return;
1136
1344
  }
1137
1345
  const config = parseConfig(options.configPath);
1138
- const client = new import_r5d_api.BinctlClient(resolveClientOptions(options, config));
1346
+ const client = new import_r5d_api.R5dctlClient(resolveClientOptions(options, config));
1139
1347
  const execution = resolveCommandExecution(options, rest);
1140
1348
  if (execution.kind === "auth-login") {
1141
1349
  await handleAuthLogin(client, options, execution.commandArgs, config);
@@ -1149,7 +1357,7 @@ async function runCommand(argv) {
1149
1357
  process.stdout.write(execution.text);
1150
1358
  return;
1151
1359
  }
1152
- await executeBinctlCommand(client, options.json, execution.pluginArgs);
1360
+ await executeR5dctlCommand(client, options.json, execution.pluginArgs);
1153
1361
  if (execution.clearAuthOnSuccess) {
1154
1362
  writeConfig(options.configPath, clearAuthFromConfig(config, options));
1155
1363
  }
@@ -1163,12 +1371,12 @@ function extractApiErrorMessage(error) {
1163
1371
  }
1164
1372
  return error.message;
1165
1373
  }
1166
- async function runBinctlCli(argv) {
1374
+ async function runR5dctlCli(argv) {
1167
1375
  try {
1168
1376
  await runCommand(argv);
1169
1377
  return 0;
1170
1378
  } catch (error) {
1171
- if (error instanceof import_r5d_api.BinctlApiError) {
1379
+ if (error instanceof import_r5d_api.R5dctlApiError) {
1172
1380
  process.stderr.write(`${extractApiErrorMessage(error)}
1173
1381
  `);
1174
1382
  return 1;
@@ -1179,7 +1387,7 @@ async function runBinctlCli(argv) {
1179
1387
  }
1180
1388
  }
1181
1389
  async function main(argv = process.argv.slice(2)) {
1182
- const exitCode = await runBinctlCli(argv);
1390
+ const exitCode = await runR5dctlCli(argv);
1183
1391
  if (exitCode !== 0) {
1184
1392
  process.exit(exitCode);
1185
1393
  }
@@ -1193,5 +1401,5 @@ async function main(argv = process.argv.slice(2)) {
1193
1401
  parseGlobalArgs,
1194
1402
  parsePromptArgs,
1195
1403
  resolveCommandExecution,
1196
- runBinctlCli
1404
+ runR5dctlCli
1197
1405
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -4,11 +4,23 @@ import path from "node:path";
4
4
  import { spawn } from "node:child_process";
5
5
  import { setTimeout as sleep } from "node:timers/promises";
6
6
  import {
7
- BinctlApiError,
8
- BinctlClient
7
+ R5dctlApiError,
8
+ R5dctlClient
9
9
  } from "@ricsam/r5d-api";
10
- const CHAT_MODES = /* @__PURE__ */ new Set(["ask", "plan", "build", "agent", "explore", "review", "test", "research"]);
10
+ const CHAT_MODES = /* @__PURE__ */ new Set([
11
+ "ask",
12
+ "plan",
13
+ "build",
14
+ "agent",
15
+ "explore",
16
+ "review",
17
+ "test",
18
+ "research",
19
+ "security_review",
20
+ "large_diff_remediation"
21
+ ]);
11
22
  const MODEL_TIERS = /* @__PURE__ */ new Set(["low", "medium", "high", "max"]);
23
+ const AGENT_TYPES = /* @__PURE__ */ new Set(["research", "debug", "test"]);
12
24
  const CLI_GLOBAL_OPTION_HELP = [
13
25
  "--base-url <url>",
14
26
  "--json",
@@ -53,14 +65,22 @@ const SHARED_HELP_ENTRIES = [
53
65
  { section: "sessions", usage: "-s <session-id> conversation [--full] [--compact] [--tail <nodes>]", description: "Read a conversation transcript, pending questions, and required envs." },
54
66
  { section: "sessions", usage: '-s <session-id> prompt --mode <mode> --model <tier> "<message>"', description: "Send a prompt to a session." },
55
67
  { section: "sessions", usage: '-s <session-id> answer-questions -a1 "<answer>" -a2 "<answer>" ...', description: "Answer pending questions in order." },
56
- { section: "sessions", usage: "-s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value", description: "Apply required backend or frontend env values." }
68
+ { section: "sessions", usage: "-s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value", description: "Apply required backend or frontend env values." },
69
+ { section: "agents", usage: '-p <project> start-agent <src-branch> <agent-type> "<prompt>"', description: "Start a detached branch agent." },
70
+ { section: "agents", usage: "agent-status <session-id>", description: "Show detached branch agent status." },
71
+ { section: "agents", usage: 'send-prompt <session-id> "<prompt>"', description: "Queue or resume a detached branch agent." },
72
+ { section: "merges", usage: "-p <project> merge-changes <target-branch> <sub-agent-branch>", description: "Squash merge an agent branch into a target branch." },
73
+ { section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
74
+ { section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
57
75
  ];
58
- const HELP_SECTION_ORDER = ["auth", "projects", "branches", "sessions"];
76
+ const HELP_SECTION_ORDER = ["auth", "projects", "branches", "sessions", "agents", "merges"];
59
77
  const HELP_SECTION_TITLES = {
60
78
  auth: "Auth",
61
79
  projects: "Projects",
62
80
  branches: "Branches",
63
- sessions: "Sessions"
81
+ sessions: "Sessions",
82
+ agents: "Agents",
83
+ merges: "Merges"
64
84
  };
65
85
  function getCliHelpText() {
66
86
  const entries = [...CLI_ONLY_HELP_ENTRIES, ...SHARED_HELP_ENTRIES];
@@ -285,9 +305,9 @@ function writeConfig(configPath, config) {
285
305
  }
286
306
  function resolveClientOptions(options, config) {
287
307
  return {
288
- baseUrl: options.baseUrl ?? process.env.R5D_BASE_URL ?? process.env.BINCTL_BASE_URL ?? config.baseUrl,
289
- token: options.token ?? process.env.R5D_TOKEN ?? process.env.BINCTL_TOKEN ?? config.token,
290
- apiKey: options.apiKey ?? process.env.R5D_API_KEY ?? process.env.BINCTL_API_KEY ?? config.apiKey
308
+ baseUrl: options.baseUrl ?? process.env.R5D_BASE_URL ?? process.env.R5DCTL_BASE_URL ?? config.baseUrl,
309
+ token: options.token ?? process.env.R5D_TOKEN ?? process.env.R5DCTL_TOKEN ?? config.token,
310
+ apiKey: options.apiKey ?? process.env.R5D_API_KEY ?? process.env.R5DCTL_API_KEY ?? config.apiKey
291
311
  };
292
312
  }
293
313
  function parseAnswerFlags(args) {
@@ -422,7 +442,7 @@ function parsePromptArgs(args) {
422
442
  throw new Error("--mode is required for `prompt`");
423
443
  }
424
444
  if (!CHAT_MODES.has(modeRaw)) {
425
- throw new Error(`Invalid --mode '${modeRaw}'. Expected one of: ask, plan, build, agent, explore, review, test, research`);
445
+ throw new Error(`Invalid --mode '${modeRaw}'. Expected one of: ${Array.from(CHAT_MODES).join(", ")}`);
426
446
  }
427
447
  if (!modelRaw) {
428
448
  throw new Error("--model is required for `prompt`");
@@ -577,6 +597,49 @@ function renderSessionList(sessions) {
577
597
  function renderSessionDescription(session) {
578
598
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
579
599
  }
600
+ function renderAgentStart(agent) {
601
+ return [
602
+ `Agent: ${agent.sessionId}`,
603
+ `Status: ${agent.status}`,
604
+ `Branch: ${agent.branchName}`,
605
+ `Base: ${agent.baseBranch} @ ${agent.baseCommit}`
606
+ ].join("\n") + "\n";
607
+ }
608
+ function renderAgentStatus(agent) {
609
+ const lines = [
610
+ `Agent: ${agent.sessionId}`,
611
+ `Status: ${agent.status}`,
612
+ `Branch: ${agent.branchName}`,
613
+ `Head: ${agent.headCommit}`
614
+ ];
615
+ if (agent.baseBranch && agent.baseCommit) {
616
+ lines.push(`Base: ${agent.baseBranch} @ ${agent.baseCommit}`);
617
+ }
618
+ if (agent.error) {
619
+ lines.push(`Error: ${agent.error}`);
620
+ }
621
+ if (agent.diffSummary) {
622
+ lines.push("", agent.diffSummary);
623
+ }
624
+ if (agent.summary) {
625
+ lines.push("", agent.summary);
626
+ }
627
+ return `${lines.join("\n")}
628
+ `;
629
+ }
630
+ function renderMergeResult(result) {
631
+ if (result.status === "merged") {
632
+ return `Merged ${result.sourceBranch} into ${result.targetBranch}: ${result.commitHash}
633
+ ${result.message}
634
+ `;
635
+ }
636
+ const files = result.conflictedFiles.length > 0 ? `
637
+ Conflicts:
638
+ ${result.conflictedFiles.map((file) => `- ${file}`).join("\n")}
639
+ ` : "\n";
640
+ return `Merge has conflicts from ${result.sourceBranch} into ${result.targetBranch}.${files}${result.message}
641
+ `;
642
+ }
580
643
  function parseProjectUpdateArgs(args) {
581
644
  const input = {};
582
645
  for (let index = 0; index < args.length; index += 1) {
@@ -701,7 +764,7 @@ function renderConversationResponse(read, options = { full: false, compact: fals
701
764
  return `${lines.join("\n")}
702
765
  `;
703
766
  }
704
- async function executeBinctlCommand(client, json, args) {
767
+ async function executeR5dctlCommand(client, json, args) {
705
768
  const write = (data, human) => writeDataOutput(json, data, human);
706
769
  const [first, second, third] = args;
707
770
  if (first === "auth" && second === "status") {
@@ -777,6 +840,65 @@ Sessions: ${result.sessions.length}
777
840
  write(session, renderSessionDescription(session));
778
841
  return;
779
842
  }
843
+ if (first === "start-agent") {
844
+ const projectRef = requireValue(args[1], "Missing project reference");
845
+ const sourceBranch = requireValue(args[2], "Missing source branch");
846
+ const agentType = requireValue(args[3], "Missing agent type");
847
+ if (!AGENT_TYPES.has(agentType)) {
848
+ throw new Error("Invalid agent type. Expected one of: research, debug, test");
849
+ }
850
+ const prompt = args.slice(4).join(" ").trim();
851
+ if (!prompt) {
852
+ throw new Error("Agent prompt is required");
853
+ }
854
+ const agent = await client.projects.agents.start(projectRef, {
855
+ sourceBranch,
856
+ agentType,
857
+ prompt
858
+ });
859
+ write(agent, renderAgentStart(agent));
860
+ return;
861
+ }
862
+ if (first === "agent-status") {
863
+ const agent = await client.agents.status(requireValue(args[1], "Missing session id"));
864
+ write(agent, renderAgentStatus(agent));
865
+ return;
866
+ }
867
+ if (first === "send-prompt") {
868
+ const sessionId = requireValue(args[1], "Missing session id");
869
+ const prompt = args.slice(2).join(" ").trim();
870
+ if (!prompt) {
871
+ throw new Error("Prompt is required");
872
+ }
873
+ const agent = await client.agents.sendPrompt(sessionId, { prompt });
874
+ write(agent, renderAgentStatus(agent));
875
+ return;
876
+ }
877
+ if (first === "merge-changes") {
878
+ const result = await client.projects.mergeChanges(requireValue(args[1], "Missing project reference"), {
879
+ targetBranch: requireValue(args[2], "Missing target branch"),
880
+ sourceBranch: requireValue(args[3], "Missing source branch")
881
+ });
882
+ write(result, renderMergeResult(result));
883
+ return;
884
+ }
885
+ if (first === "continue-merge") {
886
+ const result = await client.projects.continueMerge(requireValue(args[1], "Missing project reference"), {
887
+ targetBranch: requireValue(args[2], "Missing target branch")
888
+ });
889
+ write(result, `Merge committed: ${result.commitHash}
890
+ ${result.message}
891
+ `);
892
+ return;
893
+ }
894
+ if (first === "abort-merge") {
895
+ const result = await client.projects.abortMerge(requireValue(args[1], "Missing project reference"), {
896
+ targetBranch: requireValue(args[2], "Missing target branch")
897
+ });
898
+ write(result, `${result.message}
899
+ `);
900
+ return;
901
+ }
780
902
  if (first === "sessions" && second === "describe" || first === "describe" && second === "session") {
781
903
  const session = await client.sessions.describe(requireValue(args[2], "Missing session id"));
782
904
  write(session, renderSessionDescription(session));
@@ -1069,6 +1191,92 @@ function resolveCommandExecution(options, rest) {
1069
1191
  pluginArgs: ["prompt", sessionId, parsed.mode, parsed.model, parsed.message]
1070
1192
  };
1071
1193
  }
1194
+ if (command === "start-agent") {
1195
+ if (!options.project) {
1196
+ throw new Error("--project/-p is required for `start-agent`");
1197
+ }
1198
+ const sourceBranch = commandArgs[0] ?? options.branch;
1199
+ if (!sourceBranch) {
1200
+ throw new Error("Source branch is required for `start-agent`");
1201
+ }
1202
+ const agentType = requireValue(commandArgs[1], "Agent type is required for `start-agent`");
1203
+ if (!AGENT_TYPES.has(agentType)) {
1204
+ throw new Error("Invalid agent type. Expected one of: research, debug, test");
1205
+ }
1206
+ const prompt = commandArgs.slice(2).join(" ").trim();
1207
+ if (!prompt) {
1208
+ throw new Error("Agent prompt is required");
1209
+ }
1210
+ return {
1211
+ kind: "plugin",
1212
+ pluginArgs: ["start-agent", options.project, sourceBranch, agentType, prompt]
1213
+ };
1214
+ }
1215
+ if (command === "agent-status") {
1216
+ const sessionId = commandArgs[0] ?? options.session;
1217
+ if (!sessionId) {
1218
+ throw new Error("Session id is required for `agent-status`");
1219
+ }
1220
+ return {
1221
+ kind: "plugin",
1222
+ pluginArgs: ["agent-status", sessionId]
1223
+ };
1224
+ }
1225
+ if (command === "send-prompt") {
1226
+ const sessionId = options.session ?? commandArgs[0];
1227
+ if (!sessionId) {
1228
+ throw new Error("Session id is required for `send-prompt`");
1229
+ }
1230
+ const promptArgs = options.session ? commandArgs : commandArgs.slice(1);
1231
+ const prompt = promptArgs.join(" ").trim();
1232
+ if (!prompt) {
1233
+ throw new Error("Prompt is required for `send-prompt`");
1234
+ }
1235
+ return {
1236
+ kind: "plugin",
1237
+ pluginArgs: ["send-prompt", sessionId, prompt]
1238
+ };
1239
+ }
1240
+ if (command === "merge-changes") {
1241
+ if (!options.project) {
1242
+ throw new Error("--project/-p is required for `merge-changes`");
1243
+ }
1244
+ const targetBranch = commandArgs[0] ?? options.branch;
1245
+ if (!targetBranch) {
1246
+ throw new Error("Target branch is required for `merge-changes`");
1247
+ }
1248
+ const sourceBranch = requireValue(commandArgs[1], "Sub-agent branch is required for `merge-changes`");
1249
+ return {
1250
+ kind: "plugin",
1251
+ pluginArgs: ["merge-changes", options.project, targetBranch, sourceBranch]
1252
+ };
1253
+ }
1254
+ if (command === "continue-merge") {
1255
+ if (!options.project) {
1256
+ throw new Error("--project/-p is required for `continue-merge`");
1257
+ }
1258
+ const targetBranch = commandArgs[0] ?? options.branch;
1259
+ if (!targetBranch) {
1260
+ throw new Error("Target branch is required for `continue-merge`");
1261
+ }
1262
+ return {
1263
+ kind: "plugin",
1264
+ pluginArgs: ["continue-merge", options.project, targetBranch]
1265
+ };
1266
+ }
1267
+ if (command === "abort-merge") {
1268
+ if (!options.project) {
1269
+ throw new Error("--project/-p is required for `abort-merge`");
1270
+ }
1271
+ const targetBranch = commandArgs[0] ?? options.branch;
1272
+ if (!targetBranch) {
1273
+ throw new Error("Target branch is required for `abort-merge`");
1274
+ }
1275
+ return {
1276
+ kind: "plugin",
1277
+ pluginArgs: ["abort-merge", options.project, targetBranch]
1278
+ };
1279
+ }
1072
1280
  if (command === "answer-questions") {
1073
1281
  const sessionId = options.session;
1074
1282
  if (!sessionId) {
@@ -1098,7 +1306,7 @@ async function runCommand(argv) {
1098
1306
  return;
1099
1307
  }
1100
1308
  const config = parseConfig(options.configPath);
1101
- const client = new BinctlClient(resolveClientOptions(options, config));
1309
+ const client = new R5dctlClient(resolveClientOptions(options, config));
1102
1310
  const execution = resolveCommandExecution(options, rest);
1103
1311
  if (execution.kind === "auth-login") {
1104
1312
  await handleAuthLogin(client, options, execution.commandArgs, config);
@@ -1112,7 +1320,7 @@ async function runCommand(argv) {
1112
1320
  process.stdout.write(execution.text);
1113
1321
  return;
1114
1322
  }
1115
- await executeBinctlCommand(client, options.json, execution.pluginArgs);
1323
+ await executeR5dctlCommand(client, options.json, execution.pluginArgs);
1116
1324
  if (execution.clearAuthOnSuccess) {
1117
1325
  writeConfig(options.configPath, clearAuthFromConfig(config, options));
1118
1326
  }
@@ -1126,12 +1334,12 @@ function extractApiErrorMessage(error) {
1126
1334
  }
1127
1335
  return error.message;
1128
1336
  }
1129
- async function runBinctlCli(argv) {
1337
+ async function runR5dctlCli(argv) {
1130
1338
  try {
1131
1339
  await runCommand(argv);
1132
1340
  return 0;
1133
1341
  } catch (error) {
1134
- if (error instanceof BinctlApiError) {
1342
+ if (error instanceof R5dctlApiError) {
1135
1343
  process.stderr.write(`${extractApiErrorMessage(error)}
1136
1344
  `);
1137
1345
  return 1;
@@ -1142,7 +1350,7 @@ async function runBinctlCli(argv) {
1142
1350
  }
1143
1351
  }
1144
1352
  async function main(argv = process.argv.slice(2)) {
1145
- const exitCode = await runBinctlCli(argv);
1353
+ const exitCode = await runR5dctlCli(argv);
1146
1354
  if (exitCode !== 0) {
1147
1355
  process.exit(exitCode);
1148
1356
  }
@@ -1155,5 +1363,5 @@ export {
1155
1363
  parseGlobalArgs,
1156
1364
  parsePromptArgs,
1157
1365
  resolveCommandExecution,
1158
- runBinctlCli
1366
+ runR5dctlCli
1159
1367
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module"
5
5
  }
@@ -10,7 +10,7 @@ export type GlobalOptions = {
10
10
  session?: string;
11
11
  help: boolean;
12
12
  };
13
- export type BinctlConfig = {
13
+ export type R5dctlConfig = {
14
14
  baseUrl?: string;
15
15
  token?: string;
16
16
  apiKey?: string;
@@ -42,6 +42,6 @@ export declare function parsePromptArgs(args: string[]): {
42
42
  message: string;
43
43
  };
44
44
  export declare function resolveCommandExecution(options: GlobalOptions, rest: string[]): CommandExecutionPlan;
45
- export declare function runBinctlCli(argv: string[]): Promise<number>;
45
+ export declare function runR5dctlCli(argv: string[]): Promise<number>;
46
46
  export declare function main(argv?: string[]): Promise<void>;
47
47
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/cli.cjs",
6
6
  "module": "./dist/mjs/cli.mjs",
@@ -20,13 +20,13 @@
20
20
  "repository": {
21
21
  "type": "git",
22
22
  "url": "git+https://github.com/ricsam/r5d-dev.git",
23
- "directory": "binctl-packages/binctl"
23
+ "directory": "packages/r5dctl"
24
24
  },
25
25
  "bin": {
26
26
  "r5dctl": "dist/cjs/main.cjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ricsam/r5d-api": "^0.0.6"
29
+ "@ricsam/r5d-api": "^0.0.7"
30
30
  },
31
31
  "files": [
32
32
  "dist",