@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.
- package/README.md +58 -315
- package/dist/index.js +6595 -75
- package/package.json +36 -15
- package/dist/agent-runner.js +0 -417
- package/dist/types.js +0 -1
- package/dist/utils/banner.js +0 -16
- package/dist/utils/logger.js +0 -107
- package/dist/utils/node-version.js +0 -91
- package/dist/utils/rich-logger.js +0 -208
- package/dist/utils/stdin.js +0 -25
- package/dist/utils/summary.js +0 -98
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supatest/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Supatest CLI - AI-powered task automation for CI/CD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,16 +11,6 @@
|
|
|
11
11
|
"README.md",
|
|
12
12
|
"LICENSE"
|
|
13
13
|
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"dev": "tsx src/index.ts",
|
|
16
|
-
"dev:bun": "bun run src/index.ts",
|
|
17
|
-
"build": "tsc && node scripts/make-executable.js",
|
|
18
|
-
"build:bun": "bun build src/index.ts --compile --external=@anthropic-ai/claude-agent-sdk --outfile dist/supatest-ai && node scripts/copy-claude-cli.js",
|
|
19
|
-
"type-check": "tsc --noEmit",
|
|
20
|
-
"clean:bundle": "rimraf dist",
|
|
21
|
-
"clean:node_modules": "rimraf node_modules",
|
|
22
|
-
"prepublishOnly": "pnpm build"
|
|
23
|
-
},
|
|
24
14
|
"keywords": [
|
|
25
15
|
"cli",
|
|
26
16
|
"ai",
|
|
@@ -48,24 +38,55 @@
|
|
|
48
38
|
"access": "public"
|
|
49
39
|
},
|
|
50
40
|
"dependencies": {
|
|
51
|
-
"@anthropic-ai/claude-agent-sdk": "^0.1.
|
|
52
|
-
"@anthropic-ai/sdk": "^0.
|
|
41
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.56",
|
|
42
|
+
"@anthropic-ai/sdk": "^0.71.0",
|
|
43
|
+
"@types/inquirer": "^9.0.0",
|
|
44
|
+
"ansi-escapes": "^7.0.0",
|
|
53
45
|
"boxen": "^8.0.1",
|
|
54
46
|
"chalk": "^5.3.0",
|
|
55
47
|
"cli-highlight": "^2.1.11",
|
|
56
48
|
"commander": "^12.1.0",
|
|
49
|
+
"diff": "^8.0.2",
|
|
50
|
+
"dotenv": "^16.6.1",
|
|
51
|
+
"highlight.js": "^11.11.1",
|
|
52
|
+
"ink": "npm:@jrichman/ink@6.4.5",
|
|
53
|
+
"ink-gradient": "^3.0.0",
|
|
54
|
+
"ink-spinner": "^5.0.0",
|
|
55
|
+
"inquirer": "^10.0.0",
|
|
56
|
+
"lowlight": "^3.3.0",
|
|
57
|
+
"marked": "^15.0.0",
|
|
57
58
|
"marked-terminal": "^7.3.0",
|
|
58
59
|
"ora": "^8.1.1",
|
|
59
60
|
"patch-package": "^8.0.1",
|
|
60
61
|
"postinstall-postinstall": "^2.1.0",
|
|
61
|
-
"
|
|
62
|
+
"react": "^19.0.0",
|
|
63
|
+
"string-width": "^8.1.0",
|
|
64
|
+
"strip-ansi": "^7.1.2",
|
|
65
|
+
"wrap-ansi": "^9.0.2",
|
|
66
|
+
"zod": "^3.25.76"
|
|
62
67
|
},
|
|
63
68
|
"devDependencies": {
|
|
64
69
|
"@types/node": "^20.12.12",
|
|
70
|
+
"@types/react": "^19.0.0",
|
|
71
|
+
"nodemon": "^3.1.11",
|
|
72
|
+
"tsup": "^8.5.1",
|
|
65
73
|
"tsx": "^4.10.0",
|
|
66
74
|
"typescript": "^5.4.5"
|
|
67
75
|
},
|
|
68
76
|
"engines": {
|
|
69
77
|
"node": ">=18.0.0"
|
|
78
|
+
},
|
|
79
|
+
"optionalDependencies": {
|
|
80
|
+
"keytar": "^7.9.0"
|
|
81
|
+
},
|
|
82
|
+
"scripts": {
|
|
83
|
+
"dev": "NODE_ENV=development tsx src/index.ts",
|
|
84
|
+
"dev:watch": "nodemon",
|
|
85
|
+
"dev:bun": "bun run src/index.ts",
|
|
86
|
+
"build": "tsup",
|
|
87
|
+
"build:bun": "bun build src/index.ts --compile --external=@anthropic-ai/claude-agent-sdk --outfile dist/supatest-ai && node scripts/copy-claude-cli.js",
|
|
88
|
+
"type-check": "tsc --noEmit",
|
|
89
|
+
"clean:bundle": "rimraf dist",
|
|
90
|
+
"clean:node_modules": "rimraf node_modules"
|
|
70
91
|
}
|
|
71
|
-
}
|
|
92
|
+
}
|
package/dist/agent-runner.js
DELETED
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
import ora from "ora";
|
|
6
|
-
import { logger } from "./utils/logger.js";
|
|
7
|
-
import { generateSummary } from "./utils/summary.js";
|
|
8
|
-
const CLI_VERSION = "0.0.1";
|
|
9
|
-
// Fun spinner messages that rotate randomly
|
|
10
|
-
const SPINNER_MESSAGES = [
|
|
11
|
-
"Brainstorming...",
|
|
12
|
-
"Brewing coffee...",
|
|
13
|
-
"Sipping espresso...",
|
|
14
|
-
"Testing theories...",
|
|
15
|
-
"Making magic...",
|
|
16
|
-
"Multiplying matrices...",
|
|
17
|
-
];
|
|
18
|
-
function getRandomSpinnerMessage() {
|
|
19
|
-
return SPINNER_MESSAGES[Math.floor(Math.random() * SPINNER_MESSAGES.length)];
|
|
20
|
-
}
|
|
21
|
-
// Create shimmer effect frames that include the spinner icon
|
|
22
|
-
function createShimmerFrames(text) {
|
|
23
|
-
const frames = [];
|
|
24
|
-
const baseText = text;
|
|
25
|
-
// Ora's default dots spinner frames
|
|
26
|
-
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
27
|
-
// Create frames with moving highlight across the text
|
|
28
|
-
for (let i = 0; i <= baseText.length; i++) {
|
|
29
|
-
const spinnerIcon = spinnerFrames[i % spinnerFrames.length];
|
|
30
|
-
const before = chalk.white(baseText.slice(0, i));
|
|
31
|
-
const current = baseText[i] || '';
|
|
32
|
-
const after = chalk.white(baseText.slice(i + 1));
|
|
33
|
-
const shimmerText = before + chalk.cyan.bold(current) + after;
|
|
34
|
-
frames.push(`${chalk.cyan(spinnerIcon)} ${shimmerText}`);
|
|
35
|
-
}
|
|
36
|
-
return frames;
|
|
37
|
-
}
|
|
38
|
-
export async function runAgent(config) {
|
|
39
|
-
const stats = {
|
|
40
|
-
startTime: Date.now(),
|
|
41
|
-
iterations: 0,
|
|
42
|
-
filesModified: new Set(),
|
|
43
|
-
commandsRun: [],
|
|
44
|
-
errors: [],
|
|
45
|
-
};
|
|
46
|
-
let claudeCodeStderr = "";
|
|
47
|
-
logger.setVerbose(config.verbose);
|
|
48
|
-
// Display metadata
|
|
49
|
-
console.log("");
|
|
50
|
-
// Get git branch if available
|
|
51
|
-
let gitBranch = "";
|
|
52
|
-
try {
|
|
53
|
-
const { execSync } = await import("node:child_process");
|
|
54
|
-
gitBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
55
|
-
encoding: "utf8",
|
|
56
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
57
|
-
}).trim();
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
// Not in a git repo or git not available
|
|
61
|
-
}
|
|
62
|
-
const metadataParts = [
|
|
63
|
-
chalk.dim("Supatest AI ") + chalk.cyan(`v${CLI_VERSION}`),
|
|
64
|
-
chalk.dim("Model: ") + chalk.cyan(process.env.ANTHROPIC_MODEL_NAME || "claude-sonnet-4-5"),
|
|
65
|
-
];
|
|
66
|
-
if (gitBranch) {
|
|
67
|
-
metadataParts.push(chalk.dim("Branch: ") + chalk.cyan(gitBranch));
|
|
68
|
-
}
|
|
69
|
-
console.log(metadataParts.join(chalk.dim(" • ")));
|
|
70
|
-
console.log(chalk.gray("─".repeat(60)));
|
|
71
|
-
// Show environment info in verbose mode
|
|
72
|
-
if (config.verbose) {
|
|
73
|
-
console.log("");
|
|
74
|
-
logger.debug("Environment & System Info:");
|
|
75
|
-
// Node.js version
|
|
76
|
-
logger.debug(` Node.js: ${process.version}`);
|
|
77
|
-
// OS & Platform
|
|
78
|
-
const os = await import("node:os");
|
|
79
|
-
logger.debug(` Platform: ${os.platform()} ${os.arch()} (${os.type()} ${os.release()})`);
|
|
80
|
-
// Working directory
|
|
81
|
-
logger.debug(` Working Dir: ${process.cwd()}`);
|
|
82
|
-
// Git status
|
|
83
|
-
try {
|
|
84
|
-
const { execSync } = await import("node:child_process");
|
|
85
|
-
const gitStatus = execSync("git status --porcelain", {
|
|
86
|
-
encoding: "utf8",
|
|
87
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
88
|
-
}).trim();
|
|
89
|
-
const statusText = gitStatus ? "dirty (uncommitted changes)" : "clean";
|
|
90
|
-
logger.debug(` Git Status: ${statusText}`);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
logger.debug(` Git Status: not a git repository`);
|
|
94
|
-
}
|
|
95
|
-
// Available disk space
|
|
96
|
-
try {
|
|
97
|
-
const fs = await import("node:fs");
|
|
98
|
-
const stats = fs.statfsSync(process.cwd());
|
|
99
|
-
const availableGB = ((stats.bavail * stats.bsize) / (1024 ** 3)).toFixed(2);
|
|
100
|
-
logger.debug(` Available Disk: ${availableGB} GB`);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
logger.debug(` Available Disk: unable to determine`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
console.log("");
|
|
107
|
-
console.log("");
|
|
108
|
-
console.log(chalk.white.bold("Task:"), chalk.cyan(config.task));
|
|
109
|
-
if (config.logs) {
|
|
110
|
-
logger.info("Processing provided logs...");
|
|
111
|
-
}
|
|
112
|
-
console.log("");
|
|
113
|
-
console.log("");
|
|
114
|
-
console.log("");
|
|
115
|
-
// Initialize spinner variable (will be created on first agent turn)
|
|
116
|
-
let spinner = null;
|
|
117
|
-
// Resolve path to Claude Code executable
|
|
118
|
-
let claudeCodePath;
|
|
119
|
-
try {
|
|
120
|
-
// Build the prompt
|
|
121
|
-
let prompt = config.task;
|
|
122
|
-
if (config.logs) {
|
|
123
|
-
prompt = `${config.task}\n\nHere are the logs to analyze:\n\`\`\`\n${config.logs}\n\`\`\``;
|
|
124
|
-
}
|
|
125
|
-
// Set API key
|
|
126
|
-
process.env.ANTHROPIC_API_KEY = config.apiKey;
|
|
127
|
-
// Allow override via environment variable for testing/debugging
|
|
128
|
-
if (process.env.CLAUDE_CODE_EXECUTABLE_PATH) {
|
|
129
|
-
claudeCodePath = process.env.CLAUDE_CODE_EXECUTABLE_PATH;
|
|
130
|
-
if (config.verbose) {
|
|
131
|
-
logger.debug(`Using CLAUDE_CODE_EXECUTABLE_PATH: ${claudeCodePath}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// Determine binary directory
|
|
136
|
-
// For compiled binaries: same directory as the executable
|
|
137
|
-
// For development: use the SDK's bundled cli.js from node_modules
|
|
138
|
-
const isCompiledBinary = process.execPath && !process.execPath.includes("node");
|
|
139
|
-
if (isCompiledBinary) {
|
|
140
|
-
// Production: claude-code-cli.js should be next to the binary
|
|
141
|
-
claudeCodePath = join(dirname(process.execPath), "claude-code-cli.js");
|
|
142
|
-
if (config.verbose) {
|
|
143
|
-
logger.debug(`Production mode: ${claudeCodePath}`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
// Development: use SDK's cli.js from node_modules
|
|
148
|
-
const require = createRequire(import.meta.url);
|
|
149
|
-
const sdkPath = require.resolve("@anthropic-ai/claude-agent-sdk/sdk.mjs");
|
|
150
|
-
claudeCodePath = join(dirname(sdkPath), "cli.js");
|
|
151
|
-
if (config.verbose) {
|
|
152
|
-
logger.debug(`Development mode: ${claudeCodePath}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// Verify the file exists
|
|
156
|
-
const fs = await import("node:fs/promises");
|
|
157
|
-
try {
|
|
158
|
-
await fs.access(claudeCodePath);
|
|
159
|
-
if (config.verbose) {
|
|
160
|
-
logger.debug(`✓ Claude Code CLI found: ${claudeCodePath}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
throw new Error(`Claude Code executable not found at: ${claudeCodePath}\n` +
|
|
165
|
-
"For compiled binaries, ensure claude-code-cli.js is in the same directory as the binary.\n" +
|
|
166
|
-
"Set CLAUDE_CODE_EXECUTABLE_PATH environment variable to override.");
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
let resultText = "";
|
|
170
|
-
let hasError = false;
|
|
171
|
-
// Log SDK configuration for debugging
|
|
172
|
-
if (config.verbose) {
|
|
173
|
-
logger.debug(`\nSDK Configuration:`);
|
|
174
|
-
logger.debug(` Prompt: ${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}`);
|
|
175
|
-
logger.debug(` Max turns: ${config.maxIterations}`);
|
|
176
|
-
logger.debug(` Working directory: ${process.cwd()}`);
|
|
177
|
-
logger.debug(` Model: ${process.env.ANTHROPIC_MODEL_NAME || "claude-sonnet-4-5"}`);
|
|
178
|
-
logger.debug(` Claude Code executable: ${claudeCodePath}`);
|
|
179
|
-
logger.debug(` API Key: ${config.apiKey ? `${config.apiKey.substring(0, 10)}...` : 'not set'}`);
|
|
180
|
-
logger.debug(` Environment ANTHROPIC_API_KEY: ${process.env.ANTHROPIC_API_KEY ? 'set' : 'not set'}`);
|
|
181
|
-
}
|
|
182
|
-
// Run the agent using the SDK
|
|
183
|
-
const queryOptions = {
|
|
184
|
-
maxTurns: config.maxIterations,
|
|
185
|
-
cwd: process.cwd(),
|
|
186
|
-
model: process.env.ANTHROPIC_MODEL_NAME || "claude-sonnet-4-5",
|
|
187
|
-
permissionMode: "bypassPermissions",
|
|
188
|
-
allowDangerouslySkipPermissions: true,
|
|
189
|
-
pathToClaudeCodeExecutable: claudeCodePath,
|
|
190
|
-
// Force Node.js runtime even when running from a Bun binary
|
|
191
|
-
// The claude-code-cli.js is a large minified JS file that Bun can't parse correctly
|
|
192
|
-
executable: 'node',
|
|
193
|
-
stderr: (msg) => {
|
|
194
|
-
claudeCodeStderr += msg + "\n";
|
|
195
|
-
if (config.verbose) {
|
|
196
|
-
logger.debug(`[Claude Code stderr] ${msg}`);
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
if (config.verbose) {
|
|
201
|
-
logger.debug("\nQuery options:");
|
|
202
|
-
logger.debug(JSON.stringify(queryOptions, null, 2));
|
|
203
|
-
}
|
|
204
|
-
for await (const msg of query({
|
|
205
|
-
prompt,
|
|
206
|
-
options: queryOptions,
|
|
207
|
-
})) {
|
|
208
|
-
// Handle different message types
|
|
209
|
-
if (msg.type === "assistant") {
|
|
210
|
-
stats.iterations++;
|
|
211
|
-
if (spinner) {
|
|
212
|
-
spinner.stop();
|
|
213
|
-
}
|
|
214
|
-
// Extract text content and tool uses
|
|
215
|
-
const content = msg.message.content;
|
|
216
|
-
if (Array.isArray(content)) {
|
|
217
|
-
for (const block of content) {
|
|
218
|
-
if (block.type === "text") {
|
|
219
|
-
console.log(block.text);
|
|
220
|
-
resultText += block.text + "\n";
|
|
221
|
-
}
|
|
222
|
-
else if (block.type === "tool_use") {
|
|
223
|
-
const toolName = block.name;
|
|
224
|
-
const input = block.input;
|
|
225
|
-
// Display tool calls to user
|
|
226
|
-
if (toolName === "Read") {
|
|
227
|
-
const filePath = input?.file_path || 'file';
|
|
228
|
-
logger.toolRead(filePath);
|
|
229
|
-
}
|
|
230
|
-
else if (toolName === "Write") {
|
|
231
|
-
const filePath = input?.file_path;
|
|
232
|
-
if (filePath) {
|
|
233
|
-
stats.filesModified.add(filePath);
|
|
234
|
-
logger.toolWrite(filePath);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
else if (toolName === "Edit") {
|
|
238
|
-
const filePath = input?.file_path;
|
|
239
|
-
if (filePath) {
|
|
240
|
-
stats.filesModified.add(filePath);
|
|
241
|
-
logger.toolEdit(filePath);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
else if (toolName === "Bash") {
|
|
245
|
-
const command = input?.command;
|
|
246
|
-
if (command) {
|
|
247
|
-
stats.commandsRun.push(command);
|
|
248
|
-
const shortCmd = command.length > 60 ? `${command.substring(0, 60)}...` : command;
|
|
249
|
-
logger.toolBash(shortCmd);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
else if (toolName === "Glob") {
|
|
253
|
-
logger.toolSearch("files", input?.pattern || '');
|
|
254
|
-
}
|
|
255
|
-
else if (toolName === "Grep") {
|
|
256
|
-
logger.toolSearch("code", input?.pattern || '');
|
|
257
|
-
}
|
|
258
|
-
else if (toolName === "Task") {
|
|
259
|
-
logger.toolAgent(input?.subagent_type || 'task');
|
|
260
|
-
}
|
|
261
|
-
else if (toolName === "TodoWrite") {
|
|
262
|
-
const todos = input?.todos;
|
|
263
|
-
if (Array.isArray(todos)) {
|
|
264
|
-
logger.todoUpdate(todos);
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
logger.info("📝 Updated todos");
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
logger.debug(`🔧 Using tool: ${toolName}`);
|
|
272
|
-
}
|
|
273
|
-
if (config.verbose) {
|
|
274
|
-
logger.debug(` Input: ${JSON.stringify(input).substring(0, 100)}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
console.log("");
|
|
280
|
-
// Stop and clear previous spinner if it exists
|
|
281
|
-
if (spinner) {
|
|
282
|
-
spinner.stop();
|
|
283
|
-
spinner.clear();
|
|
284
|
-
}
|
|
285
|
-
// Create a new spinner instance with a random message
|
|
286
|
-
const message = getRandomSpinnerMessage();
|
|
287
|
-
spinner = ora({
|
|
288
|
-
spinner: {
|
|
289
|
-
interval: 80,
|
|
290
|
-
frames: createShimmerFrames(message),
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
spinner.start();
|
|
294
|
-
}
|
|
295
|
-
else if (msg.type === "stream_event") {
|
|
296
|
-
// Handle streaming events
|
|
297
|
-
if (config.verbose) {
|
|
298
|
-
const event = msg.event;
|
|
299
|
-
if (event.type === "content_block_start") {
|
|
300
|
-
logger.debug("Content block started");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
else if (msg.type === "tool_progress") {
|
|
305
|
-
spinner.text = `Using ${msg.tool_name}... (${msg.elapsed_time_seconds.toFixed(1)}s)`;
|
|
306
|
-
}
|
|
307
|
-
else if (msg.type === "result") {
|
|
308
|
-
spinner.stop();
|
|
309
|
-
stats.iterations = msg.num_turns;
|
|
310
|
-
if (msg.subtype === "success") {
|
|
311
|
-
resultText = msg.result || resultText;
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
hasError = true;
|
|
315
|
-
if ("errors" in msg && Array.isArray(msg.errors)) {
|
|
316
|
-
stats.errors.push(...msg.errors);
|
|
317
|
-
for (const error of msg.errors) {
|
|
318
|
-
logger.error(error);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (config.verbose) {
|
|
322
|
-
logger.debug("Result message details:");
|
|
323
|
-
logger.debug(JSON.stringify(msg, null, 2));
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
if (spinner) {
|
|
329
|
-
spinner.stop();
|
|
330
|
-
}
|
|
331
|
-
stats.endTime = Date.now();
|
|
332
|
-
// Generate result
|
|
333
|
-
const result = {
|
|
334
|
-
success: !hasError && stats.errors.length === 0,
|
|
335
|
-
summary: resultText || "Task completed",
|
|
336
|
-
filesModified: Array.from(stats.filesModified),
|
|
337
|
-
iterations: stats.iterations,
|
|
338
|
-
error: stats.errors.length > 0 ? stats.errors.join("; ") : undefined,
|
|
339
|
-
};
|
|
340
|
-
// Print summary
|
|
341
|
-
const summaryText = generateSummary(stats, result, config.verbose);
|
|
342
|
-
console.log(summaryText);
|
|
343
|
-
return result;
|
|
344
|
-
}
|
|
345
|
-
catch (error) {
|
|
346
|
-
if (spinner) {
|
|
347
|
-
spinner.stop();
|
|
348
|
-
}
|
|
349
|
-
stats.endTime = Date.now();
|
|
350
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
351
|
-
// Check if this is a Claude Code process error and extract details
|
|
352
|
-
const isProcessError = errorMessage.includes("Claude Code process exited");
|
|
353
|
-
let exitCode;
|
|
354
|
-
let stderr;
|
|
355
|
-
let stdout;
|
|
356
|
-
// Try to extract exit code from error message (e.g., "exited with code 1")
|
|
357
|
-
const exitCodeMatch = errorMessage.match(/exited with code (\d+)/i);
|
|
358
|
-
if (exitCodeMatch) {
|
|
359
|
-
exitCode = Number.parseInt(exitCodeMatch[1], 10);
|
|
360
|
-
}
|
|
361
|
-
if (error && typeof error === "object") {
|
|
362
|
-
// Try to extract exit code and process output from error object
|
|
363
|
-
if ("exitCode" in error && typeof error.exitCode === "number") {
|
|
364
|
-
exitCode = error.exitCode;
|
|
365
|
-
}
|
|
366
|
-
if ("code" in error && typeof error.code === "number") {
|
|
367
|
-
exitCode = error.code;
|
|
368
|
-
}
|
|
369
|
-
if ("stderr" in error) {
|
|
370
|
-
stderr = String(error.stderr);
|
|
371
|
-
}
|
|
372
|
-
if ("stdout" in error) {
|
|
373
|
-
stdout = String(error.stdout);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
logger.error(`Fatal error: ${errorMessage}`);
|
|
377
|
-
// Show captured stderr if available
|
|
378
|
-
if (claudeCodeStderr && claudeCodeStderr.trim()) {
|
|
379
|
-
logger.error("\nClaude Code stderr output:");
|
|
380
|
-
logger.error(claudeCodeStderr);
|
|
381
|
-
}
|
|
382
|
-
if (isProcessError) {
|
|
383
|
-
if (exitCode !== undefined) {
|
|
384
|
-
logger.error(`Claude Code process exited with code ${exitCode}`);
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
logger.error("Claude Code process exited with an error");
|
|
388
|
-
}
|
|
389
|
-
// Provide helpful guidance for process errors
|
|
390
|
-
// Show guidance for exit code 1 or if exit code is unknown
|
|
391
|
-
if (exitCode === 1 || exitCode === undefined) {
|
|
392
|
-
logger.warn("\nPossible causes:");
|
|
393
|
-
logger.warn(" • Task may not be actionable (Claude Code works with code-related tasks)");
|
|
394
|
-
logger.warn(" • Task description may be too vague or not code-focused");
|
|
395
|
-
logger.warn(" • API authentication or rate limiting issue");
|
|
396
|
-
logger.warn("\nTry tasks like:");
|
|
397
|
-
logger.warn(" • 'Fix the failing tests in calculator.test.js'");
|
|
398
|
-
logger.warn(" • 'Add error handling to the divide function'");
|
|
399
|
-
logger.warn(" • 'Create a new file hello.js with a greeting function'");
|
|
400
|
-
logger.warn(" • 'Update imports to use the new package structure'");
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (config.verbose && error instanceof Error && error.stack) {
|
|
404
|
-
console.error(error.stack);
|
|
405
|
-
}
|
|
406
|
-
const result = {
|
|
407
|
-
success: false,
|
|
408
|
-
summary: `Failed: ${errorMessage}`,
|
|
409
|
-
filesModified: Array.from(stats.filesModified),
|
|
410
|
-
iterations: stats.iterations,
|
|
411
|
-
error: errorMessage,
|
|
412
|
-
};
|
|
413
|
-
const summaryText = generateSummary(stats, result, config.verbose);
|
|
414
|
-
console.log(summaryText);
|
|
415
|
-
return result;
|
|
416
|
-
}
|
|
417
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/banner.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
export function getBanner() {
|
|
3
|
-
const banner = `
|
|
4
|
-
╔═══════════════════════════════════════════════════════════════════════════╗
|
|
5
|
-
║ ║
|
|
6
|
-
║ ███████╗██╗ ██╗██████╗ █████╗ ████████╗███████╗███████╗████████╗ ║
|
|
7
|
-
║ ██╔════╝██║ ██║██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ ║
|
|
8
|
-
║ ███████╗██║ ██║██████╔╝███████║ ██║ █████╗ ███████╗ ██║ ║
|
|
9
|
-
║ ╚════██║██║ ██║██╔═══╝ ██╔══██║ ██║ ██╔══╝ ╚════██║ ██║ ║
|
|
10
|
-
║ ███████║╚██████╔╝██║ ██║ ██║ ██║ ███████╗███████║ ██║ ║
|
|
11
|
-
║ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ ║
|
|
12
|
-
║ ║
|
|
13
|
-
╚═══════════════════════════════════════════════════════════════════════════╝
|
|
14
|
-
`;
|
|
15
|
-
return chalk.hex("#C96868")(banner);
|
|
16
|
-
}
|
package/dist/utils/logger.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
class Logger {
|
|
3
|
-
verbose = false;
|
|
4
|
-
setVerbose(enabled) {
|
|
5
|
-
this.verbose = enabled;
|
|
6
|
-
}
|
|
7
|
-
info(message) {
|
|
8
|
-
console.log(chalk.blue("ℹ"), message);
|
|
9
|
-
}
|
|
10
|
-
success(message) {
|
|
11
|
-
console.log(chalk.green("✓"), message);
|
|
12
|
-
}
|
|
13
|
-
error(message) {
|
|
14
|
-
console.error(chalk.red("✗"), message);
|
|
15
|
-
}
|
|
16
|
-
warn(message) {
|
|
17
|
-
console.warn(chalk.yellow("⚠"), message);
|
|
18
|
-
}
|
|
19
|
-
debug(message) {
|
|
20
|
-
if (this.verbose) {
|
|
21
|
-
console.log(chalk.gray("→"), message);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
section(title) {
|
|
25
|
-
console.log("\n" + chalk.bold.red(`━━━ ${title} ━━━`));
|
|
26
|
-
}
|
|
27
|
-
summary(title) {
|
|
28
|
-
console.log("\n" + chalk.bold.cyan(`╔═══ ${title} ═══╗`));
|
|
29
|
-
}
|
|
30
|
-
raw(message) {
|
|
31
|
-
console.log(message);
|
|
32
|
-
}
|
|
33
|
-
stream(chunk) {
|
|
34
|
-
process.stdout.write(chalk.dim(chunk));
|
|
35
|
-
}
|
|
36
|
-
toolRead(filePath) {
|
|
37
|
-
console.log("");
|
|
38
|
-
console.log(chalk.blue("📖"), chalk.dim("Reading:"), chalk.white(filePath));
|
|
39
|
-
}
|
|
40
|
-
toolWrite(filePath) {
|
|
41
|
-
console.log("");
|
|
42
|
-
console.log(chalk.green("✏️"), chalk.dim("Writing:"), chalk.white(filePath));
|
|
43
|
-
}
|
|
44
|
-
toolEdit(filePath) {
|
|
45
|
-
console.log("");
|
|
46
|
-
console.log(chalk.yellow("✏️"), chalk.dim("Editing:"), chalk.white(filePath));
|
|
47
|
-
}
|
|
48
|
-
toolBash(command) {
|
|
49
|
-
console.log("");
|
|
50
|
-
console.log(chalk.cyan("🔨"), chalk.dim("Running:"), chalk.white(command));
|
|
51
|
-
}
|
|
52
|
-
toolSearch(type, pattern) {
|
|
53
|
-
console.log("");
|
|
54
|
-
console.log(chalk.cyan("🔍"), chalk.dim(`Searching ${type}:`), chalk.white(pattern));
|
|
55
|
-
}
|
|
56
|
-
toolAgent(agentType) {
|
|
57
|
-
console.log("");
|
|
58
|
-
console.log(chalk.cyan("🤖"), chalk.dim("Launching agent:"), chalk.white(agentType));
|
|
59
|
-
}
|
|
60
|
-
todoUpdate(todos) {
|
|
61
|
-
const completed = todos.filter((t) => t.status === "completed");
|
|
62
|
-
const inProgress = todos.filter((t) => t.status === "in_progress");
|
|
63
|
-
const pending = todos.filter((t) => t.status === "pending");
|
|
64
|
-
const total = todos.length;
|
|
65
|
-
const completedCount = completed.length;
|
|
66
|
-
const progress = total > 0 ? Math.round((completedCount / total) * 100) : 0;
|
|
67
|
-
// Progress bar
|
|
68
|
-
const barLength = 20;
|
|
69
|
-
const filledLength = Math.round((barLength * completedCount) / total);
|
|
70
|
-
const bar = "█".repeat(filledLength) + "░".repeat(barLength - filledLength);
|
|
71
|
-
console.log("");
|
|
72
|
-
console.log(chalk.blue("📝"), chalk.dim("Todo Progress:"), chalk.cyan(bar), chalk.white(`${progress}%`), chalk.gray(`(${completedCount}/${total})`));
|
|
73
|
-
// Show individual todos with status indicators
|
|
74
|
-
if (inProgress.length > 0) {
|
|
75
|
-
for (const todo of inProgress) {
|
|
76
|
-
console.log(" ", chalk.yellow("→"), chalk.white(todo.content), chalk.gray("[in progress]"));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (completed.length > 0 && completed.length <= 3) {
|
|
80
|
-
for (const todo of completed) {
|
|
81
|
-
console.log(" ", chalk.green("✓"), chalk.gray(todo.content), chalk.dim("[completed]"));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (pending.length > 0 && pending.length <= 3) {
|
|
85
|
-
for (const todo of pending) {
|
|
86
|
-
console.log(" ", chalk.dim("⏳"), chalk.white(todo.content), chalk.gray("[pending]"));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
divider() {
|
|
91
|
-
console.log(chalk.gray("─".repeat(60)));
|
|
92
|
-
}
|
|
93
|
-
box(title) {
|
|
94
|
-
const width = 60;
|
|
95
|
-
const padding = Math.max(0, width - title.length - 2);
|
|
96
|
-
const leftPad = Math.floor(padding / 2);
|
|
97
|
-
const rightPad = Math.ceil(padding / 2);
|
|
98
|
-
console.log(chalk.cyan("╔" + "═".repeat(width) + "╗"));
|
|
99
|
-
console.log(chalk.cyan("║") +
|
|
100
|
-
" ".repeat(leftPad) +
|
|
101
|
-
chalk.bold.white(title) +
|
|
102
|
-
" ".repeat(rightPad) +
|
|
103
|
-
chalk.cyan("║"));
|
|
104
|
-
console.log(chalk.cyan("╚" + "═".repeat(width) + "╝"));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
export const logger = new Logger();
|