@marshalz/git-somnia-agent 1.0.0

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.
@@ -0,0 +1,57 @@
1
+ # Publishing MantlePush CLI to npm
2
+
3
+ ## Publishing Steps
4
+
5
+ 1. **Verify npm authentication:**
6
+ ```bash
7
+ npm whoami
8
+ ```
9
+ If not logged in:
10
+ ```bash
11
+ npm login
12
+ # Or create account: npm adduser
13
+ ```
14
+
15
+ 2. **Check package name availability:**
16
+ ```bash
17
+ npm view git-somnia-agent
18
+ ```
19
+ Should return 404 (not found) - package name is available!
20
+
21
+ 3. **Prepare for publishing:**
22
+ ```bash
23
+ cd git-agent-cli
24
+ npm version patch # Increment version (e.g., 1.0.2 → 1.0.3)
25
+ npm publish
26
+ ```
27
+
28
+ 4. **Post-publishing installation:**
29
+ Users can now install the MantlePush CLI globally:
30
+ ```bash
31
+ npm install -g git-somnia-agent
32
+ ```
33
+
34
+ ## Package Details
35
+
36
+ - **Package name**: `git-somnia-agent` (npm registry identifier)
37
+ - **Command name**: `git-somnia-agent` (accessed via Git alias)
38
+ - **Versioning**: Follow semantic versioning (major.minor.patch)
39
+ - **Dependencies**: All required packages must be listed in `package.json`
40
+
41
+ ## User Setup Instructions
42
+
43
+ After installing from npm, users configure the Git alias:
44
+ ```bash
45
+ git config --global alias.somnia-agent '!git-somnia-agent'
46
+ ```
47
+
48
+ Alternatively, users can invoke the CLI directly:
49
+ ```bash
50
+ git-somnia-agent stats
51
+ git-somnia-agent compare main aggressive
52
+ git-somnia-agent logs
53
+ ```
54
+
55
+ ## Mantle Network Integration
56
+
57
+ This CLI package is designed specifically for Mantle Sepolia testnet deployments. All agent contracts are deployed to Mantle's Layer 2 infrastructure, providing fast and cost-effective AI agent execution.
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # MantlePush CLI
2
+
3
+ **Deploy AI Agents on Mantle with Git** - Professional command-line interface for the MantlePush deployment platform.
4
+
5
+ MantlePush transforms AI agent deployment into a seamless Git-native workflow. Deploy autonomous agents to Mantle Sepolia testnet with a single `git push`. Each branch becomes a unique smart contract, enabling true parallel A/B testing of trading strategies.
6
+
7
+ ## Installation
8
+
9
+ ### Quick Install (For Users)
10
+
11
+ **Option 1: Install from npm (Recommended)**
12
+ ```bash
13
+ npm install -g git-somnia-agent
14
+
15
+ # Configure Git alias for seamless integration
16
+ git config --global alias.somnia-agent '!git-somnia-agent'
17
+
18
+ # Start using MantlePush CLI
19
+ git somnia-agent stats
20
+ git somnia-agent compare main aggressive
21
+ ```
22
+
23
+ **Option 2: Install from source (Development)**
24
+ ```bash
25
+ git clone https://github.com/xaviersharwin10/somnia-git-agent.git
26
+ cd somnia-git-agent/git-agent-cli
27
+ npm install -g .
28
+
29
+ # Set up Git alias
30
+ git config --global alias.somnia-agent '!git-somnia-agent'
31
+ ```
32
+
33
+ ### Development
34
+ ```bash
35
+ npm link
36
+ ```
37
+
38
+ ## CLI Commands
39
+
40
+ * `git somnia-agent init` - Configure MantlePush deployment pipeline for your repository
41
+ * `git somnia-agent secrets set <KEY=VALUE>` - Store encrypted secrets for the current branch (e.g., `GROQ_API_KEY=sk-...`)
42
+ * `git somnia-agent secrets check` - Verify which secrets are configured for your agent
43
+ * `git somnia-agent stats` - Display real-time performance metrics from your Mantle agent
44
+ * `git somnia-agent logs` - Stream the latest 50 log entries from your agent process
45
+ * `git somnia-agent compare <branch1> <branch2>` - Side-by-side comparison of two agent strategies
46
+
47
+ **Note**: After installing, you need to set up the Git alias once:
48
+ ```bash
49
+ git config --global alias.somnia-agent '!git-somnia-agent'
50
+ ```
51
+
52
+ Then you can use `git somnia-agent` as shown above.
53
+
54
+ ## Key Features
55
+
56
+ - šŸš€ **One-Command Setup**: Interactive initialization that configures everything automatically
57
+ - šŸ” **Enterprise-Grade Security**: AES-256 encrypted secret storage, one set per Git branch
58
+ - šŸ“Š **Live Performance Tracking**: Real-time metrics from agents executing on Mantle Sepolia
59
+ - šŸ”„ **Parallel Strategy Testing**: Compare multiple trading strategies running simultaneously
60
+ - ā›“ļø **Native Mantle Integration**: Built specifically for Mantle's Layer 2 infrastructure
61
+ - šŸŽØ **Developer-Friendly UX**: Color-coded terminal output with clear, actionable feedback
62
+ - ⚔ **Lightweight & Fast**: Minimal dependencies, instant command execution
63
+
64
+ ## Mantle Sepolia Integration
65
+
66
+ MantlePush leverages Mantle's high-performance Layer 2 infrastructure to deploy and manage AI agents:
67
+
68
+ - **Smart Contract Deployment**: Each Git branch deploys as a unique `Agent.sol` contract on Mantle Sepolia
69
+ - **On-Chain Identity**: Every agent has a permanent contract address on Mantle blockchain
70
+ - **DEX Integration**: Agents execute real trades on Mantle-compatible decentralized exchanges
71
+ - **Factory Pattern**: Centralized `AgentFactory` contract manages all agent deployments
72
+ - **Blockchain-Backed Registry**: Agent addresses persist on-chain, surviving backend failures
73
+
74
+ ## Usage
75
+
76
+ ### Initialize Repository for Mantle Deployment
77
+
78
+ ```bash
79
+ git somnia-agent init
80
+ ```
81
+
82
+ The initialization process:
83
+ - Creates `.gitagent.json` configuration file in your repository root
84
+ - Prompts for GitHub repository URL (used for webhook auto-configuration)
85
+ - Provides step-by-step instructions for connecting to MantlePush backend
86
+ - Guides you through secret management and first deployment workflow
87
+
88
+ ### Configure Agent Secrets
89
+
90
+ ```bash
91
+ git somnia-agent secrets set GROQ_API_KEY=sk-your-key-here
92
+ git somnia-agent secrets set AGENT_PRIVATE_KEY=0x...
93
+ git somnia-agent secrets set AI_PROMPT="You are an aggressive trader"
94
+ ```
95
+
96
+ **Security Features:**
97
+ - Secrets are encrypted using AES-256 before storage
98
+ - Each Git branch maintains its own isolated secret set
99
+ - Secrets are automatically injected as environment variables when agents deploy to Mantle
100
+ - Never stored in plaintext - all encryption handled by MantlePush backend
101
+
102
+ ### Monitor Agent Performance
103
+
104
+ ```bash
105
+ git somnia-agent stats # View comprehensive performance metrics
106
+ git somnia-agent logs # Stream real-time agent decision logs
107
+ ```
108
+
109
+ The `stats` command displays:
110
+ - Total trading decisions made
111
+ - BUY vs HOLD signal breakdown
112
+ - Number of executed trades
113
+ - Success rate percentage
114
+ - Price statistics (average, min, max)
115
+ - Activity timeline (first/last decision timestamps)
116
+
117
+ ### Compare Trading Strategies
118
+
119
+ Run parallel A/B tests by comparing different Git branches deployed as separate Mantle contracts:
120
+ ```bash
121
+ git somnia-agent compare main aggressive
122
+ ```
123
+
124
+ **Comparison Metrics:**
125
+ - Decision volume (total decisions per strategy)
126
+ - Signal distribution (BUY vs HOLD ratios)
127
+ - Trade execution counts
128
+ - Win rate percentages
129
+ - Average trading prices
130
+ - Performance winner analysis
131
+
132
+ Perfect for testing different AI prompts, trading thresholds, or risk parameters across multiple Mantle agent contracts simultaneously.
133
+
134
+ ## Configuration
135
+
136
+ The CLI creates a `.gitagent.json` file in your repository root:
137
+
138
+ ```json
139
+ {
140
+ "repo_url": "https://github.com/username/repo.git"
141
+ }
142
+ ```
143
+
144
+ ## Development
145
+
146
+ To test the CLI locally:
147
+
148
+ ```bash
149
+ npm link
150
+ git somnia-agent --help
151
+ ```
152
+
153
+ ## About MantlePush
154
+
155
+ MantlePush is a Git-native deployment platform built specifically for Mantle Network. It brings the simplicity of Vercel-style deployments to blockchain AI agents, making Mantle the easiest network to deploy autonomous agents on.
156
+
157
+ **Key Benefits:**
158
+ - ⚔ **30-second deployments** - From `git push` to live on-chain agent
159
+ - šŸ”„ **Branch-based A/B testing** - Test multiple strategies simultaneously
160
+ - šŸ”’ **Enterprise security** - Encrypted secrets, on-chain identity
161
+ - šŸ“Š **Real-time monitoring** - CLI and web dashboard for agent metrics
162
+
163
+ **Repository**: https://github.com/xaviersharwin10/somnia-git-agent
164
+ **Live Dashboard**: https://somnia-git-agent.onrender.com/dashboard
165
+ **Mantle Explorer**: https://sepolia.mantlescan.xyz
166
+
167
+ ## Requirements
168
+
169
+ - **Node.js** 16+ (for CLI tool)
170
+ - **Git** repository (for version control)
171
+ - **MantlePush Backend** (deployed at https://somnia-git-agent.onrender.com or self-hosted)
172
+ - **Mantle Sepolia Testnet** access (for contract deployment)
173
+
174
+
175
+
176
+
177
+
178
+
package/index.js ADDED
@@ -0,0 +1,444 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const axios = require('axios');
5
+ const shell = require('shelljs');
6
+ const { createPromptModule } = require('inquirer');
7
+ const prompt = createPromptModule();
8
+ const chalk = require('chalk');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const program = new Command();
13
+ program.version('1.0.0');
14
+
15
+ // --- Configuration ---
16
+ // MantlePush backend API endpoint
17
+ // For local development, use http://localhost:3005
18
+ // For production deployments, use your deployed backend URL
19
+ const API_BASE_URL = 'https://somnia-git-agent.onrender.com'; // Production MantlePush backend
20
+ const CONFIG_FILE = '.gitagent.json';
21
+
22
+ // --- Helper Functions ---
23
+
24
+ // Reads the .gitagent.json file
25
+ function getConfig() {
26
+ if (!fs.existsSync(CONFIG_FILE)) {
27
+ console.error(chalk.red(`Error: This repository is not configured for MantlePush. Missing ${CONFIG_FILE}.`));
28
+ console.log(chalk.yellow('Run `git somnia-agent init` to initialize MantlePush in this repository.'));
29
+ process.exit(1);
30
+ }
31
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
32
+ return config;
33
+ }
34
+
35
+ // Gets the current git branch
36
+ function getCurrentBranch() {
37
+ const branch = shell.exec('git rev-parse --abbrev-ref HEAD', { silent: true }).stdout.trim();
38
+ if (!branch) {
39
+ console.error(chalk.red('Error: Could not determine git branch.'));
40
+ process.exit(1);
41
+ }
42
+ return branch;
43
+ }
44
+
45
+ // Calculate branch_hash (same as backend)
46
+ const { ethers } = require('ethers');
47
+
48
+ function calculateBranchHash(repo_url, branch_name) {
49
+ return ethers.id(repo_url + "/" + branch_name);
50
+ }
51
+
52
+ // Helper function to fetch stats for a specific branch
53
+ async function getStats(repo_url, branch_name) {
54
+ try {
55
+ const branch_hash = calculateBranchHash(repo_url, branch_name);
56
+ const url = `${API_BASE_URL}/api/stats/${branch_hash}`;
57
+ const { data } = await axios.get(url);
58
+ return { ...data, branch_name, repo_url };
59
+ } catch (err) {
60
+ const errorMsg = err.response?.data?.error || err.message;
61
+ if (err.response?.status === 404) {
62
+ console.error(chalk.red(`Agent not found for branch "${branch_name}"`));
63
+ console.log(chalk.yellow(` → Make sure you've pushed this branch: ${chalk.cyan(`git push origin ${branch_name}`)}`));
64
+ console.log(chalk.yellow(` → The backend webhook will deploy it automatically`));
65
+ } else {
66
+ console.error(chalk.red(`Error fetching stats for ${branch_name}: ${errorMsg}`));
67
+ }
68
+ return null;
69
+ }
70
+ }
71
+
72
+ // --- CLI Commands ---
73
+
74
+ /**
75
+ * 1. INIT
76
+ * Initializes the project by creating .gitagent.json
77
+ */
78
+ program
79
+ .command('init')
80
+ .description('Initialize MantlePush deployment pipeline for this repository')
81
+ .action(async () => {
82
+ if (fs.existsSync(CONFIG_FILE)) {
83
+ console.log(chalk.yellow(`This project is already initialized.`));
84
+ return;
85
+ }
86
+
87
+ const answers = await prompt([
88
+ {
89
+ type: 'input',
90
+ name: 'repo_url',
91
+ message: 'What is your GitHub repository URL (e.g., https://github.com/user/repo.git)?',
92
+ default: shell.exec('git remote get-url origin', { silent: true }).stdout.trim(),
93
+ }
94
+ ]);
95
+
96
+ if (!answers.repo_url) {
97
+ console.error(chalk.red('Error: Repository URL is required.'));
98
+ return;
99
+ }
100
+
101
+ const config = { repo_url: answers.repo_url };
102
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
103
+
104
+ console.log(chalk.green(`āœ… ${CONFIG_FILE} created.`));
105
+ console.log('');
106
+ console.log(chalk.bold('šŸ“‹ Next Steps:'));
107
+ console.log('');
108
+
109
+ // Show OAuth URL for automatic setup
110
+ const repoUrl = answers.repo_url;
111
+ const oauthUrl = `https://somnia-git-agent.onrender.com/auth/github?repo_url=${encodeURIComponent(repoUrl)}`;
112
+
113
+ console.log(chalk.cyan('šŸš€ Option A: Automatic Webhook Configuration (Recommended)'));
114
+ console.log(` Visit: ${chalk.underline(oauthUrl)}`);
115
+ console.log(` Authorize GitHub to automatically set up deployment webhooks`);
116
+ console.log('');
117
+
118
+ console.log(chalk.yellow('āš™ļø Option B: Manual Webhook Configuration'));
119
+ console.log(` Navigate to: GitHub → ${answers.repo_url.split('/').slice(-2).join('/')} → Settings → Webhooks`);
120
+ console.log(` Webhook URL: ${chalk.cyan('https://somnia-git-agent.onrender.com/webhook/github/push')}`);
121
+ console.log(` Content type: ${chalk.cyan('application/json')}`);
122
+ console.log(` Events: ${chalk.cyan('Just the push event')}`);
123
+ console.log('');
124
+
125
+ console.log(chalk.bold('šŸ” Configure Agent Secrets:'));
126
+ console.log(` ${chalk.cyan('git somnia-agent secrets set GROQ_API_KEY=your-key-here')}`);
127
+ console.log(` ${chalk.cyan('git somnia-agent secrets set AGENT_PRIVATE_KEY=0x...')}`);
128
+ console.log(` These secrets are encrypted and securely stored for each branch`);
129
+ console.log('');
130
+
131
+ console.log(chalk.bold('šŸš€ Deploy to Mantle Sepolia:'));
132
+ console.log(` ${chalk.cyan('git push origin main')}`);
133
+ console.log(` Each push automatically deploys a new smart contract on Mantle Sepolia testnet`);
134
+ console.log(` Your agent will be live on-chain within 30 seconds!`);
135
+ console.log('');
136
+
137
+ console.log(chalk.bold('šŸ“Š Monitor Your Agents:'));
138
+ console.log(` ${chalk.cyan('git somnia-agent stats')} - View real-time performance metrics`);
139
+ console.log(` ${chalk.cyan('git somnia-agent logs')} - Stream live agent decision logs`);
140
+ console.log(` Web Dashboard: ${chalk.underline('https://somnia-git-agent.onrender.com/dashboard')}`);
141
+ });
142
+
143
+ /**
144
+ * 2. SECRETS - Create a command group for secrets
145
+ */
146
+ const secretsCommand = program
147
+ .command('secrets')
148
+ .description('Manage secrets for the current branch');
149
+
150
+ // SECRETS SET - Set a secret
151
+ secretsCommand
152
+ .command('set <KEY_VALUE>')
153
+ .description('Set a secret for the current branch (e.g., KEY=VALUE)')
154
+ .action(async (keyValue) => {
155
+ // Handle the case where commander might parse this incorrectly
156
+ const fullCommand = process.argv.slice(2).join(' ');
157
+ const match = fullCommand.match(/secrets set (.+)/);
158
+
159
+ if (!match) {
160
+ console.error(chalk.red('Error: Invalid format. Use KEY=VALUE'));
161
+ return;
162
+ }
163
+
164
+ const keyValueStr = match[1];
165
+ const [key, ...valueParts] = keyValueStr.split('=');
166
+ const value = valueParts.join('=');
167
+
168
+ if (!key || !value) {
169
+ console.error(chalk.red('Error: Invalid format. Use KEY=VALUE'));
170
+ return;
171
+ }
172
+
173
+ const config = getConfig();
174
+ const branch_name = getCurrentBranch();
175
+
176
+ try {
177
+ console.log(chalk.cyan(`Setting secret ${key} for branch ${branch_name}...`));
178
+ await axios.post(`${API_BASE_URL}/api/secrets`, {
179
+ repo_url: config.repo_url,
180
+ branch_name: branch_name,
181
+ key: key,
182
+ value: value,
183
+ });
184
+ console.log(chalk.green(`āœ… Secret ${key} set.`));
185
+ } catch (err) {
186
+ console.error(chalk.red(`Error setting secret: ${err.response?.data?.error || err.message}`));
187
+ }
188
+ });
189
+
190
+ // SECRETS CHECK - Check which secrets are set
191
+ secretsCommand
192
+ .command('check')
193
+ .description('Check which required secrets are set for the current branch')
194
+ .action(async () => {
195
+ const config = getConfig();
196
+ const branch_name = getCurrentBranch();
197
+ const branch_hash = calculateBranchHash(config.repo_url, branch_name);
198
+
199
+ try {
200
+ console.log(chalk.cyan(`šŸ” Checking secrets for branch: ${chalk.bold(branch_name)}...`));
201
+ const { data } = await axios.get(`${API_BASE_URL}/api/secrets/check/${branch_hash}`);
202
+
203
+ console.log(chalk.bold(`\n--- Secrets Status for ${branch_name} ---`));
204
+
205
+ // Required secrets
206
+ console.log(chalk.bold('\nšŸ“‹ Required Secrets:'));
207
+ data.secrets.required.forEach(secret => {
208
+ const status = secret.set ? chalk.green('āœ… Set') : chalk.red('āŒ Missing');
209
+ console.log(` ${status} ${chalk.bold(secret.key)}`);
210
+ });
211
+
212
+ // Optional secrets
213
+ if (data.secrets.optional.length > 0) {
214
+ console.log(chalk.bold('\nāš™ļø Optional Secrets:'));
215
+ data.secrets.optional.forEach(secret => {
216
+ const status = secret.set ? chalk.cyan('āœ“ Set') : chalk.gray('ā—‹ Not set');
217
+ console.log(` ${status} ${secret.key}`);
218
+ });
219
+ }
220
+
221
+ // Overall status
222
+ console.log(chalk.bold('\nšŸ“Š Status:'));
223
+ if (data.all_required_set) {
224
+ console.log(chalk.green(` āœ… All required secrets are set! Agent is ready to run.`));
225
+ } else {
226
+ console.log(chalk.red(` āŒ Missing required secrets: ${chalk.bold(data.missing.join(', '))}`));
227
+ console.log(chalk.yellow(`\nšŸ’” Set missing secrets with:`));
228
+ data.missing.forEach(key => {
229
+ console.log(chalk.cyan(` git somnia-agent secrets set ${key}=<your-value>`));
230
+ });
231
+ }
232
+
233
+ } catch (err) {
234
+ if (err.response?.status === 404) {
235
+ console.error(chalk.red(`Agent not found for branch "${branch_name}"`));
236
+ console.log(chalk.yellow(` → Make sure you've pushed this branch: ${chalk.cyan(`git push origin ${branch_name}`)}`));
237
+ } else {
238
+ console.error(chalk.red(`Error checking secrets: ${err.response?.data?.error || err.message}`));
239
+ }
240
+ }
241
+ });
242
+
243
+ /**
244
+ * 4. STATS
245
+ * Gets stats for the current branch
246
+ */
247
+ program
248
+ .command('stats')
249
+ .description('View real-time performance metrics for your Mantle agent')
250
+ .action(async () => {
251
+ const config = getConfig();
252
+ const branch_name = getCurrentBranch();
253
+
254
+ console.log(chalk.cyan(`šŸ“Š Fetching stats for ${branch_name}...`));
255
+ const result = await getStats(config.repo_url, branch_name);
256
+
257
+ if (!result) {
258
+ console.log(chalk.yellow(`\nāš ļø Could not fetch stats for "${branch_name}"`));
259
+ console.log(chalk.yellow(` The agent may not be deployed yet, or there was an error.`));
260
+ return;
261
+ }
262
+
263
+ if (result && result.stats) {
264
+ const s = result.stats;
265
+ const totalDecisions = s.total_decisions || 0;
266
+
267
+ console.log(chalk.bold(`\n--- Mantle Agent Performance: ${branch_name} ---`));
268
+ console.log(chalk.green(` Total Decisions: ${totalDecisions}`));
269
+ console.log(chalk.cyan(` BUY Signals: ${s.buy_count || 0}`));
270
+ console.log(chalk.yellow(` HOLD Signals: ${s.hold_count || 0}`));
271
+ console.log(chalk.magenta(` Trades Executed: ${s.trades_executed || 0}`));
272
+
273
+ if (totalDecisions === 0) {
274
+ console.log(chalk.yellow(`\nāš ļø No trading decisions recorded yet.`));
275
+ console.log(chalk.yellow(` → Check agent status: ${chalk.cyan('git somnia-agent logs')}`));
276
+ console.log(chalk.yellow(` → Verify the agent process is running on MantlePush backend`));
277
+ return;
278
+ }
279
+
280
+ if (s.avg_price) {
281
+ console.log(`\n Price Statistics:`);
282
+ console.log(` Average: $${parseFloat(s.avg_price).toFixed(4)}`);
283
+ console.log(` Min: $${parseFloat(s.min_price).toFixed(4)}`);
284
+ console.log(` Max: $${parseFloat(s.max_price).toFixed(4)}`);
285
+ }
286
+
287
+ if (s.first_decision && s.last_decision) {
288
+ console.log(`\n Activity:`);
289
+ console.log(` First Decision: ${s.first_decision}`);
290
+ console.log(` Last Decision: ${s.last_decision}`);
291
+ }
292
+
293
+ if (s.trades_executed > 0 && totalDecisions > 0) {
294
+ const successRate = ((s.trades_executed / totalDecisions) * 100).toFixed(1);
295
+ console.log(chalk.green(`\n Success Rate: ${successRate}%`));
296
+ }
297
+ } else {
298
+ console.log(chalk.yellow('No performance metrics available yet. The agent needs to make trading decisions first.'));
299
+ }
300
+ });
301
+
302
+ /**
303
+ * 5. LOGS
304
+ * Gets logs for the current branch
305
+ */
306
+ program
307
+ .command('logs')
308
+ .description('Stream real-time logs from your Mantle agent process')
309
+ .action(async () => {
310
+ const config = getConfig();
311
+ const branch_name = getCurrentBranch();
312
+
313
+ try {
314
+ console.log(chalk.cyan(`Fetching logs for ${branch_name}...`));
315
+ const url = `${API_BASE_URL}/api/logs/${encodeURIComponent(config.repo_url)}/${encodeURIComponent(branch_name)}`;
316
+ const { data } = await axios.get(url);
317
+
318
+ console.log(chalk.bold(`--- Recent Agent Logs: ${branch_name} (Last 50 entries) ---`));
319
+ if (data.logs && data.logs.length > 0) {
320
+ data.logs.forEach(line => console.log(line));
321
+ } else {
322
+ console.log(chalk.yellow('No logs found.'));
323
+ }
324
+ } catch (err) {
325
+ console.error(chalk.red(`Error fetching logs: ${err.response?.data?.error || err.message}`));
326
+ }
327
+ });
328
+
329
+ /**
330
+ * 6. COMPARE
331
+ * Compares two branches side-by-side
332
+ */
333
+ program
334
+ .command('compare <branch1> <branch2>')
335
+ .description('Compare performance metrics between two agent strategies on Mantle')
336
+ .action(async (branch1, branch2) => {
337
+ const config = getConfig();
338
+
339
+ console.log(chalk.cyan(`šŸ“Š Comparing Mantle agent strategies: ${chalk.bold(branch1)} vs ${chalk.bold(branch2)}...`));
340
+
341
+ const [result1, result2] = await Promise.all([
342
+ getStats(config.repo_url, branch1),
343
+ getStats(config.repo_url, branch2)
344
+ ]);
345
+
346
+ if (!result1 || !result2) {
347
+ console.error(chalk.red('Could not fetch stats for comparison.'));
348
+ if (!result1) {
349
+ console.log(chalk.yellow(` ${branch1}: Agent not found or error occurred`));
350
+ console.log(chalk.yellow(` → Make sure you've pushed: ${chalk.cyan(`git push origin ${branch1}`)}`));
351
+ }
352
+ if (!result2) {
353
+ console.log(chalk.yellow(` ${branch2}: Agent not found or error occurred`));
354
+ console.log(chalk.yellow(` → Make sure you've pushed: ${chalk.cyan(`git push origin ${branch2}`)}`));
355
+ }
356
+ return;
357
+ }
358
+
359
+ if (!result1.stats || !result2.stats) {
360
+ console.log(chalk.yellow('\nāš ļø One or both agents have no metrics yet.'));
361
+ if (!result1.stats) console.log(chalk.yellow(` ${branch1}: Waiting for first decision...`));
362
+ if (!result2.stats) console.log(chalk.yellow(` ${branch2}: Waiting for first decision...`));
363
+ return;
364
+ }
365
+
366
+ const s1 = result1.stats;
367
+ const s2 = result2.stats;
368
+
369
+ // Helper function to strip ANSI codes for width calculation
370
+ const stripAnsi = (str) => str.replace(/\u001b\[[0-9;]*m/g, '');
371
+
372
+ // Helper function to pad string accounting for ANSI codes
373
+ const padWithAnsi = (str, width) => {
374
+ const visibleLength = stripAnsi(str).length;
375
+ const padding = Math.max(0, width - visibleLength);
376
+ return str + ' '.repeat(padding);
377
+ };
378
+
379
+ console.log(chalk.bold('\n╔═════════════════════╦═══════════════════════════╦════════════════════════════╗'));
380
+ const titleText = ' Mantle Agent Strategy Comparison';
381
+ const titlePadding = 78 - titleText.length; // 77 total width minus title and borders
382
+ console.log(chalk.bold(`ā•‘${titleText}${' '.repeat(titlePadding)}ā•‘`));
383
+ console.log(chalk.bold('╠═════════════════════╬═══════════════════════════╬════════════════════════════╣'));
384
+
385
+ // Create comparison table
386
+ const metrics = [
387
+ { label: 'Total Decisions', v1: s1.total_decisions || 0, v2: s2.total_decisions || 0, format: (v) => v.toString() },
388
+ { label: 'BUY Signals', v1: s1.buy_count || 0, v2: s2.buy_count || 0, format: (v) => chalk.cyan(v.toString()) },
389
+ { label: 'HOLD Signals', v1: s1.hold_count || 0, v2: s2.hold_count || 0, format: (v) => chalk.yellow(v.toString()) },
390
+ { label: 'Trades Executed', v1: s1.trades_executed || 0, v2: s2.trades_executed || 0, format: (v) => chalk.magenta(v.toString()) },
391
+ { label: 'Avg Price', v1: s1.avg_price || 0, v2: s2.avg_price || 0, format: (v) => `$${parseFloat(v).toFixed(4)}` },
392
+ { label: 'Success Rate',
393
+ v1: s1.total_decisions ? ((s1.trades_executed / s1.total_decisions) * 100).toFixed(1) : '0.0',
394
+ v2: s2.total_decisions ? ((s2.trades_executed / s2.total_decisions) * 100).toFixed(1) : '0.0',
395
+ format: (v) => chalk.green(`${v}%`)
396
+ },
397
+ ];
398
+
399
+ // Header row
400
+ const headerMetric = padWithAnsi('Metric', 19);
401
+ const header1 = padWithAnsi(chalk.bold(branch1), 27);
402
+ const header2 = padWithAnsi(chalk.bold(branch2), 27);
403
+ console.log(`ā•‘ ${headerMetric}ā•‘ ${header1}ā•‘ ${header2}ā•‘`);
404
+ console.log(chalk.bold('╠═════════════════════╬═══════════════════════════╬═══════════════════════════╣'));
405
+
406
+ // Data rows
407
+ metrics.forEach((m, idx) => {
408
+ const label = padWithAnsi(m.label, 19);
409
+ const v1Str = padWithAnsi(m.format(m.v1), 27);
410
+ const v2Str = padWithAnsi(m.format(m.v2), 27);
411
+ console.log(`ā•‘ ${label}ā•‘ ${v1Str}ā•‘ ${v2Str}ā•‘`);
412
+ });
413
+
414
+ console.log(chalk.bold('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•©ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•©ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•'));
415
+
416
+ // Determine winner
417
+ console.log('\n' + chalk.bold('šŸ† Performance Analysis:'));
418
+ if ((s1.trades_executed || 0) > (s2.trades_executed || 0)) {
419
+ console.log(chalk.green(` ${branch1} has executed more trades`));
420
+ } else if ((s2.trades_executed || 0) > (s1.trades_executed || 0)) {
421
+ console.log(chalk.green(` ${branch2} has executed more trades`));
422
+ } else {
423
+ console.log(chalk.yellow(` Both strategies show similar trade execution patterns`));
424
+ }
425
+
426
+ if (s1.total_decisions && s2.total_decisions) {
427
+ const rate1 = (s1.trades_executed / s1.total_decisions) * 100;
428
+ const rate2 = (s2.trades_executed / s2.total_decisions) * 100;
429
+ if (rate1 > rate2) {
430
+ console.log(chalk.green(` ${branch1} has better success rate (${rate1.toFixed(1)}% vs ${rate2.toFixed(1)}%)`));
431
+ } else if (rate2 > rate1) {
432
+ console.log(chalk.green(` ${branch2} has better success rate (${rate2.toFixed(1)}% vs ${rate1.toFixed(1)}%)`));
433
+ }
434
+ }
435
+ });
436
+
437
+ // --- Parse and Run ---
438
+ program.parse(process.argv);
439
+
440
+
441
+
442
+
443
+
444
+
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@marshalz/git-somnia-agent",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool for deploying and managing AI agents on Mantle Sepolia via Git workflows",
5
+ "main": "index.js",
6
+ "readme": "README.md",
7
+ "bin": {
8
+ "git-somnia-agent": "./index.js"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/xaviersharwin10/somnia-git-agent.git",
13
+ "directory": "git-agent-cli"
14
+ },
15
+ "keywords": [
16
+ "git",
17
+ "agent",
18
+ "cli",
19
+ "ai",
20
+ "trading",
21
+ "blockchain",
22
+ "mantle",
23
+ "web3",
24
+ "defi",
25
+ "autonomous-agents"
26
+ ],
27
+ "author": "marshal.25ec@licet.ac.in",
28
+ "license": "MIT",
29
+ "scripts": {
30
+ "test": "echo \"Error: no test specified\" && exit 1"
31
+ },
32
+ "dependencies": {
33
+ "axios": "^1.13.1",
34
+ "chalk": "^4.1.2",
35
+ "commander": "^14.0.2",
36
+ "ethers": "^6.15.0",
37
+ "inquirer": "^12.10.0",
38
+ "shelljs": "^0.10.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=16.0.0"
42
+ }
43
+ }