@supatest/cli 0.0.39 → 0.0.40

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 (2) hide show
  1. package/dist/index.js +86 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5891,7 +5891,7 @@ var CLI_VERSION;
5891
5891
  var init_version = __esm({
5892
5892
  "src/version.ts"() {
5893
5893
  "use strict";
5894
- CLI_VERSION = "0.0.39";
5894
+ CLI_VERSION = "0.0.40";
5895
5895
  }
5896
5896
  });
5897
5897
 
@@ -7528,10 +7528,11 @@ var init_react = __esm({
7528
7528
 
7529
7529
  // src/ui/contexts/SessionContext.tsx
7530
7530
  import React, { createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
7531
- var SessionContext, SessionProvider, useSession;
7531
+ var UsageStatsContext, SessionContext, SessionProvider, useSession, useUsageStats;
7532
7532
  var init_SessionContext = __esm({
7533
7533
  "src/ui/contexts/SessionContext.tsx"() {
7534
7534
  "use strict";
7535
+ UsageStatsContext = createContext(null);
7535
7536
  SessionContext = createContext(null);
7536
7537
  SessionProvider = ({
7537
7538
  children,
@@ -7671,8 +7672,6 @@ var init_SessionContext = __esm({
7671
7672
  setTodos,
7672
7673
  stats,
7673
7674
  updateStats,
7674
- usageStats,
7675
- setUsageStats,
7676
7675
  isAgentRunning,
7677
7676
  setIsAgentRunning,
7678
7677
  shouldInterruptAgent,
@@ -7706,7 +7705,6 @@ var init_SessionContext = __esm({
7706
7705
  todos,
7707
7706
  stats,
7708
7707
  updateStats,
7709
- usageStats,
7710
7708
  isAgentRunning,
7711
7709
  shouldInterruptAgent,
7712
7710
  sessionId,
@@ -7718,7 +7716,11 @@ var init_SessionContext = __esm({
7718
7716
  staticRemountKey,
7719
7717
  refreshStatic
7720
7718
  ]);
7721
- return /* @__PURE__ */ React.createElement(SessionContext.Provider, { value }, children);
7719
+ const usageStatsValue = useMemo(() => ({
7720
+ usageStats,
7721
+ setUsageStats
7722
+ }), [usageStats]);
7723
+ return /* @__PURE__ */ React.createElement(SessionContext.Provider, { value }, /* @__PURE__ */ React.createElement(UsageStatsContext.Provider, { value: usageStatsValue }, children));
7722
7724
  };
7723
7725
  useSession = () => {
7724
7726
  const context = useContext(SessionContext);
@@ -7727,6 +7729,13 @@ var init_SessionContext = __esm({
7727
7729
  }
7728
7730
  return context;
7729
7731
  };
7732
+ useUsageStats = () => {
7733
+ const context = useContext(UsageStatsContext);
7734
+ if (!context) {
7735
+ throw new Error("useUsageStats must be used within SessionProvider");
7736
+ }
7737
+ return context;
7738
+ };
7730
7739
  }
7731
7740
  });
7732
7741
 
@@ -8621,8 +8630,8 @@ var init_QueuedMessageDisplay = __esm({
8621
8630
 
8622
8631
  // src/ui/components/MessageList.tsx
8623
8632
  import { Box as Box12, Static } from "ink";
8624
- import React13, { memo as memo2, useCallback as useCallback2, useMemo as useMemo3, useRef as useRef3 } from "react";
8625
- var MessageList;
8633
+ import React13, { memo as memo2, useCallback as useCallback2, useMemo as useMemo3 } from "react";
8634
+ var StaticHeader, MessageList;
8626
8635
  var init_MessageList = __esm({
8627
8636
  "src/ui/components/MessageList.tsx"() {
8628
8637
  "use strict";
@@ -8637,6 +8646,11 @@ var init_MessageList = __esm({
8637
8646
  init_ToolMessage();
8638
8647
  init_UserMessage();
8639
8648
  init_QueuedMessageDisplay();
8649
+ StaticHeader = memo2(({ currentFolder, gitBranch, headless, staticRemountKey }) => {
8650
+ const headerItems = useMemo3(() => [{ id: "header" }], []);
8651
+ return /* @__PURE__ */ React13.createElement(Static, { items: headerItems, key: `header-${staticRemountKey}` }, () => /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: "header" }, /* @__PURE__ */ React13.createElement(Header, { currentFolder, gitBranch, headless })));
8652
+ });
8653
+ StaticHeader.displayName = "StaticHeader";
8640
8654
  MessageList = memo2(({ terminalWidth, currentFolder, gitBranch, headless = false, queuedTasks = [] }) => {
8641
8655
  const { messages, updateMessageById, isAgentRunning, staticRemountKey, toolGroupsExpanded, toggleToolGroups } = useSession();
8642
8656
  const handleToggle = useCallback2((id, currentExpanded) => {
@@ -8738,31 +8752,29 @@ var init_MessageList = __esm({
8738
8752
  }
8739
8753
  return renderMessage(group.messages[0]);
8740
8754
  }, [toolGroupsExpanded, toggleToolGroups, handleToggle, renderMessage]);
8741
- const lastUserMessageIndex = useMemo3(() => {
8742
- for (let i = messages.length - 1; i >= 0; i--) {
8743
- if (messages[i].type === "user") {
8744
- return i;
8745
- }
8746
- }
8747
- return -1;
8748
- }, [messages]);
8749
8755
  const hasPendingAssistant = useMemo3(
8750
8756
  () => messages.some((m) => m.type === "assistant" && m.isPending),
8751
8757
  [messages]
8752
8758
  );
8753
- const completedBoundaryRef = useRef3(-1);
8754
- const completedBoundaryKey = useMemo3(() => {
8755
- const currentBoundary = lastUserMessageIndex;
8756
- if (currentBoundary !== completedBoundaryRef.current) {
8757
- completedBoundaryRef.current = currentBoundary;
8758
- return `boundary-${currentBoundary}`;
8759
- }
8760
- return `boundary-${completedBoundaryRef.current}`;
8761
- }, [lastUserMessageIndex]);
8762
8759
  const { completedGroups, currentTurnGroups } = useMemo3(() => {
8763
8760
  const completed = [];
8764
8761
  const currentTurn = [];
8765
- const processTurn = (turnMessages2, targetArray) => {
8762
+ const isMessageComplete = (msg) => {
8763
+ switch (msg.type) {
8764
+ case "user":
8765
+ case "error":
8766
+ case "todo":
8767
+ case "thinking":
8768
+ return true;
8769
+ case "tool":
8770
+ return msg.toolResult !== void 0;
8771
+ case "assistant":
8772
+ return !msg.isPending;
8773
+ default:
8774
+ return true;
8775
+ }
8776
+ };
8777
+ const processTurn = (turnMessages, targetArray) => {
8766
8778
  let currentToolGroup = [];
8767
8779
  const flushToolGroup = () => {
8768
8780
  if (currentToolGroup.length === 0) return;
@@ -8773,7 +8785,7 @@ var init_MessageList = __esm({
8773
8785
  }
8774
8786
  currentToolGroup = [];
8775
8787
  };
8776
- for (const msg of turnMessages2) {
8788
+ for (const msg of turnMessages) {
8777
8789
  if (msg.type === "tool") {
8778
8790
  currentToolGroup.push(msg);
8779
8791
  } else {
@@ -8783,38 +8795,46 @@ var init_MessageList = __esm({
8783
8795
  }
8784
8796
  flushToolGroup();
8785
8797
  };
8786
- let turnMessages = [];
8787
- for (let i = 0; i < lastUserMessageIndex; i++) {
8788
- const msg = messages[i];
8789
- if (msg.type === "user") {
8790
- processTurn(turnMessages, completed);
8791
- turnMessages = [];
8792
- completed.push({ type: "single", messages: [msg] });
8798
+ const completeMessages = [];
8799
+ const inProgressMessages = [];
8800
+ for (const msg of messages) {
8801
+ if (isMessageComplete(msg)) {
8802
+ completeMessages.push(msg);
8793
8803
  } else {
8794
- turnMessages.push(msg);
8804
+ inProgressMessages.push(msg);
8795
8805
  }
8796
8806
  }
8797
- processTurn(turnMessages, completed);
8798
- if (lastUserMessageIndex >= 0) {
8799
- completed.push({ type: "single", messages: [messages[lastUserMessageIndex]] });
8807
+ let splitIndex = messages.length;
8808
+ for (let i = messages.length - 1; i >= 0; i--) {
8809
+ if (isMessageComplete(messages[i])) {
8810
+ splitIndex = i + 1;
8811
+ break;
8812
+ }
8800
8813
  }
8801
- const currentTurnMessages = lastUserMessageIndex >= 0 ? messages.slice(lastUserMessageIndex + 1) : messages;
8802
- processTurn(currentTurnMessages, currentTurn);
8814
+ const finalCompleteMessages = messages.slice(0, splitIndex);
8815
+ const finalInProgressMessages = messages.slice(splitIndex);
8816
+ processTurn(finalCompleteMessages, completed);
8817
+ processTurn(finalInProgressMessages, currentTurn);
8803
8818
  return { completedGroups: completed, currentTurnGroups: currentTurn };
8804
- }, [messages, lastUserMessageIndex, completedBoundaryKey]);
8805
- const staticItems = useMemo3(() => [
8806
- { id: "header", type: "header" },
8807
- ...completedGroups.map((group, idx) => {
8819
+ }, [messages]);
8820
+ const staticItems = useMemo3(
8821
+ () => completedGroups.map((group, idx) => {
8808
8822
  if (group.type === "group") {
8809
- return { ...group, _isGroup: true, id: `group-${idx}` };
8823
+ return { ...group, _isGroup: true, id: `group-${group.messages[0]?.id || idx}` };
8810
8824
  }
8811
8825
  return { ...group.messages[0], _isMessage: true };
8812
- })
8813
- ], [completedGroups]);
8814
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: staticRemountKey }, (item) => {
8815
- if (item.type === "header") {
8816
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: "header" }, /* @__PURE__ */ React13.createElement(Header, { currentFolder, gitBranch, headless }));
8826
+ }),
8827
+ [completedGroups]
8828
+ );
8829
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(
8830
+ StaticHeader,
8831
+ {
8832
+ currentFolder,
8833
+ gitBranch,
8834
+ headless,
8835
+ staticRemountKey
8817
8836
  }
8837
+ ), staticItems.length > 0 && /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: `messages-${staticRemountKey}-${toolGroupsExpanded}` }, (item) => {
8818
8838
  if (item._isGroup) {
8819
8839
  const content2 = renderGroupedMessage(item);
8820
8840
  if (!content2) {
@@ -11042,7 +11062,8 @@ var init_TestSelector = __esm({
11042
11062
  // Only fetch failed tests
11043
11063
  });
11044
11064
  setTotalTests(result.total);
11045
- setHasMore(allTests.length + result.tests.length < result.total);
11065
+ const loadedCount = allTests.length + result.tests.length;
11066
+ setHasMore(result.tests.length === PAGE_SIZE2 && loadedCount < result.total);
11046
11067
  setAllTests((prev) => [...prev, ...result.tests]);
11047
11068
  } catch (err) {
11048
11069
  setError(err instanceof Error ? err.message : String(err));
@@ -11154,7 +11175,7 @@ var init_TestSelector = __esm({
11154
11175
  const indicator = isSelected ? "\u25B6 " : " ";
11155
11176
  const bgColor = isSelected ? theme.text.accent : void 0;
11156
11177
  return /* @__PURE__ */ React21.createElement(Box18, { key: test.id, marginBottom: 0 }, /* @__PURE__ */ React21.createElement(Text16, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React21.createElement(Text16, { backgroundColor: bgColor, color: isChecked ? "green" : isSelected ? "black" : theme.text.dim }, checkbox), /* @__PURE__ */ React21.createElement(Text16, null, " "), /* @__PURE__ */ React21.createElement(Text16, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, file, line && /* @__PURE__ */ React21.createElement(Text16, { color: isSelected ? "black" : theme.text.dim }, ":", line), /* @__PURE__ */ React21.createElement(Text16, { color: isSelected ? "black" : theme.text.dim }, " - "), title));
11157
- })), /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column", marginTop: 1 }, allTests.length > VISIBLE_ITEMS2 && /* @__PURE__ */ React21.createElement(Box18, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "yellow" }, "Showing ", adjustedStart + 1, "-", adjustedEnd, " of ", totalTests || allTests.length, " failed tests", hasMore && !isLoading && /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React21.createElement(Box18, null, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "Space"), " toggle \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "a"), " all \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "n"), " none \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "Enter"), " fix selected")), selectedTests.size > 0 && /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "green" }, selectedTests.size, " test", selectedTests.size !== 1 ? "s" : "", " selected")), isLoading && /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "cyan" }, "Loading more tests..."))));
11178
+ })), /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column", marginTop: 1 }, allTests.length > VISIBLE_ITEMS2 && /* @__PURE__ */ React21.createElement(Box18, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "yellow" }, "Showing ", adjustedStart + 1, "-", adjustedEnd, " of ", allTests.length, " failed tests", hasMore && !isLoading && /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, " (scroll for more)"))), /* @__PURE__ */ React21.createElement(Box18, null, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "Space"), " toggle \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "a"), " all \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "n"), " none \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "Enter"), " fix selected")), selectedTests.size > 0 && /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "green" }, selectedTests.size, " test", selectedTests.size !== 1 ? "s" : "", " selected")), isLoading && /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "cyan" }, "Loading more tests..."))));
11158
11179
  };
11159
11180
  }
11160
11181
  });
@@ -11454,7 +11475,8 @@ var init_InputPrompt = __esm({
11454
11475
  onInputChange,
11455
11476
  isClaudeMax = false
11456
11477
  }, ref) => {
11457
- const { agentMode, selectedModel, setSelectedModel, isAgentRunning, usageStats } = useSession();
11478
+ const { agentMode, selectedModel, setSelectedModel, isAgentRunning } = useSession();
11479
+ const { usageStats } = useUsageStats();
11458
11480
  const [value, setValue] = useState10("");
11459
11481
  const [cursorOffset, setCursorOffset] = useState10(0);
11460
11482
  const [allFiles, setAllFiles] = useState10([]);
@@ -13158,11 +13180,14 @@ async function runInteractive(config2) {
13158
13180
  stdout: inkStdout,
13159
13181
  stderr: inkStderr,
13160
13182
  stdin: process.stdin,
13183
+ // Keep alternateBuffer: false for native terminal scrollback and text selection
13161
13184
  alternateBuffer: false,
13162
- // Like Gemini CLI - allows terminal to handle scroll & selection
13163
13185
  exitOnCtrlC: false,
13164
- // Reduce flickering by using incremental updates (only changed lines, not entire screen)
13165
- incrementalRendering: true
13186
+ // Disable incrementalRendering - it doesn't work well without alternateBuffer
13187
+ // and can cause more flickering issues
13188
+ incrementalRendering: false,
13189
+ // Prevent Ink from patching console methods
13190
+ patchConsole: false
13166
13191
  }
13167
13192
  );
13168
13193
  unmountFn = unmount;
@@ -13212,7 +13237,6 @@ var init_interactive = __esm({
13212
13237
  setIsAgentRunning,
13213
13238
  updateStats,
13214
13239
  setTodos,
13215
- setUsageStats,
13216
13240
  shouldInterruptAgent,
13217
13241
  setShouldInterruptAgent,
13218
13242
  agentMode,
@@ -13221,6 +13245,7 @@ var init_interactive = __esm({
13221
13245
  selectedModel,
13222
13246
  llmProvider
13223
13247
  } = useSession();
13248
+ const { setUsageStats } = useUsageStats();
13224
13249
  const agentRef = useRef8(null);
13225
13250
  useEffect15(() => {
13226
13251
  if (shouldInterruptAgent && agentRef.current) {
@@ -13333,9 +13358,9 @@ var init_interactive = __esm({
13333
13358
  addMessage,
13334
13359
  loadMessages,
13335
13360
  setSessionId: setContextSessionId,
13336
- setIsAgentRunning,
13337
- setUsageStats
13361
+ setIsAgentRunning
13338
13362
  } = useSession();
13363
+ const { setUsageStats } = useUsageStats();
13339
13364
  const [sessionId, setSessionId] = React31.useState(initialSessionId);
13340
13365
  const [currentTask, setCurrentTask] = React31.useState(config2.task);
13341
13366
  const [taskId, setTaskId] = React31.useState(0);
@@ -13567,9 +13592,9 @@ var HeadlessAgentRunner = ({ config: config2, sessionId, apiClient, onComplete }
13567
13592
  updateMessageByToolId,
13568
13593
  setIsAgentRunning,
13569
13594
  updateStats,
13570
- setTodos,
13571
- setUsageStats
13595
+ setTodos
13572
13596
  } = useSession();
13597
+ const { setUsageStats } = useUsageStats();
13573
13598
  const agentRef = useRef4(null);
13574
13599
  useEffect2(() => {
13575
13600
  let isMounted = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supatest/cli",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "Supatest CLI - AI-powered task automation for CI/CD",
5
5
  "type": "module",
6
6
  "bin": {