377 lines
21 KiB
Markdown
377 lines
21 KiB
Markdown
# Architecture Patterns
|
|
|
|
**Domain:** tmux-Claude CLI integration + OpenClaw skill bridge
|
|
**Researched:** 2026-03-10
|
|
|
|
## Recommended Architecture
|
|
|
|
The gsd-openclaw skill acts as a bridge between OpenClaw's multi-channel messaging system and GSD's Claude CLI-based project management. The architecture follows a **session-orchestrator-bridge** pattern.
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ USER INTERFACES │
|
|
│ WhatsApp / Telegram / Slack / Discord / Feishu / WeChat / WebChat │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ OpenClaw Gateway │
|
|
│ (WebSocket Control Plane) │
|
|
│ ws://127.0.0.1:18789 │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Channel │ │ Session │ │ Skill │ │
|
|
│ │ Handlers │ │ Manager │ │ Registry │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ Skill Invocation (SKILL.md trigger)
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ gsd-openclaw Skill Layer │
|
|
│ ~/.openclaw/workspace/skills/gsd-openclaw/ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ SKILL.md │ │
|
|
│ │ - Trigger patterns: "/gsd:*", "gsd*", project management keywords │ │
|
|
│ │ - Routes to orchestrator.py with parsed intent │ │
|
|
│ └──────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ orchestrator.py │ │
|
|
│ │ - Command routing and validation │ │
|
|
│ │ - User/project authentication │ │
|
|
│ │ - State coordination │ │
|
|
│ │ - Response formatting for messaging channels │ │
|
|
│ └──────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ tmux_session_manager.py │ │
|
|
│ │ - Create/destroy tmux sessions per user-project │ │
|
|
│ │ - Send commands to Claude CLI │ │
|
|
│ │ - Capture output (sync/async) │ │
|
|
│ │ - Session health monitoring │ │
|
|
│ └──────────────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ state_manager.py │ │
|
|
│ │ - Persistent state storage (JSON) │ │
|
|
│ │ - Project workspace tracking │ │
|
|
│ │ - User session mapping │ │
|
|
│ │ - Conversation history (for context recovery) │ │
|
|
│ └──────────────────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ libtmux / tmux commands
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ tmux Session Layer │
|
|
│ │
|
|
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌────────────────────┐ │
|
|
│ │ tmux:gsd-user1-projA│ │ tmux:gsd-user1-projB│ │ tmux:gsd-user2-projC│ │
|
|
│ │ (Claude CLI) │ │ (Claude CLI) │ │ (Claude CLI) │ │
|
|
│ │ workspace: ~/projA │ │ workspace: ~/projB │ │ workspace: ~/projC│ │
|
|
│ └─────────────────────┘ └─────────────────────┘ └────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ Slash commands (/gsd:*)
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ GSD System │
|
|
│ ~/.claude/get-shit-done/ │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ /gsd:new- │ │ /gsd:plan │ │ /gsd:ship │ │ /gsd:status │ │
|
|
│ │ project │ │ │ │ │ │ │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ GSD Core │ │
|
|
│ │ - Project scaffolding (.planning/ structure) │ │
|
|
│ │ - Roadmap generation │ │
|
|
│ │ - Task management │ │
|
|
│ │ - Git integration │ │
|
|
│ └──────────────────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Component Boundaries
|
|
|
|
| Component | Responsibility | Communicates With |
|
|
|-----------|---------------|-------------------|
|
|
| **SKILL.md** | Trigger pattern matching, skill description | OpenClaw Gateway |
|
|
| **orchestrator.py** | Command routing, user auth, response formatting | state_manager, tmux_session_manager |
|
|
| **tmux_session_manager.py** | tmux session lifecycle, I/O with Claude CLI | tmux (via libtmux/subprocess), orchestrator |
|
|
| **state_manager.py** | Persistent state, workspace tracking, user mapping | orchestrator (reads/writes JSON) |
|
|
| **prompts/** | LLM prompt templates for different scenarios | orchestrator (loads as needed) |
|
|
|
|
### Data Flow
|
|
|
|
```
|
|
1. User sends message: "Create a new project for user authentication"
|
|
│
|
|
▼
|
|
2. OpenClaw Gateway receives via channel (WhatsApp/Telegram/etc.)
|
|
│
|
|
▼
|
|
3. Gateway matches trigger pattern → invokes gsd-openclaw skill
|
|
│
|
|
▼
|
|
4. orchestrator.py:
|
|
a. Extracts intent: "new-project" command
|
|
b. Validates user authentication
|
|
c. Checks state_manager for existing sessions
|
|
│
|
|
▼
|
|
5. tmux_session_manager.py:
|
|
a. Gets or creates tmux session for user+project
|
|
b. Sends "/gsd:new-project user authentication" to Claude CLI
|
|
│
|
|
▼
|
|
6. Claude CLI processes in tmux (persistent context):
|
|
a. GSD system scaffolds project
|
|
b. Generates initial planning files
|
|
c. Outputs progress/response
|
|
│
|
|
▼
|
|
7. tmux_session_manager captures output (async polling)
|
|
│
|
|
▼
|
|
8. orchestrator formats response for channel:
|
|
- Compact for mobile (WhatsApp/Telegram)
|
|
- Rich for Slack/Discord
|
|
│
|
|
▼
|
|
9. Response sent back through OpenClaw Gateway to user
|
|
│
|
|
▼
|
|
10. state_manager updates:
|
|
- Session last activity
|
|
- Project state changes
|
|
- Conversation checkpoint
|
|
```
|
|
|
|
## Patterns to Follow
|
|
|
|
### Pattern 1: Session-per-Project Isolation
|
|
**What:** Each user-project combination gets its own tmux session with isolated Claude context.
|
|
**When:** Always, for any project-specific work.
|
|
**Why:** Prevents context pollution between projects, enables parallel work.
|
|
|
|
```python
|
|
# tmux_session_manager.py
|
|
class TmuxSessionManager:
|
|
def get_or_create_session(self, user_id: str, project_id: str) -> TmuxSession:
|
|
session_name = f"gsd-{user_id}-{project_id}"
|
|
|
|
# Check if session exists
|
|
session = self.find_session(session_name)
|
|
if session:
|
|
return session
|
|
|
|
# Create new session with project workspace
|
|
workspace = self.state_manager.get_project_workspace(user_id, project_id)
|
|
return self.create_session(
|
|
name=session_name,
|
|
working_dir=workspace,
|
|
command="claude" # Start Claude CLI
|
|
)
|
|
```
|
|
|
|
### Pattern 2: Async Response Handling
|
|
**What:** Non-blocking output capture with callback-based response delivery.
|
|
**When:** Any command that may take time (GSD operations can be slow).
|
|
**Why:** Messaging channels expect timely responses; Claude operations may take minutes.
|
|
|
|
```python
|
|
# orchestrator.py
|
|
async def handle_command(self, user_id: str, command: str, context: dict):
|
|
session = self.tmux_manager.get_or_create_session(
|
|
user_id,
|
|
context.get("project_id")
|
|
)
|
|
|
|
# Send command
|
|
session.send_keys(command)
|
|
|
|
# Start async output capture
|
|
output_future = self.tmux_manager.capture_output_async(
|
|
session,
|
|
timeout=300, # 5 min max
|
|
idle_timeout=30 # 30s of no output = done
|
|
)
|
|
|
|
# Return immediately with "processing" status
|
|
# Callback will send response when complete
|
|
return Response(status="processing", estimated_time="1-2 minutes")
|
|
```
|
|
|
|
### Pattern 3: State Checkpointing
|
|
**What:** Periodic state snapshots to enable recovery from interruption.
|
|
**When:** After each significant GSD operation, every N minutes during long operations.
|
|
**Why:** Network interruptions, server restarts, user timeouts should not lose work.
|
|
|
|
```python
|
|
# state_manager.py
|
|
def checkpoint_session(self, session_id: str, claude_output: str):
|
|
state = self.load_state()
|
|
|
|
state["sessions"][session_id]["last_checkpoint"] = {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"claude_context_summary": self._extract_context_summary(claude_output),
|
|
"project_phase": self._detect_current_phase(claude_output),
|
|
"pending_actions": self._extract_pending_actions(claude_output)
|
|
}
|
|
|
|
self.save_state(state)
|
|
```
|
|
|
|
### Pattern 4: Channel-Adaptive Formatting
|
|
**What:** Same content rendered differently based on target channel.
|
|
**When:** Every response going back to user.
|
|
**Why:** WhatsApp has 4096 char limit, Discord supports markdown, mobile needs compactness.
|
|
|
|
```python
|
|
# response_formatter.py
|
|
def format_response(self, content: dict, channel: str) -> str:
|
|
formatters = {
|
|
"whatsapp": self._format_compact,
|
|
"telegram": self._format_markdown_lite,
|
|
"slack": self._format_rich_slack,
|
|
"discord": self._format_rich_discord,
|
|
"feishu": self._format_feishu_card
|
|
}
|
|
|
|
formatter = formatters.get(channel, self._format_generic)
|
|
return formatter(content)
|
|
```
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
### Anti-Pattern 1: Shared Global tmux Session
|
|
**What:** One tmux session for all users/projects.
|
|
**Why bad:** Context mixing, race conditions, security issues (users see each other's data).
|
|
**Instead:** Session-per-project isolation (Pattern 1).
|
|
|
|
### Anti-Pattern 2: Synchronous Blocking on Claude Output
|
|
**What:** Wait for Claude to complete before returning any response.
|
|
**Why bad:** Timeouts on messaging channels (often 30-60s), poor UX, gateway hangs.
|
|
**Instead:** Async with status updates (Pattern 2).
|
|
|
|
### Anti-Pattern 3: State in Memory Only
|
|
**What:** Keep all state in Python process memory.
|
|
**Why bad:** Lost on restart, cannot scale horizontally, no recovery from crash.
|
|
**Instead:** Persistent JSON state with checkpointing (Pattern 3).
|
|
|
|
### Anti-Pattern 4: Direct GSD Code Modification
|
|
**What:** Modify GSD source to add OpenClaw-specific features.
|
|
**Why bad:** Breaks GSD updates, creates maintenance burden, diverges from upstream.
|
|
**Instead:** Pure bridge pattern - send slash commands, capture output, no GSD modifications.
|
|
|
|
### Anti-Pattern 5: Claude CLI in Non-Interactive Mode
|
|
**What:** Use Claude CLI with `--no-interactive` or pipe-only input.
|
|
**Why bad:** Loses conversational context, breaks GSD's interactive prompts, no approval flows.
|
|
**Instead:** Full tmux session with pseudo-terminal.
|
|
|
|
## Scalability Considerations
|
|
|
|
| Concern | At 1 user | At 10 users | At 100 users |
|
|
|---------|-----------|-------------|--------------|
|
|
| **tmux sessions** | 1-5 per user | 10-50 total | 100-500 total (consider session pooling/LRU eviction) |
|
|
| **Memory** | ~100MB (Claude) | ~1GB (10x Claude) | ~10GB (need session limits, swap to disk) |
|
|
| **State storage** | ~1MB JSON | ~10MB | ~100MB (consider SQLite migration) |
|
|
| **Response latency** | Immediate | Slight queue | Need job queue, async workers |
|
|
| **Channel limits** | N/A | Monitor rate limits | Implement rate limiting, batching |
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
~/.openclaw/workspace/skills/gsd-openclaw/
|
|
├── SKILL.md # Skill definition and triggers
|
|
├── orchestrator.py # Main orchestration logic
|
|
├── tmux_session_manager.py # tmux session lifecycle
|
|
├── state_manager.py # Persistent state management
|
|
├── response_formatter.py # Channel-adaptive formatting
|
|
├── prompts/
|
|
│ ├── context_recovery.md # Prompt for recovering session context
|
|
│ ├── command_mapping.md # Natural language to GSD commands
|
|
│ └── error_recovery.md # Prompt for handling errors
|
|
└── state/
|
|
├── sessions.json # Active sessions mapping
|
|
├── users.json # User project mappings
|
|
└── projects/ # Per-project state snapshots
|
|
├── user1-projA.json
|
|
└── user1-projB.json
|
|
```
|
|
|
|
## Build Order Implications
|
|
|
|
Based on component dependencies, recommended build order:
|
|
|
|
1. **Phase 1: Core Infrastructure**
|
|
- `state_manager.py` (no dependencies, needed by all)
|
|
- `tmux_session_manager.py` (depends on state_manager for workspace paths)
|
|
|
|
2. **Phase 2: Integration Layer**
|
|
- `response_formatter.py` (standalone, needed by orchestrator)
|
|
- `prompts/` (static files, needed by orchestrator)
|
|
|
|
3. **Phase 3: Orchestration**
|
|
- `orchestrator.py` (depends on all above)
|
|
- `SKILL.md` (defines triggers, depends on orchestrator interface)
|
|
|
|
4. **Phase 4: Testing & Polish**
|
|
- Integration tests
|
|
- Channel-specific formatting refinement
|
|
- Error handling edge cases
|
|
|
|
## Key Integration Points
|
|
|
|
### OpenClaw Gateway Integration
|
|
- Skill is auto-loaded from `~/.openclaw/workspace/skills/gsd-openclaw/`
|
|
- SKILL.md frontmatter defines trigger patterns
|
|
- Gateway calls orchestrator with: user_id, message, channel, context
|
|
|
|
### GSD Integration
|
|
- **No code changes to GSD** - pure bridge pattern
|
|
- All interaction via tmux + slash commands
|
|
- Workspace paths from GSD conventions (`~/projects/{name}`)
|
|
|
|
### tmux Integration Options
|
|
1. **libtmux (Python)** - Recommended, cleaner API
|
|
2. **subprocess + tmux commands** - Fallback, more control
|
|
|
|
```python
|
|
# Option 1: libtmux
|
|
import libtmux
|
|
server = libtmux.Server()
|
|
session = server.new_session("gsd-user1-projA", attach=False)
|
|
pane = session.attached_window.attached_pane
|
|
pane.send_keys("/gsd:status")
|
|
|
|
# Option 2: subprocess
|
|
import subprocess
|
|
subprocess.run(["tmux", "new-session", "-d", "-s", "gsd-user1-projA"])
|
|
subprocess.run(["tmux", "send-keys", "-t", "gsd-user1-projA", "/gsd:status", "Enter"])
|
|
```
|
|
|
|
## Sources
|
|
|
|
- OpenClaw GitHub Repository: https://github.com/openclaw/openclaw (HIGH confidence - official source)
|
|
- OpenClaw Website: https://openclaw.ai/ (HIGH confidence - official documentation)
|
|
- dev-workflow skill reference implementation (HIGH confidence - existing pattern in same ecosystem)
|
|
- GSD project context from PROJECT.md (HIGH confidence - project requirements)
|
|
|
|
## Confidence Assessment
|
|
|
|
| Area | Confidence | Reason |
|
|
|------|------------|--------|
|
|
| OpenClaw skill structure | HIGH | Direct reference from dev-workflow, official docs |
|
|
| tmux integration patterns | MEDIUM | Standard Python/tmux patterns, needs validation with Claude CLI specifics |
|
|
| Async response handling | HIGH | Standard pattern for messaging bots, well-established |
|
|
| GSD bridge approach | HIGH | Project requirement explicitly states "bridge not modify" |
|
|
| Scalability estimates | LOW | Theoretical, needs real-world testing |
|