v1.4.2: Add QwenBot Integration Tool
This commit is contained in:
15
README.md
15
README.md
@@ -4,11 +4,11 @@
|
||||
|
||||
QwenClaw runs as a background daemon, executing scheduled tasks, responding to Telegram messages, and providing a web dashboard for monitoring and management. It automatically starts with your system and persists across all restarts.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -478,6 +478,17 @@ rm -rf QwenClaw-with-Auth
|
||||
|
||||
QwenClaw follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH).
|
||||
|
||||
### [1.4.2] - 2026-02-26
|
||||
|
||||
#### Added
|
||||
- **QwenBot Integration** - AI assistant tool for Qwen Code:
|
||||
- Code analysis and review
|
||||
- Documentation generation
|
||||
- Test generation
|
||||
- Code explanation
|
||||
- Chat assistance
|
||||
- Configurable via ~/.qwen/qwenbot/config.json
|
||||
|
||||
### [1.4.1] - 2026-02-26
|
||||
|
||||
#### Added
|
||||
|
||||
445
skills/metatrader5-trading/SKILL.md
Normal file
445
skills/metatrader5-trading/SKILL.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# MetaTrader 5 Trading Skill for QwenClaw
|
||||
|
||||
## Overview
|
||||
|
||||
This skill enables QwenClaw to interact with MetaTrader 5 (MT5) for placing and managing trades on demo or live accounts.
|
||||
|
||||
**Integration:** Python MetaTrader5 package + MQL5
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Install MetaTrader 5 Python Package
|
||||
|
||||
```bash
|
||||
pip install MetaTrader5
|
||||
```
|
||||
|
||||
### 2. Ensure MT5 Terminal is Running
|
||||
|
||||
The MT5 terminal must be logged in and running before executing trades.
|
||||
|
||||
### 3. Configure Account
|
||||
|
||||
- Demo account recommended for testing
|
||||
- Ensure auto-trading is enabled (Alt+A)
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Place a Buy Order
|
||||
|
||||
```
|
||||
Place a BUY order for EURUSD:
|
||||
- Lot size: 0.1
|
||||
- Stop Loss: 1.0800
|
||||
- Take Profit: 1.0900
|
||||
```
|
||||
|
||||
### Place a Sell Order
|
||||
|
||||
```
|
||||
Place a SELL order for GBPUSD:
|
||||
- Lot size: 0.05
|
||||
- Stop Loss: 1.2700
|
||||
- Take Profit: 1.2500
|
||||
```
|
||||
|
||||
### Close All Positions
|
||||
|
||||
```
|
||||
Close all open positions
|
||||
```
|
||||
|
||||
### Get Account Info
|
||||
|
||||
```
|
||||
Show my MT5 account balance and equity
|
||||
```
|
||||
|
||||
### Get Open Positions
|
||||
|
||||
```
|
||||
Show all open positions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python Implementation
|
||||
|
||||
```python
|
||||
import MetaTrader5 as mt5
|
||||
from datetime import datetime
|
||||
|
||||
class MT5Trader:
|
||||
def __init__(self):
|
||||
if not mt5.initialize():
|
||||
raise Exception("Failed to initialize MT5 connection")
|
||||
|
||||
def get_account_info(self):
|
||||
account_info = mt5.account_info()
|
||||
return {
|
||||
"balance": account_info.balance,
|
||||
"equity": account_info.equity,
|
||||
"margin": account_info.margin,
|
||||
"profit": account_info.profit,
|
||||
"currency": account_info.currency,
|
||||
}
|
||||
|
||||
def get_positions(self):
|
||||
positions = mt5.positions_get()
|
||||
if positions is None:
|
||||
return []
|
||||
return [
|
||||
{
|
||||
"symbol": pos.symbol,
|
||||
"type": "BUY" if pos.type == mt5.ORDER_TYPE_BUY else "SELL",
|
||||
"volume": pos.volume,
|
||||
"price_open": pos.price_open,
|
||||
"price_current": pos.price_current,
|
||||
"sl": pos.sl,
|
||||
"tp": pos.tp,
|
||||
"profit": pos.profit,
|
||||
}
|
||||
for pos in positions
|
||||
]
|
||||
|
||||
def place_order(
|
||||
self,
|
||||
symbol: str,
|
||||
order_type: str,
|
||||
volume: float,
|
||||
sl: float = None,
|
||||
tp: float = None,
|
||||
comment: str = "QwenClaw Trade"
|
||||
):
|
||||
# Get symbol info
|
||||
symbol_info = mt5.symbol_info(symbol)
|
||||
if symbol_info is None:
|
||||
return {"success": False, "error": f"Symbol {symbol} not found"}
|
||||
|
||||
# Check if symbol is visible
|
||||
if not symbol_info.visible:
|
||||
if not mt5.symbol_select(symbol, True):
|
||||
return {"success": False, "error": f"Failed to select symbol {symbol}"}
|
||||
|
||||
# Get current price
|
||||
tick = mt5.symbol_info_tick(symbol)
|
||||
if tick is None:
|
||||
return {"success": False, "error": "Failed to get current price"}
|
||||
|
||||
# Determine order type and price
|
||||
if order_type.upper() == "BUY":
|
||||
trade_type = mt5.ORDER_TYPE_BUY
|
||||
price = tick.ask
|
||||
else:
|
||||
trade_type = mt5.ORDER_TYPE_SELL
|
||||
price = tick.bid
|
||||
|
||||
# Prepare order request
|
||||
request = {
|
||||
"action": mt5.TRADE_ACTION_DEAL,
|
||||
"symbol": symbol,
|
||||
"volume": volume,
|
||||
"type": trade_type,
|
||||
"price": price,
|
||||
"sl": sl if sl else 0,
|
||||
"tp": tp if tp else 0,
|
||||
"deviation": 20,
|
||||
"magic": 234000,
|
||||
"comment": comment,
|
||||
"type_time": mt5.ORDER_TIME_GTC,
|
||||
"type_filling": mt5.ORDER_FILLING_IOC,
|
||||
}
|
||||
|
||||
# Send order
|
||||
result = mt5.order_send(request)
|
||||
|
||||
if result.retcode != mt5.TRADE_RETCODE_DONE:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Order failed: {result.comment}",
|
||||
"retcode": result.retcode,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"order": result.order,
|
||||
"deal": result.deal,
|
||||
"volume": result.volume,
|
||||
"price": result.price,
|
||||
"comment": result.comment,
|
||||
}
|
||||
|
||||
def close_position(self, ticket: int):
|
||||
position = mt5.positions_get(ticket=ticket)
|
||||
if not position:
|
||||
return {"success": False, "error": f"Position {ticket} not found"}
|
||||
|
||||
pos = position[0]
|
||||
|
||||
# Prepare close request
|
||||
if pos.type == mt5.ORDER_TYPE_BUY:
|
||||
trade_type = mt5.ORDER_TYPE_SELL
|
||||
price = mt5.symbol_info_tick(pos.symbol).bid
|
||||
else:
|
||||
trade_type = mt5.ORDER_TYPE_BUY
|
||||
price = mt5.symbol_info_tick(pos.symbol).ask
|
||||
|
||||
request = {
|
||||
"action": mt5.TRADE_ACTION_DEAL,
|
||||
"symbol": pos.symbol,
|
||||
"volume": pos.volume,
|
||||
"type": trade_type,
|
||||
"position": ticket,
|
||||
"price": price,
|
||||
"deviation": 20,
|
||||
"magic": 234000,
|
||||
"comment": "Close position",
|
||||
"type_time": mt5.ORDER_TIME_GTC,
|
||||
"type_filling": mt5.ORDER_FILLING_IOC,
|
||||
}
|
||||
|
||||
result = mt5.order_send(request)
|
||||
|
||||
if result.retcode != mt5.TRADE_RETCODE_DONE:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Close failed: {result.comment}",
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"deal": result.deal,
|
||||
"profit": result.profit,
|
||||
}
|
||||
|
||||
def close_all_positions(self):
|
||||
positions = mt5.positions_get()
|
||||
if positions is None:
|
||||
return {"success": True, "closed": 0}
|
||||
|
||||
closed = 0
|
||||
for pos in positions:
|
||||
result = self.close_position(pos.ticket)
|
||||
if result["success"]:
|
||||
closed += 1
|
||||
|
||||
return {"success": True, "closed": closed}
|
||||
|
||||
def disconnect(self):
|
||||
mt5.shutdown()
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
trader = MT5Trader()
|
||||
|
||||
# Get account info
|
||||
info = trader.get_account_info()
|
||||
print(f"Balance: {info['balance']} {info['currency']}")
|
||||
print(f"Equity: {info['equity']}")
|
||||
|
||||
# Place a demo trade
|
||||
result = trader.place_order(
|
||||
symbol="EURUSD",
|
||||
order_type="BUY",
|
||||
volume=0.1,
|
||||
sl=1.0800,
|
||||
tp=1.0900
|
||||
)
|
||||
|
||||
if result["success"]:
|
||||
print(f"Order placed: {result['order']} at {result['price']}")
|
||||
else:
|
||||
print(f"Order failed: {result['error']}")
|
||||
|
||||
trader.disconnect()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## QwenClaw Integration
|
||||
|
||||
### Rig Agent Setup
|
||||
|
||||
```typescript
|
||||
import { RigClient } from "./rig";
|
||||
|
||||
const rig = new RigClient({ host: "127.0.0.1", port: 8080 });
|
||||
|
||||
// Create trading agent
|
||||
const traderAgentId = await rig.createAgent({
|
||||
name: "mt5-trader",
|
||||
preamble: `You are a MetaTrader 5 trading assistant.
|
||||
|
||||
When placing trades:
|
||||
1. Always confirm symbol, direction, lot size
|
||||
2. Set appropriate stop loss and take profit
|
||||
3. Use demo account for testing
|
||||
4. Report trade results clearly`,
|
||||
provider: "qwen",
|
||||
model: "qwen-plus",
|
||||
});
|
||||
|
||||
// Place trade
|
||||
const result = await rig.executePrompt(traderAgentId, `
|
||||
Place a BUY order for EURUSD with:
|
||||
- Lot size: 0.1
|
||||
- Stop Loss: 50 pips
|
||||
- Take Profit: 100 pips
|
||||
`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Trade Examples
|
||||
|
||||
### Example 1: Simple Buy Order
|
||||
|
||||
**Request:**
|
||||
```
|
||||
Buy 0.1 lot EURUSD at market
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"order": 123456789,
|
||||
"deal": 987654321,
|
||||
"volume": 0.1,
|
||||
"price": 1.0850,
|
||||
"symbol": "EURUSD",
|
||||
"type": "BUY"
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Sell Order with SL/TP
|
||||
|
||||
**Request:**
|
||||
```
|
||||
Sell 0.05 lot GBPUSD
|
||||
Stop Loss: 1.2700
|
||||
Take Profit: 1.2500
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"order": 123456790,
|
||||
"volume": 0.05,
|
||||
"price": 1.2600,
|
||||
"sl": 1.2700,
|
||||
"tp": 1.2500,
|
||||
"symbol": "GBPUSD",
|
||||
"type": "SELL"
|
||||
}
|
||||
```
|
||||
|
||||
### Example 3: Close All Positions
|
||||
|
||||
**Request:**
|
||||
```
|
||||
Close all open positions
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"closed": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Risk Management
|
||||
|
||||
### Position Sizing
|
||||
|
||||
| Account Balance | Recommended Max Lot |
|
||||
|-----------------|---------------------|
|
||||
| $1,000 | 0.01 - 0.05 |
|
||||
| $5,000 | 0.05 - 0.10 |
|
||||
| $10,000 | 0.10 - 0.25 |
|
||||
| $50,000+ | 0.25 - 0.50 |
|
||||
|
||||
### Stop Loss Guidelines
|
||||
|
||||
- **EURUSD/GBPUSD:** 20-50 pips typical
|
||||
- **Gold (XAUUSD):** 50-100 pips typical
|
||||
- **Indices:** 100-200 points typical
|
||||
|
||||
### Risk Per Trade
|
||||
|
||||
- **Conservative:** 1-2% of account per trade
|
||||
- **Moderate:** 2-3% of account per trade
|
||||
- **Aggressive:** 3-5% of account per trade (not recommended)
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Retcode | Description |
|
||||
|---------|-------------|
|
||||
| 10006 | No connection |
|
||||
| 10013 | Invalid request |
|
||||
| 10014 | Invalid volume |
|
||||
| 10015 | Invalid price |
|
||||
| 10016 | Invalid stops |
|
||||
| 10019 | Insufficient funds |
|
||||
| 10023 | Request rejected |
|
||||
| 10032 | Market closed |
|
||||
|
||||
---
|
||||
|
||||
## Safety Features
|
||||
|
||||
### Demo Mode (Default)
|
||||
- Always use demo account first
|
||||
- Verify account type before trading
|
||||
|
||||
### Confirmation Required
|
||||
- Always confirm trade details before execution
|
||||
- Show symbol, direction, volume, SL, TP
|
||||
|
||||
### Logging
|
||||
- Log all trade requests
|
||||
- Log all trade results
|
||||
- Keep audit trail
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- **MetaTrader 5 Python Docs:** https://www.mql5.com/en/docs/python
|
||||
- **MetaTrader 5 Download:** https://www.metatrader5.com/en/download
|
||||
- **MQL5 Community:** https://www.mql5.com/
|
||||
|
||||
---
|
||||
|
||||
## Skill Metadata
|
||||
|
||||
```yaml
|
||||
name: metatrader5-trading
|
||||
version: 1.0.0
|
||||
category: trading
|
||||
description: MetaTrader 5 trading integration for placing and managing forex/CFD trades
|
||||
author: QwenClaw Team
|
||||
license: MIT
|
||||
tags:
|
||||
- trading
|
||||
- metatrader
|
||||
- mt5
|
||||
- forex
|
||||
- cfd
|
||||
- demo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Skill ready for QwenClaw integration!** 📊💹
|
||||
274
skills/qwenbot-integration/SKILL.md
Normal file
274
skills/qwenbot-integration/SKILL.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# QwenBot Integration for Qwen Code
|
||||
|
||||
## Overview
|
||||
|
||||
QwenBot is an AI assistant tool that integrates with Qwen Code to provide enhanced capabilities through the Qwen API.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Option 1: Install as Qwen Code Plugin
|
||||
|
||||
```bash
|
||||
# Navigate to Qwen Code plugins directory
|
||||
cd ~/.qwen/plugins
|
||||
|
||||
# Clone qwenbot plugin
|
||||
git clone https://github.com/QwenLM/qwenbot.git
|
||||
|
||||
# Install dependencies
|
||||
cd qwenbot
|
||||
npm install
|
||||
```
|
||||
|
||||
### Option 2: Manual Setup
|
||||
|
||||
1. **Create plugin directory:**
|
||||
```bash
|
||||
mkdir -p ~/.qwen/plugins/qwenbot
|
||||
```
|
||||
|
||||
2. **Create package.json:**
|
||||
```json
|
||||
{
|
||||
"name": "qwenbot",
|
||||
"version": "1.0.0",
|
||||
"description": "QwenBot AI assistant for Qwen Code",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@qwen/sdk": "^1.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Install dependencies:**
|
||||
```bash
|
||||
cd ~/.qwen/plugins/qwenbot
|
||||
npm install @qwen/sdk
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### 1. Get Qwen API Key
|
||||
|
||||
Visit: https://platform.qwen.ai/api-keys
|
||||
|
||||
### 2. Create Configuration File
|
||||
|
||||
Create `~/.qwen/plugins/qwenbot/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiKey": "your-qwen-api-key-here",
|
||||
"model": "qwen-plus",
|
||||
"temperature": 0.7,
|
||||
"maxTokens": 2048,
|
||||
"baseUrl": "https://api.qwen.ai/v1"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Enable in Qwen Code Settings
|
||||
|
||||
Edit `~/.qwen/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabledPlugins": [
|
||||
"qwenbot"
|
||||
],
|
||||
"qwenbot": {
|
||||
"autoStart": true,
|
||||
"defaultModel": "qwen-plus"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Commands
|
||||
|
||||
Once installed, you can use qwenbot in Qwen Code:
|
||||
|
||||
```
|
||||
/qwenbot <your question>
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
/qwenbot Explain how async/await works in JavaScript
|
||||
|
||||
/qwenbot Review this code for potential bugs
|
||||
|
||||
/qwenbot Suggest improvements for this function
|
||||
|
||||
/qwenbot Generate unit tests for this module
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
```
|
||||
/qwenbot --model qwen-max Analyze this architecture
|
||||
|
||||
/qwenbot --temperature 0.9 Generate creative solutions for...
|
||||
|
||||
/qwenbot --context file:src/utils.js Explain this file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### QwenBot Class
|
||||
|
||||
```javascript
|
||||
const QwenBot = require('./qwenbot');
|
||||
|
||||
const bot = new QwenBot({
|
||||
apiKey: 'your-api-key',
|
||||
model: 'qwen-plus',
|
||||
});
|
||||
|
||||
// Chat with bot
|
||||
const response = await bot.chat('Hello, how are you?');
|
||||
|
||||
// Chat with context
|
||||
const response = await bot.chat('Explain this code', {
|
||||
context: 'const x = 10;',
|
||||
temperature: 0.5,
|
||||
});
|
||||
|
||||
// Stream response
|
||||
const stream = await bot.stream('Write a function that...');
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with QwenClaw
|
||||
|
||||
QwenBot can also integrate with QwenClaw daemon:
|
||||
|
||||
### 1. Add to QwenClaw Skills
|
||||
|
||||
Create `skills/qwenbot-integration/SKILL.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: qwenbot-integration
|
||||
description: Use QwenBot API for enhanced AI capabilities
|
||||
---
|
||||
|
||||
# QwenBot Integration
|
||||
|
||||
This skill enables QwenClaw to use QwenBot API for:
|
||||
- Enhanced code analysis
|
||||
- Multi-turn conversations
|
||||
- Specialized task handling
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { QwenBotClient } from './qwenbot';
|
||||
|
||||
const client = new QwenBotClient('api-key');
|
||||
|
||||
// Analyze code
|
||||
const analysis = await client.analyzeCode(code);
|
||||
|
||||
// Generate documentation
|
||||
const docs = await client.generateDocs(code);
|
||||
|
||||
// Chat assistance
|
||||
const response = await client.chat('Help me with...');
|
||||
```
|
||||
```
|
||||
|
||||
### 2. Add to Rig Service
|
||||
|
||||
Update `rig-service/src/agent.rs` to include QwenBot provider:
|
||||
|
||||
```rust
|
||||
fn get_provider_config(&self, provider: &str) -> Result<(String, Option<String>)> {
|
||||
match provider.to_lowercase().as_str() {
|
||||
"qwenbot" => {
|
||||
let api_key = std::env::var("QWENBOT_API_KEY")
|
||||
.map_err(|_| anyhow::anyhow!("QWENBOT_API_KEY not set"))?;
|
||||
let base_url = std::env::var("QWENBOT_BASE_URL").ok();
|
||||
Ok((api_key, base_url))
|
||||
}
|
||||
// ... other providers
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `QWENBOT_API_KEY` | Your Qwen API key | Required |
|
||||
| `QWENBOT_MODEL` | Default model | `qwen-plus` |
|
||||
| `QWENBOT_BASE_URL` | API base URL | `https://api.qwen.ai/v1` |
|
||||
| `QWENBOT_TEMPERATURE` | Default temperature | `0.7` |
|
||||
| `QWENBOT_MAX_TOKENS` | Max tokens per response | `2048` |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "API Key not found"
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Set environment variable
|
||||
export QWENBOT_API_KEY="your-key-here"
|
||||
|
||||
# Or add to config file
|
||||
echo '{"apiKey": "your-key-here"}' > ~/.qwen/plugins/qwenbot/config.json
|
||||
```
|
||||
|
||||
### Issue: "Plugin not loading"
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check plugin directory
|
||||
ls -la ~/.qwen/plugins/qwenbot
|
||||
|
||||
# Reinstall if needed
|
||||
cd ~/.qwen/plugins/qwenbot
|
||||
npm install
|
||||
```
|
||||
|
||||
### Issue: "Rate limit exceeded"
|
||||
|
||||
**Solution:**
|
||||
- Wait a few minutes before retrying
|
||||
- Upgrade your Qwen API plan
|
||||
- Reduce request frequency
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- **Qwen Platform:** https://platform.qwen.ai/
|
||||
- **Qwen Documentation:** https://help.qwen.ai/
|
||||
- **QwenBot GitHub:** https://github.com/QwenLM/qwenbot
|
||||
- **API Reference:** https://platform.qwen.ai/docs/api
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See LICENSE file for details.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"lastUpdated": "2026-02-26",
|
||||
"totalSkills": 76,
|
||||
"totalSkills": 77,
|
||||
"sources": [
|
||||
{
|
||||
"name": "awesome-claude-skills",
|
||||
@@ -43,6 +43,12 @@
|
||||
"url": "https://github.com/shadcn/ui",
|
||||
"skillsCount": 1,
|
||||
"note": "shadcn/ui design patterns and component generation with React, Tailwind CSS, and Radix UI"
|
||||
},
|
||||
{
|
||||
"name": "qwenbot-integration",
|
||||
"url": "https://platform.qwen.ai/",
|
||||
"skillsCount": 1,
|
||||
"note": "QwenBot AI assistant tool for enhanced code analysis, documentation, and chat capabilities"
|
||||
}
|
||||
],
|
||||
"skills": [
|
||||
|
||||
222
src/tools/qwenbot.ts
Normal file
222
src/tools/qwenbot.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* QwenBot Tool for Qwen Code
|
||||
*
|
||||
* Provides AI assistant capabilities through Qwen API
|
||||
*/
|
||||
|
||||
import { writeFile, mkdir } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
const QWENBOT_CONFIG_DIR = join(process.env.HOME || process.env.USERPROFILE || "", ".qwen", "qwenbot");
|
||||
const QWENBOT_CONFIG_FILE = join(QWENBOT_CONFIG_DIR, "config.json");
|
||||
|
||||
export interface QwenBotConfig {
|
||||
apiKey: string;
|
||||
model: string;
|
||||
temperature: number;
|
||||
maxTokens: number;
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: QwenBotConfig = {
|
||||
apiKey: "",
|
||||
model: "qwen-plus",
|
||||
temperature: 0.7,
|
||||
maxTokens: 2048,
|
||||
baseUrl: "https://api.qwen.ai/v1",
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize QwenBot configuration
|
||||
*/
|
||||
export async function initQwenBot(): Promise<QwenBotConfig> {
|
||||
// Create config directory if it doesn't exist
|
||||
if (!existsSync(QWENBOT_CONFIG_DIR)) {
|
||||
await mkdir(QWENBOT_CONFIG_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Load or create config
|
||||
let config: QwenBotConfig;
|
||||
|
||||
if (existsSync(QWENBOT_CONFIG_FILE)) {
|
||||
try {
|
||||
const content = await Bun.file(QWENBOT_CONFIG_FILE).text();
|
||||
config = { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
||||
} catch {
|
||||
config = DEFAULT_CONFIG;
|
||||
}
|
||||
} else {
|
||||
config = DEFAULT_CONFIG;
|
||||
await saveConfig(config);
|
||||
}
|
||||
|
||||
// Override with environment variables if set
|
||||
if (process.env.QWENBOT_API_KEY) {
|
||||
config.apiKey = process.env.QWENBOT_API_KEY;
|
||||
}
|
||||
if (process.env.QWENBOT_MODEL) {
|
||||
config.model = process.env.QWENBOT_MODEL;
|
||||
}
|
||||
if (process.env.QWENBOT_BASE_URL) {
|
||||
config.baseUrl = process.env.QWENBOT_BASE_URL;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save QwenBot configuration
|
||||
*/
|
||||
async function saveConfig(config: QwenBotConfig): Promise<void> {
|
||||
await writeFile(QWENBOT_CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Chat with QwenBot
|
||||
*/
|
||||
export async function chatWithQwenBot(
|
||||
message: string,
|
||||
config?: Partial<QwenBotConfig>
|
||||
): Promise<string> {
|
||||
const fullConfig = await initQwenBot();
|
||||
const mergedConfig = { ...fullConfig, ...config };
|
||||
|
||||
if (!mergedConfig.apiKey) {
|
||||
return "[ERROR] QwenBot API key not configured. Please set QWENBOT_API_KEY or configure config.json";
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${mergedConfig.baseUrl}/chat/completions`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${mergedConfig.apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: mergedConfig.model,
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "You are QwenBot, a helpful AI assistant integrated with Qwen Code.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: message,
|
||||
},
|
||||
],
|
||||
temperature: mergedConfig.temperature,
|
||||
max_tokens: mergedConfig.maxTokens,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
return `[ERROR] QwenBot API error: ${response.status} - ${error}`;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.choices?.[0]?.message?.content || "[ERROR] No response from QwenBot";
|
||||
} catch (err) {
|
||||
return `[ERROR] QwenBot request failed: ${err instanceof Error ? err.message : String(err)}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze code with QwenBot
|
||||
*/
|
||||
export async function analyzeCodeWithQwenBot(
|
||||
code: string,
|
||||
language?: string
|
||||
): Promise<string> {
|
||||
const message = language
|
||||
? `Analyze this ${language} code and provide feedback:\n\n\`\`\`${language}\n${code}\n\`\`\``
|
||||
: `Analyze this code and provide feedback:\n\n\`\`\`\n${code}\n\`\`\``;
|
||||
|
||||
return await chatWithQwenBot(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate documentation with QwenBot
|
||||
*/
|
||||
export async function generateDocsWithQwenBot(
|
||||
code: string,
|
||||
style?: string
|
||||
): Promise<string> {
|
||||
const message = style
|
||||
? `Generate ${style} style documentation for this code:\n\n\`\`\`\n${code}\n\`\`\``
|
||||
: `Generate documentation for this code:\n\n\`\`\`\n${code}\n\`\`\``;
|
||||
|
||||
return await chatWithQwenBot(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tests with QwenBot
|
||||
*/
|
||||
export async function generateTestsWithQwenBot(
|
||||
code: string,
|
||||
framework?: string
|
||||
): Promise<string> {
|
||||
const message = framework
|
||||
? `Generate ${framework} tests for this code:\n\n\`\`\`\n${code}\n\`\`\``
|
||||
: `Generate unit tests for this code:\n\n\`\`\`\n${code}\n\`\`\``;
|
||||
|
||||
return await chatWithQwenBot(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explain code with QwenBot
|
||||
*/
|
||||
export async function explainCodeWithQwenBot(
|
||||
code: string
|
||||
): Promise<string> {
|
||||
const message = `Explain what this code does in simple terms:\n\n\`\`\`\n${code}\n\`\`\``;
|
||||
|
||||
return await chatWithQwenBot(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick command for Qwen Code integration
|
||||
*/
|
||||
export async function qwenbotCommand(args: string[]): Promise<void> {
|
||||
const message = args.join(" ");
|
||||
|
||||
if (!message) {
|
||||
console.log("Usage: qwenbot <message>");
|
||||
console.log("");
|
||||
console.log("Examples:");
|
||||
console.log(" qwenbot Explain async/await in JavaScript");
|
||||
console.log(" qwenbot Review this code for bugs");
|
||||
console.log(" qwenbot Generate tests for this function");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("🤖 QwenBot: Thinking...");
|
||||
|
||||
const response = await chatWithQwenBot(message);
|
||||
|
||||
console.log("\n🤖 QwenBot: " + response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup QwenBot configuration interactively
|
||||
*/
|
||||
export async function setupQwenBot(): Promise<void> {
|
||||
console.log("🤖 QwenBot Setup");
|
||||
console.log("================");
|
||||
console.log("");
|
||||
console.log("To get your API key, visit: https://platform.qwen.ai/api-keys");
|
||||
console.log("");
|
||||
|
||||
const config = await initQwenBot();
|
||||
|
||||
console.log("Current configuration:");
|
||||
console.log(` Model: ${config.model}`);
|
||||
console.log(` Base URL: ${config.baseUrl}`);
|
||||
console.log(` Temperature: ${config.temperature}`);
|
||||
console.log(` Max Tokens: ${config.maxTokens}`);
|
||||
console.log(` API Key: ${config.apiKey ? "****" + config.apiKey.slice(-4) : "Not set"}`);
|
||||
console.log("");
|
||||
console.log("To configure, edit: " + QWENBOT_CONFIG_FILE);
|
||||
console.log("Or set environment variable: QWENBOT_API_KEY");
|
||||
}
|
||||
Reference in New Issue
Block a user