@supatest/cli 0.0.2 → 0.0.4

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.
@@ -1,91 +0,0 @@
1
- import { execSync } from "node:child_process";
2
- import { logger } from "./logger.js";
3
- const MINIMUM_NODE_VERSION = 18;
4
- /**
5
- * Parse a version string like "v18.17.0" or "18.17.0" into components
6
- */
7
- function parseVersion(versionString) {
8
- const cleaned = versionString.trim().replace(/^v/, "");
9
- const match = cleaned.match(/^(\d+)\.(\d+)\.(\d+)/);
10
- if (!match) {
11
- return null;
12
- }
13
- return {
14
- major: Number.parseInt(match[1], 10),
15
- minor: Number.parseInt(match[2], 10),
16
- patch: Number.parseInt(match[3], 10),
17
- raw: versionString.trim(),
18
- };
19
- }
20
- /**
21
- * Get the installed Node.js version
22
- */
23
- function getNodeVersion() {
24
- try {
25
- // Try to get version from child process (works when Node.js is available)
26
- const versionOutput = execSync("node --version", {
27
- encoding: "utf-8",
28
- stdio: ["ignore", "pipe", "ignore"],
29
- });
30
- return parseVersion(versionOutput);
31
- }
32
- catch (error) {
33
- // Node.js not found or not accessible
34
- return null;
35
- }
36
- }
37
- /**
38
- * Check if Node.js is installed and meets minimum version requirements
39
- * @throws Error with helpful message if requirements not met
40
- */
41
- export function checkNodeVersion() {
42
- const nodeVersion = getNodeVersion();
43
- if (!nodeVersion) {
44
- logger.error("Node.js is not installed or not accessible");
45
- logger.error("");
46
- logger.error("Supatest AI requires Node.js 18 or higher to run.");
47
- logger.error("");
48
- logger.error("Please install Node.js:");
49
- logger.error(" • macOS: brew install node");
50
- logger.error(" • Linux: Use your package manager or nvm");
51
- logger.error(" • Windows: Download from https://nodejs.org/");
52
- logger.error("");
53
- logger.error("Or use nvm (Node Version Manager):");
54
- logger.error(" nvm install 18");
55
- logger.error(" nvm use 18");
56
- logger.error("");
57
- logger.error("Verify installation: node --version");
58
- process.exit(1);
59
- }
60
- if (nodeVersion.major < MINIMUM_NODE_VERSION) {
61
- logger.error(`Node.js ${nodeVersion.raw} is installed, but version ${MINIMUM_NODE_VERSION} or higher is required`);
62
- logger.error("");
63
- logger.error(`Current version: ${nodeVersion.raw}`);
64
- logger.error(`Required version: ${MINIMUM_NODE_VERSION}.0.0 or higher`);
65
- logger.error("");
66
- logger.error("Please upgrade Node.js:");
67
- logger.error(" • macOS: brew upgrade node");
68
- logger.error(" • Linux: Use your package manager or nvm");
69
- logger.error(" • Windows: Download from https://nodejs.org/");
70
- logger.error("");
71
- logger.error("Or use nvm to install a newer version:");
72
- logger.error(` nvm install ${MINIMUM_NODE_VERSION}`);
73
- logger.error(` nvm use ${MINIMUM_NODE_VERSION}`);
74
- logger.error("");
75
- logger.error("Verify upgrade: node --version");
76
- process.exit(1);
77
- }
78
- // Success - version is adequate
79
- // Silent unless verbose mode is enabled
80
- logger.debug(`✓ Node.js ${nodeVersion.raw} detected (meets minimum requirement of ${MINIMUM_NODE_VERSION}.0.0)`);
81
- }
82
- /**
83
- * Get Node.js version info for display purposes
84
- */
85
- export function getNodeVersionInfo() {
86
- const version = getNodeVersion();
87
- if (!version) {
88
- return "Not installed";
89
- }
90
- return version.raw;
91
- }
@@ -1,208 +0,0 @@
1
- import chalk from "chalk";
2
- import boxen from "boxen";
3
- import { highlight } from "cli-highlight";
4
- /**
5
- * Rich logger optimized for CI/CD environments
6
- * Provides structured, timestamped, grep-able output
7
- */
8
- export class RichLogger {
9
- startTime = Date.now();
10
- verbose = false;
11
- setVerbose(enabled) {
12
- this.verbose = enabled;
13
- }
14
- timestamp() {
15
- const now = new Date();
16
- return chalk.gray(`[${now.toISOString()}]`);
17
- }
18
- elapsed() {
19
- const ms = Date.now() - this.startTime;
20
- const seconds = (ms / 1000).toFixed(1);
21
- return chalk.dim(`(+${seconds}s)`);
22
- }
23
- /** Print major section header */
24
- section(title) {
25
- console.log("");
26
- console.log(chalk.bold.cyan("═".repeat(60)));
27
- console.log(chalk.bold.cyan(` ${title}`));
28
- console.log(chalk.bold.cyan("═".repeat(60)));
29
- }
30
- /** Print subsection divider */
31
- subsection(title) {
32
- console.log("");
33
- console.log(chalk.cyan("─".repeat(60)));
34
- console.log(chalk.cyan(` ${title}`));
35
- console.log(chalk.cyan("─".repeat(60)));
36
- }
37
- /** Agent task started */
38
- taskStart(task, model, maxIterations) {
39
- this.startTime = Date.now();
40
- this.section("Supatest AI Agent - Task Started");
41
- console.log(`${this.timestamp()} ${chalk.bold("Task:")} ${task}`);
42
- if (model) {
43
- console.log(`${this.timestamp()} ${chalk.bold("Model:")} ${model}`);
44
- }
45
- if (maxIterations) {
46
- console.log(`${this.timestamp()} ${chalk.bold("Max Iterations:")} ${maxIterations}`);
47
- }
48
- console.log(`${this.timestamp()} ${chalk.bold("Started:")} ${new Date().toISOString()}`);
49
- }
50
- /** Start of iteration */
51
- iteration(current, total) {
52
- this.subsection(`Iteration ${current}/${total}`);
53
- }
54
- /** Agent thinking/response text */
55
- agentText(text) {
56
- console.log(`${this.timestamp()} ${chalk.blue("🤖 Agent:")} ${text}`);
57
- }
58
- /** Tool call started */
59
- toolCall(toolName, input, verbose = false) {
60
- console.log("");
61
- console.log(`${this.timestamp()} ${chalk.yellow("🔧 Tool Call:")} ${chalk.bold(toolName)}`);
62
- if (verbose || this.verbose) {
63
- // Show formatted input
64
- const inputStr = JSON.stringify(input, null, 2);
65
- console.log(chalk.dim(boxen(inputStr, {
66
- padding: 0,
67
- margin: { left: 11 },
68
- borderStyle: "round",
69
- borderColor: "gray",
70
- title: "Input",
71
- titleAlignment: "left",
72
- })));
73
- }
74
- else {
75
- // Show compact input
76
- for (const [key, value] of Object.entries(input)) {
77
- console.log(`${" ".repeat(11)} ${chalk.dim(key + ":")} ${String(value).substring(0, 80)}`);
78
- }
79
- }
80
- }
81
- /** Tool call completed successfully */
82
- toolSuccess(toolName, duration) {
83
- const durationStr = duration ? chalk.dim(` (${duration.toFixed(2)}s)`) : "";
84
- console.log(`${this.timestamp()} ${chalk.green("✓")} ${chalk.bold(toolName)} completed${durationStr}`);
85
- }
86
- /** Tool call failed */
87
- toolError(toolName, error) {
88
- console.log(`${this.timestamp()} ${chalk.red("✗")} ${chalk.bold(toolName)} failed`);
89
- console.log(chalk.red(boxen(error, {
90
- padding: { left: 1, right: 1 },
91
- margin: { left: 11 },
92
- borderStyle: "round",
93
- borderColor: "red",
94
- title: "Error",
95
- })));
96
- }
97
- /** Show tool output (file contents, command output, etc) */
98
- toolOutput(content, language) {
99
- // Limit output length for readability
100
- const maxLength = this.verbose ? 5000 : 500;
101
- const truncated = content.length > maxLength;
102
- const displayContent = truncated
103
- ? content.substring(0, maxLength) + "\n... (truncated)"
104
- : content;
105
- try {
106
- // Try syntax highlighting
107
- const highlighted = language && displayContent.length < 2000
108
- ? highlight(displayContent, { language })
109
- : displayContent;
110
- console.log(boxen(highlighted, {
111
- padding: { left: 1, right: 1 },
112
- margin: { left: 11 },
113
- borderStyle: "round",
114
- borderColor: "gray",
115
- title: language ? `Output (${language})` : "Output",
116
- }));
117
- }
118
- catch {
119
- // Fallback to plain output if highlighting fails
120
- console.log(boxen(displayContent, {
121
- padding: { left: 1, right: 1 },
122
- margin: { left: 11 },
123
- borderStyle: "round",
124
- borderColor: "gray",
125
- title: "Output",
126
- }));
127
- }
128
- }
129
- /** Show bash command being executed */
130
- bashCommand(command) {
131
- console.log(`${this.timestamp()} ${chalk.yellow("🔧 Running:")} ${chalk.bold(command)}`);
132
- }
133
- /** Show bash command output */
134
- bashOutput(output) {
135
- if (output.trim()) {
136
- this.toolOutput(output, "bash");
137
- }
138
- }
139
- /** File being read */
140
- fileRead(path) {
141
- console.log(`${this.timestamp()} ${chalk.blue("📖 Reading:")} ${chalk.underline(path)}`);
142
- }
143
- /** File being written */
144
- fileWrite(path) {
145
- console.log(`${this.timestamp()} ${chalk.green("✏️ Writing:")} ${chalk.underline(path)}`);
146
- }
147
- /** General info message */
148
- info(message) {
149
- console.log(`${this.timestamp()} ${chalk.blue("ℹ")} ${message}`);
150
- }
151
- /** Success message */
152
- success(message) {
153
- console.log(`${this.timestamp()} ${chalk.green("✓")} ${message}`);
154
- }
155
- /** Error message */
156
- error(message) {
157
- console.log(`${this.timestamp()} ${chalk.red("✗")} ${message}`);
158
- }
159
- /** Warning message */
160
- warn(message) {
161
- console.log(`${this.timestamp()} ${chalk.yellow("⚠")} ${message}`);
162
- }
163
- /** Debug message (only shown in verbose mode) */
164
- debug(message) {
165
- if (this.verbose) {
166
- console.log(`${this.timestamp()} ${chalk.gray("→")} ${message}`);
167
- }
168
- }
169
- /** Final summary */
170
- summary(data) {
171
- this.section("Summary");
172
- const status = data.success
173
- ? chalk.green("✓ Success")
174
- : chalk.red("✗ Failed");
175
- console.log(`${this.timestamp()} ${chalk.bold("Status:")} ${status}`);
176
- console.log(`${this.timestamp()} ${chalk.bold("Duration:")} ${(data.duration / 1000).toFixed(1)}s`);
177
- console.log(`${this.timestamp()} ${chalk.bold("Iterations:")} ${data.iterations}`);
178
- if (data.filesModified.length > 0) {
179
- console.log("");
180
- console.log(`${this.timestamp()} ${chalk.bold("Files Modified:")}`);
181
- for (const file of data.filesModified) {
182
- console.log(`${" ".repeat(11)} ${chalk.cyan("→")} ${file}`);
183
- }
184
- }
185
- if (data.commandsRun.length > 0) {
186
- console.log("");
187
- console.log(`${this.timestamp()} ${chalk.bold("Commands Executed:")}`);
188
- for (const cmd of data.commandsRun) {
189
- console.log(`${" ".repeat(11)} ${chalk.gray("$")} ${cmd}`);
190
- }
191
- }
192
- if (data.errors && data.errors.length > 0) {
193
- console.log("");
194
- console.log(`${this.timestamp()} ${chalk.bold.red("Errors Encountered:")}`);
195
- for (const error of data.errors) {
196
- console.log(`${" ".repeat(11)} ${chalk.red("✗")} ${error}`);
197
- }
198
- }
199
- console.log(chalk.cyan("═".repeat(60)));
200
- console.log("");
201
- }
202
- /** Raw output without formatting */
203
- raw(text) {
204
- console.log(text);
205
- }
206
- }
207
- // Export singleton instance
208
- export const richLogger = new RichLogger();
@@ -1,25 +0,0 @@
1
- export async function readStdin() {
2
- return new Promise((resolve, reject) => {
3
- const chunks = [];
4
- // Check if stdin is connected (not a TTY)
5
- if (process.stdin.isTTY) {
6
- resolve("");
7
- return;
8
- }
9
- process.stdin.on("data", (chunk) => {
10
- chunks.push(chunk);
11
- });
12
- process.stdin.on("end", () => {
13
- resolve(Buffer.concat(chunks).toString("utf-8"));
14
- });
15
- process.stdin.on("error", (error) => {
16
- reject(error);
17
- });
18
- // Set a timeout to avoid hanging
19
- setTimeout(() => {
20
- if (chunks.length === 0) {
21
- resolve("");
22
- }
23
- }, 100);
24
- });
25
- }
@@ -1,98 +0,0 @@
1
- import chalk from "chalk";
2
- export function generateSummary(stats, result, verbose = false) {
3
- const duration = stats.endTime
4
- ? ((stats.endTime - stats.startTime) / 1000).toFixed(2)
5
- : "N/A";
6
- const lines = [];
7
- lines.push("");
8
- lines.push(chalk.bold.cyan("AGENT EXECUTION SUMMARY"));
9
- lines.push(chalk.gray("─".repeat(60)));
10
- lines.push("");
11
- // Status with colored indicator
12
- const statusIcon = result.success ? chalk.green("●") : chalk.red("●");
13
- const statusText = result.success
14
- ? chalk.green.bold("SUCCESS")
15
- : chalk.red.bold("FAILED");
16
- lines.push(statusIcon +
17
- " " +
18
- chalk.white.bold("Status:") +
19
- " ".repeat(11) +
20
- statusText);
21
- // Duration with clock icon
22
- lines.push(chalk.blue("◷") +
23
- " " +
24
- chalk.white.bold("Duration:") +
25
- " ".repeat(9) +
26
- chalk.white(`${duration}s`));
27
- // Iterations with counter icon (verbose only)
28
- if (verbose) {
29
- lines.push(chalk.cyan("🔄") +
30
- " " +
31
- chalk.white.bold("Iterations:") +
32
- " ".repeat(7) +
33
- chalk.white(`${stats.iterations}`));
34
- }
35
- // Files modified section
36
- if (stats.filesModified.size > 0) {
37
- lines.push("");
38
- lines.push(chalk.green("📝") +
39
- " " +
40
- chalk.white.bold(`Files Modified (${stats.filesModified.size})`));
41
- let count = 0;
42
- for (const file of stats.filesModified) {
43
- if (count >= 5) {
44
- const remaining = stats.filesModified.size - 5;
45
- lines.push(chalk.gray(` ... and ${remaining} more`));
46
- break;
47
- }
48
- lines.push(chalk.yellow(" →") + " " + chalk.white(file));
49
- count++;
50
- }
51
- }
52
- // Commands executed section (verbose only)
53
- if (verbose && stats.commandsRun.length > 0) {
54
- lines.push("");
55
- lines.push(chalk.cyan("🔨") +
56
- " " +
57
- chalk.white.bold(`Commands Executed (${stats.commandsRun.length})`));
58
- let count = 0;
59
- for (const cmd of stats.commandsRun) {
60
- if (count >= 3) {
61
- const remaining = stats.commandsRun.length - 3;
62
- lines.push(chalk.gray(` ... and ${remaining} more`));
63
- break;
64
- }
65
- lines.push(chalk.gray(" $") + " " + chalk.white(cmd));
66
- count++;
67
- }
68
- }
69
- // Errors section
70
- if (stats.errors.length > 0) {
71
- lines.push("");
72
- lines.push(chalk.red("❌") +
73
- " " +
74
- chalk.red.bold(`Errors Encountered (${stats.errors.length})`));
75
- for (const error of stats.errors.slice(0, 3)) {
76
- lines.push(chalk.red(" ✗") + " " + chalk.white(error));
77
- }
78
- }
79
- // Summary section (agent's output already includes its own header)
80
- if (result.summary && result.summary.trim()) {
81
- lines.push("");
82
- // Split summary into lines
83
- const summaryLines = result.summary.split("\n");
84
- let lineCount = 0;
85
- for (const line of summaryLines) {
86
- if (lineCount >= 10) {
87
- lines.push(chalk.gray("... (truncated)"));
88
- break;
89
- }
90
- lines.push(line);
91
- lineCount++;
92
- }
93
- }
94
- lines.push("");
95
- lines.push(chalk.gray("─".repeat(60)));
96
- lines.push("");
97
- return lines.join("\n");
98
- }