@meistrari/remy-cli 1.14.1 → 1.15.0

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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/remy.js +97 -32
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -36,7 +36,7 @@ The dashboard is the starting point for all interactive work. It refreshes the v
36
36
 
37
37
  ### Start work
38
38
 
39
- Press `n` in the dashboard. The new-session flow asks what you want done, selects a GitHub installation when needed, suggests repositories based on your request, and lets you review those suggestions. If the request links an open pull request in a selected repository, Remy checks the current pull request state before accepting branch confirmation and adds a third branch step: continue from that pull request's current branch or create a new session branch. Before creating the session, choose the agent model and reasoning effort. The wizard starts with `gpt-6-astra` and `medium` reasoning by default. Remy then opens the session and streams its activity.
39
+ Press `n` in the dashboard. The new-session flow asks what you want done, selects a GitHub installation when needed, suggests repositories based on your request, and lets you review those suggestions. Remy analyzes the request for existing work: a Remy session ID or number, a PR URL or number (`PR 267`, `#267`, or `owner/repo#267`), or an exact or approximate branch name. It verifies matches against the selected repositories and offers up to five branches per repository. In the branch step, use arrows to move, Space to select, and Enter to select the current option and review your choices. Each repository can continue one branch or start a new session branch. A lookup failure stays on a retry screen; press `r` to retry, `n` to explicitly choose new branches, or Esc to edit the request. Before creating the session, choose the agent model and reasoning effort. The wizard starts with `gpt-6-astra` and `medium` reasoning by default. Remy then opens the session and streams its activity.
40
40
 
41
41
  Use `Tab` while writing the request to complete a local file or directory path. Remy attaches selected paths when you confirm the request. Each file may be at most 20 MiB. A directory may contain at most 20 MiB across its regular files before compression; Remy uploads it as a temporary `<directory>.tar.gz` archive with the selected directory preserved as its root, so the remote agent can extract and use its files. Remy rejects oversized attachments with guidance to choose a smaller file or directory, and rejects directory attachments that contain symbolic links or other non-regular entries. On macOS, `Ctrl+V` adds a PNG from the system clipboard to the request; Remy attaches it on confirmation. Remy uploads attachments before creating the session and shows their filenames beneath your message.
42
42
 
@@ -91,6 +91,8 @@ remy new --repository owner/repository "Add a health check endpoint"
91
91
 
92
92
  Repeat `--repository` to select more repositories from the same GitHub installation. If you do not select a repository, supply `--installation <github-installation-id>`. Repeat `--attach <path>` to upload files or directories; the 20 MiB per-attachment limit is measured directly for a file and across all regular files before compressing a directory. Remy sends each accepted directory as a gzip-compressed tar archive. Direct creation uses `medium` reasoning unless `--reasoning-effort` overrides it; omit `--model` to use the server's `gpt-6-astra` default. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
93
93
 
94
+ The branch-suggestion flow belongs to the interactive new-session wizard (`remy`, then `n`). Direct `remy new` creation does not analyze continuation references.
95
+
94
96
  ### Open a known session
95
97
 
96
98
  ```bash
package/dist/remy.js CHANGED
@@ -31690,6 +31690,12 @@ var branchSuggestionsResponseSchema = exports_external2.strictObject({
31690
31690
  url: exports_external2.url()
31691
31691
  })
31692
31692
  }),
31693
+ exports_external2.strictObject({
31694
+ repository_id: exports_external2.string().min(1),
31695
+ suggestion_kind: exports_external2.literal("continue_existing_branch"),
31696
+ branch_name: exports_external2.string().min(1),
31697
+ pull_request: exports_external2.null()
31698
+ }),
31693
31699
  exports_external2.strictObject({
31694
31700
  repository_id: exports_external2.string().min(1),
31695
31701
  suggestion_kind: exports_external2.literal("create_session_branch"),
@@ -31791,6 +31797,7 @@ async function suggestRemoteBranches({
31791
31797
  method: "POST",
31792
31798
  headers: { "content-type": "application/json" },
31793
31799
  body: JSON.stringify({
31800
+ suggestion_mode: "prompt",
31794
31801
  github_installation_id: parsedInput.installationId,
31795
31802
  prompt: parsedInput.prompt,
31796
31803
  repository_ids: parsedInput.repositoryIds
@@ -31810,6 +31817,11 @@ async function suggestRemoteBranches({
31810
31817
  repositoryId: repository.repository_id,
31811
31818
  branchName: repository.branch_name,
31812
31819
  pullRequest: repository.pull_request
31820
+ } : repository.suggestion_kind === "continue_existing_branch" ? {
31821
+ suggestionKind: "continueExistingBranch",
31822
+ repositoryId: repository.repository_id,
31823
+ branchName: repository.branch_name,
31824
+ pullRequest: null
31813
31825
  } : {
31814
31826
  suggestionKind: "createSessionBranch",
31815
31827
  repositoryId: repository.repository_id,
@@ -36935,6 +36947,19 @@ async function createNewSessionWizard({
36935
36947
  overrides.delete(repositoryId);
36936
36948
  else
36937
36949
  overrides.set(repositoryId, isSelected);
36950
+ }, branchOverridesForSelection = function() {
36951
+ return [...selectedBranches].map(([repositoryId, branchName]) => ({ repositoryId, branchName }));
36952
+ }, branchOptions = function() {
36953
+ return [...selectedRepositoryIds()].flatMap((repositoryId) => {
36954
+ const existing = branchSuggestions.filter((suggestion) => suggestion.repositoryId === repositoryId && suggestion.suggestionKind !== "createSessionBranch");
36955
+ return [...existing, { suggestionKind: "createSessionBranch", repositoryId, branchName: null, pullRequest: null }];
36956
+ });
36957
+ }, selectBranchOption = function() {
36958
+ const option = branchOptions()[branchOptionIndex];
36959
+ if (option.branchName === null)
36960
+ selectedBranches.delete(option.repositoryId);
36961
+ else
36962
+ selectedBranches.set(option.repositoryId, option.branchName);
36938
36963
  }, renderRepositoryLoadingSkeleton = function() {
36939
36964
  return new StyledText5([
36940
36965
  dim4(fg6(PALETTE.dimText)(`\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
@@ -36947,7 +36972,7 @@ async function createNewSessionWizard({
36947
36972
  const orient = (body) => joinStyled([renderStepIndicator({
36948
36973
  step,
36949
36974
  hasInstallationChoice: installationIds.length > 1,
36950
- hasBranchChoice: step === "loadingBranches" || branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")
36975
+ hasBranchChoice: step === "loadingBranches" || step === "branchError" || branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")
36951
36976
  }), body], `
36952
36977
  `);
36953
36978
  if (composerVisible && !composerMounted) {
@@ -37023,31 +37048,39 @@ async function createNewSessionWizard({
37023
37048
  } else if (step === "loadingBranches") {
37024
37049
  content.content = orient(joinStyled([
37025
37050
  new StyledText5([stepHeader("Choose branches")]),
37026
- new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is checking for related open pull requests\u2026`)]),
37051
+ new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is analyzing session, PR, and branch references\u2026`)]),
37027
37052
  new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
37028
37053
  stringToStyledText4("esc back")
37029
37054
  ], `
37030
37055
 
37056
+ `));
37057
+ } else if (step === "branchError") {
37058
+ content.content = orient(joinStyled([
37059
+ new StyledText5([stepHeader("Branch lookup failed")]),
37060
+ stringToStyledText4(status),
37061
+ stringToStyledText4("r retry \xB7 n explicitly choose new branches \xB7 esc back")
37062
+ ], `
37063
+
37031
37064
  `));
37032
37065
  } else if (step === "branches") {
37033
- const existing = branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
37034
- const parts = [new StyledText5([stepHeader("Choose branches")])];
37035
- if (existing.length > 0) {
37036
- parts.push(stringToStyledText4("Remy found related open pull-request work:"));
37037
- for (const suggestion of existing) {
37038
- const repository = installationRepositories().find((candidate) => candidate.id === suggestion.repositoryId);
37039
- parts.push(stringToStyledText4(`${repository?.fullName ?? suggestion.repositoryId} \u2014 ${suggestion.branchName}${suggestion.pullRequest ? ` (PR #${suggestion.pullRequest.number}: ${suggestion.pullRequest.title})` : ""}`));
37040
- }
37041
- parts.push(new StyledText5(renderSelectableRow({ label: "Continue from the suggested branch", isCursor: branchChoice === "existing" })));
37042
- parts.push(new StyledText5(renderSelectableRow({ label: "Create a new session branch", isCursor: branchChoice === "new" })));
37043
- parts.push(stringToStyledText4("\u2191\u2193 choose \xB7 \u23CE continue \xB7 esc back"));
37044
- } else {
37045
- parts.push(stringToStyledText4("Remy recommends creating a new session branch."));
37046
- parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(status || "No related open pull request was identified from your request."))]));
37047
- parts.push(stringToStyledText4("\u23CE continue \xB7 esc back"));
37066
+ const options = branchOptions();
37067
+ const visibleCount = Math.max(2, renderer.height - 12);
37068
+ const start = Math.max(0, Math.min(branchOptionIndex - Math.floor(visibleCount / 2), options.length - visibleCount));
37069
+ const parts = [
37070
+ new StyledText5([stepHeader("Choose branches")]),
37071
+ stringToStyledText4("Continue from the suggested branch, or choose new work for each repository:")
37072
+ ];
37073
+ for (const [index, option] of options.slice(start, start + visibleCount).entries()) {
37074
+ const repository = installationRepositories().find((repository2) => repository2.id === option.repositoryId);
37075
+ const selected = (selectedBranches.get(option.repositoryId) ?? null) === option.branchName;
37076
+ const label = option.branchName === null ? "Create a new session branch" : `${option.branchName}${option.pullRequest ? ` (PR #${option.pullRequest.number}: ${option.pullRequest.title})` : ""}`;
37077
+ parts.push(new StyledText5(renderSelectableRow({
37078
+ label: `${selected ? "\u25CF" : "\u25CB"} ${repository?.fullName ?? option.repositoryId} \u2014 ${label}`,
37079
+ isCursor: start + index === branchOptionIndex
37080
+ })));
37048
37081
  }
37082
+ parts.push(stringToStyledText4(`\u2191\u2193 move \xB7 space select \xB7 \u23CE select & review \xB7 esc back (${branchOptionIndex + 1}/${options.length})`));
37049
37083
  content.content = orient(joinStyled(parts, `
37050
-
37051
37084
  `));
37052
37085
  } else if (repositoryListVisible) {
37053
37086
  const selectionOverrides = searchSelections ?? manualSelections;
@@ -37092,13 +37125,13 @@ async function createNewSessionWizard({
37092
37125
  editor.placeholder = "e.g. worker, migration, dashboard";
37093
37126
  } else {
37094
37127
  const selected = installationRepositories().filter((repository) => selectedRepositoryIds().has(repository.id));
37095
- const branchOverrides = branchChoice === "existing" ? branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch") : [];
37128
+ const branchOverrides = branchOverridesForSelection();
37096
37129
  const meta5 = new StyledText5([
37097
37130
  fg6(PALETTE.bodyText)(`Installation: ${selectedInstallationId() ?? "(missing)"}
37098
37131
  `),
37099
37132
  fg6(PALETTE.bodyText)(`Repositories: ${selected.map((repository) => repository.fullName).join(", ") || "(none)"}
37100
37133
  `),
37101
- fg6(PALETTE.bodyText)(`Branches: ${branchOverrides.length > 0 ? branchOverrides.map((override) => override.branchName).join(", ") : "new session branches"}
37134
+ fg6(PALETTE.bodyText)(`Branches: ${branchOverrides.length > 0 ? selected.map((repository) => `${repository.fullName}: ${selectedBranches.get(repository.id) ?? "new session branch"}`).join(", ") : "new session branches"}
37102
37135
  `),
37103
37136
  fg6(PALETTE.bodyText)(`Model: ${newSessionModelLabel(model)} \xB7 ${newSessionReasoningEffortLabel(reasoningEffort)} reasoning`),
37104
37137
  dim4(fg6(PALETTE.dimText)(" \u25B8 m model \xB7 \u2190\u2192 reasoning"))
@@ -37111,7 +37144,7 @@ async function createNewSessionWizard({
37111
37144
  ];
37112
37145
  if (attachmentText.chunks.length > 0)
37113
37146
  parts.push(attachmentText);
37114
- parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(branchOverrides.length > 0 ? "Remy will continue on the selected existing branch and update its pull request; you land in the session view." : "Remy will clone the selected repositories, work on new session branches, and open a pull request for each repository; you land in the session view."))]));
37147
+ parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(branchOverrides.length > 0 ? "Remy will continue on the selected branches and create new branches for other repositories; you land in the session view." : "Remy will clone the selected repositories, work on new session branches, and open a pull request for each repository; you land in the session view."))]));
37115
37148
  parts.push(stringToStyledText4(branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") ? "\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 b branches \xB7 esc back" : "\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 esc back"));
37116
37149
  if (status)
37117
37150
  parts.push(stringToStyledText4(status));
@@ -37244,7 +37277,7 @@ async function createNewSessionWizard({
37244
37277
  returnToPromptFromRepositoryLoading();
37245
37278
  return;
37246
37279
  }
37247
- if (step === "loadingBranches" || step === "branches") {
37280
+ if (step === "loadingBranches" || step === "branches" || step === "branchError") {
37248
37281
  branchRequestGeneration += 1;
37249
37282
  step = "repositories";
37250
37283
  render();
@@ -37354,6 +37387,17 @@ async function createNewSessionWizard({
37354
37387
  return;
37355
37388
  }
37356
37389
  }
37390
+ if (step === "branchError" && (key.name === "r" || key.name === "n")) {
37391
+ key.preventDefault();
37392
+ if (key.name === "r") {
37393
+ startBranchReview();
37394
+ } else {
37395
+ status = "";
37396
+ step = "confirmation";
37397
+ render();
37398
+ }
37399
+ return;
37400
+ }
37357
37401
  if (step === "repositorySearch")
37358
37402
  return;
37359
37403
  if (step === "loadingSuggestions" && key.name === "s") {
@@ -37373,15 +37417,23 @@ async function createNewSessionWizard({
37373
37417
  return;
37374
37418
  }
37375
37419
  }
37420
+ if (step === "branches" && key.name === "space") {
37421
+ key.preventDefault();
37422
+ selectBranchOption();
37423
+ render();
37424
+ return;
37425
+ }
37376
37426
  if (step === "branches" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") && (key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right")) {
37377
37427
  key.preventDefault();
37378
- branchChoice = branchChoice === "existing" ? "new" : "existing";
37428
+ const options = branchOptions();
37429
+ const direction = key.name === "up" || key.name === "left" ? -1 : 1;
37430
+ branchOptionIndex = (branchOptionIndex + direction + options.length) % options.length;
37379
37431
  render();
37380
37432
  return;
37381
37433
  }
37382
37434
  if (!composerMounted && (key.name === "return" || key.name === "enter")) {
37383
37435
  key.preventDefault();
37384
- if (step === "loadingBranches")
37436
+ if (step === "loadingBranches" || step === "branchError")
37385
37437
  return;
37386
37438
  submitEditor();
37387
37439
  return;
@@ -37500,7 +37552,8 @@ async function createNewSessionWizard({
37500
37552
  branchName: override.branchName,
37501
37553
  pullRequest: null
37502
37554
  })) ?? [];
37503
- let branchChoice = initialDraft?.repositoryBranchOverrides?.length ? "existing" : "new";
37555
+ const selectedBranches = new Map(initialDraft?.repositoryBranchOverrides?.map((override) => [override.repositoryId, override.branchName]));
37556
+ let branchOptionIndex = 0;
37504
37557
  let status = initialError ?? "";
37505
37558
  let attachmentFeedback;
37506
37559
  let pendingPastedImageWrites = 0;
@@ -37575,9 +37628,16 @@ async function createNewSessionWizard({
37575
37628
  if (!installationId)
37576
37629
  return;
37577
37630
  const repositoryIds = [...selectedRepositoryIds()];
37631
+ if (repositoryIds.length === 0) {
37632
+ branchSuggestions = [];
37633
+ selectedBranches.clear();
37634
+ step = "confirmation";
37635
+ render();
37636
+ return;
37637
+ }
37578
37638
  const requestGeneration = ++branchRequestGeneration;
37579
37639
  branchSuggestions = [];
37580
- branchChoice = "new";
37640
+ selectedBranches.clear();
37581
37641
  step = "loadingBranches";
37582
37642
  status = "";
37583
37643
  render();
@@ -37587,15 +37647,19 @@ async function createNewSessionWizard({
37587
37647
  return;
37588
37648
  branchSuggestions = suggestions;
37589
37649
  const hasExistingBranch = branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
37590
- branchChoice = hasExistingBranch ? "existing" : "new";
37650
+ for (const suggestion of branchSuggestions) {
37651
+ if (suggestion.suggestionKind !== "createSessionBranch" && !selectedBranches.has(suggestion.repositoryId))
37652
+ selectedBranches.set(suggestion.repositoryId, suggestion.branchName);
37653
+ }
37654
+ branchOptionIndex = 0;
37591
37655
  step = hasExistingBranch ? "branches" : "confirmation";
37592
37656
  } catch (error93) {
37593
37657
  if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
37594
37658
  return;
37595
37659
  branchSuggestions = [];
37596
- branchChoice = "new";
37660
+ selectedBranches.clear();
37597
37661
  status = error93 instanceof Error ? error93.message : String(error93);
37598
- step = "confirmation";
37662
+ step = "branchError";
37599
37663
  }
37600
37664
  render();
37601
37665
  }
@@ -37668,7 +37732,7 @@ async function createNewSessionWizard({
37668
37732
  fail(new Error("Selected repositories must belong to the chosen GitHub installation."));
37669
37733
  return;
37670
37734
  }
37671
- const repositoryBranchOverrides = branchChoice === "existing" ? branchSuggestions.flatMap((suggestion) => suggestion.suggestionKind === "createSessionBranch" ? [] : [{ repositoryId: suggestion.repositoryId, branchName: suggestion.branchName }]) : [];
37735
+ const repositoryBranchOverrides = branchOverridesForSelection();
37672
37736
  const confirmation = Symbol("confirmation");
37673
37737
  const confirmedAttachments = [...attachments];
37674
37738
  const confirmedPrompt = prompt;
@@ -37755,6 +37819,7 @@ async function createNewSessionWizard({
37755
37819
  return;
37756
37820
  }
37757
37821
  if (step === "branches") {
37822
+ selectBranchOption();
37758
37823
  step = "confirmation";
37759
37824
  render();
37760
37825
  return;
@@ -37870,7 +37935,7 @@ function wizardStepKey(step) {
37870
37935
  return "describe";
37871
37936
  if (step === "installation")
37872
37937
  return "installation";
37873
- if (step === "loadingBranches" || step === "branches")
37938
+ if (step === "loadingBranches" || step === "branches" || step === "branchError")
37874
37939
  return "branches";
37875
37940
  if (step === "confirmation")
37876
37941
  return "confirm";
@@ -39430,7 +39495,7 @@ var compactMarkRows = 9;
39430
39495
  var compactMinWidth = 48;
39431
39496
  var compactMinHeight = 20;
39432
39497
  var markBrightnessGain = 4.2;
39433
- var remyCliVersion = "1.14.1";
39498
+ var remyCliVersion = "1.15.0";
39434
39499
  async function showRemySplash({
39435
39500
  createRenderer = createRemyRenderer,
39436
39501
  durationMs = splashDurationMs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {