@supatest/cli 0.0.22 → 0.0.24

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 +149 -18
  2. package/package.json +5 -2
package/dist/index.js CHANGED
@@ -3817,6 +3817,34 @@ function installChromium() {
3817
3817
  };
3818
3818
  }
3819
3819
  }
3820
+ function createSupatestConfig(cwd) {
3821
+ const supatestDir = path.join(cwd, ".supatest");
3822
+ const mcpJsonPath = path.join(supatestDir, "mcp.json");
3823
+ try {
3824
+ if (fs.existsSync(mcpJsonPath)) {
3825
+ return {
3826
+ ok: true,
3827
+ message: "mcp.json already exists",
3828
+ created: false
3829
+ };
3830
+ }
3831
+ if (!fs.existsSync(supatestDir)) {
3832
+ fs.mkdirSync(supatestDir, { recursive: true });
3833
+ }
3834
+ fs.writeFileSync(mcpJsonPath, JSON.stringify(DEFAULT_MCP_CONFIG, null, 2) + "\n", "utf-8");
3835
+ return {
3836
+ ok: true,
3837
+ message: "Created .supatest/mcp.json with Playwright MCP server configuration",
3838
+ created: true
3839
+ };
3840
+ } catch (error) {
3841
+ return {
3842
+ ok: false,
3843
+ message: `Failed to create mcp.json: ${error instanceof Error ? error.message : String(error)}`,
3844
+ created: false
3845
+ };
3846
+ }
3847
+ }
3820
3848
  function getVersionSummary() {
3821
3849
  const nodeVersion = getNodeVersion();
3822
3850
  const playwrightVersion = getPlaywrightVersion();
@@ -3828,7 +3856,7 @@ function getVersionSummary() {
3828
3856
  lines.push(` Chromium: ${chromiumVersion ? `build ${chromiumVersion}` : "Not installed"}`);
3829
3857
  return lines.join("\n");
3830
3858
  }
3831
- async function setupCommand() {
3859
+ async function setupCommand(options) {
3832
3860
  const output = [];
3833
3861
  const log = (msg) => {
3834
3862
  output.push(msg);
@@ -3876,6 +3904,18 @@ async function setupCommand() {
3876
3904
  result.errors.push(chromiumResult.message);
3877
3905
  }
3878
3906
  }
3907
+ log("\n3. Setting up MCP configuration...");
3908
+ const configResult = createSupatestConfig(options.cwd);
3909
+ if (configResult.ok) {
3910
+ if (configResult.created) {
3911
+ log(` \u2705 ${configResult.message}`);
3912
+ } else {
3913
+ log(` \u2705 ${configResult.message}`);
3914
+ }
3915
+ } else {
3916
+ log(` \u274C ${configResult.message}`);
3917
+ result.errors.push(configResult.message);
3918
+ }
3879
3919
  const versionSummary = getVersionSummary();
3880
3920
  log(versionSummary);
3881
3921
  log("\n" + "\u2500".repeat(50));
@@ -3891,11 +3931,19 @@ async function setupCommand() {
3891
3931
  result.output = output.join("\n");
3892
3932
  return result;
3893
3933
  }
3894
- var MINIMUM_NODE_VERSION;
3934
+ var MINIMUM_NODE_VERSION, DEFAULT_MCP_CONFIG;
3895
3935
  var init_setup = __esm({
3896
3936
  "src/commands/setup.ts"() {
3897
3937
  "use strict";
3898
3938
  MINIMUM_NODE_VERSION = 18;
3939
+ DEFAULT_MCP_CONFIG = {
3940
+ mcpServers: {
3941
+ playwright: {
3942
+ command: "npx",
3943
+ args: ["@playwright/mcp@latest"]
3944
+ }
3945
+ }
3946
+ };
3899
3947
  }
3900
3948
  });
3901
3949
 
@@ -3904,7 +3952,7 @@ var CLI_VERSION;
3904
3952
  var init_version = __esm({
3905
3953
  "src/version.ts"() {
3906
3954
  "use strict";
3907
- CLI_VERSION = "0.0.22";
3955
+ CLI_VERSION = "0.0.24";
3908
3956
  }
3909
3957
  });
3910
3958
 
@@ -4956,11 +5004,23 @@ ${projectInstructions}`,
4956
5004
  resultText = msg.result || resultText;
4957
5005
  } else {
4958
5006
  hasError = true;
4959
- if ("errors" in msg && Array.isArray(msg.errors)) {
5007
+ if (msg.subtype === "error_max_turns") {
5008
+ const maxTurnsError = `Agent stopped after ${msg.num_turns} turns (max iterations reached). Use --max-iterations to increase the limit.`;
5009
+ errors.push(maxTurnsError);
5010
+ this.presenter.onError(maxTurnsError);
5011
+ } else if (msg.subtype === "error_max_budget_usd") {
5012
+ const budgetError = "Agent stopped: budget limit exceeded.";
5013
+ errors.push(budgetError);
5014
+ this.presenter.onError(budgetError);
5015
+ } else if ("errors" in msg && Array.isArray(msg.errors)) {
4960
5016
  errors.push(...msg.errors);
4961
5017
  for (const error of msg.errors) {
4962
5018
  this.presenter.onError(error);
4963
5019
  }
5020
+ } else {
5021
+ const unknownError = `Agent stopped unexpectedly (${msg.subtype || "unknown reason"})`;
5022
+ errors.push(unknownError);
5023
+ this.presenter.onError(unknownError);
4964
5024
  }
4965
5025
  }
4966
5026
  } else if (msg.type === "user") {
@@ -5253,7 +5313,7 @@ var init_react = __esm({
5253
5313
  }
5254
5314
  onUsageUpdate(usage) {
5255
5315
  const contextWindow = CONTEXT_WINDOWS[usage.model] || 2e5;
5256
- const contextTokens = usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens;
5316
+ const contextTokens = usage.inputTokens + usage.cacheReadTokens;
5257
5317
  const contextPct = contextTokens / contextWindow * 100;
5258
5318
  this.callbacks.setUsageStats((prev) => {
5259
5319
  if (prev && prev.inputTokens > contextTokens) {
@@ -5298,7 +5358,7 @@ var init_react = __esm({
5298
5358
  if (result.usage) {
5299
5359
  const usage = result.usage;
5300
5360
  const contextWindow = CONTEXT_WINDOWS[usage.model] || 2e5;
5301
- const contextTokens = usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens;
5361
+ const contextTokens = usage.inputTokens + usage.cacheReadTokens;
5302
5362
  const contextPct = contextTokens / contextWindow * 100;
5303
5363
  this.callbacks.setUsageStats((prev) => {
5304
5364
  if (prev && prev.inputTokens > contextTokens) {
@@ -5415,6 +5475,9 @@ var init_SessionContext = __esm({
5415
5475
  });
5416
5476
  setStaticRemountKey((prev) => prev + 1);
5417
5477
  }, []);
5478
+ const refreshStatic = useCallback(() => {
5479
+ setStaticRemountKey((prev) => prev + 1);
5480
+ }, []);
5418
5481
  const updateStats = useCallback((updates) => {
5419
5482
  setStats((prev) => ({ ...prev, ...updates }));
5420
5483
  }, []);
@@ -5460,7 +5523,8 @@ var init_SessionContext = __esm({
5460
5523
  setPlanFilePath,
5461
5524
  selectedModel,
5462
5525
  setSelectedModel,
5463
- staticRemountKey
5526
+ staticRemountKey,
5527
+ refreshStatic
5464
5528
  };
5465
5529
  return /* @__PURE__ */ React.createElement(SessionContext.Provider, { value }, children);
5466
5530
  };
@@ -6734,7 +6798,7 @@ var init_message_bridge = __esm({
6734
6798
  });
6735
6799
 
6736
6800
  // src/commands/login.ts
6737
- import { spawn } from "child_process";
6801
+ import { spawn as spawn2 } from "child_process";
6738
6802
  import crypto2 from "crypto";
6739
6803
  import http from "http";
6740
6804
  import { platform } from "os";
@@ -6770,7 +6834,7 @@ function openBrowser(url) {
6770
6834
  default:
6771
6835
  command = "xdg-open";
6772
6836
  }
6773
- spawn(command, [url], { detached: true, stdio: "ignore" }).unref();
6837
+ spawn2(command, [url], { detached: true, stdio: "ignore" }).unref();
6774
6838
  }
6775
6839
  function startCallbackServer(port, expectedState) {
6776
6840
  return new Promise((resolve2, reject) => {
@@ -8681,9 +8745,9 @@ var init_useOverlayEscapeGuard = __esm({
8681
8745
  });
8682
8746
 
8683
8747
  // src/ui/App.tsx
8684
- import { execSync as execSync4 } from "child_process";
8748
+ import { execSync as execSync5 } from "child_process";
8685
8749
  import { homedir as homedir5 } from "os";
8686
- import { Box as Box20, Text as Text18, useApp as useApp2 } from "ink";
8750
+ import { Box as Box20, Text as Text18, useApp as useApp2, useStdout as useStdout2 } from "ink";
8687
8751
  import Spinner3 from "ink-spinner";
8688
8752
  import React23, { useEffect as useEffect10, useRef as useRef4, useState as useState10 } from "react";
8689
8753
  var getGitBranch2, getCurrentFolder2, AppContent, App;
@@ -8713,7 +8777,7 @@ var init_App = __esm({
8713
8777
  init_theme();
8714
8778
  getGitBranch2 = () => {
8715
8779
  try {
8716
- return execSync4("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
8780
+ return execSync5("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
8717
8781
  } catch {
8718
8782
  return "";
8719
8783
  }
@@ -8728,7 +8792,8 @@ var init_App = __esm({
8728
8792
  };
8729
8793
  AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession }) => {
8730
8794
  const { exit } = useApp2();
8731
- const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel } = useSession();
8795
+ const { stdout } = useStdout2();
8796
+ const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic } = useSession();
8732
8797
  useModeToggle();
8733
8798
  const [terminalWidth, setTerminalWidth] = useState10(process.stdout.columns || 80);
8734
8799
  const [showHelp, setShowHelp] = useState10(false);
@@ -8881,7 +8946,7 @@ var init_App = __esm({
8881
8946
  content: "Running setup..."
8882
8947
  });
8883
8948
  try {
8884
- const result = await setupCommand();
8949
+ const result = await setupCommand({ cwd: config2.cwd || process.cwd() });
8885
8950
  addMessage({
8886
8951
  type: "assistant",
8887
8952
  content: result.output
@@ -9005,6 +9070,7 @@ var init_App = __esm({
9005
9070
  markOverlayClosed();
9006
9071
  setShowHelp(false);
9007
9072
  };
9073
+ const isInitialMount = useRef4(true);
9008
9074
  useEffect10(() => {
9009
9075
  const handleResize = () => {
9010
9076
  setTerminalWidth(process.stdout.columns || 80);
@@ -9014,6 +9080,19 @@ var init_App = __esm({
9014
9080
  process.stdout.off("resize", handleResize);
9015
9081
  };
9016
9082
  }, []);
9083
+ useEffect10(() => {
9084
+ if (isInitialMount.current) {
9085
+ isInitialMount.current = false;
9086
+ return;
9087
+ }
9088
+ const handler = setTimeout(() => {
9089
+ stdout?.write("\x1B[3J\x1B[H\x1B[2J");
9090
+ refreshStatic();
9091
+ }, 300);
9092
+ return () => {
9093
+ clearTimeout(handler);
9094
+ };
9095
+ }, [terminalWidth, refreshStatic, stdout]);
9017
9096
  useKeypress(
9018
9097
  (key) => {
9019
9098
  if (key.name === "escape" && isAgentRunning && !isOverlayOpen) {
@@ -9941,6 +10020,57 @@ async function runAgent(config2) {
9941
10020
  });
9942
10021
  }
9943
10022
 
10023
+ // src/utils/auto-update.ts
10024
+ init_error_logger();
10025
+ init_version();
10026
+ import { execSync as execSync3, spawn } from "child_process";
10027
+ import latestVersion from "latest-version";
10028
+ import { gt as gt2 } from "semver";
10029
+ var UPDATE_CHECK_TIMEOUT = 3e3;
10030
+ var INSTALL_TIMEOUT = 6e4;
10031
+ async function checkAndAutoUpdate() {
10032
+ if (process.env.NODE_ENV === "development") {
10033
+ return;
10034
+ }
10035
+ if (process.env.SUPATEST_SKIP_UPDATE === "1") {
10036
+ return;
10037
+ }
10038
+ try {
10039
+ const latest = await Promise.race([
10040
+ latestVersion("@supatest/cli"),
10041
+ new Promise(
10042
+ (_2, reject) => setTimeout(() => reject(new Error("timeout")), UPDATE_CHECK_TIMEOUT)
10043
+ )
10044
+ ]);
10045
+ if (!gt2(latest, CLI_VERSION)) {
10046
+ return;
10047
+ }
10048
+ console.log(`
10049
+ Updating Supatest CLI ${CLI_VERSION} \u2192 ${latest}...`);
10050
+ try {
10051
+ execSync3("npm install -g @supatest/cli@latest", {
10052
+ stdio: "inherit",
10053
+ timeout: INSTALL_TIMEOUT
10054
+ });
10055
+ console.log("\u2713 Updated successfully\n");
10056
+ const child = spawn(process.argv[0], process.argv.slice(1), {
10057
+ stdio: "inherit",
10058
+ detached: false
10059
+ });
10060
+ child.on("exit", (code) => process.exit(code ?? 0));
10061
+ await new Promise(() => {
10062
+ });
10063
+ } catch (installError) {
10064
+ logError(installError, { source: "auto-update", action: "install" });
10065
+ const errMsg = installError instanceof Error ? installError.message : String(installError);
10066
+ console.error("Failed to update:", errMsg);
10067
+ console.log("Continuing with current version...\n");
10068
+ }
10069
+ } catch (error) {
10070
+ logError(error, { source: "auto-update", action: "check" });
10071
+ }
10072
+ }
10073
+
9944
10074
  // src/index.ts
9945
10075
  init_banner();
9946
10076
  init_error_logger();
@@ -9948,7 +10078,7 @@ init_logger();
9948
10078
 
9949
10079
  // src/utils/node-version.ts
9950
10080
  init_logger();
9951
- import { execSync as execSync3 } from "child_process";
10081
+ import { execSync as execSync4 } from "child_process";
9952
10082
  var MINIMUM_NODE_VERSION2 = 18;
9953
10083
  function parseVersion2(versionString) {
9954
10084
  const cleaned = versionString.trim().replace(/^v/, "");
@@ -9965,7 +10095,7 @@ function parseVersion2(versionString) {
9965
10095
  }
9966
10096
  function getNodeVersion2() {
9967
10097
  try {
9968
- const versionOutput = execSync3("node --version", {
10098
+ const versionOutput = execSync4("node --version", {
9969
10099
  encoding: "utf-8",
9970
10100
  stdio: ["ignore", "pipe", "ignore"]
9971
10101
  });
@@ -10085,6 +10215,7 @@ program.name("supatest").description(
10085
10215
  ).option("--supatest-api-key <key>", "Supatest API key (or use SUPATEST_API_KEY env)").option("--supatest-api-url <url>", "Supatest API URL (or use SUPATEST_API_URL env, defaults to https://code-api.supatest.ai)").option("--headless", "Run in headless mode (for CI/CD, minimal output)").option("--verbose", "Enable verbose logging").option("--model <model>", "Claude model to use (or use ANTHROPIC_MODEL_NAME env)").action(async (task, options) => {
10086
10216
  try {
10087
10217
  checkNodeVersion2();
10218
+ await checkAndAutoUpdate();
10088
10219
  const isHeadlessMode = options.headless || process.stdin.isTTY === false;
10089
10220
  if (options.verbose) {
10090
10221
  logger.setVerbose(true);
@@ -10193,9 +10324,9 @@ program.name("supatest").description(
10193
10324
  process.exit(1);
10194
10325
  }
10195
10326
  });
10196
- program.command("setup").description("Check prerequisites and set up required tools (Node.js, Playwright MCP)").action(async () => {
10327
+ program.command("setup").description("Check prerequisites and set up required tools (Node.js, Playwright MCP)").option("-C, --cwd <path>", "Working directory for setup", process.cwd()).action(async (options) => {
10197
10328
  try {
10198
- const result = await setupCommand();
10329
+ const result = await setupCommand({ cwd: options.cwd });
10199
10330
  process.exit(result.errors.length === 0 ? 0 : 1);
10200
10331
  } catch (error) {
10201
10332
  logError(error, { source: "setup" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supatest/cli",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Supatest CLI - AI-powered task automation for CI/CD",
5
5
  "type": "module",
6
6
  "bin": {
@@ -64,10 +64,13 @@
64
64
  "string-width": "^8.1.0",
65
65
  "strip-ansi": "^7.1.2",
66
66
  "undici": "^7.16.0",
67
- "wrap-ansi": "^9.0.2"
67
+ "wrap-ansi": "^9.0.2",
68
+ "latest-version": "^9.0.0",
69
+ "semver": "^7.6.0"
68
70
  },
69
71
  "devDependencies": {
70
72
  "@types/node": "^20.12.12",
73
+ "@types/semver": "^7.5.8",
71
74
  "@types/react": "^19.0.0",
72
75
  "nodemon": "^3.1.11",
73
76
  "tsup": "^8.5.1",