@supatest/cli 0.0.29 → 0.0.31
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.
- package/dist/index.js +106 -23
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -5296,7 +5296,9 @@ function getPlaywrightVersion() {
|
|
|
5296
5296
|
try {
|
|
5297
5297
|
const result = spawnSync("npx", ["playwright", "--version"], {
|
|
5298
5298
|
encoding: "utf-8",
|
|
5299
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
5299
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
5300
|
+
shell: true
|
|
5301
|
+
// Required for Windows where npx is npx.cmd
|
|
5300
5302
|
});
|
|
5301
5303
|
if (result.status === 0 && result.stdout) {
|
|
5302
5304
|
return result.stdout.trim().replace("Version ", "");
|
|
@@ -5364,7 +5366,9 @@ function installChromium() {
|
|
|
5364
5366
|
console.log("");
|
|
5365
5367
|
try {
|
|
5366
5368
|
const result = spawnSync("npx", ["playwright", "install", "chromium"], {
|
|
5367
|
-
stdio: "inherit"
|
|
5369
|
+
stdio: "inherit",
|
|
5370
|
+
shell: true
|
|
5371
|
+
// Required for Windows where npx is npx.cmd
|
|
5368
5372
|
});
|
|
5369
5373
|
if (result.status === 0) {
|
|
5370
5374
|
return {
|
|
@@ -5518,7 +5522,7 @@ var CLI_VERSION;
|
|
|
5518
5522
|
var init_version = __esm({
|
|
5519
5523
|
"src/version.ts"() {
|
|
5520
5524
|
"use strict";
|
|
5521
|
-
CLI_VERSION = "0.0.
|
|
5525
|
+
CLI_VERSION = "0.0.31";
|
|
5522
5526
|
}
|
|
5523
5527
|
});
|
|
5524
5528
|
|
|
@@ -6462,6 +6466,7 @@ var init_agent = __esm({
|
|
|
6462
6466
|
await init_config();
|
|
6463
6467
|
init_command_discovery();
|
|
6464
6468
|
init_error_logger();
|
|
6469
|
+
init_logger();
|
|
6465
6470
|
init_mcp_loader();
|
|
6466
6471
|
init_project_instructions();
|
|
6467
6472
|
CoreAgent = class {
|
|
@@ -6484,9 +6489,6 @@ var init_agent = __esm({
|
|
|
6484
6489
|
async run(config2) {
|
|
6485
6490
|
this.abortController = new AbortController();
|
|
6486
6491
|
this.presenter.onStart(config2);
|
|
6487
|
-
if (config2.providerSessionId) {
|
|
6488
|
-
this.presenter.onLog(`Resuming with providerSessionId: ${config2.providerSessionId}`);
|
|
6489
|
-
}
|
|
6490
6492
|
const claudeCodePath = await this.resolveClaudeCodePath();
|
|
6491
6493
|
let prompt = config2.task;
|
|
6492
6494
|
if (config2.logs) {
|
|
@@ -6530,6 +6532,7 @@ ${projectInstructions}`,
|
|
|
6530
6532
|
"ANTHROPIC_BASE_URL",
|
|
6531
6533
|
"ANTHROPIC_AUTH_TOKEN",
|
|
6532
6534
|
"CLAUDE_CODE_AUTH_TOKEN",
|
|
6535
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
6533
6536
|
"CLAUDE_API_KEY"
|
|
6534
6537
|
]);
|
|
6535
6538
|
for (const [key, value] of Object.entries(process.env)) {
|
|
@@ -6537,12 +6540,32 @@ ${projectInstructions}`,
|
|
|
6537
6540
|
cleanEnv[key] = value;
|
|
6538
6541
|
}
|
|
6539
6542
|
}
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6543
|
+
logger.debug("[agent] Setting up authentication", {
|
|
6544
|
+
useClaudeMax: !!config2.oauthToken,
|
|
6545
|
+
hasSupatestApiKey: !!config2.supatestApiKey,
|
|
6546
|
+
supatestApiKeyPrefix: config2.supatestApiKey ? config2.supatestApiKey.slice(0, 15) + "..." : null,
|
|
6547
|
+
anthropicBaseUrl: process.env.ANTHROPIC_BASE_URL
|
|
6548
|
+
});
|
|
6549
|
+
if (config2.oauthToken) {
|
|
6550
|
+
cleanEnv.ANTHROPIC_API_KEY = "";
|
|
6551
|
+
cleanEnv.ANTHROPIC_BASE_URL = "";
|
|
6552
|
+
this.presenter.onLog(`Auth: Using Claude Max (default Claude Code credentials)`);
|
|
6553
|
+
logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared ANTHROPIC_API_KEY and ANTHROPIC_BASE_URL");
|
|
6554
|
+
} else {
|
|
6555
|
+
const internalConfigDir = join6(homedir2(), ".supatest", "claude-internal");
|
|
6556
|
+
cleanEnv.CLAUDE_CONFIG_DIR = internalConfigDir;
|
|
6557
|
+
cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
|
|
6558
|
+
cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
|
|
6559
|
+
this.presenter.onLog(`Auth: Using Supatest API key${process.env.ANTHROPIC_BASE_URL ? ` (base: ${process.env.ANTHROPIC_BASE_URL})` : ""}`);
|
|
6560
|
+
logger.debug("[agent] API key mode: Set ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, and CLAUDE_CONFIG_DIR");
|
|
6561
|
+
}
|
|
6544
6562
|
cleanEnv.ANTHROPIC_AUTH_TOKEN = "";
|
|
6545
6563
|
cleanEnv.CLAUDE_CODE_AUTH_TOKEN = "";
|
|
6564
|
+
logger.debug("[agent] Final auth env vars", {
|
|
6565
|
+
ANTHROPIC_API_KEY: cleanEnv.ANTHROPIC_API_KEY ? cleanEnv.ANTHROPIC_API_KEY.slice(0, 15) + "..." : "(not set)",
|
|
6566
|
+
ANTHROPIC_BASE_URL: cleanEnv.ANTHROPIC_BASE_URL || "(not set)",
|
|
6567
|
+
CLAUDE_CONFIG_DIR: cleanEnv.CLAUDE_CONFIG_DIR || "(using default ~/.claude/)"
|
|
6568
|
+
});
|
|
6546
6569
|
cleanEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1";
|
|
6547
6570
|
const queryOptions = {
|
|
6548
6571
|
// AbortController for cancellation support
|
|
@@ -6615,6 +6638,13 @@ ${projectInstructions}`,
|
|
|
6615
6638
|
return expiredPatterns.some((pattern) => lowerError.includes(pattern));
|
|
6616
6639
|
};
|
|
6617
6640
|
const runQuery = async (options) => {
|
|
6641
|
+
logger.debug("[agent] Starting query", {
|
|
6642
|
+
model: options.model,
|
|
6643
|
+
maxTurns: options.maxTurns,
|
|
6644
|
+
permissionMode: options.permissionMode,
|
|
6645
|
+
hasResume: !!options.resume,
|
|
6646
|
+
cwd: options.cwd
|
|
6647
|
+
});
|
|
6618
6648
|
const queryIterator = query({ prompt, options });
|
|
6619
6649
|
if (this.messageBridge) {
|
|
6620
6650
|
queryIterator.streamInput(this.messageBridge).catch((err) => {
|
|
@@ -6737,16 +6767,24 @@ ${projectInstructions}`,
|
|
|
6737
6767
|
};
|
|
6738
6768
|
try {
|
|
6739
6769
|
await runQuery(queryOptions);
|
|
6770
|
+
logger.debug("[agent] Query completed successfully");
|
|
6740
6771
|
} catch (error) {
|
|
6741
6772
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6773
|
+
logger.debug("[agent] Query error caught", {
|
|
6774
|
+
errorName: error instanceof Error ? error.name : "unknown",
|
|
6775
|
+
errorMessage,
|
|
6776
|
+
stack: error instanceof Error ? error.stack?.slice(0, 500) : void 0
|
|
6777
|
+
});
|
|
6742
6778
|
const isAbortError = error instanceof Error && error.name === "AbortError" || errorMessage.toLowerCase().includes("aborted");
|
|
6743
6779
|
if (isAbortError) {
|
|
6744
6780
|
wasInterrupted = true;
|
|
6781
|
+
logger.debug("[agent] Query was aborted by user");
|
|
6745
6782
|
} else if (config2.providerSessionId && isSessionExpiredError(errorMessage)) {
|
|
6746
6783
|
const expiredMessage = "Can't continue conversation older than 30 days. Please start a new session.";
|
|
6747
6784
|
this.presenter.onError(expiredMessage, true);
|
|
6748
6785
|
hasError = true;
|
|
6749
6786
|
errors.push(expiredMessage);
|
|
6787
|
+
logger.debug("[agent] Session expired error");
|
|
6750
6788
|
} else {
|
|
6751
6789
|
logError(error, {
|
|
6752
6790
|
source: "CoreAgent.run",
|
|
@@ -6758,6 +6796,7 @@ ${projectInstructions}`,
|
|
|
6758
6796
|
this.presenter.onError(errorMessage, true);
|
|
6759
6797
|
hasError = true;
|
|
6760
6798
|
errors.push(errorMessage);
|
|
6799
|
+
logger.debug("[agent] General error, logged to error-logger");
|
|
6761
6800
|
}
|
|
6762
6801
|
} finally {
|
|
6763
6802
|
this.messageBridge?.close();
|
|
@@ -8544,7 +8583,8 @@ function openBrowser(url) {
|
|
|
8544
8583
|
default:
|
|
8545
8584
|
command = "xdg-open";
|
|
8546
8585
|
}
|
|
8547
|
-
|
|
8586
|
+
const options = { detached: true, stdio: "ignore", shell: os3 === "win32" };
|
|
8587
|
+
spawn2(command, [url], options).unref();
|
|
8548
8588
|
}
|
|
8549
8589
|
function startCallbackServer(port, expectedState) {
|
|
8550
8590
|
return new Promise((resolve2, reject) => {
|
|
@@ -10958,7 +10998,7 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
10958
10998
|
});
|
|
10959
10999
|
|
|
10960
11000
|
// src/ui/App.tsx
|
|
10961
|
-
import { execSync as
|
|
11001
|
+
import { execSync as execSync6 } from "child_process";
|
|
10962
11002
|
import { homedir as homedir5 } from "os";
|
|
10963
11003
|
import { Box as Box23, Text as Text21, useApp as useApp2, useStdout as useStdout2 } from "ink";
|
|
10964
11004
|
import Spinner3 from "ink-spinner";
|
|
@@ -10992,7 +11032,7 @@ var init_App = __esm({
|
|
|
10992
11032
|
init_theme();
|
|
10993
11033
|
getGitBranch2 = () => {
|
|
10994
11034
|
try {
|
|
10995
|
-
return
|
|
11035
|
+
return execSync6("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
|
|
10996
11036
|
} catch {
|
|
10997
11037
|
return "";
|
|
10998
11038
|
}
|
|
@@ -11970,9 +12010,7 @@ var init_interactive = __esm({
|
|
|
11970
12010
|
const uiMessages = convertQueriesToUIMessages(queries);
|
|
11971
12011
|
setSessionId(session.id);
|
|
11972
12012
|
setContextSessionId(session.id);
|
|
11973
|
-
|
|
11974
|
-
logger.debug(`Resume session: ${session.id}, providerSessionId: ${resumeProviderSessionId || "NOT SET"}`);
|
|
11975
|
-
setProviderSessionId(resumeProviderSessionId);
|
|
12013
|
+
setProviderSessionId(session.providerSessionId || void 0);
|
|
11976
12014
|
const contextData = await apiClient.getSessionContext(session.id);
|
|
11977
12015
|
if (contextData.contextTokens > 0) {
|
|
11978
12016
|
const cacheRead = contextData.cacheReadTokens || 0;
|
|
@@ -12369,12 +12407,43 @@ Updating Supatest CLI ${CLI_VERSION} \u2192 ${latest}...`);
|
|
|
12369
12407
|
|
|
12370
12408
|
// src/index.ts
|
|
12371
12409
|
init_banner();
|
|
12410
|
+
|
|
12411
|
+
// src/utils/claude-max.ts
|
|
12412
|
+
init_logger();
|
|
12413
|
+
import { execSync as execSync4 } from "child_process";
|
|
12414
|
+
function isClaudeMaxAvailable() {
|
|
12415
|
+
logger.debug("[claude-max] Checking if Claude Code credentials exist in keychain");
|
|
12416
|
+
try {
|
|
12417
|
+
const credentialsJson = execSync4(
|
|
12418
|
+
'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
|
|
12419
|
+
{ encoding: "utf-8" }
|
|
12420
|
+
).trim();
|
|
12421
|
+
if (!credentialsJson) {
|
|
12422
|
+
logger.debug("[claude-max] No credentials found in keychain");
|
|
12423
|
+
return false;
|
|
12424
|
+
}
|
|
12425
|
+
const credentials = JSON.parse(credentialsJson);
|
|
12426
|
+
const hasOauth = !!credentials.claudeAiOauth?.accessToken;
|
|
12427
|
+
logger.debug("[claude-max] Credentials check", {
|
|
12428
|
+
hasOauth,
|
|
12429
|
+
hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
|
|
12430
|
+
});
|
|
12431
|
+
return hasOauth;
|
|
12432
|
+
} catch (error) {
|
|
12433
|
+
logger.debug("[claude-max] Error checking keychain", {
|
|
12434
|
+
error: error instanceof Error ? error.message : String(error)
|
|
12435
|
+
});
|
|
12436
|
+
return false;
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12439
|
+
|
|
12440
|
+
// src/index.ts
|
|
12372
12441
|
init_error_logger();
|
|
12373
12442
|
init_logger();
|
|
12374
12443
|
|
|
12375
12444
|
// src/utils/node-version.ts
|
|
12376
12445
|
init_logger();
|
|
12377
|
-
import { execSync as
|
|
12446
|
+
import { execSync as execSync5 } from "child_process";
|
|
12378
12447
|
var MINIMUM_NODE_VERSION2 = 18;
|
|
12379
12448
|
function parseVersion2(versionString) {
|
|
12380
12449
|
const cleaned = versionString.trim().replace(/^v/, "");
|
|
@@ -12391,7 +12460,7 @@ function parseVersion2(versionString) {
|
|
|
12391
12460
|
}
|
|
12392
12461
|
function getNodeVersion2() {
|
|
12393
12462
|
try {
|
|
12394
|
-
const versionOutput =
|
|
12463
|
+
const versionOutput = execSync5("node --version", {
|
|
12395
12464
|
encoding: "utf-8",
|
|
12396
12465
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12397
12466
|
});
|
|
@@ -12505,10 +12574,10 @@ var program = new Command();
|
|
|
12505
12574
|
program.name("supatest").description(
|
|
12506
12575
|
"AI-powered task automation CLI for CI/CD - fix tests, lint issues, and more"
|
|
12507
12576
|
).version(CLI_VERSION).argument("[task]", "Task description or prompt for the AI agent").option("-l, --logs <file>", "Path to log file to analyze").option("--stdin", "Read logs from stdin").option("-C, --cwd <path>", "Working directory for the agent", process.cwd()).option(
|
|
12508
|
-
"-m, --max-iterations <number>",
|
|
12577
|
+
"-m, --claude-max-iterations <number>",
|
|
12509
12578
|
"Maximum number of iterations",
|
|
12510
12579
|
"100"
|
|
12511
|
-
).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) => {
|
|
12580
|
+
).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)").option("--claude-max", "Use Claude Max subscription (requires Claude Code to be logged in)").action(async (task, options) => {
|
|
12512
12581
|
try {
|
|
12513
12582
|
checkNodeVersion2();
|
|
12514
12583
|
await checkAndAutoUpdate();
|
|
@@ -12543,7 +12612,19 @@ program.name("supatest").description(
|
|
|
12543
12612
|
process.exit(1);
|
|
12544
12613
|
}
|
|
12545
12614
|
let supatestApiKey;
|
|
12615
|
+
let oauthToken;
|
|
12546
12616
|
const supatestApiUrl = options.supatestApiUrl || config.supatestApiUrl;
|
|
12617
|
+
if (options.claudeMax) {
|
|
12618
|
+
if (!isClaudeMaxAvailable()) {
|
|
12619
|
+
logger.error("Claude Max not available. Please ensure:");
|
|
12620
|
+
logger.error(" 1. Claude Code is installed");
|
|
12621
|
+
logger.error(" 2. You are logged in with a Claude Max subscription");
|
|
12622
|
+
logger.error(" 3. Run 'claude' and authenticate if needed");
|
|
12623
|
+
process.exit(1);
|
|
12624
|
+
}
|
|
12625
|
+
oauthToken = "use-claude-max";
|
|
12626
|
+
logger.info("Using Claude Max subscription for LLM calls");
|
|
12627
|
+
}
|
|
12547
12628
|
if (isHeadlessMode) {
|
|
12548
12629
|
supatestApiKey = normalizeSupatestKey(
|
|
12549
12630
|
options.supatestApiKey || config.supatestApiKey,
|
|
@@ -12551,7 +12632,7 @@ program.name("supatest").description(
|
|
|
12551
12632
|
);
|
|
12552
12633
|
if (!supatestApiKey) {
|
|
12553
12634
|
logger.error(
|
|
12554
|
-
"API key required in CI/headless mode. Please either:"
|
|
12635
|
+
"Supatest API key required in CI/headless mode. Please either:"
|
|
12555
12636
|
);
|
|
12556
12637
|
logger.error(" 1. Set SUPATEST_API_KEY environment variable");
|
|
12557
12638
|
logger.error(" 2. Use --supatest-api-key option");
|
|
@@ -12593,7 +12674,8 @@ program.name("supatest").description(
|
|
|
12593
12674
|
verbose: options.verbose || false,
|
|
12594
12675
|
cwd: options.cwd,
|
|
12595
12676
|
systemPromptAppend: config.headlessSystemPrompt,
|
|
12596
|
-
selectedModel
|
|
12677
|
+
selectedModel,
|
|
12678
|
+
oauthToken
|
|
12597
12679
|
});
|
|
12598
12680
|
process.exit(result.success ? 0 : 1);
|
|
12599
12681
|
} else {
|
|
@@ -12608,7 +12690,8 @@ program.name("supatest").description(
|
|
|
12608
12690
|
verbose: options.verbose || false,
|
|
12609
12691
|
cwd: options.cwd,
|
|
12610
12692
|
systemPromptAppend: config.interactiveSystemPrompt,
|
|
12611
|
-
selectedModel
|
|
12693
|
+
selectedModel,
|
|
12694
|
+
oauthToken
|
|
12612
12695
|
});
|
|
12613
12696
|
}
|
|
12614
12697
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supatest/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.31",
|
|
4
4
|
"description": "Supatest CLI - AI-powered task automation for CI/CD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@anthropic-ai/claude-agent-sdk": "^0.
|
|
41
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.61",
|
|
42
42
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
43
43
|
"@types/inquirer": "^9.0.0",
|
|
44
44
|
"ansi-escapes": "^7.0.0",
|
|
@@ -52,7 +52,6 @@
|
|
|
52
52
|
"ink-gradient": "^3.0.0",
|
|
53
53
|
"ink-spinner": "^5.0.0",
|
|
54
54
|
"inquirer": "^10.0.0",
|
|
55
|
-
"latest-version": "^9.0.0",
|
|
56
55
|
"lowlight": "^3.3.0",
|
|
57
56
|
"marked": "^15.0.0",
|
|
58
57
|
"marked-terminal": "^7.3.0",
|
|
@@ -60,17 +59,18 @@
|
|
|
60
59
|
"patch-package": "^8.0.1",
|
|
61
60
|
"postinstall-postinstall": "^2.1.0",
|
|
62
61
|
"react": "^19.0.0",
|
|
63
|
-
"semver": "^7.6.0",
|
|
64
62
|
"string-width": "^8.1.0",
|
|
65
63
|
"strip-ansi": "^7.1.2",
|
|
66
64
|
"undici": "^7.16.0",
|
|
67
|
-
"wrap-ansi": "^9.0.2"
|
|
65
|
+
"wrap-ansi": "^9.0.2",
|
|
66
|
+
"latest-version": "^9.0.0",
|
|
67
|
+
"semver": "^7.6.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "^20.12.12",
|
|
71
|
-
"@types/react": "^19.0.0",
|
|
72
|
-
"@types/semver": "^7.5.8",
|
|
73
71
|
"dotenv": "^16.6.1",
|
|
72
|
+
"@types/semver": "^7.5.8",
|
|
73
|
+
"@types/react": "^19.0.0",
|
|
74
74
|
"nodemon": "^3.1.11",
|
|
75
75
|
"tsup": "^8.5.1",
|
|
76
76
|
"tsx": "^4.10.0",
|