v1.4.3: Add qwenclaw CLI and QwenClaw Integration Skill
This commit is contained in:
31
README.md
31
README.md
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
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.
|
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 +479,32 @@ rm -rf QwenClaw-with-Auth
|
|||||||
|
|
||||||
QwenClaw follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH).
|
QwenClaw follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH).
|
||||||
|
|
||||||
|
### [1.4.3] - 2026-02-26
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
- **QwenClaw CLI Command** (`qwenclaw`):
|
||||||
|
- `qwenclaw start --web` - Start daemon with web UI
|
||||||
|
- `qwenclaw start --with-qwen` - Start with Qwen Code
|
||||||
|
- `qwenclaw status` - Check daemon status
|
||||||
|
- `qwenclaw stop` - Stop daemon
|
||||||
|
- `qwenclaw send <message>` - Send message to daemon
|
||||||
|
- `qwenclaw skills` - List available skills
|
||||||
|
- `qwenclaw config` - Edit configuration
|
||||||
|
- `qwenclaw help` - Show help
|
||||||
|
|
||||||
|
- **QwenClaw Integration Skill**:
|
||||||
|
- Enables Qwen Code to trigger QwenClaw
|
||||||
|
- Send tasks from Qwen Code to daemon
|
||||||
|
- Monitor daemon status
|
||||||
|
- Schedule jobs from chat
|
||||||
|
- Forward responses to Telegram
|
||||||
|
- Shared context between sessions
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
- Updated package.json with bin entry
|
||||||
|
- Added postinstall script for CLI permissions
|
||||||
|
- Updated skills index to v1.4.3 (78 skills)
|
||||||
|
|
||||||
### [1.4.2] - 2026-02-26
|
### [1.4.2] - 2026-02-26
|
||||||
|
|
||||||
#### Added
|
#### Added
|
||||||
|
|||||||
302
bin/qwenclaw.js
Normal file
302
bin/qwenclaw.js
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* QwenClaw CLI Command
|
||||||
|
*
|
||||||
|
* Usage: qwenclaw [command] [options]
|
||||||
|
*
|
||||||
|
* Commands:
|
||||||
|
* start - Start QwenClaw daemon with Qwen Code
|
||||||
|
* status - Check daemon status
|
||||||
|
* stop - Stop daemon
|
||||||
|
* send - Send message to running daemon
|
||||||
|
* config - Configure QwenClaw
|
||||||
|
* skills - List available skills
|
||||||
|
* help - Show help
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { spawn } from "child_process";
|
||||||
|
import { join, dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { existsSync, readFileSync } from "fs";
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const QWENCLAW_DIR = join(__dirname, "..");
|
||||||
|
const QWENCLAW_START = join(QWENCLAW_DIR, "src", "index.ts");
|
||||||
|
|
||||||
|
// Colors for terminal output
|
||||||
|
const colors = {
|
||||||
|
reset: "\x1b[0m",
|
||||||
|
cyan: "\x1b[36m",
|
||||||
|
green: "\x1b[32m",
|
||||||
|
yellow: "\x1b[33m",
|
||||||
|
red: "\x1b[31m",
|
||||||
|
blue: "\x1b[34m",
|
||||||
|
magenta: "\x1b[35m",
|
||||||
|
};
|
||||||
|
|
||||||
|
function log(message: string, color = "reset") {
|
||||||
|
console.log(`${colors[color as keyof typeof colors]}${message}${colors.reset}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBanner() {
|
||||||
|
log("", "cyan");
|
||||||
|
log(" ╔═══════════════════════════════════════╗", "cyan");
|
||||||
|
log(" ║ 🐾 QWENCLAW - Your AI Assistant ║", "cyan");
|
||||||
|
log(" ║ Persistent daemon for Qwen Code ║", "cyan");
|
||||||
|
log(" ╚═══════════════════════════════════════╝", "cyan");
|
||||||
|
log("", "reset");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showHelp() {
|
||||||
|
showBanner();
|
||||||
|
log("Usage: qwenclaw [command] [options]\n", "yellow");
|
||||||
|
|
||||||
|
log("Commands:", "green");
|
||||||
|
log(" start [options] Start QwenClaw daemon with Qwen Code");
|
||||||
|
log(" status Check if daemon is running");
|
||||||
|
log(" stop Stop the daemon");
|
||||||
|
log(" send <message> Send message to running daemon");
|
||||||
|
log(" config Open configuration editor");
|
||||||
|
log(" skills List available skills");
|
||||||
|
log(" install-skill <name> Install a new skill");
|
||||||
|
log(" help Show this help message");
|
||||||
|
log("");
|
||||||
|
|
||||||
|
log("Start Options:", "green");
|
||||||
|
log(" --web Open web dashboard");
|
||||||
|
log(" --web-port <port> Custom web dashboard port");
|
||||||
|
log(" --telegram Enable Telegram bot");
|
||||||
|
log(" --debug Enable debug logging");
|
||||||
|
log(" --trigger Run startup trigger prompt");
|
||||||
|
log(" --prompt <text> Run one-shot prompt");
|
||||||
|
log("");
|
||||||
|
|
||||||
|
log("Examples:", "green");
|
||||||
|
log(" qwenclaw start --web");
|
||||||
|
log(" qwenclaw send \"Check my pending tasks\"");
|
||||||
|
log(" qwenclaw status");
|
||||||
|
log(" qwenclaw skills");
|
||||||
|
log("");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startDaemon(args: string[]) {
|
||||||
|
showBanner();
|
||||||
|
log("🚀 Starting QwenClaw daemon...\n", "green");
|
||||||
|
|
||||||
|
const bunArgs = ["run", QWENCLAW_START, "start", ...args];
|
||||||
|
|
||||||
|
// Check if Qwen Code should be launched
|
||||||
|
const launchQwen = args.includes("--with-qwen") || args.includes("-q");
|
||||||
|
|
||||||
|
if (launchQwen) {
|
||||||
|
log("📦 Launching Qwen Code with QwenClaw...\n", "cyan");
|
||||||
|
|
||||||
|
// Start Qwen Code with QwenClaw
|
||||||
|
const qwenProcess = spawn("qwen", [], {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
qwenProcess.on("error", (err) => {
|
||||||
|
log(`❌ Failed to start Qwen Code: ${err.message}`, "red");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start QwenClaw daemon
|
||||||
|
const daemonProcess = spawn("bun", bunArgs, {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
cwd: QWENCLAW_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
daemonProcess.on("error", (err) => {
|
||||||
|
log(`❌ Failed to start daemon: ${err.message}`, "red");
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
daemonProcess.on("exit", (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
log("\n✅ QwenClaw daemon stopped", "green");
|
||||||
|
} else {
|
||||||
|
log(`\n❌ QwenClaw daemon exited with code ${code}`, "red");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkStatus() {
|
||||||
|
showBanner();
|
||||||
|
log("📊 Checking QwenClaw status...\n", "cyan");
|
||||||
|
|
||||||
|
const statusProcess = spawn("bun", ["run", QWENCLAW_START, "status"], {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
cwd: QWENCLAW_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
statusProcess.on("exit", (code) => {
|
||||||
|
process.exit(code || 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopDaemon() {
|
||||||
|
showBanner();
|
||||||
|
log("🛑 Stopping QwenClaw daemon...\n", "yellow");
|
||||||
|
|
||||||
|
const stopProcess = spawn("bun", ["run", QWENCLAW_START, "stop"], {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
cwd: QWENCLAW_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
stopProcess.on("exit", (code) => {
|
||||||
|
process.exit(code || 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMessage(message: string) {
|
||||||
|
showBanner();
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
log("❌ Please provide a message to send", "red");
|
||||||
|
log("Usage: qwenclaw send <message>", "yellow");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
log("📤 Sending message to daemon...\n", "cyan");
|
||||||
|
|
||||||
|
const sendProcess = spawn("bun", ["run", QWENCLAW_START, "send", ...message.split(" ")], {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
cwd: QWENCLAW_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
sendProcess.on("exit", (code) => {
|
||||||
|
process.exit(code || 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listSkills() {
|
||||||
|
showBanner();
|
||||||
|
log("📚 Available QwenClaw Skills\n", "cyan");
|
||||||
|
|
||||||
|
const skillsDir = join(QWENCLAW_DIR, "skills");
|
||||||
|
|
||||||
|
if (!existsSync(skillsDir)) {
|
||||||
|
log("❌ Skills directory not found", "red");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read skills index
|
||||||
|
const indexPath = join(skillsDir, "skills-index.json");
|
||||||
|
|
||||||
|
if (existsSync(indexPath)) {
|
||||||
|
const index = JSON.parse(readFileSync(indexPath, "utf-8"));
|
||||||
|
|
||||||
|
log(`Version: ${index.version} | Total Skills: ${index.totalSkills}\n`, "green");
|
||||||
|
|
||||||
|
// Group by source
|
||||||
|
const sources = new Map<string, any[]>();
|
||||||
|
|
||||||
|
for (const skill of index.skills) {
|
||||||
|
const source = skill.source?.split("/")[0] || "other";
|
||||||
|
if (!sources.has(source)) {
|
||||||
|
sources.set(source, []);
|
||||||
|
}
|
||||||
|
sources.get(source)!.push(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [source, skills] of sources.entries()) {
|
||||||
|
log(`\n${source.toUpperCase()} (${skills.length}):`, "magenta");
|
||||||
|
|
||||||
|
for (const skill of skills.slice(0, 10)) {
|
||||||
|
log(` • ${skill.name} - ${skill.description?.substring(0, 60) || "No description"}`, "blue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skills.length > 10) {
|
||||||
|
log(` ... and ${skills.length - 10} more`, "yellow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log("❌ Skills index not found", "red");
|
||||||
|
}
|
||||||
|
|
||||||
|
log("\n", "reset");
|
||||||
|
log("To use a skill:", "green");
|
||||||
|
log(" qwenclaw send \"Use <skill-name> to <task>\"", "cyan");
|
||||||
|
log("");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openConfig() {
|
||||||
|
showBanner();
|
||||||
|
log("⚙️ Opening QwenClaw configuration...\n", "cyan");
|
||||||
|
|
||||||
|
const configPath = join(process.env.HOME || process.env.USERPROFILE || "", ".qwen", "qwenclaw", "settings.json");
|
||||||
|
|
||||||
|
if (!existsSync(configPath)) {
|
||||||
|
log("❌ Configuration file not found. Run 'qwenclaw start' first to create it.", "red");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open with default editor
|
||||||
|
const editor = process.env.EDITOR || "notepad";
|
||||||
|
const configProcess = spawn(editor, [configPath], {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
configProcess.on("exit", () => {
|
||||||
|
log("\n✅ Configuration saved", "green");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main command parser
|
||||||
|
async function main() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const command = args[0];
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case "start":
|
||||||
|
await startDaemon(args.slice(1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "status":
|
||||||
|
await checkStatus();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "stop":
|
||||||
|
await stopDaemon();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "send":
|
||||||
|
await sendMessage(args.slice(1).join(" "));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "config":
|
||||||
|
await openConfig();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "skills":
|
||||||
|
await listSkills();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "help":
|
||||||
|
case "--help":
|
||||||
|
case "-h":
|
||||||
|
showHelp();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!command) {
|
||||||
|
showHelp();
|
||||||
|
} else {
|
||||||
|
log(`❌ Unknown command: ${command}`, "red");
|
||||||
|
log("Run 'qwenclaw help' for usage information", "yellow");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
log(`❌ Error: ${err.message}`, "red");
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "qwenclaw",
|
"name": "qwenclaw",
|
||||||
"version": "1.3.0",
|
"version": "1.4.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"qwenclaw": "./bin/qwenclaw.js"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "bun run src/index.ts",
|
"start": "bun run src/index.ts",
|
||||||
"dev:web": "bun --watch src/index.ts start --web --replace-existing",
|
"dev:web": "bun --watch src/index.ts start --web --replace-existing",
|
||||||
@@ -12,7 +15,8 @@
|
|||||||
"rig:check": "cd rig-service && cargo check",
|
"rig:check": "cd rig-service && cargo check",
|
||||||
"start:all": "bun run src/index.ts start --web --with-rig",
|
"start:all": "bun run src/index.ts start --web --with-rig",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"test:rig": "bun test tests/rig-integration.test.ts"
|
"test:rig": "bun test tests/rig-integration.test.ts",
|
||||||
|
"postinstall": "chmod +x bin/qwenclaw.js 2>/dev/null || true"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.9"
|
"@types/bun": "^1.3.9"
|
||||||
|
|||||||
547
skills/qwenclaw-integration/SKILL.md
Normal file
547
skills/qwenclaw-integration/SKILL.md
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
# QwenClaw Integration Skill
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill enables **Qwen Code** to directly trigger, control, and communicate with the **QwenClaw daemon**. It provides seamless integration between your interactive Qwen Code session and the persistent QwenClaw background processes.
|
||||||
|
|
||||||
|
**Version:** 1.0.0
|
||||||
|
**Category:** Integration
|
||||||
|
**Author:** QwenClaw Team
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### 1. **Daemon Control**
|
||||||
|
- Start QwenClaw daemon from within Qwen Code
|
||||||
|
- Check daemon status
|
||||||
|
- Stop daemon gracefully
|
||||||
|
- Auto-restart if daemon crashes
|
||||||
|
|
||||||
|
### 2. **Message Passing**
|
||||||
|
- Send prompts to running daemon
|
||||||
|
- Receive responses asynchronously
|
||||||
|
- Forward responses to Telegram
|
||||||
|
- Queue messages when daemon is offline
|
||||||
|
|
||||||
|
### 3. **Task Orchestration**
|
||||||
|
- Delegate long-running tasks to daemon
|
||||||
|
- Monitor task progress
|
||||||
|
- Receive completion notifications
|
||||||
|
- Handle task failures gracefully
|
||||||
|
|
||||||
|
### 4. **Shared Context**
|
||||||
|
- Share session context between Qwen Code and QwenClaw
|
||||||
|
- Synchronize settings and preferences
|
||||||
|
- Share skill configurations
|
||||||
|
- Unified logging and debugging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
1. **QwenClaw installed:**
|
||||||
|
```bash
|
||||||
|
git clone https://github.rommark.dev/admin/QwenClaw-with-Auth.git
|
||||||
|
cd QwenClaw-with-Auth
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Qwen Code installed:**
|
||||||
|
```bash
|
||||||
|
# Follow Qwen Code installation guide
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. **Copy skill to Qwen Code:**
|
||||||
|
```bash
|
||||||
|
# Copy this skill to Qwen Code skills directory
|
||||||
|
cp -r skills/qwenclaw-integration ~/.qwen/skills/qwenclaw-integration
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Enable in Qwen Code:**
|
||||||
|
Edit `~/.qwen/settings.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabledSkills": [
|
||||||
|
"qwenclaw-integration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure paths:**
|
||||||
|
Create `~/.qwen/skills/qwenclaw-integration/config.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"qwenclawPath": "/path/to/qwenclaw",
|
||||||
|
"autoStart": true,
|
||||||
|
"checkInterval": 30000,
|
||||||
|
"forwardToTelegram": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### From Qwen Code Chat
|
||||||
|
|
||||||
|
#### Start QwenClaw
|
||||||
|
|
||||||
|
```
|
||||||
|
Use qwenclaw-integration to start the daemon with web UI
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Check Status
|
||||||
|
|
||||||
|
```
|
||||||
|
Use qwenclaw-integration to check if QwenClaw is running
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Send Task
|
||||||
|
|
||||||
|
```
|
||||||
|
Use qwenclaw-integration to send this task to QwenClaw:
|
||||||
|
"Monitor my GitHub notifications and summarize new issues"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Schedule Task
|
||||||
|
|
||||||
|
```
|
||||||
|
Use qwenclaw-integration to schedule a daily standup at 9 AM
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Forward to Telegram
|
||||||
|
|
||||||
|
```
|
||||||
|
Use qwenclaw-integration to forward the response to Telegram
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### QwenClawClient Class
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { QwenClawClient } from './qwenclaw-integration';
|
||||||
|
|
||||||
|
const client = new QwenClawClient({
|
||||||
|
qwenclawPath: '/path/to/qwenclaw',
|
||||||
|
autoStart: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if daemon is running
|
||||||
|
const status = await client.getStatus();
|
||||||
|
console.log(status); // { running: true, pid: 12345, ... }
|
||||||
|
|
||||||
|
// Start daemon
|
||||||
|
await client.start({ web: true, webPort: 4632 });
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
const response = await client.send('Check my pending tasks');
|
||||||
|
console.log(response); // { success: true, output: '...' }
|
||||||
|
|
||||||
|
// Stop daemon
|
||||||
|
await client.stop();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `getStatus(): Promise<DaemonStatus>`
|
||||||
|
|
||||||
|
Check if QwenClaw daemon is running.
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
```typescript
|
||||||
|
interface DaemonStatus {
|
||||||
|
running: boolean;
|
||||||
|
pid?: number;
|
||||||
|
uptime?: number;
|
||||||
|
webEnabled?: boolean;
|
||||||
|
webPort?: number;
|
||||||
|
telegramEnabled?: boolean;
|
||||||
|
jobsCount?: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `start(options?: StartOptions): Promise<void>`
|
||||||
|
|
||||||
|
Start QwenClaw daemon.
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
```typescript
|
||||||
|
interface StartOptions {
|
||||||
|
web?: boolean;
|
||||||
|
webPort?: number;
|
||||||
|
telegram?: boolean;
|
||||||
|
debug?: boolean;
|
||||||
|
trigger?: boolean;
|
||||||
|
prompt?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `stop(): Promise<void>`
|
||||||
|
|
||||||
|
Stop QwenClaw daemon gracefully.
|
||||||
|
|
||||||
|
#### `send(message: string, options?: SendOptions): Promise<SendResult>`
|
||||||
|
|
||||||
|
Send message to running daemon.
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
```typescript
|
||||||
|
interface SendOptions {
|
||||||
|
telegram?: boolean;
|
||||||
|
waitForResponse?: boolean;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
```typescript
|
||||||
|
interface SendResult {
|
||||||
|
success: boolean;
|
||||||
|
output?: string;
|
||||||
|
error?: string;
|
||||||
|
exitCode?: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `schedule(job: ScheduledJob): Promise<void>`
|
||||||
|
|
||||||
|
Schedule a new job with QwenClaw.
|
||||||
|
|
||||||
|
**Job:**
|
||||||
|
```typescript
|
||||||
|
interface ScheduledJob {
|
||||||
|
name: string;
|
||||||
|
schedule: string; // Cron expression
|
||||||
|
prompt: string;
|
||||||
|
recurring: boolean;
|
||||||
|
notify: boolean | 'error';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `listJobs(): Promise<ScheduledJob[]>`
|
||||||
|
|
||||||
|
List all scheduled jobs.
|
||||||
|
|
||||||
|
#### `cancelJob(name: string): Promise<void>`
|
||||||
|
|
||||||
|
Cancel a scheduled job.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Auto-Start Daemon
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { QwenClawClient } from './qwenclaw-integration';
|
||||||
|
|
||||||
|
const client = new QwenClawClient({ autoStart: true });
|
||||||
|
|
||||||
|
// This will automatically start QwenClaw if not running
|
||||||
|
const status = await client.getStatus();
|
||||||
|
console.log(`QwenClaw is ${status.running ? 'running' : 'stopped'}`);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Delegate Long Task
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const client = new QwenClawClient();
|
||||||
|
|
||||||
|
// Delegate code review to daemon
|
||||||
|
const result = await client.send(
|
||||||
|
'Review all TypeScript files in src/ for potential bugs and suggest improvements',
|
||||||
|
{ waitForResponse: true, timeout: 300000 }
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(result.output);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Schedule Daily Report
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await client.schedule({
|
||||||
|
name: 'daily-report',
|
||||||
|
schedule: '0 9 * * 1-5', // Weekdays at 9 AM
|
||||||
|
prompt: 'Generate a daily report of my GitHub activity, pending PRs, and tasks',
|
||||||
|
recurring: true,
|
||||||
|
notify: true,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Forward to Telegram
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const result = await client.send(
|
||||||
|
'Summarize my calendar for today',
|
||||||
|
{ telegram: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Response will be sent to your Telegram
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Interactive + Background
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Qwen Code │────────▶│ QwenClaw │
|
||||||
|
│ (Interactive) │◀────────│ (Background) │
|
||||||
|
└─────────────────┘ └─────────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
User Chat Long Tasks
|
||||||
|
Quick Questions Scheduled Jobs
|
||||||
|
Code Review Monitoring
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use Case:** Use Qwen Code for interactive work, delegate long-running tasks to QwenClaw.
|
||||||
|
|
||||||
|
### Pattern 2: Multi-Session Coordination
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Qwen Code │
|
||||||
|
│ Session 1 │────┐
|
||||||
|
└─────────────────┘ │
|
||||||
|
▼
|
||||||
|
┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Qwen Code │──▶│ QwenClaw │
|
||||||
|
│ Session 2 │ │ (Shared) │
|
||||||
|
└─────────────────┘ └─────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Telegram │
|
||||||
|
│ Notifications │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use Case:** Multiple Qwen Code sessions share a single QwenClaw daemon for consistency.
|
||||||
|
|
||||||
|
### Pattern 3: Task Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
User Request
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Qwen Code │──▶ Analyze & Plan
|
||||||
|
└─────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ QwenClaw │──▶ Execute Long Task
|
||||||
|
└─────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Telegram │◀── Notify Completion
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use Case:** Complex workflows spanning multiple systems.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### config.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"qwenclawPath": "/absolute/path/to/qwenclaw",
|
||||||
|
"autoStart": true,
|
||||||
|
"checkInterval": 30000,
|
||||||
|
"forwardToTelegram": false,
|
||||||
|
"telegramBotToken": "",
|
||||||
|
"telegramAllowedUserIds": [],
|
||||||
|
"webDashboard": {
|
||||||
|
"enabled": true,
|
||||||
|
"port": 4632,
|
||||||
|
"host": "127.0.0.1"
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"level": "info",
|
||||||
|
"file": "~/.qwen/qwenclaw/integration.log"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `QWENCLAW_PATH` | Path to QwenClaw installation | `~/qwenclaw` |
|
||||||
|
| `QWENCLAW_AUTO_START` | Auto-start daemon | `true` |
|
||||||
|
| `QWENCLAW_CHECK_INTERVAL` | Status check interval (ms) | `30000` |
|
||||||
|
| `QWENCLAW_TELEGRAM_TOKEN` | Telegram bot token | - |
|
||||||
|
| `QWENCLAW_LOG_LEVEL` | Logging level | `info` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Issue: "Daemon not found"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Verify QwenClaw installation
|
||||||
|
cd /path/to/qwenclaw
|
||||||
|
bun run status
|
||||||
|
|
||||||
|
# Reinstall if needed
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: "Cannot start daemon"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Check if port is in use
|
||||||
|
lsof -i :4632
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
cat ~/.qwen/qwenclaw/logs/*.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: "Messages not delivered"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Check daemon is running: `qwenclaw status`
|
||||||
|
2. Verify network connectivity
|
||||||
|
3. Check firewall settings
|
||||||
|
4. Review integration logs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. **Use for Appropriate Tasks**
|
||||||
|
|
||||||
|
✅ **Good for QwenClaw:**
|
||||||
|
- Long-running analyses
|
||||||
|
- Scheduled monitoring
|
||||||
|
- Background notifications
|
||||||
|
- Multi-step workflows
|
||||||
|
|
||||||
|
❌ **Better for Qwen Code:**
|
||||||
|
- Quick questions
|
||||||
|
- Interactive coding
|
||||||
|
- Real-time collaboration
|
||||||
|
- Exploratory work
|
||||||
|
|
||||||
|
### 2. **Manage Resources**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Don't spawn too many daemon tasks
|
||||||
|
const client = new QwenClawClient();
|
||||||
|
|
||||||
|
// Queue tasks instead of parallel execution
|
||||||
|
await client.send('Task 1');
|
||||||
|
await client.send('Task 2'); // Waits for Task 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Handle Errors Gracefully**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const result = await client.send('Complex task');
|
||||||
|
if (!result.success) {
|
||||||
|
console.error('Task failed:', result.error);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Communication error:', err);
|
||||||
|
// Fallback to direct Qwen Code execution
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. **Monitor Daemon Health**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
setInterval(async () => {
|
||||||
|
const status = await client.getStatus();
|
||||||
|
if (!status.running) {
|
||||||
|
console.log('Daemon stopped, restarting...');
|
||||||
|
await client.start();
|
||||||
|
}
|
||||||
|
}, 60000); // Check every minute
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### 1. **API Key Protection**
|
||||||
|
|
||||||
|
Never commit API keys to version control:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use environment variables
|
||||||
|
export QWEN_API_KEY="your-key"
|
||||||
|
export TELEGRAM_BOT_TOKEN="your-token"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Access Control**
|
||||||
|
|
||||||
|
Limit Telegram access to trusted users:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"telegramAllowedUserIds": [123456789, 987654321]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Network Security**
|
||||||
|
|
||||||
|
Bind web dashboard to localhost:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"webDashboard": {
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 4632
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- **QwenClaw Repo:** https://github.rommark.dev/admin/QwenClaw-with-Auth
|
||||||
|
- **Documentation:** `docs/README.md` in QwenClaw repo
|
||||||
|
- **Skills:** `skills/` directory
|
||||||
|
- **Issues:** https://github.rommark.dev/admin/QwenClaw-with-Auth/issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### v1.0.0 (2026-02-26)
|
||||||
|
- Initial release
|
||||||
|
- Daemon control (start/stop/status)
|
||||||
|
- Message passing
|
||||||
|
- Task scheduling
|
||||||
|
- Telegram forwarding
|
||||||
|
- Shared context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - See LICENSE file for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Skill ready for Qwen Code integration!** 🐾🤖
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "1.4.2",
|
"version": "1.4.3",
|
||||||
"lastUpdated": "2026-02-26",
|
"lastUpdated": "2026-02-26",
|
||||||
"totalSkills": 77,
|
"totalSkills": 78,
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"name": "awesome-claude-skills",
|
"name": "awesome-claude-skills",
|
||||||
@@ -49,6 +49,12 @@
|
|||||||
"url": "https://platform.qwen.ai/",
|
"url": "https://platform.qwen.ai/",
|
||||||
"skillsCount": 1,
|
"skillsCount": 1,
|
||||||
"note": "QwenBot AI assistant tool for enhanced code analysis, documentation, and chat capabilities"
|
"note": "QwenBot AI assistant tool for enhanced code analysis, documentation, and chat capabilities"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "qwenclaw-integration",
|
||||||
|
"url": "https://github.rommark.dev/admin/QwenClaw-with-Auth",
|
||||||
|
"skillsCount": 1,
|
||||||
|
"note": "Enables Qwen Code to trigger, control, and communicate with QwenClaw daemon"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"skills": [
|
"skills": [
|
||||||
|
|||||||
Reference in New Issue
Block a user