diff --git a/.gitignore b/.gitignore index 3219698..be92769 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,4 @@ temp/ !config/*.json sLo8NkvM2wyRy6xism6uH4dSUUpdM29MeVow9mxz1xe.json CONTEXT.md +PROJECT.md diff --git a/CHANGELOG.md b/CHANGELOG.md index ad8ccd1..87aa00d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.1] - 2026-01-29 + +### Added +- **Issue #33**: Monitoring lifecycle management improvements + - `slopesniper status` now shows monitoring info (daemon status, active targets) + - Auto-cleanup: Selling entire position automatically removes monitoring targets + - No more orphaned monitors - selling stops watching automatically + - MoltBot compatibility assessment document (`docs/MOLTBOT_COMPATIBILITY.md`) + - Persistent developer documentation (`PROJECT.md`, updated `CONTEXT.md`) + +### Changed +- SKILL.md install method updated: curl script is now primary (recommended) +- Synced duplicate SKILL.md files between `skills/` and `mcp-extension/` + +### Fixed +- Monitoring targets now respect position lifecycle (auto-remove on position close) + ## [0.3.03] - 2026-01-28 ### Added diff --git a/docs/MOLTBOT_COMPATIBILITY.md b/docs/MOLTBOT_COMPATIBILITY.md new file mode 100644 index 0000000..9ed7706 --- /dev/null +++ b/docs/MOLTBOT_COMPATIBILITY.md @@ -0,0 +1,396 @@ +# SlopeSniper + MoltBot Compatibility Assessment + +*Created: 2026-01-28 | Updated: 2026-01-28 | Version: 0.3.03* + +## Executive Summary + +SlopeSniper is **fully compatible** with MoltBot workflows. After reviewing MoltBot's architecture and real-world usage patterns, the key insight is: + +**MoltBot is the intelligent orchestrator.** It: +1. Receives user's natural language (Telegram, Discord, WhatsApp, etc.) +2. Reads our SKILL.md for guidance on what commands to use +3. Executes our CLI and receives JSON output +4. **Interprets the JSON and formats a human-friendly response** + +This means we **don't need to worry about output formatting for different platforms** - MoltBot handles that. Our job is to return clean, structured JSON with meaningful field names. + +| Category | Status | Notes | +|----------|--------|-------| +| CLI Output Format | PASS | JSON output, meaningful field names | +| SKILL.md Guidance | PASS | Comprehensive NLP examples, clear instructions | +| Error Handling | PASS | JSON errors with context | +| Script Interoperability | PASS | Clean JSON, exit codes | +| Background Tasks | INVESTIGATE | MoltBot has sessionId pattern - need to verify alignment | +| Real-time Data | PASS | SKILL.md instructs AI to always fetch fresh | + +--- + +## 1. CLI Output Format Compatibility + +### Current Implementation + +SlopeSniper uses **JSON output to stdout** for all commands: + +```python +def print_json(data: dict) -> None: + print(json.dumps(data, indent=2, default=str)) +``` + +**Exit Codes:** +- `0` = Success +- `1` = Error (with JSON error in stdout) + +**Quiet Mode:** +```bash +slopesniper buy BONK 25 --quiet +``` +- Suppresses all logging +- Returns only JSON result + +### MoltBot Compatibility + +| Pattern | SlopeSniper | MoltBot Expected | Status | +|---------|-------------|------------------|--------| +| JSON output | `{"success": true, ...}` | JSON or text | PASS | +| Error format | `{"error": "msg"}` | Any parseable | PASS | +| Exit codes | 0/1 | 0/non-zero | PASS | +| Quiet mode | `--quiet` flag | Suppressed noise | PASS | + +**Verdict: FULLY COMPATIBLE** + +--- + +## 2. Messaging Platform Considerations + +### Why This Is NOT a Concern + +MoltBot's AI interprets our JSON output and generates human-friendly responses. It doesn't send raw JSON to users. + +**Example flow:** +``` +User (Telegram): "What's in my wallet?" + ↓ +MoltBot executes: slopesniper wallet --quiet + ↓ +SlopeSniper returns: {"wallet": {"address": "7xK...", "tokens": [...]}} + ↓ +MoltBot AI formats: "You have 3 tokens worth $260: +• 1M BONK ($25.50) +• 500 WIF ($150.00) +• 100 JUP ($85.00)" +``` + +The AI naturally summarizes large outputs. MoltBot also has: +- Block streaming with configurable chunk sizes +- Platform-aware formatting +- Markdown support where available + +### What We DO Need + +1. **Meaningful JSON field names** - So MoltBot can describe them naturally +2. **Consistent structure** - So patterns are predictable +3. **Error context** - So MoltBot can explain what went wrong + +**Our current output is already well-structured for this.** + +--- + +## 3. Long-Running Task Patterns + +### How MoltBot Handles Background Tasks + +MoltBot has a built-in pattern for long-running commands using `exec` with `background:true`: + +```bash +# MoltBot can run any command in background +bash background:true command:"slopesniper watch BONK --target-price 0.01" +# Returns: {"sessionId": "abc123"} + +# MoltBot's process tool manages the session +process action:poll sessionId:abc123 # Get output +process action:log sessionId:abc123 # Get logs +process action:kill sessionId:abc123 # Terminate +``` + +### SlopeSniper Current Implementation + +**Daemon Mode (for auto-sell targets):** +```bash +slopesniper daemon start # Starts background monitoring +slopesniper daemon status # Check status +slopesniper daemon logs --tail 20 # View logs +slopesniper daemon stop # Stop +``` + +**Watch Mode (foreground, blocking):** +```bash +slopesniper watch BONK --target-price 0.01 --interval 5 +# Prints progress to stdout every interval +# Blocks until target hit or Ctrl+C +``` + +### Analysis: Is This a Problem? + +**Likely NO.** MoltBot's `bash background:true` can wrap ANY command: + +```bash +# MoltBot would do: +bash background:true command:"slopesniper watch BONK --mcap 1000000000 --sell all" + +# This creates a MoltBot-managed session that: +# - Captures stdout (our progress output) +# - Can be polled via process action:poll +# - Can be killed via process action:kill +``` + +Our `watch` command already outputs progress to stdout, which MoltBot captures. + +### What About the Daemon? + +The daemon is for **persistent background monitoring** that survives MoltBot restarts. It's complementary: + +| Use Case | Solution | +|----------|----------| +| "Watch BONK until $1B mcap" | MoltBot runs `watch` in background | +| "Monitor all my targets 24/7" | User starts daemon with `daemon start` | + +### Recommendation: Verify, Don't Over-Engineer + +**Action:** Test actual MoltBot behavior before adding complexity. + +1. Test `bash background:true command:"slopesniper watch ..."` in MoltBot +2. Verify `process action:poll` captures our stdout +3. If it works, document it in SKILL.md +4. Only add sessionId pattern if MoltBot's built-in backgrounding doesn't work + +**Do NOT add a parallel `slopesniper process` interface unless proven necessary.** + +--- + +## 4. Script Interoperability + +### Current Strengths + +SlopeSniper is **well-suited** for script interoperability: + +```bash +# Parse JSON output +RESULT=$(slopesniper buy BONK 25 --quiet) +SUCCESS=$(echo "$RESULT" | jq -r '.success') + +# Check exit code +if slopesniper check SCAM_TOKEN --quiet; then + echo "Token is safe" +else + echo "Token failed safety check" +fi + +# Chain commands +slopesniper price BONK --quiet | jq -r '.price_usd' +``` + +### Interop with Other MoltBot Skills + +SlopeSniper can be called from other skills: + +```bash +# From another skill's context +bash command:"slopesniper buy BONK 25 --quiet" + +# With environment variables +SOLANA_PRIVATE_KEY=xxx bash command:"slopesniper buy BONK 25" +``` + +### Recommendations + +**Priority: LOW** (already good) + +1. **Document JSON schemas** for each command output +2. **Add `--format` flag** for future flexibility + ```bash + slopesniper wallet --format json # Default + slopesniper wallet --format csv # For spreadsheets + ``` + +--- + +## 5. Cross-Provider Messaging + +### MoltBot Cross-Provider Config + +```json +{ + "agents": { + "defaults": { + "tools": { + "message": { + "crossContext": { + "allowAcrossProviders": true, + "marker": { "enabled": true, "prefix": "[from {channel}] " } + } + } + } + } + } +} +``` + +### SlopeSniper Considerations + +- Users may start a trade on Telegram, check status on Discord +- Wallet state is **local to the machine**, not session-specific +- Trade history is persisted in `~/.slopesniper/trades.db` + +**No changes needed** - SlopeSniper's stateless CLI model works well with cross-provider messaging. + +--- + +## 6. Block Streaming for Long Responses + +### MoltBot Streaming Config + +```json +{ + "agents": { + "defaults": { + "blockStreamingChunk": { + "minChars": 800, + "maxChars": 1200 + } + } + } +} +``` + +### SlopeSniper Implications + +Long outputs (wallet, status, scan) will be chunked by MoltBot. This works but can feel disjointed. + +### Recommendations + +**Priority: MEDIUM** + +1. **Structure output with clear sections** + ```json + { + "summary": "3 tokens, $125.50 total", + "tokens": [...], + "details": {...} + } + ``` + +2. **Add summary-first pattern** + - First line: actionable summary + - Following: details (can be truncated) + +--- + +## 7. Recommended Actions + +### Immediate: Verify Before Building + +**DO NOT add features until testing actual MoltBot behavior:** + +1. **Test background tasks** + ```bash + # In MoltBot/Telegram: + "Watch BONK until it hits $500M mcap, then sell all" + # Does MoltBot use background:true? Does polling work? + ``` + +2. **Test large outputs** + ```bash + # With 20+ tokens in wallet: + "Show my wallet" + # Does MoltBot summarize appropriately? + ``` + +3. **Test daemon interaction** + ```bash + "Start monitoring my targets in background" + "What's the daemon status?" + # Does the conversation flow work? + ``` + +### If Issues Found: Targeted Fixes + +| Problem | Solution | +|---------|----------| +| Background watch doesn't work | Add SKILL.md guidance for daemon instead | +| Large output confuses AI | Add summary fields to JSON | +| Daemon status unclear | Improve daemon status output | + +### What NOT To Do + +- **Don't add `--compact` flags** - MoltBot handles formatting +- **Don't add `--limit` defaults** - User can ask for "top 3" naturally +- **Don't add `slopesniper process`** - MoltBot has this built-in +- **Don't over-engineer** - Trust MoltBot's intelligence + +--- + +## 8. Testing Checklist + +### Telegram Integration + +- [ ] Buy command returns within 4096 chars +- [ ] Wallet with 10+ tokens fits in message +- [ ] Scan trending with `--limit 5` fits +- [ ] Error messages are clear and actionable + +### Discord Integration + +- [ ] All outputs under 2000 chars with `--compact` +- [ ] Embeds render correctly (if MoltBot uses them) +- [ ] Long operations show progress + +### Background Tasks + +- [ ] `daemon start` returns sessionId +- [ ] `process poll` returns current output +- [ ] `process log` returns historical output +- [ ] `process kill` terminates cleanly +- [ ] Watch mode works in background + +### Script Interoperability + +- [ ] JSON output is valid and parseable +- [ ] Exit codes are correct (0=success, 1=error) +- [ ] `--quiet` suppresses all non-JSON output +- [ ] Can chain commands with pipes + +--- + +## 9. SKILL.md Verification + +Current SKILL.md should document: + +1. **All CLI commands** with examples +2. **Output format** (JSON) +3. **Long-running task patterns** (daemon, watch) +4. **Error handling** (JSON errors, exit codes) + +**Current Status:** SKILL.md is comprehensive (355 lines) with NLP examples and CLI reference. + +--- + +## Appendix: MoltBot Tool Patterns Reference + +### exec (Background Execution) +```json +{"tool": "exec", "command": "slopesniper daemon start", "background": true} +// Returns: {"sessionId": "..."} +``` + +### process (Session Management) +```json +{"tool": "process", "action": "poll", "sessionId": "..."} +{"tool": "process", "action": "log", "sessionId": "...", "offset": 0, "limit": 100} +{"tool": "process", "action": "kill", "sessionId": "..."} +``` + +### bash (Direct Execution) +```bash +bash command:"slopesniper buy BONK 25 --quiet" +bash pty:true background:true command:"slopesniper watch BONK --target-price 0.01" +``` diff --git a/mcp-extension/pyproject.toml b/mcp-extension/pyproject.toml index b4a6c46..70fb565 100644 --- a/mcp-extension/pyproject.toml +++ b/mcp-extension/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "slopesniper-mcp" -version = "0.3.03" +version = "0.3.1" description = "SlopeSniper MCP Server - Safe Solana Token Trading" requires-python = ">=3.10" dependencies = [ diff --git a/mcp-extension/src/slopesniper_skill/SKILL.md b/mcp-extension/src/slopesniper_skill/SKILL.md index 53fa09d..ce1c5c3 100644 --- a/mcp-extension/src/slopesniper_skill/SKILL.md +++ b/mcp-extension/src/slopesniper_skill/SKILL.md @@ -1,7 +1,7 @@ --- name: slopesniper description: Trade Solana tokens via Jupiter DEX with auto-execution and safety limits -metadata: {"moltbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install SlopeSniper via uv"}]},"clawdbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install SlopeSniper via uv"}]}} +metadata: {"moltbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"curl-install","kind":"script","url":"https://raw.githubusercontent.com/BAGWATCHER/SlopeSniper/main/skills/install.sh","bins":["slopesniper"],"label":"Install SlopeSniper (recommended)"},{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install via uv (advanced)"}]},"clawdbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"curl-install","kind":"script","url":"https://raw.githubusercontent.com/BAGWATCHER/SlopeSniper/main/skills/install.sh","bins":["slopesniper"],"label":"Install SlopeSniper (recommended)"},{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install via uv (advanced)"}]}} user-invocable: true homepage: https://github.com/BAGWATCHER/SlopeSniper --- diff --git a/mcp-extension/src/slopesniper_skill/__init__.py b/mcp-extension/src/slopesniper_skill/__init__.py index 8d5619d..ca10113 100644 --- a/mcp-extension/src/slopesniper_skill/__init__.py +++ b/mcp-extension/src/slopesniper_skill/__init__.py @@ -25,7 +25,7 @@ # Version is the single source of truth - update here for releases # Follow semantic versioning: MAJOR.MINOR.PATCH # Beta versions use 0.x.x (0.MINOR.PATCH) -__version__ = "0.3.03" +__version__ = "0.3.1" from .tools import ( export_wallet, diff --git a/mcp-extension/src/slopesniper_skill/cli.py b/mcp-extension/src/slopesniper_skill/cli.py index 0f2f194..feb9878 100644 --- a/mcp-extension/src/slopesniper_skill/cli.py +++ b/mcp-extension/src/slopesniper_skill/cli.py @@ -71,9 +71,10 @@ def print_json(data: dict) -> None: async def cmd_status() -> None: - """Full status: wallet, holdings, strategy, and config.""" + """Full status: wallet, holdings, strategy, config, and monitoring.""" from . import get_status, get_strategy, solana_get_wallet from .tools.config import get_config_status + from .tools.targets import get_active_targets # Get all status info status = await get_status() @@ -81,6 +82,31 @@ async def cmd_status() -> None: strategy = await get_strategy() config = get_config_status() + # Get monitoring info (targets + daemon) + try: + active_targets = get_active_targets() + targets_summary = [ + { + "id": t.id, + "symbol": t.symbol or t.mint[:8], + "type": t.target_type.value, + "target": t.target_value, + "sell": t.sell_amount, + } + for t in active_targets + ] + except Exception: + targets_summary = [] + + # Check daemon status + try: + from .daemon import get_daemon_status + + daemon_info = get_daemon_status() + daemon_running = daemon_info.get("running", False) + except Exception: + daemon_running = False + result = { "wallet": { "configured": status.get("wallet_configured", False), @@ -100,6 +126,11 @@ async def cmd_status() -> None: "jupiter_api_key": config.get("jupiter_api_key_status"), "rpc": config.get("rpc"), }, + "monitoring": { + "daemon_running": daemon_running, + "active_targets": len(targets_summary), + "targets": targets_summary, + }, "ready_to_trade": status.get("ready_to_trade", False), } print_json(result) diff --git a/mcp-extension/src/slopesniper_skill/tools/solana_tools.py b/mcp-extension/src/slopesniper_skill/tools/solana_tools.py index dfee975..5eb87d6 100644 --- a/mcp-extension/src/slopesniper_skill/tools/solana_tools.py +++ b/mcp-extension/src/slopesniper_skill/tools/solana_tools.py @@ -695,7 +695,43 @@ async def solana_swap_confirm(intent_id: str) -> dict[str, Any]: except Exception: pass # Don't fail the trade if recording fails - return { + # Auto-cleanup: If this was a sell and position is now closed, remove targets + targets_removed = 0 + cleanup_note = None + sol_mint = SYMBOL_TO_MINT["SOL"] + if intent.to_mint == sol_mint: # Selling token for SOL + try: + # Check if position is now zero + wallet_address = get_wallet_address() + holdings = await ultra_client.get_holdings(wallet_address) + tokens_data = holdings.get("tokens", {}) + + remaining_balance = 0 + if intent.from_mint in tokens_data: + token_accounts = tokens_data[intent.from_mint] + if isinstance(token_accounts, list): + remaining_balance = sum( + _safe_float(acc.get("uiAmount")) for acc in token_accounts + ) + elif isinstance(token_accounts, dict): + remaining_balance = _safe_float(token_accounts.get("uiAmount")) + + # If position is now zero or near-zero, remove targets + if remaining_balance < 0.0001: + from .targets import cancel_target, get_active_targets + + active_targets = get_active_targets() + symbol = _get_symbol_for_mint(intent.from_mint) + for target in active_targets: + if target.mint == intent.from_mint: + cancel_target(target.id) + targets_removed += 1 + if targets_removed > 0: + cleanup_note = f"Removed {targets_removed} monitoring target(s) for {symbol} (position closed)" + except Exception: + pass # Don't fail the trade if cleanup fails + + result_dict = { "success": True, "signature": signature, "from_mint": intent.from_mint, @@ -704,6 +740,12 @@ async def solana_swap_confirm(intent_id: str) -> dict[str, Any]: "out_amount_actual": f"{out_amount_ui:.6f}".rstrip("0").rstrip("."), "explorer_url": f"https://solscan.io/tx/{signature}", } + + if targets_removed > 0: + result_dict["targets_removed"] = targets_removed + result_dict["cleanup_note"] = cleanup_note + + return result_dict else: return { "success": False, @@ -924,13 +966,33 @@ async def quick_trade( # Auto-execute the trade exec_result = await solana_swap_confirm(quote_result["intent_id"]) - return { + # Auto-cleanup: Remove targets for this token if we sold the entire position + targets_removed = 0 + if action == "sell" and sell_all and exec_result.get("success"): + try: + from .targets import cancel_target, get_active_targets + + active_targets = get_active_targets() + for target in active_targets: + if target.mint == mint: + cancel_target(target.id) + targets_removed += 1 + except Exception: + pass # Don't fail the trade if target cleanup fails + + result = { "auto_executed": True, "action": action, "token": token_symbol, "amount_usd": amount_usd, **exec_result, } + + if targets_removed > 0: + result["targets_removed"] = targets_removed + result["cleanup_note"] = f"Removed {targets_removed} monitoring target(s) for {token_symbol} (position closed)" + + return result else: # Return quote for manual confirmation return { diff --git a/mcp-extension/uv.lock b/mcp-extension/uv.lock index 976950c..4fdad3f 100644 --- a/mcp-extension/uv.lock +++ b/mcp-extension/uv.lock @@ -1244,7 +1244,7 @@ wheels = [ [[package]] name = "slopesniper-mcp" -version = "0.3.0" +version = "0.3.3" source = { editable = "." } dependencies = [ { name = "aiohttp" }, diff --git a/skills/slopesniper/SKILL.md b/skills/slopesniper/SKILL.md index 53fa09d..ce1c5c3 100644 --- a/skills/slopesniper/SKILL.md +++ b/skills/slopesniper/SKILL.md @@ -1,7 +1,7 @@ --- name: slopesniper description: Trade Solana tokens via Jupiter DEX with auto-execution and safety limits -metadata: {"moltbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install SlopeSniper via uv"}]},"clawdbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install SlopeSniper via uv"}]}} +metadata: {"moltbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"curl-install","kind":"script","url":"https://raw.githubusercontent.com/BAGWATCHER/SlopeSniper/main/skills/install.sh","bins":["slopesniper"],"label":"Install SlopeSniper (recommended)"},{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install via uv (advanced)"}]},"clawdbot":{"requires":{"bins":["slopesniper"]},"emoji":"🎯","homepage":"https://github.com/BAGWATCHER/SlopeSniper","install":[{"id":"curl-install","kind":"script","url":"https://raw.githubusercontent.com/BAGWATCHER/SlopeSniper/main/skills/install.sh","bins":["slopesniper"],"label":"Install SlopeSniper (recommended)"},{"id":"uv-install","kind":"uv","package":"slopesniper-mcp","from":"git+https://github.com/BAGWATCHER/SlopeSniper.git#subdirectory=mcp-extension","bins":["slopesniper"],"label":"Install via uv (advanced)"}]}} user-invocable: true homepage: https://github.com/BAGWATCHER/SlopeSniper ---