Skill: koba42corp/mintgarden (version 1.0.0)

# MintGarden Skill Browse, search, and analyze Chia NFTs via the MintGarden API. ## What It Does - Search NFTs and collections - View collection stats, floor prices, trading volume - Track NFT ownership and trade history - Monitor recent sales and activity - Get trending collections and top traders - Access profiles and portfolios ## Commands All commands can be triggered via: - `/mg <command>` in Telegram - `/mintgarden <command>` in Telegram - `mg <command>` in CLI - `mintgarden <command>` in CLI ### Search ```bash /mg search <query> # Search everything /mg search nfts "rare zombie" # Search NFTs only /mg search collections "pixel" # Search collections only ``` ### Collections ```bash /mg collections list # Top collections by volume /mg collection <id> # Collection details /mg collection nfts <id> # NFTs in collection /mg collection stats <id> # Collection statistics /mg collection activity <id> # Recent sales/transfers ``` ### NFTs ```bash /mg nft <launcher_id> # NFT details /mg nft history <launcher_id> # Trade history /mg nft offers <launcher_id> # Active offers ``` ### Profiles ```bash /mg profile <username> # Profile details /mg profile nfts <username> # User's NFTs /mg profile activity <username> # User's recent activity ``` ### Events & Stats ```bash /mg events # Recent global activity /mg events <collection_id> # Collection-specific events /mg stats # Global marketplace stats /mg trending # Trending collections (24h) /mg top collectors # Top collectors (7d) /mg top traders # Top traders (7d) ``` ### Shortcuts ```bash /mg col1abc... # Quick collection lookup /mg nft1abc... # Quick NFT lookup /mg did:chia:... # Quick profile lookup ``` ## Agent Usage When users ask about Chia NFTs, collections, or MintGarden: ```javascript const { handleCommand } = require('./skills/mintgarden'); // Natural language → formatted response const output = await handleCommand('show me trending collections'); ``` The skill handles: - Command parsing and normalization - API calls with error handling - Formatted text output (CLI/Telegram friendly) - Pagination for large results ## API Client For custom integrations, use the API client directly: ```javascript const MintGardenAPI = require('./skills/mintgarden/lib/api'); const api = new MintGardenAPI(); // Search const results = await api.search('zombie'); const nfts = await api.searchNFTs('rare', { limit: 50 }); // Collections const collections = await api.getCollections({ sort: 'volume_7d' }); const collection = await api.getCollection('col1abc...'); const stats = await api.getCollectionStats('col1abc...'); // NFTs const nft = await api.getNFT('nft1abc...'); const history = await api.getNFTHistory('nft1abc...'); // Profiles const profile = await api.getProfile('username'); const profileNFTs = await api.getProfileNFTs('did:chia:...'); // Events const events = await api.getEvents({ limit: 20 }); const trending = await api.getTrending({ period: '24h' }); // Stats const globalStats = await api.getGlobalStats(); const topCollectors = await api.getTopCollectors({ period: '7d' }); ``` ## Installation ```bash cd skills/mintgarden npm install chmod +x cli.js npm link # Makes 'mg' and 'mintgarden' global ``` ## Configuration No API key required — MintGarden API is public. Optional: Set custom base URL via environment: ```bash export MINTGARDEN_API_URL=https://api.mintgarden.io ``` ## Output Format All commands return plain text suitable for: - Terminal output (CLI) - Telegram messages - Discord messages - WhatsApp messages No markdown tables (for WhatsApp compatibility). ## Error Handling - Invalid IDs → Clear error message - API failures → Retry-friendly error - Network issues → Timeout after 30s - Empty results → Helpful "not found" message ## Limits - Default limit: 50 results per query - Max limit: 100 results per query - No rate limiting (MintGarden is generous) - Pagination available via API client ## Examples **Find rare NFTs in a collection:** ```bash /mg collection nfts col1abc... ``` **Check floor price:** ```bash /mg collection col1abc... ``` **See what's hot:** ```bash /mg trending ``` **Track a specific NFT:** ```bash /mg nft history nft1abc... ``` **Monitor marketplace:** ```bash /mg events ``` ## Tips - Use shortcuts for quick lookups (paste IDs directly) - Collection IDs start with `col1` - NFT launcher IDs start with `nft1` - Profile DIDs start with `did:chia:` - Trending updates every hour - Volume stats use 7-day window by default ## Support - MintGarden API: https://api.mintgarden.io/docs - Chia NFTs: https://mintgarden.io - Bug reports: File in skill repository

path: CHANGELOG.md
content: # Changelog

All notable changes to the MintGarden skill will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2026-01-29

### Added
- Initial release
- Complete MintGarden API wrapper (40+ endpoints)
- CLI commands via `mg` and `mintgarden`
- Telegram integration with `/mg` command
- Natural language command parsing
- Search functionality (NFTs, collections, global)
- Collection browsing and statistics
- NFT details, history, and offers
- Profile lookup and portfolio tracking
- Event monitoring and activity feeds
- Trending collections and top traders/collectors
- Global marketplace statistics
- Pagination support
- Error handling and user-friendly messages
- Formatted output for CLI and messaging platforms
- API client for programmatic access
- ClawdHub package structure
- Comprehensive documentation (SKILL.md, README.md)
- Installation script
- MIT License

### Features
- 🔍 Search: Universal, NFT-specific, and collection-specific search
- 📊 Stats: Floor prices, volumes, sales, global statistics
- 🖼️ NFTs: Details, attributes, rarity, ownership, trade history
- 📚 Collections: Browse, filter, sort, view activity
- 👤 Profiles: User portfolios, holdings, activity
- 📈 Events: Real-time marketplace activity monitoring
- 💰 Offers: Active bids and listings
- 🔥 Trending: 24-hour trending collections
- ⚡ Shortcuts: Direct ID/DID lookups

### Technical
- Node.js 18+ compatibility
- Axios for HTTP requests
- 30-second timeout for API calls
- Graceful error handling
- No API key required
- Production-ready code

## [Unreleased]

### Planned
- Image preview support (fetch NFT images)
- Price alerts and notifications
- Collection comparison tools
- Portfolio value tracking
- Rarity calculator
- Offer recommendation engine
- Historical price charts
- Export data to CSV/JSON
- Webhook support for real-time events
- Custom API base URL configuration
- Rate limiting and caching
- GraphQL query builder
- Batch operations
- Advanced filtering options

---

[1.0.0]: https://github.com/yourusername/mintgarden-skill/releases/tag/v1.0.0


path: README.md
content: <p align="center">
  <h1 align="center">🌱 MintGarden API Client</h1>
  <p align="center">
    <strong>Browse, search, and analyze Chia NFTs via MintGarden API</strong>
  </p>
</p>

<p align="center">
  <a href="https://opensource.org/licenses/MIT">
    <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
  </a>
  <a href="https://nodejs.org/">
    <img src="https://img.shields.io/badge/Node.js-v18+-green.svg" alt="Node.js: v18+">
  </a>
  <a href="https://api.mintgarden.io/docs">
    <img src="https://img.shields.io/badge/API-MintGarden-blue.svg" alt="MintGarden API">
  </a>
  <a href="https://clawd.bot">
    <img src="https://img.shields.io/badge/Framework-Clawdbot-orange.svg" alt="Built with Clawdbot">
  </a>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/Status-Production%20Ready-brightgreen.svg" alt="Status">
  <img src="https://img.shields.io/badge/Version-1.0.0-blue.svg" alt="Version">
</p>

---

## 🎯 Overview

Access the complete MintGarden API to interact with NFTs on the Chia blockchain. Search collections, view floor prices, track trading history, browse profiles, and more.

**No API key required** — MintGarden's API is free and public.

## ✨ Features

- 🔍 **Search** NFTs and collections with natural language
- 📊 **Stats** — floor prices, volumes, sales counts, trending
- 🖼️ **NFT Details** — metadata, attributes, rarity, ownership
- 📚 **Collections** — browse top collections, view activity
- 👤 **Profiles** — user portfolios, trade history, holdings
- 📈 **Events** — real-time marketplace activity
- 💰 **Offers** — active bids and listings
- 🔥 **Trending** — what's hot in the last 24h

## 🚀 Quick Start

### Installation

```bash
# Via ClawdHub (recommended)
clawdhub install mintgarden

# Or manually
cd ~/.clawd/skills
git clone https://github.com/yourusername/mintgarden-skill.git mintgarden
cd mintgarden
npm install
chmod +x cli.js
npm link
```

### Usage

#### CLI

```bash
# Search
mg search "zombie"
mg search nfts "rare pixel"
mg search collections "art"

# Collections
mg collections list
mg collection col1abc...
mg collection stats col1abc...

# NFTs
mg nft nft1abc...
mg nft history nft1abc...

# Profiles
mg profile username
mg profile did:chia:...

# Stats
mg stats
mg trending
mg top collectors
```

#### Telegram

```
/mg search zombie
/mg collection col1abc...
/mg trending
/mg events
```

#### Clawdbot Agent

```javascript
const { handleCommand } = require('./skills/mintgarden');

const output = await handleCommand('show me trending collections');
console.log(output);
```

#### API Client

```javascript
const MintGardenAPI = require('./skills/mintgarden/lib/api');
const api = new MintGardenAPI();

// Get trending collections
const trending = await api.getTrending({ period: '24h', limit: 10 });

// Search NFTs
const results = await api.searchNFTs('zombie', { limit: 20 });

// Get collection details
const collection = await api.getCollection('col1abc...');

// Get NFT details
const nft = await api.getNFT('nft1abc...');

// Get profile
const profile = await api.getProfile('username');
```

## 📖 Command Reference

### Search Commands

| Command | Description | Example |
|---------|-------------|---------|
| `mg search <query>` | Search everything | `mg search zombie` |
| `mg search nfts "<query>"` | Search NFTs only | `mg search nfts "rare pixel"` |
| `mg search collections "<query>"` | Search collections | `mg search collections "art"` |

### Collection Commands

| Command | Description | Example |
|---------|-------------|---------|
| `mg collections list` | Top collections by volume | `mg collections list` |
| `mg collection <id>` | Collection details | `mg collection col1abc...` |
| `mg collection nfts <id>` | NFTs in collection | `mg collection nfts col1abc...` |
| `mg collection stats <id>` | Collection statistics | `mg collection stats col1abc...` |
| `mg collection activity <id>` | Recent activity | `mg collection activity col1abc...` |

### NFT Commands

| Command | Description | Example |
|---------|-------------|---------|
| `mg nft <launcher_id>` | NFT details | `mg nft nft1abc...` |
| `mg nft history <launcher_id>` | Trade history | `mg nft history nft1abc...` |
| `mg nft offers <launcher_id>` | Active offers | `mg nft offers nft1abc...` |

### Profile Commands

| Command | Description | Example |
|---------|-------------|---------|
| `mg profile <username\|did>` | Profile details | `mg profile alice` |
| `mg profile nfts <username>` | User's NFTs | `mg profile nfts alice` |
| `mg profile activity <username>` | User's activity | `mg profile activity alice` |

### Stats & Events

| Command | Description | Example |
|---------|-------------|---------|
| `mg events` | Recent global events | `mg events` |
| `mg events <collection_id>` | Collection events | `mg events col1abc...` |
| `mg stats` | Global stats | `mg stats` |
| `mg trending` | Trending (24h) | `mg trending` |
| `mg top collectors` | Top collectors (7d) | `mg top collectors` |
| `mg top traders` | Top traders (7d) | `mg top traders` |

### Shortcuts

| Input | Action |
|-------|--------|
| `mg col1abc...` | Get collection by ID |
| `mg nft1abc...` | Get NFT by launcher ID |
| `mg did:chia:...` | Get profile by DID |

## 🛠️ API Methods

Complete method reference for the API client:

### Search
- `search(query, options)` — Search all
- `searchNFTs(query, options)` — Search NFTs
- `searchCollections(query, options)` — Search collections

### Collections
- `getCollections(options)` — List collections
- `getCollection(id)` — Get collection
- `getCollectionNFTs(id, options)` — Collection NFTs
- `getCollectionAttributes(id)` — Collection attributes
- `getCollectionStats(id)` — Collection stats
- `getCollectionActivity(id, options)` — Collection activity

### NFTs
- `getNFT(launcherId)` — Get NFT
- `getNFTHistory(launcherId, options)` — Trade history
- `getNFTOffers(launcherId, options)` — Active offers

### Profiles
- `getProfile(identifier)` — Get profile
- `getProfileNFTs(identifier, options)` — Profile's NFTs
- `getProfileCollections(identifier, options)` — Profile's collections
- `getProfileActivity(identifier, options)` — Profile activity
- `getProfileOffers(identifier, options)` — Profile offers

### Events & Stats
- `getEvents(options)` — Global/collection events
- `getGlobalActivity(options)` — Global activity
- `getGlobalStats()` — Global stats
- `getTrending(options)` — Trending collections
- `getTopCollectors(options)` — Top collectors
- `getTopTraders(options)` — Top traders

### Offers
- `getOffers(options)` — List offers
- `getOffer(id)` — Get offer

### Utilities
- `resolveAddress(address)` — Resolve address
- `getCAT(assetId)` — Get CAT info

## 📊 Output Examples

### Collection Details
```
📚 Chia Friends
✓ Verified

Floor Price: 0.450 XCH
Volume 24h: 12.300 XCH
Volume 7d: 89.500 XCH
Total Volume: 1247.800 XCH

Items: 10000
Owners: 3421
Sales: 8756

ID: col1abc...
```

### NFT Details
```
🖼  Rare Zombie #4567

Collection: Zombie NFTs
Price: 2.500 XCH
Floor: 1.200 XCH

Rarity: #127 of 10000
Owner: did:chia:...

Attributes:
  • Background: Purple
  • Eyes: Red
  • Hat: Crown

Launcher ID: nft1abc...
```

### Trending Collections
```
🔥 Trending Collections (24h):

1. Chia Friends
   Floor: 0.450 XCH | Vol: 12.300 XCH
   Sales: 27

2. Space Cats
   Floor: 0.800 XCH | Vol: 8.700 XCH
   Sales: 11
```

## 🔧 Configuration

### Environment Variables

```bash
# Optional: Custom API base URL
export MINTGARDEN_API_URL=https://api.mintgarden.io
```

### Options

Most methods accept an `options` object:

```javascript
{
  offset: 0,        // Pagination offset
  limit: 50,        // Results per page (max 100)
  sort: 'volume_7d', // Sort field
  order: 'desc'     // Sort order
}
```

## 🧪 Examples

### Track a Collection's Activity

```javascript
const api = new MintGardenAPI();

// Get collection stats
const stats = await api.getCollectionStats('col1abc...');
console.log(`Floor: ${stats.floor_price / 1e12} XCH`);

// Get recent sales
const activity = await api.getCollectionActivity('col1abc...', { limit: 10 });
activity.events.forEach(event => {
  console.log(`${event.type}: ${event.price / 1e12} XCH`);
});
```

### Find Underpriced NFTs

```javascript
const api = new MintGardenAPI();

const collection = await api.getCollection('col1abc...');
const floor = collection.floor_price;

const nfts = await api.getCollectionNFTs('col1abc...', { limit: 100 });
const deals = nfts.nfts.filter(nft => nft.price < floor * 0.8);

console.log(`Found ${deals.length} NFTs below 80% of floor!`);
```

### Monitor Marketplace

```javascript
const api = new MintGardenAPI();

setInterval(async () => {
  const events = await api.getEvents({ limit: 5 });
  events.events.forEach(event => {
    console.log(`${event.type}: ${event.nft.name} - ${event.price / 1e12} XCH`);
  });
}, 60000); // Check every minute
```

## 🤝 Contributing

Contributions welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request

## 📄 License

MIT License - see [LICENSE](LICENSE) for details

## 🔗 Links

- **MintGarden**: https://mintgarden.io
- **API Docs**: https://api.mintgarden.io/docs
- **Clawdbot**: https://clawd.bot
- **ClawdHub**: https://clawdhub.com
- **Chia Network**: https://chia.net

## 💬 Support

- Issues: [GitHub Issues](https://github.com/yourusername/mintgarden-skill/issues)
- Discord: [Clawdbot Community](https://discord.gg/clawd)
- Telegram: [@clawdbot](https://t.me/clawdbot)

---

<p align="center">Made with 🖖 by the Clawdbot community</p>


path: cli.js
content: #!/usr/bin/env node

const MintGardenAPI = require('./lib/api');
const { handleCommand } = require('./index');

const api = new MintGardenAPI();

// CLI interface
(async () => {
  const [,, ...args] = process.argv;
  const input = args.join(' ').trim();

  if (!input || input === 'help' || input === '--help' || input === '-h') {
    console.log(showHelp());
    process.exit(0);
  }

  try {
    const output = await handleCommand(input);
    console.log(output);
  } catch (error) {
    console.error(`❌ Error: ${error.message}`);
    process.exit(1);
  }
})();

function showHelp() {
  return `🌱 MintGarden API Client

SEARCH
  mg search <query>              Search everything
  mg search nfts "query"         Search NFTs
  mg search collections "query"  Search collections

COLLECTIONS
  mg collections list            List top collections
  mg collection <id>             Get collection details
  mg collection nfts <id>        Get NFTs in collection
  mg collection stats <id>       Get collection statistics
  mg collection activity <id>    Get collection activity

NFTs
  mg nft <launcher_id>           Get NFT details
  mg nft history <launcher_id>   Get NFT trade history
  mg nft offers <launcher_id>    Get active offers

PROFILES
  mg profile <username|did>      Get profile details
  mg profile nfts <username|did> Get profile's NFTs
  mg profile activity <username> Get profile activity

EVENTS
  mg events                      Get recent global events
  mg events <collection_id>      Get collection events

STATS
  mg stats                       Get global stats
  mg trending                    Get trending collections
  mg top collectors              Get top collectors
  mg top traders                 Get top traders

SHORTCUTS
  mg col1abc...                  Get collection by ID
  mg nft1abc...                  Get NFT by launcher ID
  mg did:chia:...                Get profile by DID

Use 'mg help' anytime for this message.`;
}


path: index.js
content: const MintGardenAPI = require('./lib/api');

const api = new MintGardenAPI();

/**
 * Handle MintGarden commands from CLI or Clawdbot
 * @param {string} input - Raw command string
 * @returns {Promise<string>} Formatted output
 */
async function handleCommand(input) {
  input = input.trim().toLowerCase();

  // Strip common prefixes
  input = input.replace(/^(\/mg|\/mintgarden|mg|mintgarden)\s+/, '');

  // Shortcuts - direct IDs
  if (input.startsWith('col1')) {
    return await getCollectionDetails(input);
  }
  if (input.startsWith('nft1')) {
    return await getNFTDetails(input);
  }
  if (input.startsWith('did:chia:')) {
    return await getProfileDetails(input);
  }

  // Parse command
  const parts = input.split(/\s+/);
  const command = parts[0];
  const subcommand = parts[1];
  const args = parts.slice(2);

  switch (command) {
    // SEARCH
    case 'search':
      if (subcommand === 'nfts') {
        return await searchNFTs(args.join(' '));
      }
      if (subcommand === 'collections') {
        return await searchCollections(args.join(' '));
      }
      return await searchAll(parts.slice(1).join(' '));

    // COLLECTIONS
    case 'collections':
      if (subcommand === 'list' || !subcommand) {
        return await listCollections();
      }
      if (subcommand === 'nfts') {
        return await getCollectionNFTs(args[0]);
      }
      return await getCollectionDetails(subcommand);

    case 'collection':
      if (subcommand === 'list') {
        return await listCollections();
      }
      if (subcommand === 'nfts') {
        return await getCollectionNFTs(args[0]);
      }
      if (subcommand === 'stats') {
        return await getCollectionStats(args[0]);
      }
      if (subcommand === 'activity') {
        return await getCollectionActivity(args[0]);
      }
      return await getCollectionDetails(subcommand);

    // NFTs
    case 'nft':
      if (subcommand === 'history') {
        return await getNFTHistory(args[0]);
      }
      if (subcommand === 'offers') {
        return await getNFTOffers(args[0]);
      }
      return await getNFTDetails(subcommand);

    // PROFILES
    case 'profile':
      if (subcommand === 'nfts') {
        return await getProfileNFTs(args[0]);
      }
      if (subcommand === 'activity') {
        return await getProfileActivity(args[0]);
      }
      return await getProfileDetails(subcommand);

    // EVENTS
    case 'events':
      return await getEvents(subcommand);

    // STATS
    case 'stats':
      return await getGlobalStats();

    case 'trending':
      return await getTrending();

    case 'top':
      if (subcommand === 'collectors') {
        return await getTopCollectors();
      }
      if (subcommand === 'traders') {
        return await getTopTraders();
      }
      return '❌ Unknown top command. Try: top collectors, top traders';

    default:
      return `❌ Unknown command: ${command}\n\nTry 'mg help' for usage.`;
  }
}

// === COMMAND IMPLEMENTATIONS ===

async function searchAll(query) {
  if (!query) return '❌ Please provide a search query';
  
  const results = await api.search(query, { limit: 20 });
  
  let output = `🔍 Search results for "${query}":\n\n`;
  
  let hasResults = false;
  
  if (results.collections?.length > 0) {
    hasResults = true;
    output += `📚 COLLECTIONS (${results.collections.length})\n`;
    results.collections.slice(0, 5).forEach(c => {
      output += `  • ${c.name}\n`;
    });
    output += '\n';
  }
  
  if (results.nfts?.length > 0) {
    hasResults = true;
    output += `🖼  NFTs (${results.nfts.length})\n`;
    results.nfts.slice(0, 5).forEach(n => {
      output += `  • ${n.name || 'Unnamed'}\n`;
    });
    output += '\n';
  }
  
  if (results.profiles?.length > 0) {
    hasResults = true;
    output += `👤 PROFILES (${results.profiles.length})\n`;
    results.profiles.slice(0, 5).forEach(p => {
      output += `  • ${p.name || p.username || 'Anonymous'}\n`;
    });
  }
  
  return hasResults ? output : '❌ No results found';
}

async function searchNFTs(query) {
  if (!query) return '❌ Please provide a search query';
  
  const results = await api.searchNFTs(query, { limit: 20 });
  
  let output = `🖼  NFT search results for "${query}":\n\n`;
  
  if (results.nfts?.length > 0) {
    results.nfts.forEach((nft, i) => {
      output += `${i + 1}. ${nft.name || 'Unnamed'}\n`;
      output += `   Collection: ${nft.collection_name || 'Unknown'}\n`;
      if (nft.price) output += `   Price: ${formatXCH(nft.price)}\n`;
      output += `   ID: ${nft.encoded_id}\n\n`;
    });
    return output;
  }
  
  return '❌ No NFTs found';
}

async function searchCollections(query) {
  if (!query) return '❌ Please provide a search query';
  
  const results = await api.searchCollections(query, { limit: 20 });
  
  let output = `📚 Collection search results for "${query}":\n\n`;
  
  if (results.collections?.length > 0) {
    results.collections.forEach((col, i) => {
      output += `${i + 1}. ${col.name}\n`;
      output += `   ID: ${col.id || col.encoded_id}\n\n`;
    });
    return output;
  }
  
  return '❌ No collections found';
}

async function listCollections() {
  const results = await api.getCollections({ limit: 25, sort: 'vol24h' });
  
  let output = `📚 Top Collections by Volume (24h):\n\n`;
  
  if (results.items?.length > 0) {
    results.items.forEach((col, i) => {
      const verified = col.creator?.verification_state ? '✓' : '';
      output += `${i + 1}. ${col.name} ${verified}\n`;
      output += `   ID: ${col.id || col.encoded_id}\n\n`;
    });
    return output;
  }
  
  return '❌ No collections found';
}

async function getCollectionDetails(collectionId) {
  if (!collectionId) return '❌ Please provide a collection ID';
  
  const collection = await api.getCollection(collectionId);
  
  let output = `📚 ${collection.name}\n`;
  const verified = collection.creator?.verification_state ? '✓ Verified' : '';
  if (verified) output += verified + '\n';
  output += '\n';
  
  if (collection.description) {
    output += `${collection.description}\n\n`;
  }
  
  if (collection.creator?.name) {
    output += `Creator: ${collection.creator.name}\n`;
  }
  
  output += `\nID: ${collection.id || collection.encoded_id}\n`;
  
  return output;
}

async function getCollectionNFTs(collectionId) {
  if (!collectionId) return '❌ Please provide a collection ID';
  
  const results = await api.getCollectionNFTs(collectionId, { limit: 20 });
  
  let output = `🖼  NFTs in collection:\n\n`;
  
  if (results.items?.length > 0) {
    results.items.forEach((nft, i) => {
      output += `${i + 1}. ${nft.name || 'Unnamed'}\n`;
      if (nft.price) output += `   Price: ${formatXCH(nft.price)}\n`;
      if (nft.openrarity_rank) output += `   Rarity: #${nft.openrarity_rank}\n`;
      output += `   ID: ${nft.encoded_id}\n\n`;
    });
    return output;
  }
  
  return '❌ No NFTs found in this collection';
}

async function getCollectionStats(collectionId) {
  // Stats are embedded in collection details, not a separate endpoint
  return await getCollectionDetails(collectionId);
}

async function getCollectionActivity(collectionId) {
  // Use events endpoint instead
  return await getEvents(collectionId);
}

async function getNFTDetails(launcherId) {
  if (!launcherId) return '❌ Please provide an NFT launcher ID';
  
  const nft = await api.getNFT(launcherId);
  
  let output = `🖼  ${nft.name || 'Unnamed NFT'}\n\n`;
  
  output += `Collection: ${nft.collection_name || 'Unknown'}\n`;
  if (nft.price) output += `Price: ${formatXCH(nft.price)}\n`;
  
  if (nft.openrarity_rank) {
    output += `Rarity: #${nft.openrarity_rank}\n`;
  }
  
  if (nft.owner_name) {
    output += `Owner: ${nft.owner_name}\n`;
  } else if (nft.owner_encoded_id) {
    output += `Owner: ${nft.owner_encoded_id}\n`;
  }
  
  if (nft.description) {
    output += `\n${nft.description}\n`;
  }
  
  output += `\nID: ${nft.encoded_id}\n`;
  
  return output;
}

async function getNFTHistory(launcherId) {
  // No direct history endpoint - would need to use events
  return '❌ NFT history not available. Try /mg events to see recent activity.';
}

async function getNFTOffers(launcherId) {
  // Check if this is a mint
  try {
    const mint = await api.getMint(launcherId);
    const offers = await api.getMintOffers(launcherId, { limit: 10 });
    
    let output = `💰 Active Offers:\n\n`;
    
    if (offers.items?.length > 0) {
      offers.items.forEach((offer, i) => {
        output += `${i + 1}. Offer details\n\n`;
      });
      return output;
    }
    
    return '❌ No active offers';
  } catch (e) {
    return '❌ No offers found for this NFT';
  }
}

async function getProfileDetails(identifier) {
  if (!identifier) return '❌ Please provide a username or ID';
  
  const profile = await api.getProfile(identifier);
  
  let output = `👤 ${profile.username || profile.name || 'Anonymous'}\n\n`;
  
  if (profile.bio) {
    output += `${profile.bio}\n\n`;
  }
  
  if (profile.encoded_id) {
    output += `ID: ${profile.encoded_id}\n`;
  }
  
  return output;
}

async function getProfileNFTs(identifier) {
  if (!identifier) return '❌ Please provide a username or ID';
  
  const results = await api.getProfileNFTs(identifier, { limit: 20 });
  
  let output = `🖼  NFTs owned by ${identifier}:\n\n`;
  
  if (results.items?.length > 0) {
    results.items.forEach((nft, i) => {
      output += `${i + 1}. ${nft.name || 'Unnamed'}\n`;
      output += `   Collection: ${nft.collection_name || 'Unknown'}\n`;
      if (nft.price) output += `   Value: ${formatXCH(nft.price)}\n`;
      output += '\n';
    });
    return output;
  }
  
  return '❌ No NFTs found';
}

async function getProfileActivity(identifier) {
  // No separate activity endpoint - use events
  return '❌ Profile activity not available. Try /mg events for global activity.';
}

async function getEvents(collectionId) {
  const results = await api.getEvents({ limit: 20 });
  
  let output = `📈 Recent marketplace events:\n\n`;
  
  if (results.items?.length > 0) {
    results.items.forEach((event, i) => {
      output += `${i + 1}. ${event.type || 'Event'}\n`;
      if (event.nft_name) output += `   NFT: ${event.nft_name}\n`;
      if (event.collection_name) output += `   Collection: ${event.collection_name}\n`;
      if (event.price) output += `   ${formatXCH(event.price)}\n`;
      if (event.timestamp) output += `   ${formatTimeAgo(event.timestamp)}\n`;
      output += '\n';
    });
    return output;
  }
  
  return '❌ No recent events';
}

async function getGlobalStats() {
  // No global stats endpoint - use collections instead
  return await listCollections();
}

async function getTrending() {
  // Trending is just sorted collections
  return await listCollections();
}

async function getTopCollectors() {
  return '❌ Top collectors data not available from this API.';
}

async function getTopTraders() {
  return '❌ Top traders data not available from this API.';
}

// === FORMATTING HELPERS ===

function formatXCH(mojos) {
  if (!mojos && mojos !== 0) return 'N/A';
  const xch = mojos / 1_000_000_000_000;
  return `${xch.toFixed(3)} XCH`;
}

function formatTimeAgo(timestamp) {
  const now = Date.now();
  const diff = now - timestamp * 1000;
  
  const minutes = Math.floor(diff / 60000);
  const hours = Math.floor(diff / 3600000);
  const days = Math.floor(diff / 86400000);
  
  if (days > 0) return `${days}d ago`;
  if (hours > 0) return `${hours}h ago`;
  if (minutes > 0) return `${minutes}m ago`;
  return 'Just now';
}

module.exports = { handleCommand };


path: install.sh
content: #!/bin/bash

cd "$(dirname "$0")"

echo "📦 Installing MintGarden skill dependencies..."
npm install --production

if [ $? -eq 0 ]; then
  echo "✅ MintGarden skill installed successfully!"
  echo ""
  echo "Usage:"
  echo "  CLI: mg <command>"
  echo "  Telegram: /mg <command>"
  echo ""
  echo "Run 'mg help' for command reference."
else
  echo "❌ Installation failed. Please check npm logs."
  exit 1
fi


path: lib/api.js
content: const axios = require('axios');

const BASE_URL = 'https://api.mintgarden.io';

class MintGardenAPI {
  constructor(baseURL = BASE_URL) {
    this.client = axios.create({
      baseURL,
      timeout: 30000,
      headers: {
        'Accept': 'application/json',
        'User-Agent': 'Clawdbot-MintGarden-Skill/1.0'
      }
    });
  }

  // === SEARCH ===
  
  async search(query, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { query, limit };
    if (cursor) params.page = cursor;
    return this._get('/search', params);
  }

  async searchNFTs(query, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { query, limit };
    if (cursor) params.page = cursor;
    return this._get('/search/nfts', params);
  }

  async searchCollections(query, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { query, limit };
    if (cursor) params.page = cursor;
    return this._get('/search/collections', params);
  }

  async searchProfiles(query, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { query, limit };
    if (cursor) params.page = cursor;
    return this._get('/search/profiles', params);
  }

  async searchAddresses(query, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { query, limit };
    if (cursor) params.page = cursor;
    return this._get('/search/addresses', params);
  }

  // === COLLECTIONS ===
  
  async getCollections(options = {}) {
    const { 
      sort = null, 
      cursor = null, 
      limit = 50,
      verified = null 
    } = options;
    const params = { limit };
    if (sort) params.sort = sort;
    if (cursor) params.page = cursor;
    if (verified !== null) params.verified = verified;
    return this._get('/collections', params);
  }

  async getCollection(collectionId) {
    return this._get(`/collections/${collectionId}`);
  }

  async getCollectionMetadata(collectionId) {
    return this._get(`/collections/${collectionId}/metadata`);
  }

  async getCollectionNFTs(collectionId, options = {}) {
    const { cursor = null, limit = 50, sort = null } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    if (sort) params.sort = sort;
    return this._get(`/collections/${collectionId}/nfts`, params);
  }

  async getCollectionOwners(collectionId, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/collections/${collectionId}/owners`, params);
  }

  async getCollectionAuctions(collectionId, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/collections/${collectionId}/auctions`, params);
  }

  // === NFTs ===
  
  async getNFT(launcherId) {
    return this._get(`/nfts/${launcherId}`);
  }

  async getNFTMetadata(launcherId) {
    return this._get(`/nfts/${launcherId}/metadata`);
  }

  async getNFTLicense(launcherId) {
    return this._get(`/nfts/${launcherId}/license`);
  }

  async getNFTEvent(nftId, eventIndex) {
    return this._get(`/nfts/${nftId}/events/${eventIndex}`);
  }

  // === PROFILES ===
  
  async getProfile(identifier) {
    // identifier can be username or ID
    return this._get(`/profile/${identifier}`);
  }

  async getProfileNFTs(idHex, options = {}) {
    const { cursor = null, limit = 50, type = 'owned', collection_id = null } = options;
    const params = { limit, type };
    if (cursor) params.page = cursor;
    if (collection_id) params.collection_id = collection_id;
    return this._get(`/profile/${idHex}/nfts`, params);
  }

  async getProfileCollections(idHex, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/profile/${idHex}/collections`, params);
  }

  // === EVENTS ===
  
  async getEvents(options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get('/events', params);
  }

  // === AUCTIONS ===
  
  async getAuctions(options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get('/auctions', params);
  }

  // === MINTS (OFFERS) ===
  
  async getMint(mintId) {
    return this._get(`/mints/${mintId}`);
  }

  async getMintOffers(mintId, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/mints/${mintId}/offers`, params);
  }

  // === GARDENS ===
  
  async getGarden(slug) {
    return this._get(`/gardens/${slug}`);
  }

  async getGardenCollections(slug, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/gardens/${slug}/collections`, params);
  }

  async getGardenEvents(slug, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/gardens/${slug}/events`, params);
  }

  async getGardenProfiles(slug, options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get(`/gardens/${slug}/profiles`, params);
  }

  // === TOKENS ===
  
  async getTokens(options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get('/tokens', params);
  }

  async getToken(tokenId) {
    return this._get(`/tokens/${tokenId}`);
  }

  // === ADDRESS ===
  
  async getAddress(idHex) {
    return this._get(`/address/${idHex}`);
  }

  async getAddressNFTs(idHex, options = {}) {
    const { cursor = null, limit = 50, type = 'owned', collection_id = null } = options;
    const params = { limit, type };
    if (cursor) params.page = cursor;
    if (collection_id) params.collection_id = collection_id;
    return this._get(`/address/${idHex}/nfts`, params);
  }

  // === SUPPORTERS ===
  
  async getSupporters(options = {}) {
    const { cursor = null, limit = 50 } = options;
    const params = { limit };
    if (cursor) params.page = cursor;
    return this._get('/supporters', params);
  }

  // === INTERNAL ===
  
  async _get(path, params = {}) {
    try {
      const response = await this.client.get(path, { params });
      return response.data;
    } catch (error) {
      if (error.response) {
        throw new Error(`MintGarden API error: ${error.response.status} - ${error.response.data?.message || error.response.statusText}`);
      } else if (error.request) {
        throw new Error('MintGarden API request failed: No response received');
      } else {
        throw new Error(`MintGarden API error: ${error.message}`);
      }
    }
  }

  // Pagination helper (cursor-based)
  async *paginate(method, ...args) {
    let cursor = null;
    const limit = 50;
    
    while (true) {
      const options = args[args.length - 1] || {};
      options.cursor = cursor;
      options.limit = limit;
      
      const result = await method.call(this, ...args.slice(0, -1), options);
      
      if (!result || !result.items || result.items.length === 0) {
        break;
      }
      
      yield result.items;
      
      // Get next cursor from response
      if (!result.next) {
        break;
      }
      
      cursor = result.next;
    }
  }
}

module.exports = MintGardenAPI;


path: package-lock.json
content: {
  "name": "@clawdbot/skill-mintgarden",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "@clawdbot/skill-mintgarden",
      "version": "1.0.0",
      "license": "MIT",
      "dependencies": {
        "axios": "^1.6.0"
      },
      "bin": {
        "mg": "cli.js",
        "mintgarden": "cli.js"
      },
      "engines": {
        "node": ">=18.0.0"
      }
    },
    "node_modules/asynckit": {
      "version": "0.4.0",
      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    },
    "node_modules/axios": {
      "version": "1.13.4",
      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "follow-redirects": "^1.15.6",
        "form-data": "^4.0.4",
        "proxy-from-env": "^1.1.0"
      }
    },
    "node_modules/call-bind-apply-helpers": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "function-bind": "^1.1.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/combined-stream": {
      "version": "1.0.8",
      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "delayed-stream": "~1.0.0"
      },
      "engines": {
        "node": ">= 0.8"
      }
    },
    "node_modules/delayed-stream": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=0.4.0"
      }
    },
    "node_modules/dunder-proto": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.1",
        "es-errors": "^1.3.0",
        "gopd": "^1.2.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-define-property": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-errors": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-object-atoms": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-set-tostringtag": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "es-errors": "^1.3.0",
        "get-intrinsic": "^1.2.6",
        "has-tostringtag": "^1.0.2",
        "hasown": "^2.0.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/follow-redirects": {
      "version": "1.15.11",
      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
      "integrity": [REDACTED_SECRET],
      "funding": [
        {
          "type": "individual",
          "url": "https://github.com/sponsors/RubenVerborgh"
        }
      ],
      "license": "MIT",
      "engines": {
        "node": ">=4.0"
      },
      "peerDependenciesMeta": {
        "debug": {
          "optional": true
        }
      }
    },
    "node_modules/form-data": {
      "version": "4.0.5",
      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "asynckit": "^0.4.0",
        "combined-stream": "^1.0.8",
        "es-set-tostringtag": "^2.1.0",
        "hasown": "^2.0.2",
        "mime-types": "^2.1.12"
      },
      "engines": {
        "node": ">= 6"
      }
    },
    "node_modules/function-bind": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/get-intrinsic": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "call-bind-apply-helpers": "^1.0.2",
        "es-define-property": "^1.0.1",
        "es-errors": "^1.3.0",
        "es-object-atoms": "^1.1.1",
        "function-bind": "^1.1.2",
        "get-proto": "^1.0.1",
        "gopd": "^1.2.0",
        "has-symbols": "^1.1.0",
        "hasown": "^2.0.2",
        "math-intrinsics": "^1.1.0"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/get-proto": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "dunder-proto": "^1.0.1",
        "es-object-atoms": "^1.0.0"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/gopd": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/has-symbols": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/has-tostringtag": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "has-symbols": "^1.0.3"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/hasown": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "function-bind": "^1.1.2"
      },
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/math-intrinsics": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/mime-db": {
      "version": "1.52.0",
      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/mime-types": {
      "version": "2.1.35",
      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "mime-db": "1.52.0"
      },
      "engines": {
        "node": ">= 0.6"
      }
    },
    "node_modules/proxy-from-env": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    }
  }
}


path: package.json
content: {
  "name": "@clawdbot/skill-mintgarden",
  "version": "1.0.0",
  "description": "Browse, search, and analyze Chia NFTs via MintGarden API",
  "main": "index.js",
  "bin": {
    "mintgarden": "./cli.js",
    "mg": "./cli.js"
  },
  "scripts": {
    "test": "echo \"No tests yet\" && exit 0"
  },
  "keywords": [
    "chia",
    "nft",
    "mintgarden",
    "blockchain",
    "clawdbot-skill"
  ],
  "author": "Jeff Coleman",
  "license": "MIT",
  "dependencies": {
    "axios": "^1.6.0"
  },
  "engines": {
    "node": ">=18.0.0"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/Koba42Corp/mintgarden-skill.git"
  },
  "homepage": "https://github.com/Koba42Corp/mintgarden-skill#readme",
  "bugs": {
    "url": "https://github.com/Koba42Corp/mintgarden-skill/issues"
  }
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)