diff --git a/.agent/agent_hub.db b/.agent/agent_hub.db deleted file mode 100644 index d936463..0000000 Binary files a/.agent/agent_hub.db and /dev/null differ diff --git a/.agent/rules/agent_protocol.md b/.agent/rules/agent_protocol.md deleted file mode 100644 index 6b0e983..0000000 --- a/.agent/rules/agent_protocol.md +++ /dev/null @@ -1,29 +0,0 @@ -# Agent Coordination Protocol (FOR AGENTS ONLY) - -## 🛡️ The Golden Rule -**NEVER modify code without verifying the lock status in the Agent Hub.** - -## 🔑 Identity Management -- `claude-code`: Official Claude CLI -- `copilot-agent`: GitHub Copilot -- `gemini-cursor`: Cursor IDE or Gemini extension -- `iflow-agent`: iFlow SDK agent - -## 🛠️ The Synchronization Tool -Script: `scripts/agent_sync.py` (SQLite-backed) - -### 🏎️ Workflow Lifecycle -1. **Initialize Session**: - - `python3 scripts/agent_sync.py status` - - `python3 scripts/agent_sync.py register ""` -2. **Resource Acquisition**: - - `python3 scripts/agent_sync.py lock ` - - If blocked, identify the owner from `status` and do not attempt to bypass. -3. **Collaboration (Research Mode)**: - - If the project mode is `RESEARCH`, prioritize the `note` command. - - Summarize findings: `python3 scripts/agent_sync.py note "" ""` -4. **Cleanup**: - - `python3 scripts/agent_sync.py unlock ` - -## 📜 Shared Memory -Read `.agent/learnings/` to avoid reinventing the wheel. diff --git a/.github/agents/plugin-planner.agent.md b/.github/agents/plugin-planner.agent.md index 69e9c8f..8d712a8 100644 --- a/.github/agents/plugin-planner.agent.md +++ b/.github/agents/plugin-planner.agent.md @@ -3,7 +3,6 @@ name: Plugin Planner description: Analyze requirements and produce a safe implementation plan for OpenWebUI plugins argument-hint: Describe the plugin goal, constraints, and target files tools: ['read/readFile', 'search', 'web', 'web/githubRepo', 'read/terminalLastCommand', 'read/terminalSelection', 'agent'] -infer: true handoffs: - label: Start Implementation agent: Plugin Implementer diff --git a/CLAUDE.md b/CLAUDE.md index b5b2924..cf28b43 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,13 +1,32 @@ -# 🤖 Claude Multi-Agent Protocol (MACP) - -## 🚀 Mandatory Startup -1. **Check Hub**: `python3 scripts/agent_sync.py status` -2. **Register**: `python3 scripts/agent_sync.py register claude-code "Claude" "Handling user request"` -3. **Lock**: `python3 scripts/agent_sync.py lock claude-code ` -4. **Handoff**: Use `python3 scripts/agent_sync.py note` for collaborative findings. +# 🤖 OpenWebUI Extensions ## 🤝 Project Standards Read these BEFORE writing any code: - `.agent/rules/plugin_standards.md` -- `.agent/rules/agent_protocol.md` -- `COOPERATION.md` + +## 📖 Development Guidelines + +### Writing Documentation +- All plugins MUST have both English (`README.md`) and Chinese (`README_CN.md`) documentation +- Follow the template at `docs/PLUGIN_README_TEMPLATE.md` +- Keep versions in sync across all files + +### Code Standards +- Use logging instead of print +- Implement Valves for configuration +- Use Lucide icons +- Include i18n support + +### Release Process +- Update version numbers in ALL relevant files (code, READMEs, docs) +- Use Conventional Commits format (`feat`, `fix`, `docs`, etc.) +- Create PRs using GitHub CLI (`gh pr create`) + +## Author + +Fu-Jie +GitHub: [Fu-Jie/openwebui-extensions](https://github.com/Fu-Jie/openwebui-extensions) + +## License + +MIT License diff --git a/COOPERATION.md b/COOPERATION.md deleted file mode 100644 index 240a75b..0000000 --- a/COOPERATION.md +++ /dev/null @@ -1,33 +0,0 @@ -# 🤖 Multi-Agent Cooperation Protocol (MACP) v2.1 - -本项目采用 **SQLite 协作中控 (Agent Hub)** 来管理多个 AI Agent 的并发任务。 - -## 🚀 核心指令 (Quick Commands) -使用 `./scripts/macp` 即可快速调用,无需记忆复杂的 Python 参数。 - -| 指令 | 描述 | -| :--- | :--- | -| **`/status`** | 查看全场状态(活跃 Agent、文件锁、任务、研究主题) | -| **`/study `** | **一键发起联合研究**。广播主题并通知所有 Agent 进入研究状态。 | -| **`/summon `** | **定向召唤**。给特定 Agent 派发高优先级任务。 | -| **`/handover `** | **任务接力**。释放当前进度并交棒给下一个 Agent。 | -| **`/broadcast `** | **全场广播**。发送紧急通知或状态同步。 | -| **`/check`** | **收件箱检查**。查看是否有分配给你的待办任务。 | -| **`/resolve `** | **归档结论**。结束研究专题并记录最终共识。 | -| **`/ping`** | **生存检查**。快速查看哪些 Agent 在线。 | - ---- - -## 🛡️ 协作准则 -1. **先查后动**:开始工作前先运行 `./scripts/macp /status`。 -2. **锁即所有权**:修改文件前必须获取锁。 -3. **意图先行**:大型重构建议先通过 `/study` 发起方案讨论。 -4. **及时解锁**:Commit 并 Push 后,请务必 `/handover` 或手动解锁。 - -## 📁 基础设施 -- **数据库**: `.agent/agent_hub.db` (不要手动编辑) -- **内核**: `scripts/agent_sync.py` -- **快捷工具**: `scripts/macp` - ---- -*Generated by Claude (Coordinator) in collaboration with Sisyphus & Copilot.* diff --git a/scripts/agent_sync_v2.py b/scripts/agent_sync_v2.py deleted file mode 100755 index a1f82c5..0000000 --- a/scripts/agent_sync_v2.py +++ /dev/null @@ -1,847 +0,0 @@ -#!/usr/bin/env python3 -""" -🤖 AGENT SYNC TOOL v2.0 - MULTI-AGENT COOPERATION PROTOCOL (MACP) ---------------------------------------------------------- -Enhanced collaboration commands for seamless multi-agent synergy. - -QUICK COMMANDS: - @research - Start a joint research topic - @join - Join an active research topic - @find - Post a finding to research topic - @consensus - Generate consensus document - @assign - Assign task to specific agent - @notify - Broadcast to all agents - @handover - Handover current task - @poll - Start a quick poll - @switch - Request switch to specific agent - -WORKFLOW: @research -> @find (xN) -> @consensus -> @assign -""" -import sqlite3 -import os -import sys -import argparse -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional - -DB_PATH = os.path.join(os.getcwd(), ".agent/agent_hub.db") - -def get_connection(): - os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) - return sqlite3.connect(DB_PATH) - -def init_db(): - conn = get_connection() - cursor = conn.cursor() - cursor.executescript(''' - CREATE TABLE IF NOT EXISTS agents ( - id TEXT PRIMARY KEY, - name TEXT, - task TEXT, - status TEXT DEFAULT 'idle', - current_research TEXT, - last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - CREATE TABLE IF NOT EXISTS file_locks ( - file_path TEXT PRIMARY KEY, - agent_id TEXT, - lock_type TEXT, - timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY(agent_id) REFERENCES agents(id) - ); - CREATE TABLE IF NOT EXISTS research_log ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - topic TEXT, - content TEXT, - finding_type TEXT DEFAULT 'note', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY(agent_id) REFERENCES agents(id) - ); - CREATE TABLE IF NOT EXISTS research_topics ( - topic TEXT PRIMARY KEY, - status TEXT DEFAULT 'active', - initiated_by TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - completed_at TIMESTAMP - ); - CREATE TABLE IF NOT EXISTS agent_research_participation ( - agent_id TEXT, - topic TEXT, - joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (agent_id, topic) - ); - CREATE TABLE IF NOT EXISTS task_assignments ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - task TEXT, - assigned_by TEXT, - status TEXT DEFAULT 'pending', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - completed_at TIMESTAMP - ); - CREATE TABLE IF NOT EXISTS notifications ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - message TEXT, - is_broadcast BOOLEAN DEFAULT 0, - is_read BOOLEAN DEFAULT 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - CREATE TABLE IF NOT EXISTS polls ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - question TEXT, - created_by TEXT, - status TEXT DEFAULT 'active', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - CREATE TABLE IF NOT EXISTS poll_votes ( - poll_id INTEGER, - agent_id TEXT, - vote TEXT, - voted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (poll_id, agent_id) - ); - CREATE TABLE IF NOT EXISTS global_settings ( - key TEXT PRIMARY KEY, - value TEXT - ); - ''') - cursor.execute("INSERT OR IGNORE INTO global_settings (key, value) VALUES ('mode', 'isolation')") - conn.commit() - conn.close() - print(f"✅ Agent Hub v2.0 initialized at {DB_PATH}") - -# ============ AGENT MANAGEMENT ============ - -def register_agent(agent_id, name, task, status="idle"): - conn = get_connection() - cursor = conn.cursor() - cursor.execute(''' - INSERT OR REPLACE INTO agents (id, name, task, status, last_seen) - VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) - ''', (agent_id, name, task, status)) - conn.commit() - conn.close() - print(f"🤖 Agent '{name}' ({agent_id}) registered.") - -def update_agent_status(agent_id, status, research_topic=None): - conn = get_connection() - cursor = conn.cursor() - if research_topic: - cursor.execute(''' - UPDATE agents SET status = ?, current_research = ?, last_seen = CURRENT_TIMESTAMP - WHERE id = ? - ''', (status, research_topic, agent_id)) - else: - cursor.execute(''' - UPDATE agents SET status = ?, last_seen = CURRENT_TIMESTAMP - WHERE id = ? - ''', (status, agent_id)) - conn.commit() - conn.close() - -# ============ RESEARCH COLLABORATION ============ - -def start_research(agent_id, topic): - """@research - Start a new research topic and notify all agents""" - conn = get_connection() - cursor = conn.cursor() - - # Create research topic - try: - cursor.execute(''' - INSERT INTO research_topics (topic, status, initiated_by) - VALUES (?, 'active', ?) - ''', (topic, agent_id)) - except sqlite3.IntegrityError: - print(f"⚠️ Research topic '{topic}' already exists") - conn.close() - return - - # Add initiator as participant - cursor.execute(''' - INSERT OR IGNORE INTO agent_research_participation (agent_id, topic) - VALUES (?, ?) - ''', (agent_id, topic)) - - # Update agent status - cursor.execute(''' - UPDATE agents SET status = 'researching', current_research = ? - WHERE id = ? - ''', (topic, agent_id)) - - # Notify all other agents - cursor.execute("SELECT id FROM agents WHERE id != ?", (agent_id,)) - other_agents = cursor.fetchall() - for (other_id,) in other_agents: - cursor.execute(''' - INSERT INTO notifications (agent_id, message, is_broadcast) - VALUES (?, ?, 0) - ''', (other_id, f"🔬 New research started: '{topic}' by {agent_id}. Use '@join {topic}' to participate.")) - - conn.commit() - conn.close() - - print(f"🔬 Research topic '{topic}' started by {agent_id}") - print(f"📢 Notified {len(other_agents)} other agents") - -def join_research(agent_id, topic): - """@join - Join an active research topic""" - conn = get_connection() - cursor = conn.cursor() - - # Check if topic exists and is active - cursor.execute("SELECT status FROM research_topics WHERE topic = ?", (topic,)) - result = cursor.fetchone() - if not result: - print(f"❌ Research topic '{topic}' not found") - conn.close() - return - if result[0] != 'active': - print(f"⚠️ Research topic '{topic}' is {result[0]}") - conn.close() - return - - # Add participant - cursor.execute(''' - INSERT OR IGNORE INTO agent_research_participation (agent_id, topic) - VALUES (?, ?) - ''', (agent_id, topic)) - - # Update agent status - cursor.execute(''' - UPDATE agents SET status = 'researching', current_research = ? - WHERE id = ? - ''', (topic, agent_id)) - - conn.commit() - conn.close() - print(f"✅ {agent_id} joined research: '{topic}'") - -def post_finding(agent_id, topic, content, finding_type="note"): - """@find - Post a finding to research topic""" - conn = get_connection() - cursor = conn.cursor() - - # Check if topic exists - cursor.execute("SELECT status FROM research_topics WHERE topic = ?", (topic,)) - result = cursor.fetchone() - if not result: - print(f"❌ Research topic '{topic}' not found") - conn.close() - return - if result[0] != 'active': - print(f"⚠️ Research topic '{topic}' is {result[0]}") - - # Add finding - cursor.execute(''' - INSERT INTO research_log (agent_id, topic, content, finding_type) - VALUES (?, ?, ?, ?) - ''', (agent_id, topic, content, finding_type)) - - # Update agent status - cursor.execute(''' - UPDATE agents SET last_seen = CURRENT_TIMESTAMP WHERE id = ? - ''', (agent_id,)) - - conn.commit() - conn.close() - print(f"📝 Finding added to '{topic}' by {agent_id}") - -def generate_consensus(topic): - """@consensus - Generate consensus document from research findings""" - conn = get_connection() - cursor = conn.cursor() - - # Get all findings - cursor.execute(''' - SELECT agent_id, content, finding_type, created_at - FROM research_log - WHERE topic = ? - ORDER BY created_at - ''', (topic,)) - findings = cursor.fetchall() - - if not findings: - print(f"⚠️ No findings found for topic '{topic}'") - conn.close() - return - - # Get participants - cursor.execute(''' - SELECT agent_id FROM agent_research_participation WHERE topic = ? - ''', (topic,)) - participants = [row[0] for row in cursor.fetchall()] - - # Mark topic as completed - cursor.execute(''' - UPDATE research_topics - SET status = 'completed', completed_at = CURRENT_TIMESTAMP - WHERE topic = ? - ''', (topic,)) - - conn.commit() - conn.close() - - # Generate consensus document - consensus_dir = os.path.join(os.getcwd(), ".agent/consensus") - os.makedirs(consensus_dir, exist_ok=True) - - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = f"{topic.replace(' ', '_').replace('/', '_')}_{timestamp}.md" - filepath = os.path.join(consensus_dir, filename) - - with open(filepath, 'w', encoding='utf-8') as f: - f.write(f"# 🎯 Consensus: {topic}\n\n") - f.write(f"**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") - f.write(f"**Participants**: {', '.join(participants)}\n\n") - f.write("---\n\n") - - for agent_id, content, finding_type, created_at in findings: - f.write(f"## [{finding_type.upper()}] {agent_id}\n\n") - f.write(f"*{created_at}*\n\n") - f.write(f"{content}\n\n") - - print(f"✅ Consensus generated: {filepath}") - print(f"📊 Total findings: {len(findings)}") - print(f"👥 Participants: {len(participants)}") - - return filepath - -# ============ TASK MANAGEMENT ============ - -def assign_task(assigned_by, agent_id, task): - """@assign - Assign task to specific agent""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute(''' - INSERT INTO task_assignments (agent_id, task, assigned_by) - VALUES (?, ?, ?) - ''', (agent_id, task, assigned_by)) - - # Notify the agent - cursor.execute(''' - INSERT INTO notifications (agent_id, message, is_broadcast) - VALUES (?, ?, 0) - ''', (agent_id, f"📋 New task assigned by {assigned_by}: {task}")) - - conn.commit() - conn.close() - print(f"📋 Task assigned to {agent_id} by {assigned_by}") - -def list_tasks(agent_id=None): - """List tasks for an agent or all agents""" - conn = get_connection() - cursor = conn.cursor() - - if agent_id: - cursor.execute(''' - SELECT id, task, assigned_by, status, created_at - FROM task_assignments - WHERE agent_id = ? AND status != 'completed' - ORDER BY created_at DESC - ''', (agent_id,)) - tasks = cursor.fetchall() - - print(f"\n📋 Tasks for {agent_id}:") - for task_id, task, assigned_by, status, created_at in tasks: - print(f" [{status.upper()}] #{task_id}: {task} (from {assigned_by})") - else: - cursor.execute(''' - SELECT agent_id, id, task, assigned_by, status - FROM task_assignments - WHERE status != 'completed' - ORDER BY agent_id - ''') - tasks = cursor.fetchall() - - print(f"\n📋 All pending tasks:") - current_agent = None - for agent, task_id, task, assigned_by, status in tasks: - if agent != current_agent: - print(f"\n {agent}:") - current_agent = agent - print(f" [{status.upper()}] #{task_id}: {task}") - - conn.close() - -def complete_task(task_id): - """Mark a task as completed""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute(''' - UPDATE task_assignments - SET status = 'completed', completed_at = CURRENT_TIMESTAMP - WHERE id = ? - ''', (task_id,)) - - if cursor.rowcount > 0: - print(f"✅ Task #{task_id} marked as completed") - else: - print(f"❌ Task #{task_id} not found") - - conn.commit() - conn.close() - -# ============ NOTIFICATIONS ============ - -def broadcast_message(from_agent, message): - """@notify - Broadcast message to all agents""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute("SELECT id FROM agents WHERE id != ?", (from_agent,)) - other_agents = cursor.fetchall() - - for (agent_id,) in other_agents: - cursor.execute(''' - INSERT INTO notifications (agent_id, message, is_broadcast) - VALUES (?, ?, 1) - ''', (agent_id, f"📢 Broadcast from {from_agent}: {message}")) - - conn.commit() - conn.close() - print(f"📢 Broadcast sent to {len(other_agents)} agents") - -def get_notifications(agent_id, unread_only=False): - """Get notifications for an agent""" - conn = get_connection() - cursor = conn.cursor() - - if unread_only: - cursor.execute(''' - SELECT id, message, is_broadcast, created_at - FROM notifications - WHERE agent_id = ? AND is_read = 0 - ORDER BY created_at DESC - ''', (agent_id,)) - else: - cursor.execute(''' - SELECT id, message, is_broadcast, created_at - FROM notifications - WHERE agent_id = ? - ORDER BY created_at DESC - LIMIT 10 - ''', (agent_id,)) - - notifications = cursor.fetchall() - - print(f"\n🔔 Notifications for {agent_id}:") - for notif_id, message, is_broadcast, created_at in notifications: - prefix = "📢" if is_broadcast else "🔔" - print(f" {prefix} {message}") - print(f" {created_at}") - - # Mark as read - cursor.execute(''' - UPDATE notifications SET is_read = 1 - WHERE agent_id = ? AND is_read = 0 - ''', (agent_id,)) - - conn.commit() - conn.close() - -# ============ POLLS ============ - -def start_poll(agent_id, question): - """@poll - Start a quick poll""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute(''' - INSERT INTO polls (question, created_by, status) - VALUES (?, ?, 'active') - ''', (question, agent_id)) - poll_id = cursor.lastrowid - - # Notify all agents - cursor.execute("SELECT id FROM agents WHERE id != ?", (agent_id,)) - other_agents = cursor.fetchall() - for (other_id,) in other_agents: - cursor.execute(''' - INSERT INTO notifications (agent_id, message, is_broadcast) - VALUES (?, ?, 0) - ''', (other_id, f"🗳️ New poll from {agent_id}: '{question}' (Poll #{poll_id}). Vote with: @vote {poll_id} ")) - - conn.commit() - conn.close() - print(f"🗳️ Poll #{poll_id} started: {question}") - return poll_id - -def vote_poll(agent_id, poll_id, vote): - """@vote - Vote on a poll""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute(''' - INSERT OR REPLACE INTO poll_votes (poll_id, agent_id, vote) - VALUES (?, ?, ?) - ''', (poll_id, agent_id, vote)) - - conn.commit() - conn.close() - print(f"✅ Vote recorded for poll #{poll_id}: {vote}") - -def show_poll_results(poll_id): - """Show poll results""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute("SELECT question FROM polls WHERE id = ?", (poll_id,)) - result = cursor.fetchone() - if not result: - print(f"❌ Poll #{poll_id} not found") - conn.close() - return - - question = result[0] - - cursor.execute(''' - SELECT vote, COUNT(*) FROM poll_votes - WHERE poll_id = ? - GROUP BY vote - ''', (poll_id,)) - votes = dict(cursor.fetchall()) - - cursor.execute(''' - SELECT agent_id, vote FROM poll_votes - WHERE poll_id = ? - ''', (poll_id,)) - details = cursor.fetchall() - - conn.close() - - print(f"\n🗳️ Poll #{poll_id}: {question}") - print("Results:") - for vote, count in votes.items(): - print(f" {vote}: {count}") - print("\nVotes:") - for agent, vote in details: - print(f" {agent}: {vote}") - -# ============ HANDOVER ============ - -def request_handover(from_agent, to_agent, context=""): - """@handover - Request task handover to another agent""" - conn = get_connection() - cursor = conn.cursor() - - # Get current task of from_agent - cursor.execute("SELECT task FROM agents WHERE id = ?", (from_agent,)) - result = cursor.fetchone() - current_task = result[0] if result else "current task" - - # Create handover notification - message = f"🔄 Handover request from {from_agent}: '{current_task}'" - if context: - message += f" | Context: {context}" - - cursor.execute(''' - INSERT INTO notifications (agent_id, message, is_broadcast) - VALUES (?, ?, 0) - ''', (to_agent, message)) - - # Update from_agent status - cursor.execute(''' - UPDATE agents SET status = 'idle', task = NULL - WHERE id = ? - ''', (from_agent,)) - - conn.commit() - conn.close() - print(f"🔄 Handover requested: {from_agent} -> {to_agent}") - -def switch_to(agent_id, to_agent): - """@switch - Request to switch to specific agent""" - conn = get_connection() - cursor = conn.cursor() - - message = f"🔄 {agent_id} requests to switch to you for continuation" - - cursor.execute(''' - INSERT INTO notifications (agent_id, message, is_broadcast) - VALUES (?, ?, 0) - ''', (to_agent, message)) - - conn.commit() - conn.close() - print(f"🔄 Switch request sent: {agent_id} -> {to_agent}") - -# ============ STATUS & MONITORING ============ - -def get_status(): - """Enhanced status view""" - conn = get_connection() - cursor = conn.cursor() - - print("\n" + "="*60) - print("🛰️ ACTIVE AGENTS") - print("="*60) - - for row in cursor.execute(''' - SELECT name, task, status, current_research, last_seen - FROM agents - ORDER BY last_seen DESC - '''): - status_emoji = { - 'active': '🟢', - 'idle': '⚪', - 'researching': '🔬', - 'busy': '🔴' - }.get(row[2], '⚪') - - research_info = f" | Research: {row[3]}" if row[3] else "" - print(f"{status_emoji} [{row[2].upper()}] {row[0]}: {row[1]}{research_info}") - print(f" Last seen: {row[4]}") - - print("\n" + "="*60) - print("🔬 ACTIVE RESEARCH TOPICS") - print("="*60) - - for row in cursor.execute(''' - SELECT t.topic, t.initiated_by, t.created_at, - (SELECT COUNT(*) FROM agent_research_participation WHERE topic = t.topic) as participants, - (SELECT COUNT(*) FROM research_log WHERE topic = t.topic) as findings - FROM research_topics t - WHERE t.status = 'active' - ORDER BY t.created_at DESC - '''): - print(f"🔬 {row[0]}") - print(f" Initiated by: {row[1]} | Participants: {row[3]} | Findings: {row[4]}") - print(f" Started: {row[2]}") - - print("\n" + "="*60) - print("🔒 FILE LOCKS") - print("="*60) - - locks = list(cursor.execute(''' - SELECT file_path, agent_id, lock_type - FROM file_locks - ORDER BY timestamp DESC - ''')) - - if locks: - for file_path, agent_id, lock_type in locks: - lock_emoji = '🔒' if lock_type == 'write' else '🔍' - print(f"{lock_emoji} {file_path} -> {agent_id} ({lock_type})") - else: - print(" No active locks") - - print("\n" + "="*60) - print("📋 PENDING TASKS") - print("="*60) - - for row in cursor.execute(''' - SELECT agent_id, COUNT(*) - FROM task_assignments - WHERE status = 'pending' - GROUP BY agent_id - '''): - print(f" {row[0]}: {row[1]} pending tasks") - - cursor.execute("SELECT value FROM global_settings WHERE key = 'mode'") - mode = cursor.fetchone()[0] - print(f"\n🌍 Global Mode: {mode.upper()}") - print("="*60) - - conn.close() - -def show_research_topic(topic): - """Show detailed view of a research topic""" - conn = get_connection() - cursor = conn.cursor() - - cursor.execute("SELECT status, initiated_by, created_at FROM research_topics WHERE topic = ?", (topic,)) - result = cursor.fetchone() - if not result: - print(f"❌ Topic '{topic}' not found") - conn.close() - return - - status, initiated_by, created_at = result - - print(f"\n🔬 Research: {topic}") - print(f"Status: {status} | Initiated by: {initiated_by} | Started: {created_at}") - - cursor.execute(''' - SELECT agent_id FROM agent_research_participation WHERE topic = ? - ''', (topic,)) - participants = [row[0] for row in cursor.fetchall()] - print(f"Participants: {', '.join(participants)}") - - print("\n--- Findings ---") - cursor.execute(''' - SELECT agent_id, content, finding_type, created_at - FROM research_log - WHERE topic = ? - ORDER BY created_at - ''', (topic,)) - - for agent_id, content, finding_type, created_at in cursor.fetchall(): - emoji = {'note': '📝', 'finding': '🔍', 'concern': '⚠️', 'solution': '✅'}.get(finding_type, '📝') - print(f"\n{emoji} [{finding_type.upper()}] {agent_id} ({created_at})") - print(f" {content}") - - conn.close() - -# ============ MAIN CLI ============ - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="🤖 Agent Sync v2.0 - Multi-Agent Cooperation Protocol", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -QUICK COMMANDS: - @research Start joint research - @join Join active research - @find Post finding to research - @consensus Generate consensus document - @assign Assign task to agent - @notify Broadcast to all agents - @handover [context] Handover task - @switch Request switch to agent - @poll Start a poll - @vote Vote on poll - @tasks [agent] List tasks - @complete Complete task - @notifications [agent] Check notifications - @topic View research topic details - -EXAMPLES: - python3 agent_sync_v2.py research claude-code "API Design" - python3 agent_sync_v2.py find copilot "API Design" "Use REST instead of GraphQL" - python3 agent_sync_v2.py assign claude-code copilot "Implement REST endpoints" - python3 agent_sync_v2.py consensus "API Design" - """ - ) - subparsers = parser.add_subparsers(dest="command", help="Command to execute") - - # Legacy commands - subparsers.add_parser("init", help="Initialize the database") - - reg = subparsers.add_parser("register", help="Register an agent") - reg.add_argument("id", help="Agent ID") - reg.add_argument("name", help="Agent name") - reg.add_argument("task", help="Current task") - reg.add_argument("--status", default="idle", help="Agent status") - - lock = subparsers.add_parser("lock", help="Lock a file") - lock.add_argument("id", help="Agent ID") - lock.add_argument("path", help="File path") - lock.add_argument("--type", default="write", choices=["write", "research"], help="Lock type") - - unlock = subparsers.add_parser("unlock", help="Unlock a file") - unlock.add_argument("id", help="Agent ID") - unlock.add_argument("path", help="File path") - - subparsers.add_parser("status", help="Show status dashboard") - - # New v2.0 commands - research = subparsers.add_parser("research", help="@research - Start joint research topic") - research.add_argument("agent_id", help="Agent initiating research") - research.add_argument("topic", help="Research topic") - - join = subparsers.add_parser("join", help="@join - Join active research") - join.add_argument("agent_id", help="Agent joining") - join.add_argument("topic", help="Topic to join") - - find = subparsers.add_parser("find", help="@find - Post finding to research") - find.add_argument("agent_id", help="Agent posting finding") - find.add_argument("topic", help="Research topic") - find.add_argument("content", help="Finding content") - find.add_argument("--type", default="note", choices=["note", "finding", "concern", "solution"], help="Type of finding") - - consensus = subparsers.add_parser("consensus", help="@consensus - Generate consensus document") - consensus.add_argument("topic", help="Topic to generate consensus for") - - assign = subparsers.add_parser("assign", help="@assign - Assign task to agent") - assign.add_argument("from_agent", help="Agent assigning the task") - assign.add_argument("to_agent", help="Agent to assign task to") - assign.add_argument("task", help="Task description") - - tasks = subparsers.add_parser("tasks", help="@tasks - List pending tasks") - tasks.add_argument("--agent", help="Filter by agent ID") - - complete = subparsers.add_parser("complete", help="@complete - Mark task as completed") - complete.add_argument("task_id", type=int, help="Task ID to complete") - - notify = subparsers.add_parser("notify", help="@notify - Broadcast message to all agents") - notify.add_argument("from_agent", help="Agent sending notification") - notify.add_argument("message", help="Message to broadcast") - - handover = subparsers.add_parser("handover", help="@handover - Handover task to another agent") - handover.add_argument("from_agent", help="Current agent") - handover.add_argument("to_agent", help="Agent to handover to") - handover.add_argument("--context", default="", help="Handover context") - - switch = subparsers.add_parser("switch", help="@switch - Request switch to specific agent") - switch.add_argument("from_agent", help="Current agent") - switch.add_argument("to_agent", help="Agent to switch to") - - poll = subparsers.add_parser("poll", help="@poll - Start a quick poll") - poll.add_argument("agent_id", help="Agent starting poll") - poll.add_argument("question", help="Poll question") - - vote = subparsers.add_parser("vote", help="@vote - Vote on a poll") - vote.add_argument("agent_id", help="Agent voting") - vote.add_argument("poll_id", type=int, help="Poll ID") - vote.add_argument("vote_choice", choices=["yes", "no", "maybe"], help="Your vote") - - poll_results = subparsers.add_parser("poll-results", help="Show poll results") - poll_results.add_argument("poll_id", type=int, help="Poll ID") - - notifications = subparsers.add_parser("notifications", help="@notifications - Check notifications") - notifications.add_argument("agent_id", help="Agent to check notifications for") - notifications.add_argument("--unread", action="store_true", help="Show only unread") - - topic = subparsers.add_parser("topic", help="@topic - View research topic details") - topic.add_argument("topic_name", help="Topic name") - - args = parser.parse_args() - - if args.command == "init": - init_db() - elif args.command == "register": - register_agent(args.id, args.name, args.task, args.status) - elif args.command == "lock": - lock_file(args.id, args.path, args.type) - elif args.command == "unlock": - unlock_file(args.id, args.path) - elif args.command == "status": - get_status() - elif args.command == "research": - start_research(args.agent_id, args.topic) - elif args.command == "join": - join_research(args.agent_id, args.topic) - elif args.command == "find": - post_finding(args.agent_id, args.topic, args.content, args.type) - elif args.command == "consensus": - generate_consensus(args.topic) - elif args.command == "assign": - assign_task(args.from_agent, args.to_agent, args.task) - elif args.command == "tasks": - list_tasks(args.agent) - elif args.command == "complete": - complete_task(args.task_id) - elif args.command == "notify": - broadcast_message(args.from_agent, args.message) - elif args.command == "handover": - request_handover(args.from_agent, args.to_agent, args.context) - elif args.command == "switch": - switch_to(args.from_agent, args.to_agent) - elif args.command == "poll": - start_poll(args.agent_id, args.question) - elif args.command == "vote": - vote_poll(args.agent_id, args.poll_id, args.vote_choice) - elif args.command == "poll-results": - show_poll_results(args.poll_id) - elif args.command == "notifications": - get_notifications(args.agent_id, args.unread) - elif args.command == "topic": - show_research_topic(args.topic_name) - else: - parser.print_help() diff --git a/scripts/macp b/scripts/macp deleted file mode 100755 index 92e7fe7..0000000 --- a/scripts/macp +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -# 🤖 MACP Quick Command v2.1 (Unified Edition) - -set -euo pipefail - -AGENT_ID_FILE=".agent/current_agent" - -resolve_agent_id() { - if [ -n "${MACP_AGENT_ID:-}" ]; then - echo "$MACP_AGENT_ID" - return - fi - - if [ -f "$AGENT_ID_FILE" ]; then - cat "$AGENT_ID_FILE" - return - fi - - echo "Error: MACP agent identity is not set. Export MACP_AGENT_ID or create .agent/current_agent." >&2 - exit 1 -} - -resolve_agent_name() { - python3 - <<'PY2' -import os -import sqlite3 -import sys - -agent_id = os.environ.get("MACP_AGENT_ID", "").strip() -if not agent_id: - path = os.path.join(os.getcwd(), ".agent", "current_agent") - if os.path.exists(path): - with open(path, "r", encoding="utf-8") as handle: - agent_id = handle.read().strip() - -db_path = os.path.join(os.getcwd(), ".agent", "agent_hub.db") -name = agent_id or "Agent" - -if agent_id and os.path.exists(db_path): - conn = sqlite3.connect(db_path) - cur = conn.cursor() - cur.execute("SELECT name FROM agents WHERE id = ?", (agent_id,)) - row = cur.fetchone() - conn.close() - if row and row[0]: - name = row[0] - -sys.stdout.write(name) -PY2 -} - -AGENT_ID="$(resolve_agent_id)" -export MACP_AGENT_ID="$AGENT_ID" -AGENT_NAME="$(resolve_agent_name)" - -CMD="${1:-}" -if [ -z "$CMD" ]; then - echo "Usage: ./scripts/macp [/status|/ping|/study|/broadcast|/summon|/handover|/note|/check|/resolve]" >&2 - exit 1 -fi -shift - -case "$CMD" in - /study) - TOPIC="$1" - shift - DESC="$*" - if [ -n "$DESC" ]; then - python3 scripts/agent_sync.py study "$AGENT_ID" "$TOPIC" --desc "$DESC" - else - python3 scripts/agent_sync.py study "$AGENT_ID" "$TOPIC" - fi - ;; - /broadcast) - python3 scripts/agent_sync.py broadcast "$AGENT_ID" manual "$*" - ;; - /summon) - TO_AGENT="$1" - shift - python3 scripts/agent_sync.py assign "$AGENT_ID" "$TO_AGENT" "$*" --role worker --priority high - ;; - /handover) - TO_AGENT="$1" - shift - python3 scripts/agent_sync.py assign "$AGENT_ID" "$TO_AGENT" "$*" --role worker - python3 scripts/agent_sync.py register "$AGENT_ID" "$AGENT_NAME" "Idle" - ;; - /note) - TOPIC="$1" - shift - python3 scripts/agent_sync.py note "$AGENT_ID" "$TOPIC" "$*" --type note - ;; - /check) - python3 scripts/agent_sync.py check - ;; - /resolve) - TOPIC="$1" - shift - python3 scripts/agent_sync.py resolve "$AGENT_ID" "$TOPIC" "$*" - ;; - /ping) - python3 scripts/agent_sync.py status | grep "\[" - ;; - /status) - python3 scripts/agent_sync.py status - ;; - *) - echo "Usage: ./scripts/macp [/status|/ping|/study|/broadcast|/summon|/handover|/note|/check|/resolve]" - ;; -esac diff --git a/zed-ai-tabs.sh b/zed-ai-tabs.sh deleted file mode 100755 index 1319338..0000000 --- a/zed-ai-tabs.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash -# ============================================================================== -# ai-tabs - Ultra Orchestrator -# Version: v1.0.0 -# License: MIT -# Author: Fu-Jie -# Description: Batch-launches and orchestrates multiple AI CLI tools as Tabs. -# ============================================================================== - -# 1. Single-Instance Lock -LOCK_FILE="/tmp/ai_terminal_launch.lock" -# If lock is less than 10 seconds old, another instance is running. Exit. -if [ -f "$LOCK_FILE" ]; then - LOCK_TIME=$(stat -f %m "$LOCK_FILE") - NOW=$(date +%s) - if (( NOW - LOCK_TIME < 10 )); then - echo "⚠️ Another launch in progress. Skipping to prevent duplicates." - exit 0 - fi -fi -touch "$LOCK_FILE" -trap 'rm -f "$LOCK_FILE"' EXIT - -# 2. Configuration & Constants -INIT_DELAY=4.5 -PASTE_DELAY=0.3 -CMD_CREATION_DELAY=0.3 -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PARENT_DIR="$(dirname "$SCRIPT_DIR")" - -# Search for .env -if [ -f "${SCRIPT_DIR}/.env" ]; then - ENV_FILE="${SCRIPT_DIR}/.env" -elif [ -f "${PARENT_DIR}/.env" ]; then - ENV_FILE="${PARENT_DIR}/.env" -fi - -# Supported Tools -SUPPORTED_TOOLS=( - "claude:--continue" - "opencode:--continue" - "gemini:--resume latest" - "copilot:--continue" - "iflow:--continue" - "kilo:--continue" -) - -FOUND_TOOLS_NAMES=() -FOUND_CMDS=() - -# 3. Part A: Load Manual Configuration -if [ -f "$ENV_FILE" ]; then - set -a; source "$ENV_FILE"; set +a - for var in $(compgen -v | grep '^TOOL_[0-9]' | sort -V); do - TPATH="${!var}" - if [ -x "$TPATH" ]; then - NAME=$(basename "$TPATH") - FLAG="--continue" - for item in "${SUPPORTED_TOOLS[@]}"; do - [[ "${item%%:*}" == "$NAME" ]] && FLAG="${item#*:}" && break - done - FOUND_TOOLS_NAMES+=("$NAME") - FOUND_CMDS+=("'$TPATH' $FLAG || '$TPATH' || exec \$SHELL") - fi - done -fi - -# 4. Part B: Automatic Tool Discovery -for item in "${SUPPORTED_TOOLS[@]}"; do - NAME="${item%%:*}" - FLAG="${item#*:}" - ALREADY_CONFIGURED=false - for configured in "${FOUND_TOOLS_NAMES[@]}"; do - [[ "$configured" == "$NAME" ]] && ALREADY_CONFIGURED=true && break - done - [[ "$ALREADY_CONFIGURED" == true ]] && continue - TPATH=$(which "$NAME" 2>/dev/null) - if [ -z "$TPATH" ]; then - SEARCH_PATHS=( - "/opt/homebrew/bin/$NAME" - "/usr/local/bin/$NAME" - "$HOME/.local/bin/$NAME" - "$HOME/bin/$NAME" - "$HOME/.$NAME/bin/$NAME" - "$HOME/.nvm/versions/node/*/bin/$NAME" - "$HOME/.npm-global/bin/$NAME" - "$HOME/.cargo/bin/$NAME" - ) - for p in "${SEARCH_PATHS[@]}"; do - for found_p in $p; do [[ -x "$found_p" ]] && TPATH="$found_p" && break 2; done - done - fi - if [ -n "$TPATH" ]; then - FOUND_TOOLS_NAMES+=("$NAME") - FOUND_CMDS+=("'$TPATH' $FLAG || '$TPATH' || exec \$SHELL") - fi -done - -NUM_FOUND=${#FOUND_CMDS[@]} -[[ "$NUM_FOUND" -eq 0 ]] && exit 1 - -# 5. Core Orchestration (Reset + Launch) -# Using Command Palette automation to avoid the need for manual shortcut binding. -AS_SCRIPT="tell application \"System Events\"\n" - -# Phase A: Creation (Using Command Palette to ensure it opens in Editor Area) -for ((i=1; i<=NUM_FOUND; i++)); do - AS_SCRIPT+=" keystroke \"p\" using {command down, shift down}\n" - AS_SCRIPT+=" delay 0.1\n" - # Ensure we are searching for the command. Using clipboard for speed and universal language support. - AS_SCRIPT+=" set the clipboard to \"workspace: new center terminal\"\n" - AS_SCRIPT+=" keystroke \"v\" using {command down}\n" - AS_SCRIPT+=" delay 0.1\n" - AS_SCRIPT+=" keystroke return\n" - AS_SCRIPT+=" delay $CMD_CREATION_DELAY\n" -done - -# Phase B: Warmup -AS_SCRIPT+=" delay $INIT_DELAY\n" - -# Phase C: Command Injection (Reverse) -for ((i=NUM_FOUND-1; i>=0; i--)); do - FULL_CMD="${FOUND_CMDS[$i]}" - CLEAN_CMD=$(echo "$FULL_CMD" | sed 's/"/\\"/g') - AS_SCRIPT+=" set the clipboard to \"$CLEAN_CMD\"\n" - AS_SCRIPT+=" delay 0.1\n" - AS_SCRIPT+=" keystroke \"v\" using {command down}\n" - AS_SCRIPT+=" delay $PASTE_DELAY\n" - AS_SCRIPT+=" keystroke return\n" - if [ $i -gt 0 ]; then - AS_SCRIPT+=" delay 0.5\n" - AS_SCRIPT+=" keystroke \"[\" using {command down, shift down}\n" - fi -done -AS_SCRIPT+="end tell" - -# Execute -echo -e "$AS_SCRIPT" | osascript -echo "✨ Ai tabs initialized successfully ($NUM_FOUND tools found)."