@meistrari/remy-cli 1.2.0 → 1.4.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 +1 -0
  2. package/dist/remy.js +67 -38
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -50,6 +50,7 @@ Drag to select visible text in any Remy view; Remy copies it to the local clipbo
50
50
  | --- | --- |
51
51
  | `Esc` | Interrupt the active turn; the session stays open. |
52
52
  | `/complete` | Complete an open session after its active turn stops. |
53
+ | `/cancel` | Cancel the current session, including an active turn. |
53
54
  | `/sessions` | Return to the dashboard. |
54
55
  | `/new` | Start another new-session flow. |
55
56
  | `/logout` | Sign out of Remy after confirmation. |
package/dist/remy.js CHANGED
@@ -32820,6 +32820,13 @@ async function completeSession({ client, sessionId }) {
32820
32820
  throw new CodingAgentProtocolError("Session completion response did not match the public API contract.", { cause: parsed.error });
32821
32821
  return parsed.data;
32822
32822
  }
32823
+ async function cancelSession({ client, sessionId }) {
32824
+ const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/cancel`, { method: "PUT" });
32825
+ const parsed = sessionLifecycleResponseSchema.safeParse(await parseJson2(response, "Session cancellation response was not valid JSON."));
32826
+ if (!parsed.success)
32827
+ throw new CodingAgentProtocolError("Session cancellation response did not match the public API contract.", { cause: parsed.error });
32828
+ return parsed.data;
32829
+ }
32823
32830
  async function listSessions({
32824
32831
  client,
32825
32832
  limit,
@@ -36061,6 +36068,7 @@ import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, b
36061
36068
  var composerSlashCommands = [
36062
36069
  { value: "/sessions", description: "Back to the session list" },
36063
36070
  { value: "/complete", description: "Finish this session" },
36071
+ { value: "/cancel", description: "Cancel this session" },
36064
36072
  { value: "/new", description: "Start a new session" },
36065
36073
  { value: "/logout", description: "Sign out of Remy" },
36066
36074
  { value: "/help", description: "Show what you can do here" },
@@ -36073,6 +36081,7 @@ async function createSessionTui({
36073
36081
  submitMessage,
36074
36082
  requestInterrupt,
36075
36083
  requestComplete,
36084
+ requestCancel,
36076
36085
  repositoryLabel,
36077
36086
  readClipboardImage,
36078
36087
  savePastedImage,
@@ -36153,8 +36162,8 @@ async function createSessionTui({
36153
36162
  const working = isRemyWorking(latestState);
36154
36163
  const startedAt = workingTurnStartedAt(latestState);
36155
36164
  const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now3() - Date.parse(startedAt));
36156
- statusBand.content = renderStatusBand({ state: latestState, working, elapsedMs, stopState, completeState, repositoryLabel });
36157
- const liveIndicator = completeState.kind === "completing" ? renderWorkingIndicator({ frame: workingSpinnerFrames[spinnerFrameIndex], elapsedMs: 0, mode: "completing" }) : working ? renderWorkingIndicator({
36165
+ statusBand.content = renderStatusBand({ state: latestState, working, elapsedMs, stopState, lifecycleRequestState, repositoryLabel });
36166
+ const liveIndicator = lifecycleRequestState.kind === "pending" ? renderWorkingIndicator({ frame: workingSpinnerFrames[spinnerFrameIndex], elapsedMs: 0, mode: lifecycleRequestState.operation }) : working ? renderWorkingIndicator({
36158
36167
  frame: workingSpinnerFrames[spinnerFrameIndex],
36159
36168
  elapsedMs,
36160
36169
  mode: stopState.kind === "stopping" ? "stopping" : "working"
@@ -36165,8 +36174,8 @@ async function createSessionTui({
36165
36174
  `), joinStyled(liveContent, `
36166
36175
 
36167
36176
  `)], "");
36168
- if (completeState.kind === "completing")
36169
- composer.placeholder = "Completing the session \u2014 messages are paused.";
36177
+ if (lifecycleRequestState.kind === "pending")
36178
+ composer.placeholder = `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.`;
36170
36179
  if (latestState.aggregateStatus === "open")
36171
36180
  composerLayout.render();
36172
36181
  const submissionStatus = renderComposerStatus({ admittedSubmissions });
@@ -36183,7 +36192,7 @@ async function createSessionTui({
36183
36192
 
36184
36193
  `) : "";
36185
36194
  hints.visible = renderer.height >= 10;
36186
- hints.content = hints.visible ? renderActionBar({ state: latestState, working, stopState, completeState }) : "";
36195
+ hints.content = hints.visible ? renderActionBar({ state: latestState, working, stopState, lifecycleRequestState }) : "";
36187
36196
  renderer.requestRender();
36188
36197
  }, syncTerminalSessionControls = function() {
36189
36198
  const terminal = latestState.aggregateStatus !== "open";
@@ -36215,7 +36224,7 @@ async function createSessionTui({
36215
36224
  renderedAdmittedSubmissions = admittedSubmissions;
36216
36225
  renderedWorking = working;
36217
36226
  }, spinnerActive = function() {
36218
- return isRemyWorking(latestState) || completeState.kind === "completing";
36227
+ return isRemyWorking(latestState) || lifecycleRequestState.kind === "pending";
36219
36228
  }, syncWorkingIndicator = function() {
36220
36229
  if (!spinnerActive()) {
36221
36230
  if (workingSpinnerTimer) {
@@ -36244,7 +36253,7 @@ async function createSessionTui({
36244
36253
  slashCompletions = [];
36245
36254
  return;
36246
36255
  }
36247
- slashCompletions = composerSlashCommands.filter((command) => command.value.startsWith(value) && command.value !== value && (command.value !== "/complete" || latestState.aggregateStatus === "open"));
36256
+ slashCompletions = composerSlashCommands.filter((command) => command.value.startsWith(value) && command.value !== value && (!["/complete", "/cancel"].includes(command.value) || latestState.aggregateStatus === "open"));
36248
36257
  slashCompletionIndex = 0;
36249
36258
  }, finish = function(result, error93) {
36250
36259
  if (settled)
@@ -36286,7 +36295,7 @@ async function createSessionTui({
36286
36295
  let pendingPastedImageWrites = 0;
36287
36296
  let admittedSubmissions = [];
36288
36297
  let stopState = { kind: "idle" };
36289
- let completeState = { kind: "idle" };
36298
+ let lifecycleRequestState = { kind: "idle" };
36290
36299
  let helpVisible = false;
36291
36300
  let logoutConfirmationOpen = false;
36292
36301
  let latestState = controller.getState();
@@ -36524,8 +36533,8 @@ async function createSessionTui({
36524
36533
  return;
36525
36534
  if (await handleComposerCommand(value))
36526
36535
  return;
36527
- if (completeState.kind === "completing") {
36528
- composerFeedback = "Completing the session \u2014 messages are paused.";
36536
+ if (lifecycleRequestState.kind === "pending") {
36537
+ composerFeedback = `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.`;
36529
36538
  render();
36530
36539
  return;
36531
36540
  }
@@ -36585,7 +36594,13 @@ async function createSessionTui({
36585
36594
  if (command === "/complete") {
36586
36595
  composer.setText("");
36587
36596
  composerDraft = { ...composerDraft, text: "" };
36588
- await handleCompleteRequest();
36597
+ await handleLifecycleRequest("complete");
36598
+ return true;
36599
+ }
36600
+ if (command === "/cancel") {
36601
+ composer.setText("");
36602
+ composerDraft = { ...composerDraft, text: "" };
36603
+ await handleLifecycleRequest("cancel");
36589
36604
  return true;
36590
36605
  }
36591
36606
  if (command === "/logout") {
@@ -36669,39 +36684,39 @@ async function createSessionTui({
36669
36684
  }
36670
36685
  render();
36671
36686
  }
36672
- async function handleCompleteRequest() {
36673
- if (completeState.kind === "completing")
36687
+ async function handleLifecycleRequest(operation) {
36688
+ if (lifecycleRequestState.kind === "pending")
36674
36689
  return;
36675
36690
  if (latestState.aggregateStatus !== "open") {
36676
- completeState = { kind: "idle" };
36691
+ lifecycleRequestState = { kind: "idle" };
36677
36692
  composerFeedback = "This session is already terminal.";
36678
36693
  render();
36679
36694
  return;
36680
36695
  }
36681
- if (isRemyWorking(latestState)) {
36696
+ if (operation === "complete" && isRemyWorking(latestState)) {
36682
36697
  composerFeedback = "Stop Remy\u2019s turn before completing the session.";
36683
36698
  render();
36684
36699
  return;
36685
36700
  }
36686
- completeState = { kind: "completing" };
36687
- composerFeedback = "Completing the session\u2026";
36701
+ lifecycleRequestState = { kind: "pending", operation };
36702
+ composerFeedback = `${lifecycleOperationPresentParticiple(operation)} the session\u2026`;
36688
36703
  render();
36689
36704
  try {
36690
- await requestComplete();
36705
+ await (operation === "complete" ? requestComplete() : requestCancel());
36691
36706
  if (destroyed)
36692
36707
  return;
36693
- if (completeState.kind !== "completing")
36708
+ if (lifecycleRequestState.kind !== "pending" || lifecycleRequestState.operation !== operation)
36694
36709
  return;
36695
- composerFeedback = "Session completed.";
36710
+ composerFeedback = lifecycleOperationSuccessFeedback(operation);
36696
36711
  if (latestState.aggregateStatus !== "open")
36697
- completeState = { kind: "idle" };
36712
+ lifecycleRequestState = { kind: "idle" };
36698
36713
  } catch (error93) {
36699
36714
  if (destroyed)
36700
36715
  return;
36701
- if (completeState.kind === "completing") {
36716
+ if (lifecycleRequestState.kind === "pending" && lifecycleRequestState.operation === operation) {
36702
36717
  const message = error93 instanceof Error ? error93.message : String(error93);
36703
- completeState = { kind: "failed", message };
36704
- composerFeedback = `Could not complete the session: ${message}`;
36718
+ lifecycleRequestState = { kind: "failed", operation, message };
36719
+ composerFeedback = `Could not ${operation} the session: ${message}`;
36705
36720
  }
36706
36721
  }
36707
36722
  render();
@@ -36713,9 +36728,10 @@ async function createSessionTui({
36713
36728
  if (composerFeedback === "Stop requested.")
36714
36729
  composerFeedback = undefined;
36715
36730
  }
36716
- if (completeState.kind === "completing" && latestState.aggregateStatus !== "open") {
36717
- completeState = { kind: "idle" };
36718
- composerFeedback = "Session completed.";
36731
+ if (lifecycleRequestState.kind === "pending" && latestState.aggregateStatus !== "open") {
36732
+ const operation = lifecycleRequestState.operation;
36733
+ lifecycleRequestState = { kind: "idle" };
36734
+ composerFeedback = lifecycleOperationSuccessFeedback(operation);
36719
36735
  }
36720
36736
  render();
36721
36737
  });
@@ -36792,6 +36808,7 @@ var helpEntries = [
36792
36808
  { group: "command", token: "/new", description: "start a new session" },
36793
36809
  { group: "command", token: "/sessions", description: "back to the session list" },
36794
36810
  { group: "command", token: "/complete", description: "finish this session" },
36811
+ { group: "command", token: "/cancel", description: "cancel this session" },
36795
36812
  { group: "command", token: "/logout", description: "sign out of Remy" },
36796
36813
  { group: "command", token: "/exit", description: "leave Remy" },
36797
36814
  { group: "key", token: "esc", description: "stop Remy\u2019s current turn" },
@@ -36817,10 +36834,10 @@ function renderHelpPanel() {
36817
36834
  function hasRunningTurn(state) {
36818
36835
  return state.aggregateStatus === "open" && Object.values(state.messageTurns).some((turn) => turn.outcome === undefined);
36819
36836
  }
36820
- function renderStatusBand({ state, working, elapsedMs, stopState, completeState, repositoryLabel }) {
36837
+ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleRequestState, repositoryLabel }) {
36821
36838
  const stopping = stopState?.kind === "stopping";
36822
- const completing = completeState?.kind === "completing";
36823
- const presentation = completing ? { label: "Completing", color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
36839
+ const pendingOperation = lifecycleRequestState?.kind === "pending" ? lifecycleRequestState.operation : undefined;
36840
+ const presentation = pendingOperation ? { label: lifecycleOperationPresentParticiple(pendingOperation), color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
36824
36841
  const elapsed = (working || stopping) && elapsedMs !== undefined && elapsedMs > 0 ? ` ${formatElapsed(elapsedMs)}` : "";
36825
36842
  const connection = state.connectionStatus === "connected" ? dim4(fg6(PALETTE.dimText)("connected")) : fg6(PALETTE.approvalQuestion)("reconnecting\u2026");
36826
36843
  const sessionLabel = state.sessionNumber !== undefined ? `Session #${state.sessionNumber}` : `Session ${state.sessionId}`;
@@ -36854,14 +36871,14 @@ function renderTerminalSessionBand({ aggregateStatus }) {
36854
36871
  ], `
36855
36872
  `);
36856
36873
  }
36857
- function renderActionBar({ state, working, stopState, completeState }) {
36858
- if (completeState.kind === "completing")
36859
- return new StyledText5([dim4(fg6(PALETTE.dimText)("completing the session\u2026"))]);
36874
+ function renderActionBar({ state, working, stopState, lifecycleRequestState }) {
36875
+ if (lifecycleRequestState.kind === "pending")
36876
+ return new StyledText5([dim4(fg6(PALETTE.dimText)(`${lifecycleOperationPresentParticiple(lifecycleRequestState.operation).toLowerCase()} the session\u2026`))]);
36860
36877
  if (state.aggregateStatus !== "open")
36861
36878
  return new StyledText5([]);
36862
36879
  const stopToken = working && stopState.kind === "failed" ? ["esc retry stop"] : [];
36863
- const idleGroup = completeState.kind === "failed" ? "/complete retries \xB7 /new \xB7 /sessions" : "/new \xB7 /sessions \xB7 /complete";
36864
- const tokens = working ? ["/ commands", "/help", ...stopToken, "ctrl+o activity"] : ["/ commands", "/help", idleGroup, "ctrl+o activity"];
36880
+ const idleGroup = lifecycleRequestState.kind === "failed" ? `/${lifecycleRequestState.operation} retries \xB7 /new \xB7 /sessions` : "/new \xB7 /sessions \xB7 /complete \xB7 /cancel";
36881
+ const tokens = working ? ["/ commands", ...stopToken, "ctrl+o activity"] : ["/ commands", idleGroup, "ctrl+o activity"];
36865
36882
  return new StyledText5([dim4(fg6(PALETTE.dimText)(tokens.join(" \xB7 ")))]);
36866
36883
  }
36867
36884
  function isRemyWorking(state) {
@@ -37022,10 +37039,10 @@ function renderWorkingIndicator({ frame, elapsedMs, mode }) {
37022
37039
  dim4(fg6(PALETTE.dimText)(` Stopping\u2026 ${formatElapsed(elapsedMs)}`))
37023
37040
  ]);
37024
37041
  }
37025
- if (mode === "completing") {
37042
+ if (mode === "complete" || mode === "cancel") {
37026
37043
  return new StyledText5([
37027
37044
  fg6(PALETTE.approvalQuestion)(frame),
37028
- dim4(fg6(PALETTE.dimText)(" Completing the session\u2026"))
37045
+ dim4(fg6(PALETTE.dimText)(` ${lifecycleOperationPresentParticiple(mode)} the session\u2026`))
37029
37046
  ]);
37030
37047
  }
37031
37048
  return new StyledText5([
@@ -37033,6 +37050,12 @@ function renderWorkingIndicator({ frame, elapsedMs, mode }) {
37033
37050
  dim4(fg6(PALETTE.dimText)(` Working ${formatElapsed(elapsedMs)}`))
37034
37051
  ]);
37035
37052
  }
37053
+ function lifecycleOperationPresentParticiple(operation) {
37054
+ return operation === "complete" ? "Completing" : "Cancelling";
37055
+ }
37056
+ function lifecycleOperationSuccessFeedback(operation) {
37057
+ return operation === "complete" ? "Session completed." : "Session cancelled.";
37058
+ }
37036
37059
  function formatElapsed(elapsedMs) {
37037
37060
  const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1000));
37038
37061
  if (totalSeconds < 60)
@@ -37174,7 +37197,7 @@ var compactMarkRows = 9;
37174
37197
  var compactMinWidth = 48;
37175
37198
  var compactMinHeight = 20;
37176
37199
  var markBrightnessGain = 4.2;
37177
- var remyCliVersion = "1.2.0";
37200
+ var remyCliVersion = "1.4.0";
37178
37201
  async function showRemySplash({
37179
37202
  createRenderer = createRemyRenderer,
37180
37203
  durationMs = splashDurationMs,
@@ -38516,6 +38539,10 @@ async function runAttachedSession({
38516
38539
  const detail = await operations.completeSession({ client: operations.client, sessionId });
38517
38540
  controller.updateDetail(detail);
38518
38541
  },
38542
+ requestCancel: async () => {
38543
+ const detail = await operations.cancelSession({ client: operations.client, sessionId });
38544
+ controller.updateDetail(detail);
38545
+ },
38519
38546
  readClipboardImage,
38520
38547
  savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir() }),
38521
38548
  attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
@@ -38630,6 +38657,7 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
38630
38657
  reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
38631
38658
  createCodexSession: dependencies.createCodexSession ?? createCodexSession,
38632
38659
  appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
38660
+ cancelSession: dependencies.cancelSession ?? cancelSession,
38633
38661
  completeSession: dependencies.completeSession ?? completeSession,
38634
38662
  interruptSession: dependencies.interruptSession ?? interruptSession,
38635
38663
  getSession: dependencies.getSession ?? getSession,
@@ -38647,6 +38675,7 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
38647
38675
  reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
38648
38676
  createCodexSession: dependencies.createCodexSession ?? createCodexSession,
38649
38677
  appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
38678
+ cancelSession: dependencies.cancelSession ?? cancelSession,
38650
38679
  completeSession: dependencies.completeSession ?? completeSession,
38651
38680
  interruptSession: dependencies.interruptSession ?? interruptSession,
38652
38681
  getSession: dependencies.getSession ?? getSession,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {