@meistrari/remy-cli 1.4.3 → 1.6.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 +6 -4
  2. package/dist/remy.js +269 -29
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -32,11 +32,11 @@ On first use, Remy opens browser-based device authorization, validates your Codi
32
32
 
33
33
  ## Your everyday workflow
34
34
 
35
- The dashboard is the starting point for all interactive work.
35
+ The dashboard is the starting point for all interactive work. It refreshes the visible session page every 10 seconds, so new sessions and status changes appear while you keep it open. If an update fails, Remy keeps showing the last successful list and retries on the next interval.
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. Before creating the session, choose the agent model and reasoning effort. 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. Before creating the session, choose the agent model and reasoning effort. Reasoning starts at `high` 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 path. Remy attaches selected paths when you confirm the request. On macOS, `Ctrl+V` adds a PNG from the system clipboard to the request; Remy attaches it on confirmation. Remy uploads files before creating the session.
42
42
 
@@ -44,6 +44,8 @@ Use `Tab` while writing the request to complete a local file path. Remy attaches
44
44
 
45
45
  Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. After the dashboard list or repository-review list has focus, Vim keys work too: `j`/`k` move, `h`/`l` go to the previous/next dashboard page (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row. Press `Shift+L` in the dashboard to start a logout confirmation. In a session, type a follow-up and press `Enter` to send it.
46
46
 
47
+ When Remy creates a plan, the session shows its checklist in the timeline and keeps `Plan <done>/<total>` with the current item pinned above the composer. Updates change the same checklist instead of producing repeated rows, and an unfinished plan remains visible after reconnecting or between turns. The collapsed timeline shows up to five plan items; press `Ctrl+O` for the complete checklist and activity detail.
48
+
47
49
  Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. When the conversation has focus, press `Tab` to return to the composer.
48
50
 
49
51
  | Control | Result |
@@ -56,7 +58,7 @@ Drag to select visible text in any Remy view; Remy copies it to the local clipbo
56
58
  | `/logout` | Sign out of Remy after confirmation. |
57
59
  | `/help` | Show session controls. |
58
60
  | `/exit` | Leave Remy. |
59
- | `Ctrl+O` | Expand or collapse activity details. |
61
+ | `Ctrl+O` | Expand or collapse full plan and activity details. |
60
62
 
61
63
  After a session becomes terminal, press `Esc` to return to the dashboard or `n` to start new work. From the dashboard, press `q` to quit; in an open session, `q` is ordinary composer text. `Shift+L` in the dashboard and `/logout` in a session ask for confirmation; press `y` to sign out or `Esc` to cancel. `Ctrl+C` exits Remy from the dashboard, wizard, or session view.
62
64
 
@@ -70,7 +72,7 @@ The interactive dashboard is the recommended path. Use these commands when a scr
70
72
  remy new --repository owner/repository "Add a health check endpoint"
71
73
  ```
72
74
 
73
- 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. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
75
+ 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. Direct creation uses `high` reasoning unless `--reasoning-effort` overrides it. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
74
76
 
75
77
  ### Open a known session
76
78
 
package/dist/remy.js CHANGED
@@ -32222,6 +32222,37 @@ var agentWorkObservationSchema = zod_default2.discriminatedUnion("kind", [
32222
32222
  var agentWorkStateSchema = zod_default2.object({
32223
32223
  items: zod_default2.array(agentWorkItemSchema)
32224
32224
  }).strict();
32225
+ function applyAgentWorkObservations(state, observations) {
32226
+ let items = state.items.map((item) => ({ ...item }));
32227
+ for (const observation of observations) {
32228
+ switch (observation.kind) {
32229
+ case "snapshot":
32230
+ items = observation.items.map((item) => ({ ...item }));
32231
+ break;
32232
+ case "item_observed": {
32233
+ const existingIndex = items.findIndex((item) => item.id === observation.item.id);
32234
+ if (existingIndex === -1)
32235
+ items = [...items, { ...observation.item }];
32236
+ else
32237
+ items = items.map((item, index) => index === existingIndex ? { ...observation.item } : item);
32238
+ break;
32239
+ }
32240
+ case "item_renamed":
32241
+ items = items.map((item) => item.id === observation.itemId ? { ...item, title: observation.title } : item);
32242
+ break;
32243
+ case "item_status_changed":
32244
+ items = items.map((item) => item.id === observation.itemId ? { ...item, status: observation.status } : item);
32245
+ break;
32246
+ case "item_removed":
32247
+ items = items.filter((item) => item.id !== observation.itemId);
32248
+ break;
32249
+ default: {
32250
+ const exhaustiveObservation = observation;
32251
+ }
32252
+ }
32253
+ }
32254
+ return { items };
32255
+ }
32225
32256
 
32226
32257
  // ../../packages/agents-protocol/src/agent-event.ts
32227
32258
  var agentErrorSchema = zod_default2.object({
@@ -33643,6 +33674,20 @@ function formatSessionJson({
33643
33674
  `;
33644
33675
  }
33645
33676
 
33677
+ // src/session-agent-options.ts
33678
+ function newSessionModelLabel(model) {
33679
+ return codexModelPresentation[model].label;
33680
+ }
33681
+ function newSessionReasoningEffortLabel(effort) {
33682
+ return agentReasoningEffortPresentation[effort].label;
33683
+ }
33684
+ var newSessionModels = codexModelIds;
33685
+ var newSessionReasoningEfforts = selectableAgentReasoningEfforts;
33686
+ var defaultNewSessionAgent = {
33687
+ model: codexModelIds[0],
33688
+ reasoningEffort: "high"
33689
+ };
33690
+
33646
33691
  // src/sessions/session-controller.ts
33647
33692
  import { randomUUID as randomUUID3 } from "crypto";
33648
33693
  import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile2 } from "fs/promises";
@@ -33852,6 +33897,8 @@ function createSessionViewState({ detail, activeMessageId }) {
33852
33897
  connectionPreviews: toConnectionPreviews(detail),
33853
33898
  previews: { assistantText: "", reasoningText: "" },
33854
33899
  commandAuthorLabels: {},
33900
+ work: { items: [] },
33901
+ workTurnBaselines: {},
33855
33902
  messageTurns: {},
33856
33903
  retainedTurnStarts: {},
33857
33904
  retainedTurnEnds: {}
@@ -34043,6 +34090,8 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId })
34043
34090
  const agentEvent = parseDurableAgentEvent(event);
34044
34091
  if (agentEvent.type === "agent.message.ended")
34045
34092
  return projectAgentMessageEnded({ state, event: agentEvent, occurredAt });
34093
+ if (agentEvent.type === "agent.work.observed" && agentEvent.actor.type === "main")
34094
+ return projectAgentWorkObserved({ state, event: agentEvent, occurredAt });
34046
34095
  const stateWithTurnStart = agentEvent.type === "agent.turn.started" && agentEvent.actor.type === "main" ? {
34047
34096
  ...state,
34048
34097
  retainedTurnStarts: { ...state.retainedTurnStarts, [agentEvent.turnId]: { actorType: agentEvent.actor.type, startedAt: occurredAt } },
@@ -34051,6 +34100,77 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId })
34051
34100
  const stateWithCompletedReasoning = agentEvent.type === "agent.reasoning.ended" ? { ...stateWithTurnStart, previews: { ...stateWithTurnStart.previews, reasoningText: "" } } : stateWithTurnStart;
34052
34101
  return appendActivity({ state: stateWithCompletedReasoning, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
34053
34102
  }
34103
+ function projectAgentWorkObserved({
34104
+ state,
34105
+ event,
34106
+ occurredAt
34107
+ }) {
34108
+ const observations = event.payload.observations.map(sanitizeWorkObservation);
34109
+ const planIndex = state.transcript.findIndex((item) => item.kind === "plan" && item.turnId === event.turnId);
34110
+ const existingPlan = planIndex === -1 ? undefined : state.transcript[planIndex];
34111
+ const turnBaseline = state.workTurnBaselines[event.turnId] ?? state.work;
34112
+ let turnWork = { items: existingPlan?.items ?? [] };
34113
+ for (const observation of observations) {
34114
+ const itemId = patchedWorkItemId(observation);
34115
+ const priorItem = itemId === null ? undefined : turnBaseline.items.find((item) => item.id === itemId);
34116
+ if (priorItem && !turnWork.items.some((item) => item.id === itemId)) {
34117
+ turnWork = applyAgentWorkObservations(turnWork, [{
34118
+ kind: "item_observed",
34119
+ item: priorItem
34120
+ }]);
34121
+ }
34122
+ turnWork = applyAgentWorkObservations(turnWork, [observation]);
34123
+ }
34124
+ const plan = {
34125
+ kind: "plan",
34126
+ planId: `plan:${event.turnId}`,
34127
+ turnId: event.turnId,
34128
+ occurredAt: existingPlan?.occurredAt ?? occurredAt,
34129
+ items: turnWork.items
34130
+ };
34131
+ const transcript = planIndex === -1 ? [...state.transcript, plan] : state.transcript.map((item, index) => index === planIndex ? plan : item);
34132
+ return {
34133
+ ...state,
34134
+ work: applyAgentWorkObservations(state.work, observations),
34135
+ workTurnBaselines: state.workTurnBaselines[event.turnId] ? state.workTurnBaselines : { ...state.workTurnBaselines, [event.turnId]: turnBaseline },
34136
+ latestWorkTurnId: event.turnId,
34137
+ transcript
34138
+ };
34139
+ }
34140
+ function sanitizeWorkObservation(observation) {
34141
+ switch (observation.kind) {
34142
+ case "snapshot":
34143
+ return {
34144
+ ...observation,
34145
+ items: observation.items.map(sanitizeWorkItem)
34146
+ };
34147
+ case "item_observed":
34148
+ return {
34149
+ ...observation,
34150
+ item: sanitizeWorkItem(observation.item)
34151
+ };
34152
+ case "item_renamed":
34153
+ return {
34154
+ ...observation,
34155
+ title: providerText(observation.title, "Untitled task")
34156
+ };
34157
+ case "item_status_changed":
34158
+ case "item_removed":
34159
+ return observation;
34160
+ default: {
34161
+ const exhaustiveObservation = observation;
34162
+ return exhaustiveObservation;
34163
+ }
34164
+ }
34165
+ }
34166
+ function sanitizeWorkItem(item) {
34167
+ return { ...item, title: providerText(item.title, "Untitled task") };
34168
+ }
34169
+ function patchedWorkItemId(observation) {
34170
+ if (observation.kind === "item_renamed" || observation.kind === "item_status_changed")
34171
+ return observation.itemId;
34172
+ return null;
34173
+ }
34054
34174
  function projectAgentMessageEnded({ state, event, occurredAt }) {
34055
34175
  const messageId = event.payload.messageId;
34056
34176
  if (state.transcript.some((item) => item.kind === "message" && item.messageId === messageId))
@@ -34163,9 +34283,10 @@ function publicMessageContentSegmentText(segment) {
34163
34283
  function jsonDetail(value) {
34164
34284
  return JSON.stringify(value);
34165
34285
  }
34166
- var ansiEscapePattern = /\u001B\[[0-9;?]*[a-z]|\u001B\][^\u0007]*\u0007/gi;
34286
+ var ansiEscapePattern = /\u001B\[[0-9;:?]*[\u0020-\u002F]*[\u0040-\u007E]|\u009B[0-9;:?]*[\u0020-\u002F]*[\u0040-\u007E]|\u001B\][^\u0007]*?(?:\u0007|\u001B\\)|\u001B[0-9<=>\u0040-\u005A\u005C-\u005F]/g;
34287
+ var terminalControlPattern = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
34167
34288
  function stripAnsi(value) {
34168
- return value.replace(ansiEscapePattern, "");
34289
+ return value.replace(ansiEscapePattern, "").replace(terminalControlPattern, "");
34169
34290
  }
34170
34291
  function providerText(raw, fallback) {
34171
34292
  const cleaned = stripAnsi(raw ?? "").trim();
@@ -34740,6 +34861,7 @@ async function renderRemyView({
34740
34861
  }
34741
34862
 
34742
34863
  // src/tui/dashboard.ts
34864
+ var DASHBOARD_POLL_INTERVAL_MS = 1e4;
34743
34865
  async function createDashboardTui({
34744
34866
  initialPage,
34745
34867
  loadPage,
@@ -34749,9 +34871,17 @@ async function createDashboardTui({
34749
34871
  let inputHandler;
34750
34872
  let keyHandler;
34751
34873
  let resizeHandler;
34874
+ let refreshTimer;
34752
34875
  let rendererDestroyed2 = false;
34876
+ const loadPageAbortController = new AbortController;
34753
34877
  try {
34754
- let render = function() {
34878
+ let stopPageRefresh = function() {
34879
+ if (refreshTimer !== undefined) {
34880
+ clearInterval(refreshTimer);
34881
+ refreshTimer = undefined;
34882
+ }
34883
+ loadPageAbortController.abort();
34884
+ }, render = function() {
34755
34885
  const contentWidth = renderer.width - 4;
34756
34886
  const rowsBelow = rowsBelowVisibleWindow({ sessionCount: page.sessions.length, selectedIndex, height: renderer.height });
34757
34887
  const isNarrow = renderer.width < 72;
@@ -34780,11 +34910,13 @@ async function createDashboardTui({
34780
34910
  if (settled)
34781
34911
  return;
34782
34912
  settled = true;
34913
+ stopPageRefresh();
34783
34914
  resolveAction(value);
34784
34915
  }, fail = function(error93) {
34785
34916
  if (settled)
34786
34917
  return;
34787
34918
  settled = true;
34919
+ stopPageRefresh();
34788
34920
  rejectDraft(error93);
34789
34921
  }, moveSelectionTo = function(index) {
34790
34922
  if (page.sessions.length === 0)
@@ -34903,6 +35035,7 @@ async function createDashboardTui({
34903
35035
  let pageIndex = 1;
34904
35036
  let selectedIndex = 0;
34905
35037
  let loadingPage = false;
35038
+ let pageRequestInFlight = false;
34906
35039
  let pageError;
34907
35040
  let awaitingVimGoToTop = false;
34908
35041
  let logoutConfirmationOpen = false;
@@ -34920,23 +35053,53 @@ async function createDashboardTui({
34920
35053
  rejectDraft = reject;
34921
35054
  });
34922
35055
  async function loadAdjacentPage(direction) {
34923
- if (loadingPage)
35056
+ if (pageRequestInFlight)
34924
35057
  return;
34925
35058
  const canLoad = direction === "next" ? page.hasMore : page.canGoPrevious;
34926
35059
  if (!canLoad)
34927
35060
  return;
35061
+ pageRequestInFlight = true;
34928
35062
  loadingPage = true;
34929
35063
  pageError = undefined;
34930
35064
  render();
34931
35065
  try {
34932
- page = await loadPage({ direction });
35066
+ const loadedPage = await loadPage({ target: direction, signal: loadPageAbortController.signal });
35067
+ if (destroyed || settled)
35068
+ return;
35069
+ page = loadedPage;
34933
35070
  pageIndex = Math.max(1, pageIndex + (direction === "next" ? 1 : -1));
34934
35071
  selectedIndex = direction === "next" ? 0 : Math.max(0, page.sessions.length - 1);
34935
35072
  } catch (error93) {
34936
- pageError = error93 instanceof Error ? error93.message : String(error93);
35073
+ if (!destroyed && !settled)
35074
+ pageError = error93 instanceof Error ? error93.message : String(error93);
34937
35075
  } finally {
35076
+ pageRequestInFlight = false;
34938
35077
  loadingPage = false;
35078
+ if (!destroyed && !settled)
35079
+ render();
35080
+ }
35081
+ }
35082
+ async function refreshCurrentPage() {
35083
+ if (pageRequestInFlight || destroyed || settled)
35084
+ return;
35085
+ pageRequestInFlight = true;
35086
+ try {
35087
+ const refreshedPage = await loadPage({ target: "current", signal: loadPageAbortController.signal });
35088
+ if (destroyed || settled)
35089
+ return;
35090
+ const selectedSessionId = page.sessions[selectedIndex]?.id;
35091
+ page = refreshedPage;
35092
+ const refreshedSelectedIndex = selectedSessionId ? page.sessions.findIndex((session) => session.id === selectedSessionId) : -1;
35093
+ selectedIndex = refreshedSelectedIndex >= 0 ? refreshedSelectedIndex : Math.max(0, Math.min(selectedIndex, page.sessions.length - 1));
35094
+ pageError = undefined;
34939
35095
  render();
35096
+ } catch (error93) {
35097
+ if (!destroyed && !settled) {
35098
+ pageError = error93 instanceof Error ? error93.message : String(error93);
35099
+ render();
35100
+ }
35101
+ } finally {
35102
+ pageRequestInFlight = false;
34940
35103
  }
34941
35104
  }
34942
35105
  async function moveSelection(direction) {
@@ -34968,6 +35131,9 @@ async function createDashboardTui({
34968
35131
  renderer.on(CliRenderEvents2.RESIZE, resizeHandler);
34969
35132
  render();
34970
35133
  listRegion.focus();
35134
+ refreshTimer = setInterval(() => {
35135
+ refreshCurrentPage();
35136
+ }, DASHBOARD_POLL_INTERVAL_MS);
34971
35137
  return {
34972
35138
  waitForAction: async () => await action,
34973
35139
  destroy() {
@@ -34980,6 +35146,7 @@ async function createDashboardTui({
34980
35146
  renderer.keyInput.off("keypress", keyHandler);
34981
35147
  if (resizeHandler)
34982
35148
  renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
35149
+ stopPageRefresh();
34983
35150
  rendererDestroyed2 = true;
34984
35151
  renderer.destroy();
34985
35152
  },
@@ -34994,6 +35161,9 @@ async function createDashboardTui({
34994
35161
  renderer.keyInput.off("keypress", keyHandler);
34995
35162
  if (resizeHandler)
34996
35163
  renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
35164
+ if (refreshTimer !== undefined)
35165
+ clearInterval(refreshTimer);
35166
+ loadPageAbortController.abort();
34997
35167
  if (!rendererDestroyed2) {
34998
35168
  rendererDestroyed2 = true;
34999
35169
  renderer.destroy();
@@ -35292,20 +35462,6 @@ function activePathToken(text) {
35292
35462
  return match?.[1];
35293
35463
  }
35294
35464
 
35295
- // src/session-agent-options.ts
35296
- function newSessionModelLabel(model) {
35297
- return codexModelPresentation[model].label;
35298
- }
35299
- function newSessionReasoningEffortLabel(effort) {
35300
- return agentReasoningEffortPresentation[effort].label;
35301
- }
35302
- var newSessionModels = codexModelIds;
35303
- var newSessionReasoningEfforts = selectableAgentReasoningEfforts;
35304
- var defaultNewSessionAgent = {
35305
- model: codexModelIds[0],
35306
- reasoningEffort: "medium"
35307
- };
35308
-
35309
35465
  // src/tui/new-session-wizard.ts
35310
35466
  var suggestionSpinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
35311
35467
  var suggestionSpinnerIntervalMs = 80;
@@ -36211,7 +36367,19 @@ async function createSessionTui({
36211
36367
  }
36212
36368
  return count;
36213
36369
  }, turnDividerLabel = function() {
36214
- if (!isRemyWorking(latestState))
36370
+ const working = isRemyWorking(latestState);
36371
+ const planItems = pinnedPlanItems(latestState);
36372
+ if (planItems.length > 0) {
36373
+ const completed = planItems.filter((item) => item.status === "completed").length;
36374
+ const current = planItems.find((item) => item.status === "in_progress") ?? planItems.find((item) => item.status === "pending");
36375
+ if (stopState.kind === "stopping")
36376
+ return renderer.width < 76 ? `Plan ${completed}/${planItems.length} \xB7 stopping\u2026` : `Plan ${completed}/${planItems.length} \xB7 stopping the turn\u2026`;
36377
+ if (renderer.width < 84)
36378
+ return `Plan ${completed}/${planItems.length} \xB7 ctrl+o${working ? " \xB7 esc stop" : ""}`;
36379
+ const currentLabel = current ? ` \xB7 ${truncate2(current.title, 20)}` : "";
36380
+ return `Plan ${completed}/${planItems.length}${currentLabel} \xB7 ctrl+o details${working ? " \xB7 esc to stop" : ""}`;
36381
+ }
36382
+ if (!working)
36215
36383
  return;
36216
36384
  if (stopState.kind === "stopping")
36217
36385
  return renderer.width < 76 ? "stopping\u2026" : "stopping the turn\u2026";
@@ -36989,6 +37157,12 @@ function workingTurnStartedAt(state) {
36989
37157
  return activeTurn.startedAt;
36990
37158
  return Object.values(state.messageTurns).find((turn) => turn.outcome === undefined && turn.startedAt !== undefined)?.startedAt;
36991
37159
  }
37160
+ function pinnedPlanItems(state) {
37161
+ if (state.aggregateStatus !== "open" || state.work.items.length === 0 || state.latestWorkTurnId === undefined)
37162
+ return [];
37163
+ const hasIncompleteItem = state.work.items.some((item) => item.status !== "completed");
37164
+ return !hasIncompleteItem && state.retainedTurnEnds[state.latestWorkTurnId] ? [] : state.work.items;
37165
+ }
36992
37166
  var collapsedActivityTailLength = 3;
36993
37167
  function isSignalActivity(item) {
36994
37168
  return item.card.weight === "signal";
@@ -37009,12 +37183,12 @@ function renderTimeline({ state, activityExpanded }) {
37009
37183
  }
37010
37184
  groups.push({ kind: "single", item });
37011
37185
  }
37012
- const blocks = groups.map((group) => group.kind === "single" ? renderTimelineItem(group.item) : renderActivityGroup({ items: group.items, activityExpanded }));
37186
+ const blocks = groups.map((group) => group.kind === "single" ? renderTimelineItem(group.item, activityExpanded) : renderActivityGroup({ items: group.items, activityExpanded }));
37013
37187
  return joinStyled(blocks.filter((block) => block.chunks.length > 0), `
37014
37188
 
37015
37189
  `);
37016
37190
  }
37017
- function renderTimelineItem(item) {
37191
+ function renderTimelineItem(item, activityExpanded) {
37018
37192
  if (item.kind === "message") {
37019
37193
  const header = new StyledText5([
37020
37194
  renderTimestampChunk(item.occurredAt),
@@ -37024,12 +37198,53 @@ function renderTimelineItem(item) {
37024
37198
  return joinStyled([header, renderMessageBody({ text: item.text, role: item.author.role })], `
37025
37199
  `);
37026
37200
  }
37201
+ if (item.kind === "plan")
37202
+ return renderPlan({ item, expanded: activityExpanded });
37027
37203
  return new StyledText5([
37028
37204
  renderTimestampChunk(item.occurredAt),
37029
37205
  bold4(fg6(PALETTE.tool)("Artifact: ")),
37030
37206
  fg6(PALETTE.bodyText)(item.filename)
37031
37207
  ]);
37032
37208
  }
37209
+ var collapsedPlanItemLimit = 5;
37210
+ function renderPlan({ item, expanded }) {
37211
+ if (item.items.length === 0)
37212
+ return new StyledText5([]);
37213
+ const completed = item.items.filter((workItem) => workItem.status === "completed").length;
37214
+ const visibleItems = expanded ? item.items : collapsedPlanItems(item.items);
37215
+ const rows = visibleItems.map((workItem) => {
37216
+ if (workItem.status === "in_progress") {
37217
+ return new StyledText5([
37218
+ fg6(PALETTE.remyAccent)(" \u25B8 "),
37219
+ bold4(fg6(PALETTE.bodyText)(workItem.title))
37220
+ ]);
37221
+ }
37222
+ const glyph = workItem.status === "completed" ? "\u2713" : "\u25CB";
37223
+ return new StyledText5([
37224
+ fg6(PALETTE.dimText)(` ${glyph} `),
37225
+ dim4(fg6(PALETTE.dimText)(workItem.title))
37226
+ ]);
37227
+ });
37228
+ const hidden = item.items.length - visibleItems.length;
37229
+ return joinStyled([
37230
+ new StyledText5([
37231
+ renderTimestampChunk(item.occurredAt),
37232
+ bold4(fg6(PALETTE.remyAccent)("Plan")),
37233
+ dim4(fg6(PALETTE.dimText)(` \xB7 ${completed}/${item.items.length} done`))
37234
+ ]),
37235
+ ...rows,
37236
+ ...hidden > 0 ? [new StyledText5([dim4(fg6(PALETTE.dimText)(` \u2026 ${hidden} more task${hidden === 1 ? "" : "s"} \xB7 Ctrl+O details`))])] : []
37237
+ ], `
37238
+ `);
37239
+ }
37240
+ function collapsedPlanItems(items) {
37241
+ if (items.length <= collapsedPlanItemLimit)
37242
+ return items;
37243
+ const current = items.find((item) => item.status === "in_progress");
37244
+ if (!current || items.slice(0, collapsedPlanItemLimit).some((item) => item.id === current.id))
37245
+ return items.slice(0, collapsedPlanItemLimit);
37246
+ return [...items.slice(0, collapsedPlanItemLimit - 1), current];
37247
+ }
37033
37248
  var boilerplateActivitySummaries = new Set([
37034
37249
  "Tool started.",
37035
37250
  "Tool completed.",
@@ -37159,6 +37374,11 @@ function formatElapsed(elapsedMs) {
37159
37374
  return `${totalSeconds}s`;
37160
37375
  return `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`;
37161
37376
  }
37377
+ function truncate2(value, width) {
37378
+ if (value.length <= width)
37379
+ return value;
37380
+ return `${value.slice(0, Math.max(0, width - 1))}\u2026`;
37381
+ }
37162
37382
  function renderComposerStatus({ admittedSubmissions }) {
37163
37383
  if (admittedSubmissions.length === 0)
37164
37384
  return new StyledText5([]);
@@ -37294,7 +37514,7 @@ var compactMarkRows = 9;
37294
37514
  var compactMinWidth = 48;
37295
37515
  var compactMinHeight = 20;
37296
37516
  var markBrightnessGain = 4.2;
37297
- var remyCliVersion = "1.4.3";
37517
+ var remyCliVersion = "1.6.0";
37298
37518
  async function showRemySplash({
37299
37519
  createRenderer = createRemyRenderer,
37300
37520
  durationMs = splashDurationMs,
@@ -38073,18 +38293,34 @@ async function dashboard({
38073
38293
  function holdTransitionScreen() {
38074
38294
  showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
38075
38295
  }
38076
- async function loadDashboardPage({ direction }) {
38296
+ async function loadDashboardPage({ target, signal }) {
38077
38297
  if (loadingPage)
38078
38298
  return await loadingPage;
38079
38299
  loadingPage = (async () => {
38080
- if (direction === "previous") {
38300
+ if (target === "current") {
38301
+ const after = pageIndex > 0 ? pages[pageIndex - 1]?.nextCursor ?? undefined : undefined;
38302
+ const refreshed = toDashboardPage(await operations.listSessions({
38303
+ client: operations.client,
38304
+ limit: 20,
38305
+ after,
38306
+ signal
38307
+ }));
38308
+ pages.splice(pageIndex, pages.length - pageIndex, refreshed);
38309
+ return { ...refreshed, canGoPrevious: pageIndex > 0 };
38310
+ }
38311
+ if (target === "previous") {
38081
38312
  pageIndex = Math.max(0, pageIndex - 1);
38082
38313
  return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
38083
38314
  }
38084
38315
  const current = pages[pageIndex];
38085
38316
  if (!current.hasMore || !current.nextCursor)
38086
38317
  return { ...current, canGoPrevious: pageIndex > 0 };
38087
- const next = toDashboardPage(await operations.listSessions({ client: operations.client, after: current.nextCursor }));
38318
+ const next = toDashboardPage(await operations.listSessions({
38319
+ client: operations.client,
38320
+ limit: 20,
38321
+ after: current.nextCursor,
38322
+ signal
38323
+ }));
38088
38324
  pages.splice(pageIndex + 1);
38089
38325
  pages.push(next);
38090
38326
  pageIndex += 1;
@@ -38100,7 +38336,11 @@ async function dashboard({
38100
38336
  holdTransitionScreen();
38101
38337
  while (true) {
38102
38338
  if (!firstOpen) {
38103
- pages.splice(0, pages.length, toDashboardPage(await operations.listSessions({ client: operations.client, limit: 20 })));
38339
+ pages.splice(0, pages.length, toDashboardPage(await operations.listSessions({
38340
+ client: operations.client,
38341
+ limit: 20,
38342
+ signal: dependencies.abortSignal
38343
+ })));
38104
38344
  pageIndex = 0;
38105
38345
  }
38106
38346
  firstOpen = false;
@@ -38362,7 +38602,7 @@ async function createNewSession({
38362
38602
  prompt: command.prompt,
38363
38603
  fileIds,
38364
38604
  ...command.model ? { model: command.model } : {},
38365
- ...command.reasoningEffort ? { reasoningEffort: command.reasoningEffort } : {},
38605
+ reasoningEffort: command.reasoningEffort ?? defaultNewSessionAgent.reasoningEffort,
38366
38606
  idempotencyKey: randomUUID5()
38367
38607
  }
38368
38608
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.4.3",
3
+ "version": "1.6.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {