feat: Add Skills Library and Session Insights (v2.3)
## Skills Library - Browse and install 36+ community skills from Anthropic & Superpowers - Local skill templates (Rails, React, testing) - Create custom project-specific skills - Beautiful UI with search and filtering - /claude-os-skills command for CLI management ## Session Insights - Parse Claude Code native .jsonl session files - Extract patterns, decisions, blockers automatically - Track tool usage and file changes - Session summary generation for LLM processing ## Documentation - Updated README with v2.3 features - Updated API_REFERENCE with new endpoints - Added SKILLS_GUIDE.md and SESSIONS_GUIDE.md - Updated GitHub Pages (docs/index.html) - Reorganized docs (internal_docs for private) ## Bug Fixes - Fixed httpx client closure in community skill install - Fixed frontend request format for skill install 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8908f2b03b
commit
a744080e65
37 changed files with 11294 additions and 45 deletions
BIN
.coverage
BIN
.coverage
Binary file not shown.
16
.gitignore
vendored
16
.gitignore
vendored
|
|
@ -96,16 +96,8 @@ frontend/e2e/screenshots/
|
|||
frontend/playwright-report/
|
||||
frontend/test-results/
|
||||
|
||||
# Docs - ignore all except README-linked files
|
||||
docs/
|
||||
!docs/API_REFERENCE.md
|
||||
!docs/HYBRID_INDEXING_DESIGN.md
|
||||
!docs/REAL_TIME_LEARNING_GUIDE.md
|
||||
!docs/SELF_LEARNING_SYSTEM.md
|
||||
!docs/guides/
|
||||
docs/guides/*
|
||||
!docs/guides/REALTIME_KANBAN_GUIDE.md
|
||||
!docs/guides/VISUAL_GUIDE.md
|
||||
# Internal docs (not for public repo)
|
||||
internal_docs/
|
||||
|
||||
# Export files (contain project-specific data)
|
||||
exports/
|
||||
|
|
@ -113,3 +105,7 @@ exports/
|
|||
# Marketing materials (not for distribution)
|
||||
brochure.html
|
||||
presentation.html
|
||||
|
||||
# Private directory (not for public repo)
|
||||
our_stuff/
|
||||
.coverage
|
||||
136
README.md
136
README.md
|
|
@ -36,11 +36,35 @@
|
|||
|
||||
---
|
||||
|
||||
## 🆕 What's New in v2.2
|
||||
## 🆕 What's New in v2.3
|
||||
|
||||
> **Latest Release: December 2025**
|
||||
|
||||
### ✨ Beautiful New Installer
|
||||
### 🎯 Skills Library & Community Skills
|
||||
|
||||
Browse and install **36+ skills** from the community with one click!
|
||||
|
||||
```bash
|
||||
/claude-os-skills # List all skills
|
||||
/claude-os-skills install pdf # Install from community
|
||||
```
|
||||
|
||||
**New Features:**
|
||||
- 📚 **Local Templates** - Pre-built skills for Rails, React, testing workflows
|
||||
- 🌐 **Community Skills** - Install from Anthropic Official (16) & Superpowers (20)
|
||||
- 🔧 **Custom Skills** - Create and share project-specific skills
|
||||
- 🎨 **Beautiful UI** - Browse, search, and install via web interface
|
||||
|
||||
### 📊 Session Insights
|
||||
|
||||
Parse Claude Code sessions and extract insights automatically!
|
||||
|
||||
**New Features:**
|
||||
- 🔍 **Session Parser** - Reads Claude Code's native `.jsonl` session files
|
||||
- 💡 **Insight Extraction** - Extracts patterns, decisions, blockers
|
||||
- 📈 **Analytics Ready** - Track tool usage and file changes across sessions
|
||||
|
||||
### ✨ Beautiful New Installer (v2.2)
|
||||
|
||||
The installer got a major upgrade with **Charm CLI (gum)** support!
|
||||
|
||||
|
|
@ -66,6 +90,7 @@ The installer got a major upgrade with **Charm CLI (gum)** support!
|
|||
|
||||
| Version | Highlights |
|
||||
|---------|------------|
|
||||
| **v2.3** | Skills library, community skills, session insights |
|
||||
| **v2.2** | Gum CLI support, safety features, lite model default |
|
||||
| **v2.1** | Unified installer, OpenAI provider support |
|
||||
| **v2.0** | Hybrid tree-sitter indexing, real-time Kanban board |
|
||||
|
|
@ -109,8 +134,10 @@ You work with Claude Code on a feature, close the terminal, come back tomorrow..
|
|||
|
||||
### Key Features
|
||||
|
||||
✅ **Lightning-Fast Indexing** - NEW! Tree-sitter hybrid indexing: 10,000 files in 30 seconds (vs 3-5 hours)
|
||||
✅ **Real-Time Kanban Board** - NEW! Auto-syncing task visualization for agent-os specs (updates within 3 seconds)
|
||||
✅ **Skills Library** - NEW! Browse & install skills from Anthropic and community repos
|
||||
✅ **Session Insights** - NEW! Parse Claude Code sessions and extract patterns automatically
|
||||
✅ **Lightning-Fast Indexing** - Tree-sitter hybrid indexing: 10,000 files in 30 seconds
|
||||
✅ **Real-Time Kanban Board** - Auto-syncing task visualization for agent-os specs
|
||||
✅ **One-Command Project Init** - `/claude-os-init` and you're done
|
||||
✅ **Automatic Context Loading** - Starts every session with relevant memories
|
||||
✅ **Session Management** - Track work, save progress, resume later
|
||||
|
|
@ -424,13 +451,19 @@ All these work in any initialized project:
|
|||
- **`/claude-os-list`** - List all knowledge bases
|
||||
- **`/claude-os-session [action]`** - Manage development sessions
|
||||
- **`/claude-os-triggers`** - Manage trigger phrases
|
||||
- **`/claude-os-skills [action]`** - Manage skills (list, install, create)
|
||||
|
||||
### Available Skills
|
||||
|
||||
**Global Skills (always available):**
|
||||
- **`initialize-project`** - Analyze codebase and generate standards
|
||||
- **`remember-this`** - Auto-save when you say "remember this:"
|
||||
- **`memory`** - Simple memory management
|
||||
|
||||
**Community Skills (install via `/claude-os-skills`):**
|
||||
- **36+ skills** from Anthropic Official and Superpowers repos
|
||||
- PDF/XLSX manipulation, frontend design, TDD, debugging, code review, and more
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Agent-OS: Spec-Driven Development (Optional)
|
||||
|
|
@ -544,6 +577,82 @@ Agent-OS agents deeply integrate with Claude OS:
|
|||
|
||||
---
|
||||
|
||||
## 🎯 Skills Library
|
||||
|
||||
**Browse, install, and manage Claude Code skills with ease!**
|
||||
|
||||
### What Are Skills?
|
||||
|
||||
Skills are reusable instruction sets that teach Claude specific capabilities. They can include:
|
||||
- Coding patterns and best practices
|
||||
- Tool usage workflows
|
||||
- Domain-specific knowledge
|
||||
- Development methodologies
|
||||
|
||||
### Skill Types
|
||||
|
||||
**Global Skills** (`~/.claude/skills/`)
|
||||
- Available in ALL projects
|
||||
- Core skills: `memory`, `remember-this`, `initialize-project`
|
||||
|
||||
**Project Skills** (`{project}/.claude/skills/`)
|
||||
- Available only in that project
|
||||
- Installed from templates or custom created
|
||||
|
||||
**Community Skills** (fetched from GitHub)
|
||||
- **Anthropic Official** - 16 skills from `anthropics/skills`
|
||||
- **Superpowers** - 20 skills from `obra/superpowers`
|
||||
|
||||
### Using the Skills Command
|
||||
|
||||
```bash
|
||||
# List all installed skills
|
||||
/claude-os-skills
|
||||
|
||||
# Browse local templates
|
||||
/claude-os-skills templates
|
||||
|
||||
# Install a template to your project
|
||||
/claude-os-skills install rails-backend
|
||||
|
||||
# Create a custom skill
|
||||
/claude-os-skills create
|
||||
|
||||
# View skill details
|
||||
/claude-os-skills view <name>
|
||||
|
||||
# Delete a project skill
|
||||
/claude-os-skills delete <name>
|
||||
```
|
||||
|
||||
### Community Skills (via Web UI)
|
||||
|
||||
1. Open the web UI at http://localhost:5173
|
||||
2. Select your project
|
||||
3. Click the **Skills** tab
|
||||
4. Click **Install Template**
|
||||
5. Switch to **Community Skills** tab
|
||||
6. Browse skills from Anthropic Official and Superpowers
|
||||
7. Click **Install** on any skill
|
||||
|
||||
### Featured Community Skills
|
||||
|
||||
**From Anthropic Official:**
|
||||
- `pdf` - Create, edit, and analyze PDF documents
|
||||
- `xlsx` - Spreadsheet manipulation with formulas
|
||||
- `frontend-design` - Production-grade UI components
|
||||
- `mcp-builder` - Create MCP servers
|
||||
- `doc-coauthoring` - Collaborative documentation
|
||||
|
||||
**From Superpowers:**
|
||||
- `test-driven-development` - TDD workflow
|
||||
- `systematic-debugging` - Four-phase debugging framework
|
||||
- `code-review` - Rigorous code review process
|
||||
- `git-worktrees` - Isolated development branches
|
||||
- `brainstorming` - Structured ideation process
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Spec Tracking & Kanban Board
|
||||
|
||||
**NEW: Real-time auto-syncing Kanban board for Agent-OS specs!**
|
||||
|
|
@ -722,11 +831,17 @@ claude-os/
|
|||
│ ├── commands/ # Slash commands (symlinked to ~/.claude/)
|
||||
│ │ ├── claude-os-init.md
|
||||
│ │ ├── claude-os-search.md
|
||||
│ │ ├── claude-os-skills.md # NEW: Skills management
|
||||
│ │ └── ...
|
||||
│ ├── skills/ # Skills (symlinked to ~/.claude/)
|
||||
│ ├── skills/ # Global skills (symlinked to ~/.claude/)
|
||||
│ │ ├── initialize-project/
|
||||
│ │ ├── remember-this/
|
||||
│ │ └── memory/
|
||||
│ ├── skill-library/ # NEW: Local skill templates
|
||||
│ │ ├── general/ # General purpose skills
|
||||
│ │ ├── rails/ # Ruby on Rails skills
|
||||
│ │ ├── react/ # React/TypeScript skills
|
||||
│ │ └── testing/ # Testing frameworks
|
||||
│ └── project-files/ # Files created during /claude-os-init
|
||||
│ ├── CLAUDE.md.template
|
||||
│ └── .claude-os/
|
||||
|
|
@ -1036,7 +1151,8 @@ ollama list | grep llama3.1
|
|||
claude-os/
|
||||
├── templates/ # Shared templates system
|
||||
│ ├── commands/ # Slash commands
|
||||
│ ├── skills/ # Skills
|
||||
│ ├── skills/ # Global skills
|
||||
│ ├── skill-library/ # Local skill templates (NEW)
|
||||
│ └── project-files/ # Files created during init
|
||||
├── cli/ # CLI tools
|
||||
│ └── claude-os-consolidate.sh
|
||||
|
|
@ -1044,13 +1160,19 @@ claude-os/
|
|||
│ ├── core/ # Core modules
|
||||
│ │ ├── sqlite_manager.py
|
||||
│ │ ├── rag_engine.py
|
||||
│ │ ├── skill_manager.py # NEW: Skills management
|
||||
│ │ ├── session_parser.py # NEW: Session parsing
|
||||
│ │ ├── insight_extractor.py # NEW: Insight extraction
|
||||
│ │ └── ...
|
||||
│ └── db/ # Database schemas
|
||||
├── frontend/ # React UI (Vite)
|
||||
│ ├── src/
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── SkillsManagement.tsx # NEW: Skills UI
|
||||
│ │ │ └── ...
|
||||
│ │ └── pages/
|
||||
│ └── public/
|
||||
│ └── assets/
|
||||
│ └── claude-os-hero.png
|
||||
├── mcp_server/ # MCP Server (HTTP)
|
||||
│ └── server.py # FastAPI + MCP endpoints
|
||||
├── data/ # SQLite database
|
||||
|
|
|
|||
312
app/core/insight_extractor.py
Normal file
312
app/core/insight_extractor.py
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
"""
|
||||
Insight Extractor for Claude Code session transcripts.
|
||||
Uses LLM to extract valuable insights (decisions, patterns, solutions, blockers).
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Any, Optional
|
||||
import httpx
|
||||
|
||||
from app.core.config import Config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Insight:
|
||||
"""An extracted insight from a session."""
|
||||
type: str # "decision", "pattern", "solution", or "blocker"
|
||||
title: str
|
||||
content: str
|
||||
confidence: float # 0.0 to 1.0
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
# Extraction prompt template
|
||||
EXTRACTION_PROMPT = """Analyze this Claude Code session transcript and extract valuable insights.
|
||||
|
||||
Look for:
|
||||
1. **Decisions**: Technical choices made with reasoning (e.g., "Decided to use SQLite instead of PostgreSQL because...")
|
||||
2. **Patterns**: Reusable approaches or code patterns discovered (e.g., "Session files use JSONL format with entry types...")
|
||||
3. **Solutions**: Bug fixes, error resolutions, implementation approaches (e.g., "Fixed authentication error by updating token refresh logic...")
|
||||
4. **Blockers**: Problems encountered, whether resolved or not (e.g., "Package installation failed due to Python version mismatch...")
|
||||
|
||||
For each insight:
|
||||
- Provide a clear title (5-10 words)
|
||||
- Write detailed content (2-4 sentences with specific technical details)
|
||||
- Rate confidence (0.0-1.0) based on how explicit/important it is
|
||||
- Categorize by type
|
||||
|
||||
**Only extract HIGH VALUE insights worth remembering for future sessions.**
|
||||
Skip routine operations (ls, git status, simple file reads, etc.).
|
||||
Focus on decisions, learnings, and technical discoveries.
|
||||
|
||||
Session transcript:
|
||||
{transcript}
|
||||
|
||||
Return valid JSON only (no markdown, no extra text):
|
||||
{{
|
||||
"insights": [
|
||||
{{"type": "decision|pattern|solution|blocker", "title": "Clear descriptive title", "content": "Detailed explanation with technical specifics...", "confidence": 0.85}}
|
||||
]
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
class InsightExtractor:
|
||||
"""Extract insights from session transcripts using LLM."""
|
||||
|
||||
def __init__(self, ollama_base_url: Optional[str] = None, model: Optional[str] = None):
|
||||
"""
|
||||
Initialize insight extractor.
|
||||
|
||||
Args:
|
||||
ollama_base_url: Ollama API base URL (defaults to Config.OLLAMA_HOST)
|
||||
model: LLM model to use (defaults to Config.OLLAMA_MODEL)
|
||||
"""
|
||||
self.ollama_base_url = ollama_base_url or Config.OLLAMA_HOST
|
||||
self.model = model or Config.get_active_llm_model()
|
||||
|
||||
logger.info(f"Initialized InsightExtractor with model: {self.model}")
|
||||
|
||||
async def extract(
|
||||
self,
|
||||
session_summary: str,
|
||||
insight_types: Optional[List[str]] = None
|
||||
) -> List[Insight]:
|
||||
"""
|
||||
Extract insights from session summary using LLM.
|
||||
|
||||
Args:
|
||||
session_summary: Condensed session transcript
|
||||
insight_types: Filter to specific types (defaults to all types)
|
||||
|
||||
Returns:
|
||||
List of Insight objects
|
||||
"""
|
||||
if insight_types is None:
|
||||
insight_types = ["decision", "pattern", "solution", "blocker"]
|
||||
|
||||
logger.info(f"Extracting insights from session summary ({len(session_summary)} chars)")
|
||||
|
||||
# Build prompt
|
||||
prompt = EXTRACTION_PROMPT.format(transcript=session_summary)
|
||||
|
||||
# Call Ollama
|
||||
try:
|
||||
insights = await self._call_ollama(prompt)
|
||||
|
||||
# Filter by requested types
|
||||
filtered_insights = [
|
||||
i for i in insights
|
||||
if i.type in insight_types
|
||||
]
|
||||
|
||||
logger.info(f"Extracted {len(filtered_insights)} insights (filtered from {len(insights)})")
|
||||
return filtered_insights
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to extract insights: {e}")
|
||||
return []
|
||||
|
||||
async def _call_ollama(self, prompt: str) -> List[Insight]:
|
||||
"""
|
||||
Call Ollama API to extract insights.
|
||||
|
||||
Args:
|
||||
prompt: Extraction prompt with session transcript
|
||||
|
||||
Returns:
|
||||
List of Insight objects
|
||||
"""
|
||||
url = f"{self.ollama_base_url}/api/generate"
|
||||
|
||||
payload = {
|
||||
"model": self.model,
|
||||
"prompt": prompt,
|
||||
"stream": False,
|
||||
"format": "json", # Request JSON response
|
||||
"options": {
|
||||
"temperature": 0.3, # Lower temperature for more focused extraction
|
||||
"num_predict": 2048, # Allow longer responses for multiple insights
|
||||
"top_k": 40,
|
||||
"top_p": 0.9
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=120.0) as client:
|
||||
response = await client.post(url, json=payload)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
response_text = result.get("response", "")
|
||||
|
||||
# Parse JSON response
|
||||
insights_data = self._parse_llm_response(response_text)
|
||||
|
||||
# Convert to Insight objects
|
||||
insights = []
|
||||
for item in insights_data.get("insights", []):
|
||||
try:
|
||||
insight = Insight(
|
||||
type=item.get("type", "pattern"),
|
||||
title=item.get("title", "Untitled"),
|
||||
content=item.get("content", ""),
|
||||
confidence=float(item.get("confidence", 0.5))
|
||||
)
|
||||
insights.append(insight)
|
||||
except (ValueError, KeyError) as e:
|
||||
logger.warning(f"Failed to parse insight item: {e}")
|
||||
continue
|
||||
|
||||
return insights
|
||||
|
||||
except httpx.HTTPError as e:
|
||||
logger.error(f"Ollama API error: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error calling Ollama: {e}")
|
||||
raise
|
||||
|
||||
def _parse_llm_response(self, response_text: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Parse LLM response JSON.
|
||||
|
||||
Handles cases where LLM might include markdown code blocks or extra text.
|
||||
|
||||
Args:
|
||||
response_text: Raw LLM response
|
||||
|
||||
Returns:
|
||||
Parsed JSON dict
|
||||
"""
|
||||
# Clean up response if it has markdown code blocks
|
||||
cleaned_text = response_text.strip()
|
||||
|
||||
# Remove markdown code blocks if present
|
||||
if cleaned_text.startswith("```json"):
|
||||
cleaned_text = cleaned_text[7:] # Remove ```json
|
||||
elif cleaned_text.startswith("```"):
|
||||
cleaned_text = cleaned_text[3:] # Remove ```
|
||||
|
||||
if cleaned_text.endswith("```"):
|
||||
cleaned_text = cleaned_text[:-3]
|
||||
|
||||
cleaned_text = cleaned_text.strip()
|
||||
|
||||
try:
|
||||
return json.loads(cleaned_text)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to parse LLM JSON response: {e}")
|
||||
logger.debug(f"Response text: {cleaned_text[:500]}")
|
||||
# Return empty insights on parse failure
|
||||
return {"insights": []}
|
||||
|
||||
def filter_by_confidence(
|
||||
self,
|
||||
insights: List[Insight],
|
||||
min_confidence: float = 0.7
|
||||
) -> List[Insight]:
|
||||
"""
|
||||
Filter insights by minimum confidence threshold.
|
||||
|
||||
Args:
|
||||
insights: List of insights
|
||||
min_confidence: Minimum confidence score (0.0-1.0)
|
||||
|
||||
Returns:
|
||||
Filtered list of insights
|
||||
"""
|
||||
return [i for i in insights if i.confidence >= min_confidence]
|
||||
|
||||
def group_by_type(self, insights: List[Insight]) -> Dict[str, List[Insight]]:
|
||||
"""
|
||||
Group insights by type.
|
||||
|
||||
Args:
|
||||
insights: List of insights
|
||||
|
||||
Returns:
|
||||
Dict mapping type to list of insights
|
||||
"""
|
||||
grouped: Dict[str, List[Insight]] = {
|
||||
"decision": [],
|
||||
"pattern": [],
|
||||
"solution": [],
|
||||
"blocker": []
|
||||
}
|
||||
|
||||
for insight in insights:
|
||||
if insight.type in grouped:
|
||||
grouped[insight.type].append(insight)
|
||||
|
||||
return grouped
|
||||
|
||||
def format_for_save(self, insight: Insight, session_id: str) -> str:
|
||||
"""
|
||||
Format insight as markdown for saving to knowledge base.
|
||||
|
||||
Args:
|
||||
insight: Insight to format
|
||||
session_id: Session ID for metadata
|
||||
|
||||
Returns:
|
||||
Markdown string
|
||||
"""
|
||||
type_emoji = {
|
||||
"decision": "💎",
|
||||
"pattern": "🔄",
|
||||
"solution": "✅",
|
||||
"blocker": "🚧"
|
||||
}
|
||||
|
||||
emoji = type_emoji.get(insight.type, "📝")
|
||||
type_label = insight.type.capitalize()
|
||||
|
||||
markdown = f"""# {emoji} {insight.title}
|
||||
|
||||
**Type:** {type_label}
|
||||
**Confidence:** {insight.confidence:.2f}
|
||||
**Source:** Session `{session_id}`
|
||||
|
||||
## Details
|
||||
|
||||
{insight.content}
|
||||
|
||||
---
|
||||
*Extracted from Claude Code session on {insight.metadata.get('extracted_date', 'unknown') if insight.metadata else 'unknown'}*
|
||||
"""
|
||||
return markdown
|
||||
|
||||
|
||||
def extract_insights_sync(
|
||||
session_summary: str,
|
||||
insight_types: Optional[List[str]] = None,
|
||||
min_confidence: float = 0.7
|
||||
) -> List[Insight]:
|
||||
"""
|
||||
Synchronous wrapper for extract_insights (for non-async contexts).
|
||||
|
||||
Args:
|
||||
session_summary: Condensed session transcript
|
||||
insight_types: Filter to specific types
|
||||
min_confidence: Minimum confidence threshold
|
||||
|
||||
Returns:
|
||||
List of high-confidence Insight objects
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
extractor = InsightExtractor()
|
||||
|
||||
# Run async extraction
|
||||
loop = asyncio.get_event_loop()
|
||||
insights = loop.run_until_complete(
|
||||
extractor.extract(session_summary, insight_types)
|
||||
)
|
||||
|
||||
# Filter by confidence
|
||||
return extractor.filter_by_confidence(insights, min_confidence)
|
||||
368
app/core/session_parser.py
Normal file
368
app/core/session_parser.py
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
"""
|
||||
Session Parser for Claude Code built-in session files.
|
||||
Parses .jsonl session files from ~/.claude/projects/ and extracts structured data.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
"""A user or assistant message."""
|
||||
role: str # "user" or "assistant"
|
||||
content: str
|
||||
timestamp: str
|
||||
uuid: str
|
||||
parent_uuid: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolCall:
|
||||
"""A tool call made during the session."""
|
||||
tool_name: str
|
||||
timestamp: str
|
||||
uuid: str
|
||||
parent_uuid: Optional[str] = None
|
||||
input_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class FileChange:
|
||||
"""A file modification tracked during the session."""
|
||||
file_path: str
|
||||
timestamp: str
|
||||
message_id: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class SessionData:
|
||||
"""Parsed session data."""
|
||||
session_id: str
|
||||
session_path: str
|
||||
messages: List[Message] = field(default_factory=list)
|
||||
tool_calls: List[ToolCall] = field(default_factory=list)
|
||||
file_changes: List[FileChange] = field(default_factory=list)
|
||||
start_time: Optional[str] = None
|
||||
end_time: Optional[str] = None
|
||||
git_branch: Optional[str] = None
|
||||
cwd: Optional[str] = None
|
||||
total_entries: int = 0
|
||||
|
||||
|
||||
class SessionParser:
|
||||
"""Parse Claude Code built-in session files (.jsonl format)."""
|
||||
|
||||
def __init__(self, session_path: str):
|
||||
"""
|
||||
Initialize parser with path to session file.
|
||||
|
||||
Args:
|
||||
session_path: Path to .jsonl session file
|
||||
"""
|
||||
self.path = Path(session_path).expanduser()
|
||||
if not self.path.exists():
|
||||
raise FileNotFoundError(f"Session file not found: {self.path}")
|
||||
|
||||
self.session_id = self.path.stem
|
||||
self.entries: List[Dict[str, Any]] = []
|
||||
|
||||
def parse(self) -> SessionData:
|
||||
"""
|
||||
Parse JSONL file and return structured session data.
|
||||
|
||||
Returns:
|
||||
SessionData object with parsed information
|
||||
"""
|
||||
logger.info(f"Parsing session file: {self.path}")
|
||||
|
||||
# Read JSONL file
|
||||
with open(self.path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
entry = json.loads(line)
|
||||
self.entries.append(entry)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(f"Failed to parse line: {e}")
|
||||
continue
|
||||
|
||||
# Build session data
|
||||
session_data = SessionData(
|
||||
session_id=self.session_id,
|
||||
session_path=str(self.path),
|
||||
total_entries=len(self.entries)
|
||||
)
|
||||
|
||||
# Extract metadata from first entry if available
|
||||
for entry in self.entries:
|
||||
if entry.get("type") in ["user", "assistant"]:
|
||||
if not session_data.cwd:
|
||||
session_data.cwd = entry.get("cwd")
|
||||
if not session_data.git_branch:
|
||||
session_data.git_branch = entry.get("gitBranch")
|
||||
break
|
||||
|
||||
# Parse entries
|
||||
for entry in self.entries:
|
||||
entry_type = entry.get("type")
|
||||
|
||||
if entry_type == "user":
|
||||
self._parse_user_message(entry, session_data)
|
||||
elif entry_type == "assistant":
|
||||
self._parse_assistant_message(entry, session_data)
|
||||
elif entry_type == "file-history-snapshot":
|
||||
self._parse_file_snapshot(entry, session_data)
|
||||
|
||||
# Set start/end times from messages
|
||||
if session_data.messages:
|
||||
session_data.start_time = session_data.messages[0].timestamp
|
||||
session_data.end_time = session_data.messages[-1].timestamp
|
||||
|
||||
logger.info(
|
||||
f"Parsed session {self.session_id}: "
|
||||
f"{len(session_data.messages)} messages, "
|
||||
f"{len(session_data.tool_calls)} tool calls, "
|
||||
f"{len(session_data.file_changes)} file changes"
|
||||
)
|
||||
|
||||
return session_data
|
||||
|
||||
def _parse_user_message(self, entry: Dict[str, Any], session_data: SessionData) -> None:
|
||||
"""Parse a user message entry."""
|
||||
message_obj = entry.get("message", {})
|
||||
content = message_obj.get("content", "")
|
||||
|
||||
session_data.messages.append(Message(
|
||||
role="user",
|
||||
content=content,
|
||||
timestamp=entry.get("timestamp", ""),
|
||||
uuid=entry.get("uuid", ""),
|
||||
parent_uuid=entry.get("parentUuid")
|
||||
))
|
||||
|
||||
def _parse_assistant_message(self, entry: Dict[str, Any], session_data: SessionData) -> None:
|
||||
"""Parse an assistant message entry."""
|
||||
message_obj = entry.get("message", {})
|
||||
content_parts = message_obj.get("content", [])
|
||||
|
||||
# Extract text content
|
||||
text_content = []
|
||||
for part in content_parts:
|
||||
if isinstance(part, dict):
|
||||
if part.get("type") == "text":
|
||||
text_content.append(part.get("text", ""))
|
||||
elif part.get("type") == "tool_use":
|
||||
# Track tool calls
|
||||
tool_call = ToolCall(
|
||||
tool_name=part.get("name", ""),
|
||||
timestamp=entry.get("timestamp", ""),
|
||||
uuid=entry.get("uuid", ""),
|
||||
parent_uuid=entry.get("parentUuid"),
|
||||
input_data=part.get("input")
|
||||
)
|
||||
session_data.tool_calls.append(tool_call)
|
||||
|
||||
# Only add message if there's text content
|
||||
if text_content:
|
||||
session_data.messages.append(Message(
|
||||
role="assistant",
|
||||
content="\n".join(text_content),
|
||||
timestamp=entry.get("timestamp", ""),
|
||||
uuid=entry.get("uuid", ""),
|
||||
parent_uuid=entry.get("parentUuid")
|
||||
))
|
||||
|
||||
def _parse_file_snapshot(self, entry: Dict[str, Any], session_data: SessionData) -> None:
|
||||
"""Parse a file history snapshot entry."""
|
||||
snapshot = entry.get("snapshot", {})
|
||||
tracked_files = snapshot.get("trackedFileBackups", {})
|
||||
|
||||
for file_path in tracked_files.keys():
|
||||
session_data.file_changes.append(FileChange(
|
||||
file_path=file_path,
|
||||
timestamp=snapshot.get("timestamp", ""),
|
||||
message_id=entry.get("messageId", "")
|
||||
))
|
||||
|
||||
def get_conversation(self) -> List[Message]:
|
||||
"""
|
||||
Extract user/assistant messages only (no tool calls).
|
||||
|
||||
Returns:
|
||||
List of Message objects in chronological order
|
||||
"""
|
||||
if not self.entries:
|
||||
session_data = self.parse()
|
||||
else:
|
||||
session_data = self.parse()
|
||||
|
||||
return session_data.messages
|
||||
|
||||
def get_tool_results(self) -> List[ToolCall]:
|
||||
"""
|
||||
Extract tool calls and results.
|
||||
|
||||
Returns:
|
||||
List of ToolCall objects
|
||||
"""
|
||||
if not self.entries:
|
||||
session_data = self.parse()
|
||||
else:
|
||||
session_data = self.parse()
|
||||
|
||||
return session_data.tool_calls
|
||||
|
||||
def get_file_changes(self) -> List[FileChange]:
|
||||
"""
|
||||
Extract file modifications from snapshots.
|
||||
|
||||
Returns:
|
||||
List of FileChange objects
|
||||
"""
|
||||
if not self.entries:
|
||||
session_data = self.parse()
|
||||
else:
|
||||
session_data = self.parse()
|
||||
|
||||
return session_data.file_changes
|
||||
|
||||
def get_summary_for_extraction(self, max_tokens: int = 8000) -> str:
|
||||
"""
|
||||
Build condensed conversation summary for LLM extraction.
|
||||
|
||||
Focuses on user/assistant messages and major tool calls.
|
||||
Truncates to fit within token limit (roughly 4 chars per token).
|
||||
|
||||
Args:
|
||||
max_tokens: Maximum tokens for summary (default: 8000)
|
||||
|
||||
Returns:
|
||||
Condensed conversation string
|
||||
"""
|
||||
session_data = self.parse()
|
||||
|
||||
summary_parts = []
|
||||
summary_parts.append(f"# Session: {self.session_id}")
|
||||
summary_parts.append(f"Project: {session_data.cwd or 'Unknown'}")
|
||||
summary_parts.append(f"Branch: {session_data.git_branch or 'Unknown'}")
|
||||
summary_parts.append(f"Duration: {session_data.start_time} to {session_data.end_time}")
|
||||
summary_parts.append(f"\n## Conversation ({len(session_data.messages)} messages)\n")
|
||||
|
||||
# Add messages
|
||||
for msg in session_data.messages:
|
||||
role_prefix = "USER" if msg.role == "user" else "ASSISTANT"
|
||||
# Truncate very long messages
|
||||
content = msg.content[:1000] if len(msg.content) > 1000 else msg.content
|
||||
summary_parts.append(f"{role_prefix}: {content}\n")
|
||||
|
||||
# Add tool calls summary
|
||||
if session_data.tool_calls:
|
||||
summary_parts.append(f"\n## Tool Calls ({len(session_data.tool_calls)})\n")
|
||||
for tool in session_data.tool_calls[:20]: # Limit to first 20
|
||||
summary_parts.append(f"- {tool.tool_name}")
|
||||
if tool.input_data:
|
||||
# Show first few input keys
|
||||
keys = list(tool.input_data.keys())[:3]
|
||||
summary_parts.append(f" ({', '.join(keys)})")
|
||||
summary_parts.append("\n")
|
||||
|
||||
# Add file changes summary
|
||||
if session_data.file_changes:
|
||||
summary_parts.append(f"\n## Files Modified ({len(session_data.file_changes)})\n")
|
||||
unique_files = list(set(fc.file_path for fc in session_data.file_changes))
|
||||
for file_path in unique_files[:10]: # Limit to first 10
|
||||
summary_parts.append(f"- {file_path}\n")
|
||||
|
||||
full_summary = "".join(summary_parts)
|
||||
|
||||
# Truncate if needed (roughly 4 chars per token)
|
||||
max_chars = max_tokens * 4
|
||||
if len(full_summary) > max_chars:
|
||||
full_summary = full_summary[:max_chars] + "\n\n[... truncated ...]"
|
||||
|
||||
return full_summary
|
||||
|
||||
|
||||
def get_project_sessions_dir(project_path: str) -> Path:
|
||||
"""
|
||||
Get the Claude Code sessions directory for a project path.
|
||||
|
||||
Claude Code stores sessions in ~/.claude/projects/{encoded-path}/
|
||||
where path like "/Users/user/Projects/myapp" becomes "-Users-user-Projects-myapp"
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to project directory
|
||||
|
||||
Returns:
|
||||
Path to sessions directory
|
||||
"""
|
||||
# Encode path: replace slashes with hyphens
|
||||
# Claude keeps the leading hyphen (e.g., "-Users-iamanmp-Projects-claude-os")
|
||||
encoded_path = project_path.replace("/", "-")
|
||||
|
||||
sessions_dir = Path.home() / ".claude" / "projects" / encoded_path
|
||||
return sessions_dir
|
||||
|
||||
|
||||
def list_session_files(project_path: str, limit: int = 50) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
List session files for a project.
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to project directory
|
||||
limit: Maximum number of sessions to return (most recent first)
|
||||
|
||||
Returns:
|
||||
List of session file info dicts
|
||||
"""
|
||||
sessions_dir = get_project_sessions_dir(project_path)
|
||||
|
||||
if not sessions_dir.exists():
|
||||
logger.warning(f"Sessions directory not found: {sessions_dir}")
|
||||
return []
|
||||
|
||||
# Find all .jsonl files
|
||||
session_files = list(sessions_dir.glob("*.jsonl"))
|
||||
|
||||
# Sort by modification time (most recent first)
|
||||
session_files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
||||
|
||||
# Limit results
|
||||
session_files = session_files[:limit]
|
||||
|
||||
# Build info list
|
||||
sessions_info = []
|
||||
for session_file in session_files:
|
||||
stat = session_file.stat()
|
||||
|
||||
# Try to get basic info without full parse
|
||||
session_id = session_file.stem
|
||||
created_time = datetime.fromtimestamp(stat.st_ctime).isoformat()
|
||||
modified_time = datetime.fromtimestamp(stat.st_mtime).isoformat()
|
||||
|
||||
# Count lines to estimate message count
|
||||
try:
|
||||
with open(session_file, 'r') as f:
|
||||
line_count = sum(1 for _ in f)
|
||||
except Exception:
|
||||
line_count = 0
|
||||
|
||||
sessions_info.append({
|
||||
"id": session_id,
|
||||
"path": str(session_file),
|
||||
"created": created_time,
|
||||
"modified": modified_time,
|
||||
"size_bytes": stat.st_size,
|
||||
"estimated_entries": line_count
|
||||
})
|
||||
|
||||
return sessions_info
|
||||
992
app/core/skill_manager.py
Normal file
992
app/core/skill_manager.py
Normal file
|
|
@ -0,0 +1,992 @@
|
|||
"""
|
||||
Skill Manager for Claude Code skills.
|
||||
Manages global skills (~/.claude/skills/) and project-level skills ({project}/.claude/skills/).
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import shutil
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Skill:
|
||||
"""A Claude Code skill."""
|
||||
name: str
|
||||
path: str
|
||||
description: str
|
||||
scope: str # "global" or "project"
|
||||
source: str # "claude-os-core", "template", "custom"
|
||||
content: str = ""
|
||||
enabled: bool = True
|
||||
category: Optional[str] = None
|
||||
tags: List[str] = field(default_factory=list)
|
||||
created: Optional[str] = None
|
||||
modified: Optional[str] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"name": self.name,
|
||||
"path": self.path,
|
||||
"description": self.description,
|
||||
"scope": self.scope,
|
||||
"source": self.source,
|
||||
"content": self.content,
|
||||
"enabled": self.enabled,
|
||||
"category": self.category,
|
||||
"tags": self.tags,
|
||||
"created": self.created,
|
||||
"modified": self.modified
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class SkillTemplate:
|
||||
"""A skill template that can be installed."""
|
||||
name: str
|
||||
category: str
|
||||
description: str
|
||||
path: str
|
||||
tags: List[str] = field(default_factory=list)
|
||||
version: str = "1.0.0"
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"name": self.name,
|
||||
"category": self.category,
|
||||
"description": self.description,
|
||||
"path": self.path,
|
||||
"tags": self.tags,
|
||||
"version": self.version
|
||||
}
|
||||
|
||||
|
||||
class SkillManager:
|
||||
"""Manage Claude Code skills (global and project-level)."""
|
||||
|
||||
GLOBAL_SKILLS_DIR = Path.home() / ".claude" / "skills"
|
||||
TEMPLATES_DIR = Path(__file__).parent.parent.parent / "templates" / "skill-library"
|
||||
|
||||
# Core skills that should remain global
|
||||
CORE_SKILLS = {"memory", "remember-this"}
|
||||
|
||||
def __init__(self, project_path: Optional[str] = None):
|
||||
"""
|
||||
Initialize skill manager.
|
||||
|
||||
Args:
|
||||
project_path: Optional project path for project-level operations
|
||||
"""
|
||||
self.project_path = Path(project_path) if project_path else None
|
||||
|
||||
# Ensure global skills directory exists
|
||||
self.GLOBAL_SKILLS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@property
|
||||
def project_skills_dir(self) -> Optional[Path]:
|
||||
"""Get project skills directory."""
|
||||
if self.project_path:
|
||||
return self.project_path / ".claude" / "skills"
|
||||
return None
|
||||
|
||||
def list_global_skills(self) -> List[Skill]:
|
||||
"""
|
||||
List skills in ~/.claude/skills/
|
||||
|
||||
Returns:
|
||||
List of global Skill objects
|
||||
"""
|
||||
skills = []
|
||||
|
||||
if not self.GLOBAL_SKILLS_DIR.exists():
|
||||
return skills
|
||||
|
||||
for item in self.GLOBAL_SKILLS_DIR.iterdir():
|
||||
if item.is_dir() or (item.is_symlink() and item.resolve().is_dir()):
|
||||
try:
|
||||
skill = self._parse_skill(item, scope="global")
|
||||
if skill:
|
||||
skills.append(skill)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to parse skill {item.name}: {e}")
|
||||
|
||||
return sorted(skills, key=lambda s: s.name)
|
||||
|
||||
def list_project_skills(self) -> List[Skill]:
|
||||
"""
|
||||
List skills in {project}/.claude/skills/
|
||||
|
||||
Returns:
|
||||
List of project Skill objects
|
||||
"""
|
||||
skills = []
|
||||
|
||||
if not self.project_skills_dir or not self.project_skills_dir.exists():
|
||||
return skills
|
||||
|
||||
for item in self.project_skills_dir.iterdir():
|
||||
if item.is_dir():
|
||||
try:
|
||||
skill = self._parse_skill(item, scope="project")
|
||||
if skill:
|
||||
skills.append(skill)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to parse project skill {item.name}: {e}")
|
||||
|
||||
return sorted(skills, key=lambda s: s.name)
|
||||
|
||||
def list_all_skills(self, include_content: bool = False) -> Dict[str, List[Skill]]:
|
||||
"""
|
||||
List all skills (global and project).
|
||||
|
||||
Args:
|
||||
include_content: Whether to include skill content
|
||||
|
||||
Returns:
|
||||
Dict with "global" and "project" skill lists
|
||||
"""
|
||||
global_skills = self.list_global_skills()
|
||||
project_skills = self.list_project_skills()
|
||||
|
||||
if not include_content:
|
||||
for skill in global_skills + project_skills:
|
||||
skill.content = ""
|
||||
|
||||
return {
|
||||
"global": global_skills,
|
||||
"project": project_skills
|
||||
}
|
||||
|
||||
def list_templates(self, category: Optional[str] = None) -> List[SkillTemplate]:
|
||||
"""
|
||||
List available skill templates.
|
||||
|
||||
Args:
|
||||
category: Optional category filter
|
||||
|
||||
Returns:
|
||||
List of SkillTemplate objects
|
||||
"""
|
||||
templates = []
|
||||
|
||||
if not self.TEMPLATES_DIR.exists():
|
||||
logger.warning(f"Templates directory not found: {self.TEMPLATES_DIR}")
|
||||
return templates
|
||||
|
||||
for category_dir in self.TEMPLATES_DIR.iterdir():
|
||||
if not category_dir.is_dir():
|
||||
continue
|
||||
|
||||
if category and category_dir.name != category:
|
||||
continue
|
||||
|
||||
for template_dir in category_dir.iterdir():
|
||||
if not template_dir.is_dir():
|
||||
continue
|
||||
|
||||
try:
|
||||
template = self._parse_template(template_dir, category_dir.name)
|
||||
if template:
|
||||
templates.append(template)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to parse template {template_dir.name}: {e}")
|
||||
|
||||
return sorted(templates, key=lambda t: (t.category, t.name))
|
||||
|
||||
def get_template_categories(self) -> List[str]:
|
||||
"""
|
||||
Get list of template categories.
|
||||
|
||||
Returns:
|
||||
List of category names
|
||||
"""
|
||||
categories = []
|
||||
|
||||
if not self.TEMPLATES_DIR.exists():
|
||||
return categories
|
||||
|
||||
for item in self.TEMPLATES_DIR.iterdir():
|
||||
if item.is_dir():
|
||||
categories.append(item.name)
|
||||
|
||||
return sorted(categories)
|
||||
|
||||
def get_skill(self, name: str, scope: str) -> Optional[Skill]:
|
||||
"""
|
||||
Get skill details including content.
|
||||
|
||||
Args:
|
||||
name: Skill name
|
||||
scope: "global" or "project"
|
||||
|
||||
Returns:
|
||||
Skill object or None if not found
|
||||
"""
|
||||
if scope == "global":
|
||||
skill_path = self.GLOBAL_SKILLS_DIR / name
|
||||
elif scope == "project" and self.project_skills_dir:
|
||||
skill_path = self.project_skills_dir / name
|
||||
else:
|
||||
return None
|
||||
|
||||
if not skill_path.exists():
|
||||
# Check if it's a symlink
|
||||
if skill_path.is_symlink():
|
||||
skill_path = skill_path.resolve()
|
||||
else:
|
||||
return None
|
||||
|
||||
return self._parse_skill(skill_path, scope, include_content=True)
|
||||
|
||||
def install_template(
|
||||
self,
|
||||
template_name: str,
|
||||
custom_name: Optional[str] = None
|
||||
) -> Skill:
|
||||
"""
|
||||
Install a skill template to the project.
|
||||
|
||||
Args:
|
||||
template_name: Name of template to install
|
||||
custom_name: Optional custom name for the installed skill
|
||||
|
||||
Returns:
|
||||
Installed Skill object
|
||||
|
||||
Raises:
|
||||
ValueError: If template not found or no project path set
|
||||
FileExistsError: If skill already exists
|
||||
"""
|
||||
if not self.project_path:
|
||||
raise ValueError("No project path set")
|
||||
|
||||
# Find template
|
||||
template = None
|
||||
for t in self.list_templates():
|
||||
if t.name == template_name:
|
||||
template = t
|
||||
break
|
||||
|
||||
if not template:
|
||||
raise ValueError(f"Template not found: {template_name}")
|
||||
|
||||
# Determine target name and path
|
||||
skill_name = custom_name or template_name
|
||||
target_dir = self.project_skills_dir / skill_name
|
||||
|
||||
if target_dir.exists():
|
||||
raise FileExistsError(f"Skill already exists: {skill_name}")
|
||||
|
||||
# Create project skills directory if needed
|
||||
self.project_skills_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy template to project
|
||||
template_path = Path(template.path)
|
||||
shutil.copytree(template_path, target_dir)
|
||||
|
||||
# Update metadata if renamed
|
||||
if custom_name:
|
||||
metadata_file = target_dir / "metadata.json"
|
||||
if metadata_file.exists():
|
||||
with open(metadata_file, 'r') as f:
|
||||
metadata = json.load(f)
|
||||
metadata["name"] = custom_name
|
||||
metadata["source"] = "template"
|
||||
metadata["installed_from"] = template_name
|
||||
metadata["installed_at"] = datetime.now().isoformat()
|
||||
with open(metadata_file, 'w') as f:
|
||||
json.dump(metadata, f, indent=2)
|
||||
|
||||
logger.info(f"Installed template {template_name} as {skill_name}")
|
||||
|
||||
return self._parse_skill(target_dir, scope="project", include_content=True)
|
||||
|
||||
def create_skill(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
content: str,
|
||||
category: Optional[str] = None,
|
||||
tags: Optional[List[str]] = None
|
||||
) -> Skill:
|
||||
"""
|
||||
Create a custom skill for the project.
|
||||
|
||||
Args:
|
||||
name: Skill name
|
||||
description: Skill description
|
||||
content: Skill content (markdown)
|
||||
category: Optional category
|
||||
tags: Optional tags
|
||||
|
||||
Returns:
|
||||
Created Skill object
|
||||
|
||||
Raises:
|
||||
ValueError: If no project path set
|
||||
FileExistsError: If skill already exists
|
||||
"""
|
||||
if not self.project_path:
|
||||
raise ValueError("No project path set")
|
||||
|
||||
target_dir = self.project_skills_dir / name
|
||||
|
||||
if target_dir.exists():
|
||||
raise FileExistsError(f"Skill already exists: {name}")
|
||||
|
||||
# Create directories
|
||||
self.project_skills_dir.mkdir(parents=True, exist_ok=True)
|
||||
target_dir.mkdir()
|
||||
|
||||
# Write skill content
|
||||
skill_file = target_dir / "skill.md"
|
||||
with open(skill_file, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
# Write metadata
|
||||
metadata = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"category": category,
|
||||
"tags": tags or [],
|
||||
"source": "custom",
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
metadata_file = target_dir / "metadata.json"
|
||||
with open(metadata_file, 'w') as f:
|
||||
json.dump(metadata, f, indent=2)
|
||||
|
||||
logger.info(f"Created custom skill: {name}")
|
||||
|
||||
return self._parse_skill(target_dir, scope="project", include_content=True)
|
||||
|
||||
def update_skill(
|
||||
self,
|
||||
name: str,
|
||||
content: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
tags: Optional[List[str]] = None
|
||||
) -> Skill:
|
||||
"""
|
||||
Update an existing project skill.
|
||||
|
||||
Args:
|
||||
name: Skill name
|
||||
content: New content (optional)
|
||||
description: New description (optional)
|
||||
tags: New tags (optional)
|
||||
|
||||
Returns:
|
||||
Updated Skill object
|
||||
|
||||
Raises:
|
||||
ValueError: If no project path set
|
||||
FileNotFoundError: If skill not found
|
||||
"""
|
||||
if not self.project_path:
|
||||
raise ValueError("No project path set")
|
||||
|
||||
skill_dir = self.project_skills_dir / name
|
||||
|
||||
if not skill_dir.exists():
|
||||
raise FileNotFoundError(f"Skill not found: {name}")
|
||||
|
||||
# Update content
|
||||
if content is not None:
|
||||
skill_file = skill_dir / "skill.md"
|
||||
with open(skill_file, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
# Update metadata
|
||||
metadata_file = skill_dir / "metadata.json"
|
||||
if metadata_file.exists():
|
||||
with open(metadata_file, 'r') as f:
|
||||
metadata = json.load(f)
|
||||
else:
|
||||
metadata = {"name": name}
|
||||
|
||||
if description is not None:
|
||||
metadata["description"] = description
|
||||
if tags is not None:
|
||||
metadata["tags"] = tags
|
||||
metadata["modified_at"] = datetime.now().isoformat()
|
||||
|
||||
with open(metadata_file, 'w') as f:
|
||||
json.dump(metadata, f, indent=2)
|
||||
|
||||
logger.info(f"Updated skill: {name}")
|
||||
|
||||
return self._parse_skill(skill_dir, scope="project", include_content=True)
|
||||
|
||||
def delete_skill(self, name: str) -> bool:
|
||||
"""
|
||||
Delete a project skill.
|
||||
|
||||
Args:
|
||||
name: Skill name
|
||||
|
||||
Returns:
|
||||
True if deleted
|
||||
|
||||
Raises:
|
||||
ValueError: If no project path set or trying to delete global skill
|
||||
FileNotFoundError: If skill not found
|
||||
"""
|
||||
if not self.project_path:
|
||||
raise ValueError("No project path set")
|
||||
|
||||
skill_dir = self.project_skills_dir / name
|
||||
|
||||
if not skill_dir.exists():
|
||||
raise FileNotFoundError(f"Skill not found: {name}")
|
||||
|
||||
shutil.rmtree(skill_dir)
|
||||
logger.info(f"Deleted skill: {name}")
|
||||
|
||||
return True
|
||||
|
||||
def _parse_skill(
|
||||
self,
|
||||
skill_path: Path,
|
||||
scope: str,
|
||||
include_content: bool = False
|
||||
) -> Optional[Skill]:
|
||||
"""
|
||||
Parse a skill directory into a Skill object.
|
||||
|
||||
Args:
|
||||
skill_path: Path to skill directory
|
||||
scope: "global" or "project"
|
||||
include_content: Whether to include skill content
|
||||
|
||||
Returns:
|
||||
Skill object or None if invalid
|
||||
"""
|
||||
# Resolve symlinks
|
||||
if skill_path.is_symlink():
|
||||
resolved_path = skill_path.resolve()
|
||||
else:
|
||||
resolved_path = skill_path
|
||||
|
||||
if not resolved_path.exists() or not resolved_path.is_dir():
|
||||
return None
|
||||
|
||||
name = skill_path.name
|
||||
|
||||
# Determine source
|
||||
if name in self.CORE_SKILLS and scope == "global":
|
||||
source = "claude-os-core"
|
||||
elif scope == "global":
|
||||
source = "template"
|
||||
else:
|
||||
source = "custom"
|
||||
|
||||
# Load metadata if exists
|
||||
metadata_file = resolved_path / "metadata.json"
|
||||
metadata = {}
|
||||
if metadata_file.exists():
|
||||
try:
|
||||
with open(metadata_file, 'r') as f:
|
||||
metadata = json.load(f)
|
||||
source = metadata.get("source", source)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load metadata for {name}: {e}")
|
||||
|
||||
# Load content
|
||||
content = ""
|
||||
if include_content:
|
||||
skill_file = resolved_path / "skill.md"
|
||||
if skill_file.exists():
|
||||
with open(skill_file, 'r') as f:
|
||||
content = f.read()
|
||||
else:
|
||||
# Try other common names
|
||||
for filename in ["README.md", "index.md", f"{name}.md"]:
|
||||
alt_file = resolved_path / filename
|
||||
if alt_file.exists():
|
||||
with open(alt_file, 'r') as f:
|
||||
content = f.read()
|
||||
break
|
||||
|
||||
# Extract description from content if not in metadata
|
||||
description = metadata.get("description", "")
|
||||
if not description and content:
|
||||
# Try to extract from first paragraph
|
||||
lines = content.strip().split('\n')
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
description = line[:200]
|
||||
break
|
||||
|
||||
# Get file stats
|
||||
stat = resolved_path.stat()
|
||||
created = datetime.fromtimestamp(stat.st_ctime).isoformat()
|
||||
modified = datetime.fromtimestamp(stat.st_mtime).isoformat()
|
||||
|
||||
return Skill(
|
||||
name=name,
|
||||
path=str(skill_path),
|
||||
description=description,
|
||||
scope=scope,
|
||||
source=source,
|
||||
content=content,
|
||||
enabled=True,
|
||||
category=metadata.get("category"),
|
||||
tags=metadata.get("tags", []),
|
||||
created=metadata.get("created_at", created),
|
||||
modified=metadata.get("modified_at", modified)
|
||||
)
|
||||
|
||||
def _parse_template(self, template_path: Path, category: str) -> Optional[SkillTemplate]:
|
||||
"""
|
||||
Parse a template directory into a SkillTemplate object.
|
||||
|
||||
Args:
|
||||
template_path: Path to template directory
|
||||
category: Template category
|
||||
|
||||
Returns:
|
||||
SkillTemplate object or None if invalid
|
||||
"""
|
||||
if not template_path.is_dir():
|
||||
return None
|
||||
|
||||
name = template_path.name
|
||||
|
||||
# Load metadata
|
||||
metadata_file = template_path / "metadata.json"
|
||||
metadata = {}
|
||||
if metadata_file.exists():
|
||||
try:
|
||||
with open(metadata_file, 'r') as f:
|
||||
metadata = json.load(f)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load template metadata for {name}: {e}")
|
||||
|
||||
# Get description
|
||||
description = metadata.get("description", "")
|
||||
if not description:
|
||||
# Try to extract from skill.md
|
||||
skill_file = template_path / "skill.md"
|
||||
if skill_file.exists():
|
||||
with open(skill_file, 'r') as f:
|
||||
content = f.read()
|
||||
lines = content.strip().split('\n')
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
description = line[:200]
|
||||
break
|
||||
|
||||
return SkillTemplate(
|
||||
name=name,
|
||||
category=category,
|
||||
description=description,
|
||||
path=str(template_path),
|
||||
tags=metadata.get("tags", []),
|
||||
version=metadata.get("version", "1.0.0")
|
||||
)
|
||||
|
||||
|
||||
# Convenience functions
|
||||
|
||||
def list_skills(project_path: Optional[str] = None) -> Dict[str, List[Dict]]:
|
||||
"""
|
||||
List all skills.
|
||||
|
||||
Args:
|
||||
project_path: Optional project path
|
||||
|
||||
Returns:
|
||||
Dict with global and project skill lists as dicts
|
||||
"""
|
||||
manager = SkillManager(project_path)
|
||||
skills = manager.list_all_skills()
|
||||
|
||||
return {
|
||||
"global": [s.to_dict() for s in skills["global"]],
|
||||
"project": [s.to_dict() for s in skills["project"]]
|
||||
}
|
||||
|
||||
|
||||
def list_skill_templates(category: Optional[str] = None) -> List[Dict]:
|
||||
"""
|
||||
List available skill templates.
|
||||
|
||||
Args:
|
||||
category: Optional category filter
|
||||
|
||||
Returns:
|
||||
List of template dicts
|
||||
"""
|
||||
manager = SkillManager()
|
||||
templates = manager.list_templates(category)
|
||||
return [t.to_dict() for t in templates]
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# COMMUNITY SKILLS - Fetch from external GitHub repositories
|
||||
# ============================================================================
|
||||
|
||||
import httpx
|
||||
import base64
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
@dataclass
|
||||
class CommunitySkill:
|
||||
"""A skill from an external community repository."""
|
||||
name: str
|
||||
description: str
|
||||
source: str # "anthropic", "superpowers", etc.
|
||||
repo: str # Full repo path e.g., "anthropics/skills"
|
||||
path: str # Path within repo e.g., "skills/frontend-design"
|
||||
tags: List[str] = field(default_factory=list)
|
||||
url: str = "" # GitHub URL
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"source": self.source,
|
||||
"repo": self.repo,
|
||||
"path": self.path,
|
||||
"tags": self.tags,
|
||||
"url": self.url
|
||||
}
|
||||
|
||||
|
||||
# Community skill sources configuration
|
||||
COMMUNITY_SOURCES = {
|
||||
"anthropic": {
|
||||
"repo": "anthropics/skills",
|
||||
"skills_path": "skills",
|
||||
"name": "Anthropic Official",
|
||||
"description": "Official skills from Anthropic"
|
||||
},
|
||||
"superpowers": {
|
||||
"repo": "obra/superpowers",
|
||||
"skills_path": "skills",
|
||||
"name": "Superpowers",
|
||||
"description": "Battle-tested skills for TDD, debugging, and collaboration"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CommunitySkillsManager:
|
||||
"""Fetch and install skills from community GitHub repositories."""
|
||||
|
||||
GITHUB_API = "https://api.github.com"
|
||||
GITHUB_RAW = "https://raw.githubusercontent.com"
|
||||
CACHE_TTL = 3600 # Cache for 1 hour
|
||||
|
||||
def __init__(self):
|
||||
self._cache: Dict[str, Any] = {}
|
||||
self._cache_times: Dict[str, float] = {}
|
||||
|
||||
def _is_cache_valid(self, key: str) -> bool:
|
||||
"""Check if cache entry is still valid."""
|
||||
if key not in self._cache_times:
|
||||
return False
|
||||
import time
|
||||
return (time.time() - self._cache_times[key]) < self.CACHE_TTL
|
||||
|
||||
def _set_cache(self, key: str, value: Any) -> None:
|
||||
"""Set cache entry."""
|
||||
import time
|
||||
self._cache[key] = value
|
||||
self._cache_times[key] = time.time()
|
||||
|
||||
async def list_community_skills(self, source: Optional[str] = None) -> List[CommunitySkill]:
|
||||
"""
|
||||
List skills from community repositories.
|
||||
|
||||
Args:
|
||||
source: Optional source filter ("anthropic", "superpowers", etc.)
|
||||
|
||||
Returns:
|
||||
List of CommunitySkill objects
|
||||
"""
|
||||
skills = []
|
||||
sources = {source: COMMUNITY_SOURCES[source]} if source else COMMUNITY_SOURCES
|
||||
|
||||
for source_key, config in sources.items():
|
||||
try:
|
||||
source_skills = await self._fetch_skills_from_repo(source_key, config)
|
||||
skills.extend(source_skills)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to fetch skills from {source_key}: {e}")
|
||||
|
||||
return skills
|
||||
|
||||
async def _fetch_skills_from_repo(
|
||||
self,
|
||||
source_key: str,
|
||||
config: Dict[str, str]
|
||||
) -> List[CommunitySkill]:
|
||||
"""Fetch skill list from a GitHub repository."""
|
||||
cache_key = f"skills_{source_key}"
|
||||
if self._is_cache_valid(cache_key):
|
||||
return self._cache[cache_key]
|
||||
|
||||
repo = config["repo"]
|
||||
skills_path = config["skills_path"]
|
||||
|
||||
# Fetch directory listing from GitHub API
|
||||
url = f"{self.GITHUB_API}/repos/{repo}/contents/{skills_path}"
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url, timeout=15.0)
|
||||
if response.status_code != 200:
|
||||
logger.error(f"GitHub API error for {repo}: {response.status_code}")
|
||||
return []
|
||||
|
||||
items = response.json()
|
||||
|
||||
skills = []
|
||||
for item in items:
|
||||
if item.get("type") != "dir":
|
||||
continue
|
||||
|
||||
skill_name = item["name"]
|
||||
skill_path = f"{skills_path}/{skill_name}"
|
||||
|
||||
# Try to fetch SKILL.md to get description
|
||||
description = await self._fetch_skill_description(repo, skill_path)
|
||||
|
||||
skill = CommunitySkill(
|
||||
name=skill_name,
|
||||
description=description,
|
||||
source=source_key,
|
||||
repo=repo,
|
||||
path=skill_path,
|
||||
tags=self._infer_tags(skill_name, description),
|
||||
url=f"https://github.com/{repo}/tree/main/{skill_path}"
|
||||
)
|
||||
skills.append(skill)
|
||||
|
||||
self._set_cache(cache_key, skills)
|
||||
return skills
|
||||
|
||||
async def _fetch_skill_description(self, repo: str, skill_path: str) -> str:
|
||||
"""Fetch skill description from SKILL.md."""
|
||||
# Try both SKILL.md and skill.md
|
||||
for filename in ["SKILL.md", "skill.md"]:
|
||||
url = f"{self.GITHUB_RAW}/{repo}/main/{skill_path}/{filename}"
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url, timeout=10.0)
|
||||
if response.status_code == 200:
|
||||
content = response.text
|
||||
# Extract description from YAML frontmatter or first paragraph
|
||||
return self._extract_description(content)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return ""
|
||||
|
||||
def _extract_description(self, content: str) -> str:
|
||||
"""Extract description from skill markdown content."""
|
||||
# Try YAML frontmatter first
|
||||
frontmatter_match = re.match(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
|
||||
if frontmatter_match:
|
||||
frontmatter = frontmatter_match.group(1)
|
||||
desc_match = re.search(r'description:\s*["\']?([^"\'\n]+)["\']?', frontmatter)
|
||||
if desc_match:
|
||||
return desc_match.group(1).strip()[:200]
|
||||
|
||||
# Fall back to first non-header paragraph
|
||||
lines = content.split('\n')
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#') and not line.startswith('---'):
|
||||
return line[:200]
|
||||
|
||||
return ""
|
||||
|
||||
def _infer_tags(self, name: str, description: str) -> List[str]:
|
||||
"""Infer tags from skill name and description."""
|
||||
tags = []
|
||||
text = f"{name} {description}".lower()
|
||||
|
||||
tag_keywords = {
|
||||
"testing": ["test", "tdd", "spec", "jest", "pytest", "rspec"],
|
||||
"debugging": ["debug", "error", "fix", "trace"],
|
||||
"frontend": ["frontend", "react", "vue", "ui", "css", "design"],
|
||||
"backend": ["backend", "api", "server", "database"],
|
||||
"documentation": ["doc", "readme", "markdown", "write"],
|
||||
"automation": ["automat", "script", "workflow"],
|
||||
"design": ["design", "canvas", "art", "visual", "brand"],
|
||||
"git": ["git", "commit", "branch", "merge"],
|
||||
}
|
||||
|
||||
for tag, keywords in tag_keywords.items():
|
||||
if any(kw in text for kw in keywords):
|
||||
tags.append(tag)
|
||||
|
||||
return tags
|
||||
|
||||
async def get_skill_content(self, repo: str, skill_path: str) -> Dict[str, str]:
|
||||
"""
|
||||
Fetch full skill content from GitHub.
|
||||
|
||||
Returns:
|
||||
Dict with filename -> content mappings
|
||||
"""
|
||||
# Get list of files in skill directory
|
||||
url = f"{self.GITHUB_API}/repos/{repo}/contents/{skill_path}"
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url, timeout=15.0)
|
||||
if response.status_code != 200:
|
||||
raise ValueError(f"Failed to fetch skill: {response.status_code}")
|
||||
|
||||
items = response.json()
|
||||
|
||||
contents = {}
|
||||
for item in items:
|
||||
if item.get("type") != "file":
|
||||
continue
|
||||
|
||||
filename = item["name"]
|
||||
# Fetch file content
|
||||
file_url = f"{self.GITHUB_RAW}/{repo}/main/{skill_path}/{filename}"
|
||||
|
||||
file_response = await client.get(file_url, timeout=10.0)
|
||||
if file_response.status_code == 200:
|
||||
contents[filename] = file_response.text
|
||||
|
||||
return contents
|
||||
|
||||
async def install_community_skill(
|
||||
self,
|
||||
skill: CommunitySkill,
|
||||
project_path: str,
|
||||
custom_name: Optional[str] = None
|
||||
) -> Skill:
|
||||
"""
|
||||
Install a community skill to a project.
|
||||
|
||||
Args:
|
||||
skill: CommunitySkill to install
|
||||
project_path: Target project path
|
||||
custom_name: Optional custom name for the skill
|
||||
|
||||
Returns:
|
||||
Installed Skill object
|
||||
"""
|
||||
# Fetch skill content
|
||||
contents = await self.get_skill_content(skill.repo, skill.path)
|
||||
|
||||
if not contents:
|
||||
raise ValueError(f"No content found for skill: {skill.name}")
|
||||
|
||||
# Determine target directory
|
||||
skill_name = custom_name or skill.name
|
||||
target_dir = Path(project_path) / ".claude" / "skills" / skill_name
|
||||
|
||||
if target_dir.exists():
|
||||
raise FileExistsError(f"Skill already exists: {skill_name}")
|
||||
|
||||
# Create directory
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write files
|
||||
for filename, content in contents.items():
|
||||
# Normalize SKILL.md to skill.md
|
||||
if filename.upper() == "SKILL.MD":
|
||||
filename = "skill.md"
|
||||
|
||||
file_path = target_dir / filename
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
# Create/update metadata
|
||||
metadata_file = target_dir / "metadata.json"
|
||||
metadata = {
|
||||
"name": skill_name,
|
||||
"description": skill.description,
|
||||
"source": f"community:{skill.source}",
|
||||
"original_repo": skill.repo,
|
||||
"original_path": skill.path,
|
||||
"installed_at": datetime.now().isoformat(),
|
||||
"tags": skill.tags
|
||||
}
|
||||
|
||||
with open(metadata_file, 'w') as f:
|
||||
json.dump(metadata, f, indent=2)
|
||||
|
||||
logger.info(f"Installed community skill {skill.name} from {skill.source}")
|
||||
|
||||
# Return as Skill object
|
||||
manager = SkillManager(project_path)
|
||||
return manager._parse_skill(target_dir, scope="project", include_content=True)
|
||||
|
||||
|
||||
# Singleton instance
|
||||
_community_manager: Optional[CommunitySkillsManager] = None
|
||||
|
||||
|
||||
def get_community_manager() -> CommunitySkillsManager:
|
||||
"""Get or create the community skills manager singleton."""
|
||||
global _community_manager
|
||||
if _community_manager is None:
|
||||
_community_manager = CommunitySkillsManager()
|
||||
return _community_manager
|
||||
|
||||
|
||||
async def list_community_skills(source: Optional[str] = None) -> List[Dict]:
|
||||
"""
|
||||
List available community skills.
|
||||
|
||||
Args:
|
||||
source: Optional source filter
|
||||
|
||||
Returns:
|
||||
List of community skill dicts
|
||||
"""
|
||||
manager = get_community_manager()
|
||||
skills = await manager.list_community_skills(source)
|
||||
return [s.to_dict() for s in skills]
|
||||
|
||||
|
||||
async def install_community_skill(
|
||||
source: str,
|
||||
skill_name: str,
|
||||
project_path: str,
|
||||
custom_name: Optional[str] = None
|
||||
) -> Dict:
|
||||
"""
|
||||
Install a community skill to a project.
|
||||
|
||||
Args:
|
||||
source: Source key ("anthropic", "superpowers")
|
||||
skill_name: Name of the skill
|
||||
project_path: Target project path
|
||||
custom_name: Optional custom name
|
||||
|
||||
Returns:
|
||||
Installed skill dict
|
||||
"""
|
||||
manager = get_community_manager()
|
||||
|
||||
# Find the skill
|
||||
skills = await manager.list_community_skills(source)
|
||||
skill = next((s for s in skills if s.name == skill_name), None)
|
||||
|
||||
if not skill:
|
||||
raise ValueError(f"Skill not found: {skill_name} in {source}")
|
||||
|
||||
installed = await manager.install_community_skill(skill, project_path, custom_name)
|
||||
return installed.to_dict()
|
||||
|
|
@ -11,13 +11,15 @@ Complete reference for all Claude OS MCP Server API endpoints.
|
|||
1. [Knowledge Base Operations](#knowledge-base-operations)
|
||||
2. [Hybrid Indexing](#hybrid-indexing-new)
|
||||
3. [Project Management](#project-management)
|
||||
4. [Agent-OS Spec Tracking](#agent-os-spec-tracking-new)
|
||||
5. [Real-Time Spec Watcher](#real-time-spec-watcher-new)
|
||||
6. [Hooks System](#hooks-system)
|
||||
7. [File Watcher](#file-watcher)
|
||||
8. [Authentication](#authentication)
|
||||
9. [Utilities](#utilities)
|
||||
10. [Health Check](#health-check)
|
||||
4. [Skills Management](#skills-management-new)
|
||||
5. [Session Parsing](#session-parsing-new)
|
||||
6. [Agent-OS Spec Tracking](#agent-os-spec-tracking-new)
|
||||
7. [Real-Time Spec Watcher](#real-time-spec-watcher-new)
|
||||
8. [Hooks System](#hooks-system)
|
||||
9. [File Watcher](#file-watcher)
|
||||
10. [Authentication](#authentication)
|
||||
11. [Utilities](#utilities)
|
||||
12. [Health Check](#health-check)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -495,6 +497,353 @@ DELETE /api/projects/{id}
|
|||
|
||||
---
|
||||
|
||||
## Skills Management (NEW)
|
||||
|
||||
Manage Claude Code skills - list, install, create, and configure.
|
||||
|
||||
### List All Skills
|
||||
```http
|
||||
GET /api/skills?project_path=/path/to/project&include_content=false
|
||||
```
|
||||
|
||||
Returns global and project-level skills.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"global": [
|
||||
{
|
||||
"name": "memory",
|
||||
"path": "/Users/username/.claude/skills/memory",
|
||||
"description": "Save and recall information",
|
||||
"scope": "global",
|
||||
"source": "custom",
|
||||
"enabled": true,
|
||||
"category": null,
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"project": [
|
||||
{
|
||||
"name": "rails-backend",
|
||||
"path": "/path/to/project/.claude/skills/rails-backend",
|
||||
"description": "Rails patterns and service objects",
|
||||
"scope": "project",
|
||||
"source": "template",
|
||||
"enabled": true,
|
||||
"category": "rails",
|
||||
"tags": ["ruby", "rails", "backend"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### List Skill Templates
|
||||
```http
|
||||
GET /api/skills/templates
|
||||
```
|
||||
|
||||
Returns available local templates organized by category.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"templates": [
|
||||
{
|
||||
"name": "rails-backend",
|
||||
"category": "rails",
|
||||
"description": "Rails patterns and service objects",
|
||||
"path": "/path/to/claude-os/templates/skill-library/rails/rails-backend",
|
||||
"tags": ["ruby", "rails", "backend"],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"categories": ["general", "rails", "react", "testing"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Install Skill Template
|
||||
```http
|
||||
POST /api/skills/install?project_path=/path/to/project
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"template_name": "rails-backend"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"skill": {
|
||||
"name": "rails-backend",
|
||||
"path": "/path/to/project/.claude/skills/rails-backend",
|
||||
"scope": "project",
|
||||
"source": "template"
|
||||
},
|
||||
"message": "Installed skill 'rails-backend' to project"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Create Custom Skill
|
||||
```http
|
||||
POST /api/skills?project_path=/path/to/project
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "my-skill",
|
||||
"description": "My custom skill",
|
||||
"content": "# My Skill\n\nSkill instructions here...",
|
||||
"category": "custom",
|
||||
"tags": ["custom"]
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"skill": {
|
||||
"name": "my-skill",
|
||||
"path": "/path/to/project/.claude/skills/my-skill",
|
||||
"scope": "project",
|
||||
"source": "custom"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Get Skill Details
|
||||
```http
|
||||
GET /api/skills/{scope}/{name}?project_path=/path/to/project
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `scope`: `global` or `project`
|
||||
- `name`: Skill name
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"name": "rails-backend",
|
||||
"path": "/path/to/project/.claude/skills/rails-backend",
|
||||
"description": "Rails patterns and service objects",
|
||||
"scope": "project",
|
||||
"source": "template",
|
||||
"content": "# Rails Backend\n\n...",
|
||||
"enabled": true,
|
||||
"category": "rails",
|
||||
"tags": ["ruby", "rails", "backend"],
|
||||
"created": "2025-12-11T10:00:00Z",
|
||||
"modified": "2025-12-11T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Delete Skill
|
||||
```http
|
||||
DELETE /api/skills/{name}?project_path=/path/to/project
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Deleted skill 'my-skill' from project"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### List Community Sources
|
||||
```http
|
||||
GET /api/skills/community/sources
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"sources": {
|
||||
"anthropic": {
|
||||
"repo": "anthropics/skills",
|
||||
"skills_path": "skills",
|
||||
"name": "Anthropic Official",
|
||||
"description": "Official skills from Anthropic"
|
||||
},
|
||||
"superpowers": {
|
||||
"repo": "obra/superpowers",
|
||||
"skills_path": "skills",
|
||||
"name": "Superpowers",
|
||||
"description": "Battle-tested skills for TDD, debugging, and collaboration"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### List Community Skills
|
||||
```http
|
||||
GET /api/skills/community?source=anthropic
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `source` (optional): Filter by source (`anthropic`, `superpowers`)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"skills": [
|
||||
{
|
||||
"name": "pdf",
|
||||
"source": "anthropic",
|
||||
"repo": "anthropics/skills",
|
||||
"path": "skills/pdf",
|
||||
"description": "Comprehensive PDF manipulation toolkit...",
|
||||
"readme_url": "https://github.com/anthropics/skills/tree/main/skills/pdf",
|
||||
"raw_url": "https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf"
|
||||
}
|
||||
],
|
||||
"sources": {...},
|
||||
"total": 36
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Install Community Skill
|
||||
```http
|
||||
POST /api/skills/community/install?project_path=/path/to/project
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "pdf",
|
||||
"source": "anthropic",
|
||||
"repo": "anthropics/skills",
|
||||
"path": "skills/pdf",
|
||||
"description": "PDF manipulation toolkit",
|
||||
"readme_url": "https://github.com/anthropics/skills/tree/main/skills/pdf",
|
||||
"raw_url": "https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"skill": {
|
||||
"name": "pdf",
|
||||
"path": "/path/to/project/.claude/skills/pdf",
|
||||
"scope": "project",
|
||||
"source": "community:anthropic"
|
||||
},
|
||||
"message": "Installed community skill 'pdf' from anthropic"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Parsing (NEW)
|
||||
|
||||
Parse Claude Code session files and extract insights.
|
||||
|
||||
### List Project Sessions
|
||||
```http
|
||||
GET /api/sessions?project_path=/path/to/project&limit=10
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"sessions": [
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"session_path": "/Users/username/.claude/projects/-path-to-project/abc123.jsonl",
|
||||
"start_time": "2025-12-11T10:00:00Z",
|
||||
"end_time": "2025-12-11T11:30:00Z",
|
||||
"message_count": 24,
|
||||
"tool_calls": 15,
|
||||
"file_changes": 8
|
||||
}
|
||||
],
|
||||
"total": 42
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Get Session Details
|
||||
```http
|
||||
GET /api/sessions/{session_id}?project_path=/path/to/project
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"session_path": "/Users/username/.claude/projects/-path-to-project/abc123.jsonl",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Help me fix the authentication bug",
|
||||
"timestamp": "2025-12-11T10:00:00Z",
|
||||
"uuid": "msg-001"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'll help you fix that...",
|
||||
"timestamp": "2025-12-11T10:00:05Z",
|
||||
"uuid": "msg-002"
|
||||
}
|
||||
],
|
||||
"tool_calls": [
|
||||
{
|
||||
"tool_name": "Read",
|
||||
"timestamp": "2025-12-11T10:00:10Z",
|
||||
"input_data": {"file_path": "/path/to/auth.py"}
|
||||
}
|
||||
],
|
||||
"file_changes": [
|
||||
{
|
||||
"file_path": "/path/to/auth.py",
|
||||
"timestamp": "2025-12-11T10:05:00Z"
|
||||
}
|
||||
],
|
||||
"start_time": "2025-12-11T10:00:00Z",
|
||||
"end_time": "2025-12-11T11:30:00Z",
|
||||
"git_branch": "fix-auth-bug",
|
||||
"cwd": "/path/to/project"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Get Session Summary
|
||||
```http
|
||||
GET /api/sessions/{session_id}/summary?project_path=/path/to/project&max_tokens=500
|
||||
```
|
||||
|
||||
Returns a formatted summary suitable for LLM processing.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"summary": "# Session: abc123\nProject: /path/to/project\nBranch: fix-auth-bug\n\n## Conversation (24 messages)\n..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent-OS Spec Tracking (NEW)
|
||||
|
||||
Real-time tracking and visualization of agent-os specifications and tasks through the Kanban board.
|
||||
|
|
@ -1102,5 +1451,5 @@ For issues, questions, or contributions:
|
|||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-10-31
|
||||
**API Version:** 1.0
|
||||
**Last Updated:** 2025-12-11
|
||||
**API Version:** 2.3
|
||||
|
|
|
|||
289
docs/assets/claude-os-architecture.svg
Normal file
289
docs/assets/claude-os-architecture.svg
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080" style="background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);">
|
||||
<defs>
|
||||
<!-- Gradient definitions -->
|
||||
<linearGradient id="cyanGlow" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#00D9FF;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#00D9FF;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="purpleGlow" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="pinkGlow" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FF00FF;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FF00FF;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cyanPurpleGlow" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#00D9FF;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="purplePinkGlow" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FF00FF;stop-opacity:0.6" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Glow filters -->
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="strongGlow">
|
||||
<feGaussianBlur stdDeviation="8" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<!-- Stars pattern -->
|
||||
<pattern id="stars" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
|
||||
<circle cx="10" cy="20" r="1" fill="white" opacity="0.3"/>
|
||||
<circle cx="40" cy="50" r="0.5" fill="white" opacity="0.5"/>
|
||||
<circle cx="70" cy="10" r="1.5" fill="white" opacity="0.2"/>
|
||||
<circle cx="85" cy="80" r="0.8" fill="white" opacity="0.4"/>
|
||||
<circle cx="25" cy="90" r="1" fill="white" opacity="0.3"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<!-- Background stars -->
|
||||
<rect width="1920" height="1080" fill="url(#stars)" opacity="0.5"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="960" y="80" font-family="Arial, sans-serif" font-size="48" font-weight="bold" fill="#FFFFFF" text-anchor="middle" filter="url(#glow)">
|
||||
CLAUDE OS ARCHITECTURE
|
||||
</text>
|
||||
|
||||
<!-- Center Hub -->
|
||||
<g id="centerHub">
|
||||
<circle cx="960" cy="540" r="140" fill="none" stroke="url(#cyanGlow)" stroke-width="3" filter="url(#strongGlow)"/>
|
||||
<circle cx="960" cy="540" r="135" fill="#1E293B" opacity="0.9"/>
|
||||
|
||||
<!-- Circuit pattern inside hub -->
|
||||
<line x1="880" y1="540" x2="1040" y2="540" stroke="#00D9FF" stroke-width="1" opacity="0.3"/>
|
||||
<line x1="960" y1="460" x2="960" y2="620" stroke="#00D9FF" stroke-width="1" opacity="0.3"/>
|
||||
<circle cx="920" cy="500" r="3" fill="#00D9FF" opacity="0.5"/>
|
||||
<circle cx="1000" cy="580" r="3" fill="#00D9FF" opacity="0.5"/>
|
||||
|
||||
<text x="960" y="510" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="#00D9FF" text-anchor="middle">
|
||||
Semantic Knowledge Base
|
||||
</text>
|
||||
<text x="960" y="540" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
SQLite + sqlite-vec
|
||||
</text>
|
||||
<text x="960" y="565" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
800+ indexed chunks
|
||||
</text>
|
||||
<text x="960" y="590" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Vector embeddings
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Component 1: TOP-LEFT - Real-Time Learning -->
|
||||
<g id="component1">
|
||||
<rect x="200" y="200" width="280" height="160" rx="15" fill="#1E293B" stroke="url(#cyanGlow)" stroke-width="2" filter="url(#glow)"/>
|
||||
<line x1="210" y1="220" x2="260" y2="220" stroke="#00D9FF" stroke-width="1" opacity="0.3"/>
|
||||
<circle cx="265" cy="220" r="2" fill="#00D9FF" opacity="0.5"/>
|
||||
|
||||
<text x="340" y="235" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#00D9FF" text-anchor="middle">
|
||||
🧠 Real-Time Learning
|
||||
</text>
|
||||
<text x="340" y="265" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Redis Pub/Sub
|
||||
</text>
|
||||
<text x="340" y="290" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
<1ms latency
|
||||
</text>
|
||||
<text x="340" y="315" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
10+ patterns
|
||||
</text>
|
||||
<text x="340" y="345" font-family="Arial, sans-serif" font-size="14" fill="#00D9FF" text-anchor="middle" opacity="0.6">
|
||||
(Cyan glow)
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Component 2: TOP-RIGHT - Memory MCP -->
|
||||
<g id="component2">
|
||||
<rect x="1440" y="200" width="280" height="160" rx="15" fill="#1E293B" stroke="url(#purpleGlow)" stroke-width="2" filter="url(#glow)"/>
|
||||
<line x1="1450" y1="220" x2="1500" y2="220" stroke="#8B5CF6" stroke-width="1" opacity="0.3"/>
|
||||
<circle cx="1505" cy="220" r="2" fill="#8B5CF6" opacity="0.5"/>
|
||||
|
||||
<text x="1580" y="235" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#8B5CF6" text-anchor="middle">
|
||||
💾 Memory MCP
|
||||
</text>
|
||||
<text x="1580" y="265" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Persistent Memory
|
||||
</text>
|
||||
<text x="1580" y="290" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Instant Recall
|
||||
</text>
|
||||
<text x="1580" y="315" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Natural Language
|
||||
</text>
|
||||
<text x="1580" y="345" font-family="Arial, sans-serif" font-size="14" fill="#8B5CF6" text-anchor="middle" opacity="0.6">
|
||||
(Purple glow)
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Component 3: RIGHT - Analyze-Project -->
|
||||
<g id="component3">
|
||||
<rect x="1440" y="460" width="280" height="160" rx="15" fill="#1E293B" stroke="url(#pinkGlow)" stroke-width="2" filter="url(#glow)"/>
|
||||
<line x1="1450" y1="480" x2="1500" y2="480" stroke="#FF00FF" stroke-width="1" opacity="0.3"/>
|
||||
<circle cx="1505" cy="480" r="2" fill="#FF00FF" opacity="0.5"/>
|
||||
|
||||
<text x="1580" y="495" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#FF00FF" text-anchor="middle">
|
||||
🔍 Analyze-Project
|
||||
</text>
|
||||
<text x="1580" y="525" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Git Hooks
|
||||
</text>
|
||||
<text x="1580" y="550" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Auto-Index
|
||||
</text>
|
||||
<text x="1580" y="575" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Smart Docs
|
||||
</text>
|
||||
<text x="1580" y="605" font-family="Arial, sans-serif" font-size="14" fill="#FF00FF" text-anchor="middle" opacity="0.6">
|
||||
(Pink glow)
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Component 4: BOTTOM-RIGHT - Session Management -->
|
||||
<g id="component4">
|
||||
<rect x="1440" y="720" width="280" height="160" rx="15" fill="#1E293B" stroke="url(#purplePinkGlow)" stroke-width="2" filter="url(#glow)"/>
|
||||
<line x1="1450" y1="740" x2="1500" y2="740" stroke="#FF00FF" stroke-width="1" opacity="0.3"/>
|
||||
<circle cx="1505" cy="740" r="2" fill="#8B5CF6" opacity="0.5"/>
|
||||
|
||||
<text x="1580" y="755" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#FF00FF" text-anchor="middle">
|
||||
🎯 Session Management
|
||||
</text>
|
||||
<text x="1580" y="785" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Auto Resume
|
||||
</text>
|
||||
<text x="1580" y="810" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Context Preservation
|
||||
</text>
|
||||
<text x="1580" y="835" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Zero Cold Start
|
||||
</text>
|
||||
<text x="1580" y="865" font-family="Arial, sans-serif" font-size="14" fill="#FF00FF" text-anchor="middle" opacity="0.6">
|
||||
(Purple-pink glow)
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Component 5: BOTTOM-LEFT - Semantic Search -->
|
||||
<g id="component5">
|
||||
<rect x="200" y="720" width="280" height="160" rx="15" fill="#1E293B" stroke="url(#cyanPurpleGlow)" stroke-width="2" filter="url(#glow)"/>
|
||||
<line x1="210" y1="740" x2="260" y2="740" stroke="#00D9FF" stroke-width="1" opacity="0.3"/>
|
||||
<circle cx="265" cy="740" r="2" fill="#8B5CF6" opacity="0.5"/>
|
||||
|
||||
<text x="340" y="755" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#00D9FF" text-anchor="middle">
|
||||
📚 Semantic Search
|
||||
</text>
|
||||
<text x="340" y="785" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Vector Similarity
|
||||
</text>
|
||||
<text x="340" y="810" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Code Understanding
|
||||
</text>
|
||||
<text x="340" y="835" font-family="Arial, sans-serif" font-size="16" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Pattern Recognition
|
||||
</text>
|
||||
<text x="340" y="865" font-family="Arial, sans-serif" font-size="14" fill="#8B5CF6" text-anchor="middle" opacity="0.6">
|
||||
(Cyan-purple glow)
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Connection lines (circuit traces) -->
|
||||
<!-- Component 1 to Center -->
|
||||
<line x1="480" y1="280" x2="820" y2="540" stroke="#00D9FF" stroke-width="2" opacity="0.6" filter="url(#glow)"/>
|
||||
<circle cx="480" cy="280" r="4" fill="#00D9FF" filter="url(#glow)"/>
|
||||
|
||||
<!-- Component 2 to Center -->
|
||||
<line x1="1440" y1="280" x2="1100" y2="540" stroke="#8B5CF6" stroke-width="2" opacity="0.6" filter="url(#glow)"/>
|
||||
<circle cx="1440" cy="280" r="4" fill="#8B5CF6" filter="url(#glow)"/>
|
||||
|
||||
<!-- Component 3 to Center -->
|
||||
<line x1="1440" y1="540" x2="1100" y2="540" stroke="#FF00FF" stroke-width="2" opacity="0.6" filter="url(#glow)"/>
|
||||
<circle cx="1440" cy="540" r="4" fill="#FF00FF" filter="url(#glow)"/>
|
||||
|
||||
<!-- Component 4 to Center -->
|
||||
<line x1="1440" y1="800" x2="1100" y2="540" stroke="#FF00FF" stroke-width="2" opacity="0.6" filter="url(#glow)"/>
|
||||
<circle cx="1440" cy="800" r="4" fill="#FF00FF" filter="url(#glow)"/>
|
||||
|
||||
<!-- Component 5 to Center -->
|
||||
<line x1="480" y1="800" x2="820" y2="540" stroke="#00D9FF" stroke-width="2" opacity="0.6" filter="url(#glow)"/>
|
||||
<circle cx="480" cy="800" r="4" fill="#00D9FF" filter="url(#glow)"/>
|
||||
|
||||
<!-- Flow Path: Center to MCP Server to Claude Code to Developer -->
|
||||
<g id="flowPath">
|
||||
<!-- Vertical connection from center hub down -->
|
||||
<line x1="960" y1="680" x2="960" y2="900" stroke="#00D9FF" stroke-width="3" opacity="0.5" stroke-dasharray="5,5" filter="url(#glow)"/>
|
||||
|
||||
<!-- MCP Server box -->
|
||||
<rect x="320" y="900" width="200" height="60" rx="10" fill="#1E293B" stroke="#00D9FF" stroke-width="2" opacity="0.9" filter="url(#glow)"/>
|
||||
<text x="420" y="925" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#00D9FF" text-anchor="middle">
|
||||
MCP Server
|
||||
</text>
|
||||
<text x="420" y="948" font-family="Arial, sans-serif" font-size="15" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
(port 8051)
|
||||
</text>
|
||||
|
||||
<!-- Arrow from center to MCP -->
|
||||
<line x1="520" y1="930" x2="740" y2="930" stroke="#00D9FF" stroke-width="3" opacity="0.8" filter="url(#glow)"/>
|
||||
<polygon points="740,930 730,925 730,935" fill="#00D9FF" opacity="0.8"/>
|
||||
|
||||
<!-- Claude Code Interface box -->
|
||||
<rect x="740" y="900" width="200" height="60" rx="10" fill="#1E293B" stroke="#8B5CF6" stroke-width="2" opacity="0.9" filter="url(#glow)"/>
|
||||
<text x="840" y="925" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#8B5CF6" text-anchor="middle">
|
||||
Claude Code
|
||||
</text>
|
||||
<text x="840" y="948" font-family="Arial, sans-serif" font-size="15" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
Interface
|
||||
</text>
|
||||
|
||||
<!-- Arrow from MCP to Claude Code -->
|
||||
<line x1="940" y1="930" x2="1160" y2="930" stroke="#8B5CF6" stroke-width="3" opacity="0.8" filter="url(#glow)"/>
|
||||
<polygon points="1160,930 1150,925 1150,935" fill="#8B5CF6" opacity="0.8"/>
|
||||
|
||||
<!-- Developer box -->
|
||||
<rect x="1160" y="900" width="200" height="60" rx="10" fill="#1E293B" stroke="#FF00FF" stroke-width="2" opacity="0.9" filter="url(#glow)"/>
|
||||
<text x="1260" y="925" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#FF00FF" text-anchor="middle">
|
||||
Developer
|
||||
</text>
|
||||
<text x="1260" y="948" font-family="Arial, sans-serif" font-size="15" fill="#FFFFFF" text-anchor="middle" opacity="0.8">
|
||||
(You!)
|
||||
</text>
|
||||
|
||||
<!-- Arrow pointing right for emphasis -->
|
||||
<line x1="1360" y1="930" x2="1420" y2="930" stroke="#FF00FF" stroke-width="3" opacity="0.8" filter="url(#glow)"/>
|
||||
<polygon points="1420,930 1410,925 1410,935" fill="#FF00FF" opacity="0.8"/>
|
||||
</g>
|
||||
|
||||
<!-- Bottom Panel: Data Flow -->
|
||||
<g id="bottomPanel">
|
||||
<rect x="360" y="980" width="1200" height="60" rx="10" fill="#1E293B" stroke="#00D9FF" stroke-width="2" opacity="0.9" filter="url(#glow)"/>
|
||||
|
||||
<text x="960" y="1005" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#FFFFFF" text-anchor="middle">
|
||||
Data Flow Pipeline:
|
||||
</text>
|
||||
<text x="960" y="1028" font-family="Arial, sans-serif" font-size="16" fill="#00D9FF" text-anchor="middle">
|
||||
Git Commit → 3s indexing → SQLite → MCP → Claude → You
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- Decorative circuit lines in background -->
|
||||
<line x1="100" y1="100" x2="300" y2="100" stroke="#00D9FF" stroke-width="1" opacity="0.1"/>
|
||||
<line x1="1620" y1="100" x2="1820" y2="100" stroke="#FF00FF" stroke-width="1" opacity="0.1"/>
|
||||
<line x1="100" y1="950" x2="300" y2="950" stroke="#8B5CF6" stroke-width="1" opacity="0.1"/>
|
||||
<circle cx="150" cy="100" r="3" fill="#00D9FF" opacity="0.2"/>
|
||||
<circle cx="1770" cy="100" r="3" fill="#FF00FF" opacity="0.2"/>
|
||||
<circle cx="150" cy="950" r="3" fill="#8B5CF6" opacity="0.2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
190
docs/guides/AUTH_SETUP.md
Normal file
190
docs/guides/AUTH_SETUP.md
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# Claude OS Authentication Setup
|
||||
|
||||
Simple email/password authentication to keep strangers out of your Claude OS frontend.
|
||||
|
||||
## Features
|
||||
|
||||
✅ **Optional Authentication** - Disabled by default, enable when needed
|
||||
✅ **Environment-based** - No database required for user management
|
||||
✅ **JWT Tokens** - 7-day token expiration
|
||||
✅ **Secure** - Bcrypt password hashing
|
||||
✅ **Simple** - Single user account via environment variables
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Enable Authentication
|
||||
|
||||
Set environment variables in your `.env` file or server environment:
|
||||
|
||||
```bash
|
||||
# Required: Email for login
|
||||
CLAUDE_OS_EMAIL=admin@example.com
|
||||
|
||||
# Option 1: Plain password (development only - will be hashed automatically)
|
||||
CLAUDE_OS_PASSWORD=your_secure_password_here
|
||||
|
||||
# Option 2: Pre-hashed password (recommended for production)
|
||||
CLAUDE_OS_PASSWORD_HASH=$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5hhA82jdg8jpu
|
||||
|
||||
# Optional: Custom secret key for JWT (auto-generated if not set)
|
||||
CLAUDE_OS_SECRET_KEY=your-super-secret-key-min-32-chars
|
||||
```
|
||||
|
||||
### 2. Generate Password Hash (Production)
|
||||
|
||||
For production, use a pre-hashed password instead of plain text:
|
||||
|
||||
```bash
|
||||
# Install dependencies first
|
||||
cd ~/Projects/claude-os
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Generate hash
|
||||
python3 -c "from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('your_password_here'))"
|
||||
```
|
||||
|
||||
Copy the output and use it as `CLAUDE_OS_PASSWORD_HASH` in your `.env` file.
|
||||
|
||||
### 3. Restart Claude OS
|
||||
|
||||
```bash
|
||||
# Local (Mac)
|
||||
./stop.sh
|
||||
./start.sh
|
||||
|
||||
# Production Server
|
||||
sudo systemctl restart claude-os
|
||||
```
|
||||
|
||||
### 4. Login
|
||||
|
||||
Visit your Claude OS frontend:
|
||||
- Local: http://localhost:5173/login
|
||||
- Production: https://your-domain.com/login
|
||||
|
||||
Use the email and password you configured.
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Server Environment Variables
|
||||
|
||||
Add to `/opt/claude-os/.env`:
|
||||
|
||||
```bash
|
||||
CLAUDE_OS_EMAIL=admin@pistn.com
|
||||
CLAUDE_OS_PASSWORD_HASH=$2b$12$xyz... # Generated hash
|
||||
CLAUDE_OS_SECRET_KEY=your-32-char-secret
|
||||
```
|
||||
|
||||
Restart the service:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart claude-os
|
||||
```
|
||||
|
||||
## Disable Authentication
|
||||
|
||||
To disable authentication (open access):
|
||||
|
||||
```bash
|
||||
# Remove or comment out these variables in .env:
|
||||
# CLAUDE_OS_EMAIL=...
|
||||
# CLAUDE_OS_PASSWORD=...
|
||||
# CLAUDE_OS_PASSWORD_HASH=...
|
||||
```
|
||||
|
||||
Restart Claude OS. The frontend will allow access without login.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Backend (FastAPI)
|
||||
|
||||
- **`/api/auth/login`** - Login endpoint, returns JWT token
|
||||
- **`/api/auth/me`** - Get current user info
|
||||
- **`/api/auth/status`** - Check if authentication is enabled
|
||||
|
||||
### Frontend (React)
|
||||
|
||||
- **Login Page** - Beautiful gradient login form
|
||||
- **Auth Context** - Manages authentication state
|
||||
- **Protected Routes** - Automatically redirects to login if not authenticated
|
||||
- **Token Storage** - JWT stored in localStorage (7-day expiration)
|
||||
|
||||
### Security Features
|
||||
|
||||
✅ **Bcrypt hashing** - Industry-standard password encryption
|
||||
✅ **JWT tokens** - Stateless authentication
|
||||
✅ **Automatic expiration** - Tokens expire after 7 days
|
||||
✅ **HTTPS recommended** - Use SSL in production
|
||||
✅ **No database needed** - Single user, environment-based
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Authentication is not configured"
|
||||
|
||||
You haven't set the `CLAUDE_OS_EMAIL` environment variable. Set it and restart.
|
||||
|
||||
### "Incorrect email or password"
|
||||
|
||||
Check your `.env` file:
|
||||
- Email matches exactly (case-sensitive)
|
||||
- If using `CLAUDE_OS_PASSWORD`, make sure it's correct
|
||||
- If using `CLAUDE_OS_PASSWORD_HASH`, regenerate the hash
|
||||
|
||||
### Login page doesn't appear
|
||||
|
||||
Make sure your Claude OS frontend is running:
|
||||
|
||||
```bash
|
||||
# Local development
|
||||
cd frontend
|
||||
npm run dev
|
||||
|
||||
# Production (should be served by Nginx)
|
||||
curl http://localhost:5173
|
||||
```
|
||||
|
||||
### Token expired
|
||||
|
||||
Tokens last 7 days. Just login again to get a new token.
|
||||
|
||||
## Multiple Users
|
||||
|
||||
This system is designed for single-user access. For multiple users:
|
||||
|
||||
1. **Simple approach**: Share one account with your team
|
||||
2. **Advanced approach**: Extend the auth system to use a database (requires custom implementation)
|
||||
|
||||
For most teams deploying Claude OS internally, a single shared account is sufficient since it's:
|
||||
- Behind your firewall
|
||||
- For trusted team members only
|
||||
- Just to keep strangers out (not enterprise-grade auth)
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Development (Local)
|
||||
|
||||
```bash
|
||||
CLAUDE_OS_EMAIL=dev@localhost
|
||||
CLAUDE_OS_PASSWORD=dev123
|
||||
```
|
||||
|
||||
Fine for local development. Password is hashed automatically.
|
||||
|
||||
### Production (Server)
|
||||
|
||||
```bash
|
||||
CLAUDE_OS_EMAIL=admin@yourcompany.com
|
||||
CLAUDE_OS_PASSWORD_HASH=$2b$12$xyz... # Pre-hashed
|
||||
CLAUDE_OS_SECRET_KEY=randomly-generated-32-char-secret
|
||||
```
|
||||
|
||||
✅ **Use hashed passwords**
|
||||
✅ **Use strong secret keys**
|
||||
✅ **Use HTTPS/SSL**
|
||||
✅ **Rotate passwords periodically**
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Check the main Claude OS README or open an issue on GitHub.
|
||||
431
docs/guides/BACKUP_RESTORE_GUIDE.md
Normal file
431
docs/guides/BACKUP_RESTORE_GUIDE.md
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
# Claude OS Backup & Restore Guide
|
||||
|
||||
This guide explains how to safely backup and restore your Claude OS installation, perfect for testing fresh installations without losing data.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Backup Your Current Setup
|
||||
|
||||
```bash
|
||||
./scripts/backup_claude_os.sh
|
||||
```
|
||||
|
||||
This creates a timestamped backup in `backups/backup_YYYYMMDD_HHMMSS/` containing:
|
||||
- SQLite database (all projects, knowledge bases, documents)
|
||||
- Configuration files (.env, JSON configs)
|
||||
- Uploaded documents
|
||||
- Recent log files
|
||||
- Symlink information
|
||||
|
||||
**Output example:**
|
||||
```
|
||||
Backup location: /Users/iamanmp/Projects/claude-os/backups/backup_20251101_135211
|
||||
Backup size: 125M
|
||||
Backup timestamp: 20251101_135211
|
||||
|
||||
To restore this backup later:
|
||||
./scripts/restore_claude_os.sh 20251101_135211
|
||||
```
|
||||
|
||||
### 2. Test Fresh Installation
|
||||
|
||||
Now you can safely test the installation process:
|
||||
|
||||
```bash
|
||||
# Option 1: Test quick install
|
||||
./install.sh
|
||||
|
||||
# Option 2: Test full setup
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
### 3. Restore Your Data
|
||||
|
||||
If you need to restore your backup:
|
||||
|
||||
```bash
|
||||
./scripts/restore_claude_os.sh 20251101_135211
|
||||
```
|
||||
|
||||
Replace `20251101_135211` with your actual backup timestamp.
|
||||
|
||||
---
|
||||
|
||||
## What Gets Backed Up?
|
||||
|
||||
### Critical Data (Automatically Backed Up)
|
||||
|
||||
1. **SQLite Database** (`data/claude-os.db`)
|
||||
- All projects
|
||||
- All knowledge bases
|
||||
- All documents and embeddings
|
||||
- Search history
|
||||
- Typically 100-200MB
|
||||
|
||||
2. **Configuration Files**
|
||||
- `.env` - Environment variables (Ollama settings, API keys, etc.)
|
||||
- `claude-os-config.json` - Project configuration
|
||||
- `claude-os-state.json` - Current session state
|
||||
- `claude-os-triggers.json` - Custom trigger phrases
|
||||
|
||||
3. **Uploaded Documents** (`data/uploads/`)
|
||||
- Any files uploaded through the UI
|
||||
|
||||
4. **Recent Logs** (`logs/`)
|
||||
- Recent log files (< 10MB each)
|
||||
- Useful for debugging
|
||||
|
||||
5. **Symlink Information**
|
||||
- Record of `~/.claude/commands/` symlinks
|
||||
- Record of `~/.claude/skills/` symlinks
|
||||
- For reference only (recreated by `install.sh`)
|
||||
|
||||
### What's NOT Backed Up (No Need)
|
||||
|
||||
- `node_modules/` - Reinstalled from package.json
|
||||
- `venv/` - Recreated during installation
|
||||
- Python packages - Reinstalled from requirements.txt
|
||||
- Ollama models - Remain in system location
|
||||
- Templates - Already in git repository
|
||||
- Large log files (> 10MB)
|
||||
|
||||
---
|
||||
|
||||
## Detailed Usage
|
||||
|
||||
### Backup Command
|
||||
|
||||
```bash
|
||||
./scripts/backup_claude_os.sh
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
1. Creates timestamped backup directory
|
||||
2. Copies all critical files
|
||||
3. Records symlink state
|
||||
4. Creates manifest file
|
||||
5. Shows backup summary
|
||||
|
||||
**No arguments needed** - timestamps are automatic.
|
||||
|
||||
### Restore Command
|
||||
|
||||
```bash
|
||||
./scripts/restore_claude_os.sh <timestamp>
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Restore specific backup
|
||||
./scripts/restore_claude_os.sh 20251101_135211
|
||||
|
||||
# List available backups
|
||||
./scripts/restore_claude_os.sh
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
1. Verifies backup exists
|
||||
2. Shows manifest
|
||||
3. Asks for confirmation (press Enter to continue)
|
||||
4. Restores all files
|
||||
5. Provides next steps
|
||||
|
||||
**⚠️ WARNING:** Restore will **overwrite** your current data! Make a new backup first if you want to keep current state.
|
||||
|
||||
---
|
||||
|
||||
## Testing Fresh Installations
|
||||
|
||||
### Recommended Workflow
|
||||
|
||||
1. **Backup first:**
|
||||
```bash
|
||||
./scripts/backup_claude_os.sh
|
||||
```
|
||||
|
||||
2. **Stop all services:**
|
||||
```bash
|
||||
./stop_all_services.sh
|
||||
```
|
||||
|
||||
3. **Clean up (optional but thorough):**
|
||||
```bash
|
||||
# Remove symlinks (will be recreated)
|
||||
rm ~/.claude/commands/claude-os-*.md
|
||||
rm ~/.claude/skills/memory
|
||||
rm ~/.claude/skills/initialize-project
|
||||
rm ~/.claude/skills/remember-this
|
||||
|
||||
# Remove virtual environment
|
||||
rm -rf venv venv_py312 venv_py313
|
||||
```
|
||||
|
||||
4. **Test installation:**
|
||||
```bash
|
||||
# Test quick install
|
||||
./install.sh
|
||||
|
||||
# OR test full setup
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
5. **Verify it works:**
|
||||
```bash
|
||||
# Start services
|
||||
./start.sh
|
||||
|
||||
# Check health
|
||||
curl http://localhost:8051/health
|
||||
|
||||
# Check UI
|
||||
open http://localhost:5173
|
||||
```
|
||||
|
||||
6. **If something goes wrong, restore:**
|
||||
```bash
|
||||
./scripts/restore_claude_os.sh 20251101_135211
|
||||
./start.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario 1: Test install.sh for a user report
|
||||
|
||||
```bash
|
||||
# 1. Backup everything
|
||||
./scripts/backup_claude_os.sh
|
||||
|
||||
# 2. Clean installation (keep data, test scripts)
|
||||
rm -rf venv
|
||||
rm ~/.claude/commands/claude-os-*.md
|
||||
rm ~/.claude/skills/{memory,initialize-project,remember-this}
|
||||
|
||||
# 3. Test install as if you're a new user
|
||||
./install.sh
|
||||
|
||||
# 4. Restore your data (database, configs)
|
||||
./scripts/restore_claude_os.sh <timestamp>
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### Scenario 2: Test setup.sh from scratch
|
||||
|
||||
```bash
|
||||
# 1. Backup everything
|
||||
./scripts/backup_claude_os.sh
|
||||
|
||||
# 2. Full clean (simulate fresh machine)
|
||||
./stop_all_services.sh
|
||||
rm -rf venv venv_*
|
||||
rm -rf node_modules
|
||||
rm -rf data/claude-os.db
|
||||
rm ~/.claude/commands/claude-os-*.md
|
||||
rm ~/.claude/skills/{memory,initialize-project,remember-this}
|
||||
|
||||
# 3. Test full setup
|
||||
./setup.sh
|
||||
./start_all_services.sh
|
||||
|
||||
# 4. Restore your data
|
||||
./scripts/restore_claude_os.sh <timestamp>
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### Scenario 3: Test project initialization
|
||||
|
||||
```bash
|
||||
# 1. Backup current state
|
||||
./scripts/backup_claude_os.sh
|
||||
|
||||
# 2. Create test project
|
||||
cd ~/test-project
|
||||
/claude-os-init # In Claude Code
|
||||
|
||||
# 3. If something breaks, restore
|
||||
cd ~/Projects/claude-os
|
||||
./scripts/restore_claude_os.sh <timestamp>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup Best Practices
|
||||
|
||||
### When to Backup
|
||||
|
||||
- **Before testing installations** - Always!
|
||||
- **Before major changes** - Modifying core files
|
||||
- **Weekly** - If actively developing
|
||||
- **Before upgrades** - Git pull, dependency updates
|
||||
|
||||
### Backup Management
|
||||
|
||||
```bash
|
||||
# List all backups
|
||||
ls -lh backups/
|
||||
|
||||
# Check backup size
|
||||
du -sh backups/backup_*
|
||||
|
||||
# Remove old backups (keep last 5)
|
||||
cd backups
|
||||
ls -t | tail -n +6 | xargs rm -rf
|
||||
|
||||
# Archive old backups
|
||||
tar -czf backups_archive_2025.tar.gz backups/
|
||||
```
|
||||
|
||||
### Multiple Backups
|
||||
|
||||
The backup script creates **new backups** each time - it never overwrites:
|
||||
|
||||
```bash
|
||||
./scripts/backup_claude_os.sh # Creates backup_20251101_135211
|
||||
./scripts/backup_claude_os.sh # Creates backup_20251101_140530
|
||||
./scripts/backup_claude_os.sh # Creates backup_20251101_141005
|
||||
```
|
||||
|
||||
You can safely run it multiple times!
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Backup location not found"
|
||||
|
||||
```bash
|
||||
# List available backups
|
||||
ls -1 backups/ | grep backup_
|
||||
|
||||
# Use exact timestamp from list
|
||||
./scripts/restore_claude_os.sh 20251101_135211
|
||||
```
|
||||
|
||||
### "Permission denied"
|
||||
|
||||
```bash
|
||||
# Make scripts executable
|
||||
chmod +x scripts/backup_claude_os.sh scripts/restore_claude_os.sh
|
||||
```
|
||||
|
||||
### "Database locked" during backup
|
||||
|
||||
```bash
|
||||
# Stop services first
|
||||
./stop_all_services.sh
|
||||
|
||||
# Then backup
|
||||
./scripts/backup_claude_os.sh
|
||||
```
|
||||
|
||||
### Restore doesn't fix everything
|
||||
|
||||
The restore script restores **data** but not:
|
||||
- Python packages (run `pip install -r requirements.txt`)
|
||||
- Node modules (run `cd frontend && npm install`)
|
||||
- Symlinks (run `./install.sh`)
|
||||
- Running services (run `./start.sh`)
|
||||
|
||||
**Full recovery process:**
|
||||
```bash
|
||||
./scripts/restore_claude_os.sh <timestamp>
|
||||
./install.sh # Recreate symlinks
|
||||
./start.sh # Start services
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Locations Reference
|
||||
|
||||
### Backed Up Locations
|
||||
|
||||
```
|
||||
claude-os/
|
||||
├── data/
|
||||
│ ├── claude-os.db ← BACKED UP (critical!)
|
||||
│ └── uploads/ ← BACKED UP
|
||||
├── .env ← BACKED UP (has secrets)
|
||||
├── claude-os-config.json ← BACKED UP
|
||||
├── claude-os-state.json ← BACKED UP
|
||||
├── claude-os-triggers.json ← BACKED UP
|
||||
└── logs/ ← BACKED UP (recent only)
|
||||
|
||||
~/.claude/
|
||||
├── commands/claude-os-*.md ← Symlink info recorded
|
||||
└── skills/{memory,etc} ← Symlink info recorded
|
||||
```
|
||||
|
||||
### Backup Storage Location
|
||||
|
||||
```
|
||||
claude-os/
|
||||
└── backups/
|
||||
├── backup_20251101_135211/
|
||||
│ ├── claude-os.db
|
||||
│ ├── .env
|
||||
│ ├── claude-os-config.json
|
||||
│ ├── claude-os-state.json
|
||||
│ ├── claude-os-triggers.json
|
||||
│ ├── uploads/
|
||||
│ ├── logs/
|
||||
│ ├── symlink_info.txt
|
||||
│ └── MANIFEST.txt
|
||||
└── backup_20251101_140530/
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
### .env File Contains Secrets
|
||||
|
||||
The `.env` file may contain:
|
||||
- `CLAUDE_OS_PASSWORD` - Admin password
|
||||
- `CLAUDE_OS_SECRET_KEY` - JWT secret
|
||||
- API keys (if using OpenAI)
|
||||
|
||||
**Backups are stored locally** in `backups/` directory.
|
||||
|
||||
**⚠️ DO NOT:**
|
||||
- Commit backups to git (.gitignore already excludes them)
|
||||
- Share backup directories
|
||||
- Store backups in public locations
|
||||
|
||||
### Recommended Backup Security
|
||||
|
||||
```bash
|
||||
# Encrypt sensitive backups
|
||||
tar -czf - backups/backup_20251101_135211 | \
|
||||
openssl enc -aes-256-cbc -pbkdf2 -out backup_encrypted.tar.gz.enc
|
||||
|
||||
# Decrypt when needed
|
||||
openssl enc -aes-256-cbc -pbkdf2 -d -in backup_encrypted.tar.gz.enc | \
|
||||
tar -xzf -
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**You now have:**
|
||||
|
||||
✅ `backup_claude_os.sh` - Backup all your data
|
||||
✅ `restore_claude_os.sh` - Restore from backup
|
||||
✅ Safe testing workflow
|
||||
✅ Your first backup created!
|
||||
|
||||
**Next steps:**
|
||||
|
||||
1. Your backup is ready: `backups/backup_20251101_135211/`
|
||||
2. You can safely test `./install.sh`
|
||||
3. If anything breaks: `./scripts/restore_claude_os.sh 20251101_135211`
|
||||
4. Test away with confidence! 🚀
|
||||
|
||||
---
|
||||
|
||||
**Questions?**
|
||||
- Check `backups/backup_*/MANIFEST.txt` for backup contents
|
||||
- Run `./scripts/restore_claude_os.sh` with no arguments to list backups
|
||||
- Backups are fast (30 seconds) - backup often!
|
||||
230
docs/guides/HOOK_SETUP_QUICK_START.md
Normal file
230
docs/guides/HOOK_SETUP_QUICK_START.md
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
# File Watcher Hooks - Quick Start Guide
|
||||
|
||||
## What Changed?
|
||||
|
||||
The `initialize-project` skill now **automatically configures file watchers** for your project's knowledge bases. No more manual setup needed!
|
||||
|
||||
## Before vs After
|
||||
|
||||
### Before (Manual Setup Required)
|
||||
```bash
|
||||
# Initialize project
|
||||
initialize-project: 1
|
||||
|
||||
# Then manually enable hooks via API
|
||||
curl -X POST http://localhost:8051/api/projects/1/hooks/knowledge_docs/enable \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"folder_path": "/path/to/knowledge_docs"}'
|
||||
|
||||
# Then manually start the file watcher
|
||||
curl -X POST http://localhost:8051/api/watcher/start/1
|
||||
|
||||
# Only THEN would new files auto-sync
|
||||
```
|
||||
|
||||
### After (Fully Automatic)
|
||||
```bash
|
||||
# Initialize project
|
||||
initialize-project: 1
|
||||
|
||||
# ✨ File watchers are now automatically configured!
|
||||
# New files will be auto-synced immediately
|
||||
```
|
||||
|
||||
## How File Watchers Work
|
||||
|
||||
When you initialize a project, the system automatically:
|
||||
|
||||
1. **Creates hooks for all 4 MCP types:**
|
||||
- `knowledge_docs` → Watches your project's documentation folder
|
||||
- `project_profile` → Watches `.claude-os/project-profile/`
|
||||
- `project_index` → Watches `.claude-os/project-index/`
|
||||
- `project_memories` → Watches `.claude-os/memories/`
|
||||
|
||||
2. **Starts the file watcher service**
|
||||
- Monitors all configured folders
|
||||
- Detects file changes within ~2 seconds
|
||||
- Automatically syncs changes to knowledge bases
|
||||
|
||||
3. **Creates the necessary folders**
|
||||
- Project folders are created if they don't exist
|
||||
- Ready to accept new files immediately
|
||||
|
||||
## Default Folder Mappings
|
||||
|
||||
| MCP Type | Default Folder | Behavior |
|
||||
|----------|---|---|
|
||||
| `knowledge_docs` | `docs/` (or `documentation/`) | Watches for markdown, text, PDF files |
|
||||
| `project_profile` | `.claude-os/project-profile/` | Auto-created, stores profile docs |
|
||||
| `project_index` | `.claude-os/project-index/` | Auto-created, stores code index |
|
||||
| `project_memories` | `.claude-os/memories/` | Auto-created, stores insights |
|
||||
|
||||
## Using Custom Folders
|
||||
|
||||
If you want to use a different folder for `knowledge_docs`:
|
||||
|
||||
```bash
|
||||
# Create your custom folder
|
||||
mkdir my-docs
|
||||
|
||||
# Enable hook for custom folder
|
||||
curl -X POST "http://localhost:8051/api/projects/1/hooks/knowledge_docs/enable" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"folder_path": "/path/to/my-docs"}'
|
||||
|
||||
# Restart the watcher to pick up changes
|
||||
curl -X POST "http://localhost:8051/api/watcher/restart/1"
|
||||
```
|
||||
|
||||
## Verify Hooks Are Working
|
||||
|
||||
### Check Watcher Status
|
||||
```bash
|
||||
curl http://localhost:8051/api/watcher/status
|
||||
```
|
||||
|
||||
Expected output shows your project is being watched:
|
||||
```json
|
||||
{
|
||||
"status": {
|
||||
"enabled": true,
|
||||
"projects_watched": 1,
|
||||
"projects": {
|
||||
"1": {
|
||||
"watched_paths": {
|
||||
"knowledge_docs": "/path/to/docs",
|
||||
"project_profile": "/path/to/.claude-os/project-profile",
|
||||
...
|
||||
},
|
||||
"event_handlers": ["knowledge_docs", "project_profile", ...]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Check Hooks Configuration
|
||||
```bash
|
||||
cat /path/to/project/.claude-os/hooks.json
|
||||
```
|
||||
|
||||
Should show something like:
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"project_id": 1,
|
||||
"hooks": {
|
||||
"knowledge_docs": {
|
||||
"enabled": true,
|
||||
"folder_path": "/path/to/docs",
|
||||
"synced_files": {}
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Auto-Sync
|
||||
|
||||
### Test knowledge_docs Auto-Sync
|
||||
```bash
|
||||
# Create a test file
|
||||
echo "# Test Document" > /path/to/docs/test.md
|
||||
|
||||
# Wait 2-3 seconds for the watcher to pick it up
|
||||
|
||||
# Verify it was indexed
|
||||
sqlite3 /path/to/claude-os/data/claude-os.db \
|
||||
"SELECT COUNT(*) FROM documents WHERE doc_id LIKE 'test.md%' AND kb_id = 1"
|
||||
```
|
||||
|
||||
Should show a count > 0 if successfully indexed.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hooks not triggering auto-sync?
|
||||
|
||||
1. **Check if watcher is running:**
|
||||
```bash
|
||||
curl http://localhost:8051/api/watcher/status
|
||||
```
|
||||
If `"enabled": false`, start it:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8051/api/watcher/start/1"
|
||||
```
|
||||
|
||||
2. **Check hooks configuration:**
|
||||
```bash
|
||||
cat .claude-os/hooks.json
|
||||
```
|
||||
Verify `"enabled": true` for your MCP type
|
||||
|
||||
3. **Check folder permissions:**
|
||||
- Ensure folder is readable by the MCP server
|
||||
- Try creating a test file manually
|
||||
|
||||
4. **Check MCP server logs:**
|
||||
- Look for errors in `/private/tmp/` or application logs
|
||||
- Check if server is running: `ps aux | grep python | grep server`
|
||||
|
||||
### Files not appearing in search?
|
||||
|
||||
1. **Verify files were indexed:**
|
||||
```bash
|
||||
sqlite3 /path/to/claude-os/data/claude-os.db \
|
||||
"SELECT COUNT(*) FROM documents WHERE kb_id = 1"
|
||||
```
|
||||
|
||||
2. **Check file format is supported:**
|
||||
- Default: `.md`, `.txt`, `.pdf`, `.py`, `.js`, `.ts`, `.json`, `.yaml`
|
||||
- Configure custom patterns in hooks.json `file_patterns` array
|
||||
|
||||
3. **Manually trigger sync:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:8051/api/projects/1/hooks/sync?mcp_type=knowledge_docs"
|
||||
```
|
||||
|
||||
## Advanced: Restart Watchers
|
||||
|
||||
If watchers stop responding:
|
||||
|
||||
```bash
|
||||
# Restart a specific project's watcher
|
||||
curl -X POST "http://localhost:8051/api/watcher/restart/1"
|
||||
|
||||
# Or restart all watchers
|
||||
# (Note: not available via API, requires server restart)
|
||||
```
|
||||
|
||||
## Git Integration
|
||||
|
||||
The system also installs **git post-commit hooks** that:
|
||||
- Auto-index changed files on each commit
|
||||
- Periodically expand the index with new files (every 10 commits)
|
||||
- Track commit count for smart indexing
|
||||
|
||||
This works seamlessly with the file watcher hooks.
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Do I need to restart anything after running initialize-project?**
|
||||
A: No! Everything is set up automatically. File watchers start immediately.
|
||||
|
||||
**Q: Can I add files while the watcher is running?**
|
||||
A: Yes! Add files anytime. They'll be indexed within ~2 seconds.
|
||||
|
||||
**Q: What if I want to disable auto-sync for a folder?**
|
||||
A: Disable the hook via API:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8051/api/projects/1/hooks/knowledge_docs/disable"
|
||||
```
|
||||
|
||||
**Q: Can multiple projects have watchers running?**
|
||||
A: Yes! The system can monitor multiple projects simultaneously.
|
||||
|
||||
**Q: Do file watchers persist after server restart?**
|
||||
A: No, watchers stop when the server restarts. They'll restart automatically when projects are loaded.
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** October 28, 2025
|
||||
373
docs/guides/IDEAL_SESSION_WORKFLOW.md
Normal file
373
docs/guides/IDEAL_SESSION_WORKFLOW.md
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
# The Ideal Claude OS Session Workflow
|
||||
|
||||
**What SHOULD happen vs what ACTUALLY happens (and how to fix it)**
|
||||
|
||||
---
|
||||
|
||||
## 🌅 Morning Coffee Scenario
|
||||
|
||||
You grab your coffee, open your terminal, and type:
|
||||
|
||||
```bash
|
||||
cd ~/Projects/pistn
|
||||
code .
|
||||
# Claude Code CLI opens
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ IDEAL: What SHOULD Happen Automatically
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🚀 CLAUDE OS INITIALIZED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Detected Project: Pistn (Ruby on Rails)
|
||||
Loading context... ⏳
|
||||
|
||||
[Claude automatically reads:]
|
||||
✓ CLAUDE.md
|
||||
✓ .claude-os/config.json
|
||||
✓ claude-os-state.json
|
||||
✓ agent-os/config.yml
|
||||
|
||||
[Claude automatically checks:]
|
||||
✓ Git status (feature/appointment-redesign, 3 files modified)
|
||||
✓ Recent commits (last 3)
|
||||
✓ Project memories (searching "appointment dashboard recent")
|
||||
|
||||
[Claude automatically loads:]
|
||||
✓ 5 relevant memories
|
||||
✓ Coding standards
|
||||
✓ Architectural patterns
|
||||
✓ Kanban board (3 specs, 52 tasks, 45% complete)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
📚 CONTEXT LOADED - Ready to code!
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
You were working on: Appointment Dashboard Redesign
|
||||
Progress: 45% complete (23 of 52 tasks done)
|
||||
|
||||
Last session: 2 days ago
|
||||
Duration: 2h 15m
|
||||
Completed: Sidebar navigation, card layouts, toggle switches
|
||||
|
||||
Still in progress:
|
||||
⏳ PHASE2-TASK3: Implement concern methods
|
||||
⏳ PHASE2-TASK4: Add helper methods
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
💡 KEY REMINDERS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
• 67-page implementation plan in memories
|
||||
• Zero functionality loss requirement
|
||||
• Tekmetric integration must work on save
|
||||
• Use service objects for business logic
|
||||
• Bootstrap 3.x only (no v4/v5)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🎯 SUGGESTED ACTIONS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
1. Continue Phase 2 (30 min estimated)
|
||||
2. Commit your 3 modified files first
|
||||
3. Start something new
|
||||
|
||||
What would you like to do?
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
**You say:** "Let's continue phase 2"
|
||||
|
||||
**Claude:** "Perfect! Let me implement the concern methods for PHASE2-TASK3. Based on our service object pattern, I'll..."
|
||||
|
||||
---
|
||||
|
||||
## ❌ CURRENT: What ACTUALLY Happens
|
||||
|
||||
```
|
||||
You: [Opens Claude Code]
|
||||
|
||||
Claude: Hello! How can I help you today?
|
||||
|
||||
You: Let's continue working on the appointment dashboard
|
||||
|
||||
Claude: I'd be happy to help with the appointment dashboard!
|
||||
Could you tell me more about the project and what
|
||||
you've been working on?
|
||||
|
||||
You: *Sigh* We're redesigning the appointment dashboard in
|
||||
Pistn. We've been working on this for 2 days. I have
|
||||
a 67-page implementation plan. We completed the sidebar
|
||||
and cards. We're on phase 2...
|
||||
|
||||
Claude: Thanks for the context! What would you like to work
|
||||
on specifically?
|
||||
|
||||
You: Let me just share the implementation plan again...
|
||||
[Copies and pastes 67 pages]
|
||||
```
|
||||
|
||||
**Problem:** You spend 15 minutes re-explaining everything Claude should already know!
|
||||
|
||||
---
|
||||
|
||||
## 🔧 THE FIX: Making It Work Today
|
||||
|
||||
**Until automatic session start is implemented in Claude Code itself, YOU need to trigger it manually:**
|
||||
|
||||
### Step 1: Add This to Your CLAUDE.md
|
||||
|
||||
```markdown
|
||||
# IMPORTANT: Session Start Protocol
|
||||
|
||||
**EVERY TIME we start a conversation, YOU (Claude) must:**
|
||||
|
||||
1. **Check if this is a Claude OS project**
|
||||
```bash
|
||||
ls -la | grep -E "(CLAUDE.md|.claude-os|agent-os)"
|
||||
```
|
||||
|
||||
2. **If yes, AUTOMATICALLY do the following WITHOUT me asking:**
|
||||
|
||||
a) Read these files:
|
||||
- CLAUDE.md (you're reading it now!)
|
||||
- .claude-os/config.json
|
||||
- claude-os-state.json (if exists)
|
||||
- agent-os/config.yml (if exists)
|
||||
|
||||
b) Check git context:
|
||||
```bash
|
||||
git branch --show-current
|
||||
git status --short
|
||||
git log -3 --oneline
|
||||
```
|
||||
|
||||
c) Search my project memories:
|
||||
- Use last session task + current branch + "recent"
|
||||
- Load top 5 memories from {project}-project_memories
|
||||
|
||||
d) Load coding standards:
|
||||
- Search {project}-project_profile for "coding standards"
|
||||
|
||||
e) Check Kanban board:
|
||||
- GET /api/projects/{id}/kanban
|
||||
- Show current spec progress
|
||||
|
||||
f) Present a session start summary showing:
|
||||
- What we were working on last
|
||||
- Current progress
|
||||
- Key reminders
|
||||
- Suggested next steps
|
||||
|
||||
3. **Then ask:** "What would you like to work on?"
|
||||
|
||||
**DO THIS AUTOMATICALLY. DO NOT wait for me to ask!**
|
||||
```
|
||||
|
||||
### Step 2: Create a Morning Routine
|
||||
|
||||
Add this alias to your `~/.zshrc` or `~/.bashrc`:
|
||||
|
||||
```bash
|
||||
alias pistn='cd ~/Projects/pistn && code . && cat .claude-os/session-reminder.txt'
|
||||
```
|
||||
|
||||
Create `.claude-os/session-reminder.txt`:
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🚨 REMINDER TO CLAUDE 🚨
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
This is a Claude OS project!
|
||||
|
||||
Please AUTOMATICALLY:
|
||||
1. Read CLAUDE.md
|
||||
2. Read .claude-os/config.json
|
||||
3. Check git status
|
||||
4. Search project_memories
|
||||
5. Load coding standards
|
||||
6. Check Kanban board
|
||||
7. Present session start summary
|
||||
|
||||
DO NOT make me paste context manually!
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
Now you just type `pistn` and Claude will see the reminder!
|
||||
|
||||
### Step 3: Use Session Commands
|
||||
|
||||
When you want to ensure context is loaded:
|
||||
|
||||
```bash
|
||||
# Option A: Explicit session start
|
||||
/claude-os-session start "continue appointment dashboard"
|
||||
|
||||
# Option B: Just remind Claude
|
||||
"Hey Claude, can you load my project context and tell me
|
||||
where we left off?"
|
||||
|
||||
# Option C: Direct command
|
||||
"Read CLAUDE.md, check git status, search my memories for
|
||||
'appointment dashboard', and load my coding standards.
|
||||
Then tell me what we were working on."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Making This Automatic (Future)
|
||||
|
||||
**What needs to happen for true automation:**
|
||||
|
||||
### In Claude Code CLI:
|
||||
|
||||
1. **Project Detection Hook**
|
||||
- When CLI starts, check for `.claude-os/`
|
||||
- If found, trigger automatic context loading
|
||||
- Display session start summary
|
||||
|
||||
2. **Auto-Load Behavior**
|
||||
- Read `CLAUDE.md` into system prompt
|
||||
- Search knowledge bases automatically
|
||||
- Present context before user types anything
|
||||
|
||||
3. **Session State Persistence**
|
||||
- Save session state on exit
|
||||
- Restore on next start
|
||||
- Track what was being worked on
|
||||
|
||||
### In Claude OS:
|
||||
|
||||
4. **Session API Endpoint**
|
||||
```http
|
||||
GET /api/projects/{id}/session/start
|
||||
|
||||
Returns:
|
||||
- Last session summary
|
||||
- Relevant memories (top 5)
|
||||
- Coding standards
|
||||
- Git context
|
||||
- Kanban status
|
||||
- Suggested actions
|
||||
```
|
||||
|
||||
5. **MCP Session Tool**
|
||||
- Expose session data via MCP
|
||||
- Claude Code can call it automatically
|
||||
- No user intervention needed
|
||||
|
||||
---
|
||||
|
||||
## 📋 Checklist: Am I Using Sessions Effectively?
|
||||
|
||||
### ✅ Good Session Practice
|
||||
|
||||
- [ ] Claude presents context at start without me asking
|
||||
- [ ] I never re-explain the project architecture
|
||||
- [ ] Claude references past decisions automatically
|
||||
- [ ] I can pick up exactly where I left off
|
||||
- [ ] Claude suggests next steps based on history
|
||||
|
||||
### ❌ Bad Session Practice
|
||||
|
||||
- [ ] I paste the same context document every session
|
||||
- [ ] I explain "we use service objects" every time
|
||||
- [ ] Claude asks "what's your tech stack?" repeatedly
|
||||
- [ ] I have to search for what I did yesterday
|
||||
- [ ] Every session feels like starting over
|
||||
|
||||
---
|
||||
|
||||
## 🔥 Power User Tips
|
||||
|
||||
### 1. Create Session Shortcuts
|
||||
|
||||
```bash
|
||||
# ~/.zshrc
|
||||
alias cs-start='echo "Claude: load context, check memories, show status"'
|
||||
alias cs-end='echo "Claude: analyze session, suggest saves, update state"'
|
||||
alias cs-blocker='echo "Claude: track blocker and search for solutions"'
|
||||
```
|
||||
|
||||
### 2. Use Quick Saves During Work
|
||||
|
||||
```bash
|
||||
# When you discover something important
|
||||
/claude-os-session save "Service objects return model on success, error on fail"
|
||||
|
||||
# When you hit a blocker
|
||||
/claude-os-session blocker "Tekmetric API 500 errors on appointment sync"
|
||||
|
||||
# When you find a pattern
|
||||
/claude-os-session pattern "Use localStorage for sidebar state persistence"
|
||||
```
|
||||
|
||||
### 3. End Sessions Properly
|
||||
|
||||
```bash
|
||||
# At end of day
|
||||
/claude-os-session end
|
||||
|
||||
# Claude will:
|
||||
# - Analyze what you did
|
||||
# - Suggest high-value saves
|
||||
# - Update statistics
|
||||
# - Prepare context for next session
|
||||
```
|
||||
|
||||
### 4. Check Status Mid-Session
|
||||
|
||||
```bash
|
||||
/claude-os-session status
|
||||
|
||||
# Shows:
|
||||
# - How long you've been working
|
||||
# - What context is loaded
|
||||
# - Active blockers
|
||||
# - Next suggested action
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 The Ultimate Goal
|
||||
|
||||
**Imagine this future:**
|
||||
|
||||
You open Claude Code. No typing needed. Claude says:
|
||||
|
||||
```
|
||||
Welcome back! It's been 2 days since your last session.
|
||||
|
||||
You were redesigning the Appointment Dashboard. You're 45% done
|
||||
(23 of 52 tasks). Last session you completed the sidebar navigation
|
||||
and card layouts.
|
||||
|
||||
You have 2 tasks in progress:
|
||||
- PHASE2-TASK3: Implement concern methods (30 min estimated)
|
||||
- PHASE2-TASK4: Add helper methods (20 min estimated)
|
||||
|
||||
I have your 67-page implementation plan loaded, plus 5 relevant
|
||||
memories and all your coding standards.
|
||||
|
||||
Your git branch has 3 uncommitted files. Want to commit those
|
||||
first or continue with PHASE2-TASK3?
|
||||
```
|
||||
|
||||
**That's zero context loss. That's the vision.**
|
||||
|
||||
---
|
||||
|
||||
## 📖 Related Docs
|
||||
|
||||
- [SESSION_START_PROTOCOL.md](./SESSION_START_PROTOCOL.md) - Detailed protocol
|
||||
- [claude-os-session.md](../../templates/commands/claude-os-session.md) - Command reference
|
||||
- [WHAT_IS_CLAUDE_OS.md](./WHAT_IS_CLAUDE_OS.md) - Overall system guide
|
||||
|
||||
---
|
||||
|
||||
**Until full automation exists, use the CLAUDE.md instructions and session commands to get the same result manually. But it WILL be automatic soon!** 🚀
|
||||
548
docs/guides/MANDATORY_SESSIONS.md
Normal file
548
docs/guides/MANDATORY_SESSIONS.md
Normal file
|
|
@ -0,0 +1,548 @@
|
|||
# Mandatory Sessions - The New Claude OS Model
|
||||
|
||||
**Every conversation is a session. Period.**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 The Philosophy
|
||||
|
||||
**Old Model:** Sessions are optional, you choose when to track work
|
||||
**New Model:** You're ALWAYS in a session. The only choice is WHAT session.
|
||||
|
||||
**Why?**
|
||||
- Zero context loss
|
||||
- Complete work history
|
||||
- Automatic tracking
|
||||
- Better insights over time
|
||||
|
||||
---
|
||||
|
||||
## 🚀 How It Works
|
||||
|
||||
### Every Conversation Start
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🚀 CLAUDE OS - SESSION MANAGER
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Project: Pistn
|
||||
Last Session: Dashboard Redesign (2 days ago, 2h 15m)
|
||||
Progress: 45% complete (PHASE2-TASK3 in progress)
|
||||
|
||||
Options:
|
||||
1. Resume "Dashboard Redesign" [loads full context]
|
||||
2. Start new session [what are you working on?]
|
||||
3. Quick question [auto-session, no setup needed]
|
||||
|
||||
Choice: _
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
**You MUST pick one. No "just chatting" option.**
|
||||
|
||||
---
|
||||
|
||||
## 📋 Session Types
|
||||
|
||||
### 1. Feature Implementation
|
||||
```
|
||||
Type: feature
|
||||
Duration: Tracked
|
||||
Kanban: Linked to spec + tasks
|
||||
Auto-saves: High-value patterns and decisions
|
||||
|
||||
Example: "Dashboard Redesign"
|
||||
Context: Spec tasks, memories, coding standards
|
||||
Tracking: Task completion, time per task, blockers
|
||||
```
|
||||
|
||||
### 2. Bug Fix
|
||||
```
|
||||
Type: bug
|
||||
Duration: Tracked
|
||||
Priority: Detected from description (high/medium/low)
|
||||
Auto-saves: Root cause, solution, prevention
|
||||
|
||||
Example: "API 500 Errors"
|
||||
Context: Error logs, similar past issues, integration patterns
|
||||
Tracking: Time to fix, solution approach, related issues
|
||||
```
|
||||
|
||||
### 3. Exploration / Learning
|
||||
```
|
||||
Type: exploration
|
||||
Duration: Tracked
|
||||
Auto-saves: Key learnings, architecture insights
|
||||
|
||||
Example: "Understand authentication flow"
|
||||
Context: Relevant code, architecture docs
|
||||
Tracking: Files explored, patterns discovered
|
||||
```
|
||||
|
||||
### 4. Refactoring / Maintenance
|
||||
```
|
||||
Type: maintenance
|
||||
Duration: Tracked
|
||||
Auto-saves: Refactoring patterns, improvements made
|
||||
|
||||
Example: "Clean up controller concerns"
|
||||
Context: Coding standards, similar refactors
|
||||
Tracking: Files changed, complexity reduced
|
||||
```
|
||||
|
||||
### 5. Code Review
|
||||
```
|
||||
Type: review
|
||||
Duration: Tracked
|
||||
Auto-saves: Review comments, patterns identified
|
||||
|
||||
Example: "Review PR #234"
|
||||
Context: Project standards, common issues
|
||||
Tracking: Issues found, suggestions made
|
||||
```
|
||||
|
||||
### 6. Quick Question (Auto-Managed)
|
||||
```
|
||||
Type: question
|
||||
Duration: Auto-tracked (ends after 5 min inactivity)
|
||||
Auto-saves: Only if valuable insight
|
||||
|
||||
Example: "How does Draper work?"
|
||||
Context: Minimal (just project standards)
|
||||
Tracking: Questions asked, answers given
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Session Lifecycle
|
||||
|
||||
### Phase 1: Session Selection (Every Conversation Start)
|
||||
|
||||
**Option 1: Resume Existing Session**
|
||||
```
|
||||
You: "1" (resume)
|
||||
|
||||
Claude:
|
||||
═══════════════════════════════════════════════════════════════
|
||||
✓ RESUMING: Dashboard Redesign
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
[Loads all context automatically:]
|
||||
✓ Spec: Group Account Rendering (45% complete)
|
||||
✓ Current Task: PHASE2-TASK3 (Implement concern methods)
|
||||
✓ 5 relevant memories loaded
|
||||
✓ Coding standards loaded
|
||||
✓ Git: 3 files modified on feature/appointment-redesign
|
||||
|
||||
Duration this session: 0h 0m
|
||||
Total duration: 2h 15m
|
||||
|
||||
Ready! Let's continue implementing PHASE2-TASK3.
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
**Option 2: Start New Session**
|
||||
```
|
||||
You: "2" (new session)
|
||||
|
||||
Claude: "What are you working on?
|
||||
|
||||
(I'll detect the type and load relevant context)"
|
||||
|
||||
You: "Fix the Tekmetric API 500 errors"
|
||||
|
||||
Claude:
|
||||
═══════════════════════════════════════════════════════════════
|
||||
✓ NEW BUG SESSION STARTED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Session: "Fix Tekmetric API 500 errors"
|
||||
Type: Bug Fix (detected)
|
||||
Priority: High (500 errors are critical)
|
||||
|
||||
[Auto-loaded context:]
|
||||
✓ Searched memories: "Tekmetric API integration errors"
|
||||
✓ Found 3 similar issues from past sessions
|
||||
✓ Loaded integration patterns
|
||||
✓ Git status checked
|
||||
|
||||
Previous session "Dashboard Redesign" paused.
|
||||
|
||||
Let's debug this! What's the error message?
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
**Option 3: Quick Question**
|
||||
```
|
||||
You: "3" (quick question)
|
||||
|
||||
Claude: "What's your question?"
|
||||
|
||||
You: "How do I use Draper decorators?"
|
||||
|
||||
Claude:
|
||||
═══════════════════════════════════════════════════════════════
|
||||
💬 QUICK QUESTION SESSION
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
[Gives answer]
|
||||
|
||||
This auto-session will end after 5 minutes of inactivity.
|
||||
Need more help, or ready to resume Dashboard Redesign?
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### Phase 2: Active Session (During Work)
|
||||
|
||||
**Live Tracking:**
|
||||
```
|
||||
[Active Session: Dashboard Redesign]
|
||||
Duration: 1h 23m
|
||||
Files changed: 3
|
||||
Quick saves: 2
|
||||
Last activity: 2 minutes ago
|
||||
|
||||
/claude-os-session status → Shows current session details
|
||||
/claude-os-session save "insight" → Quick save to memories
|
||||
/claude-os-session switch "bug fix" → Switch to different session
|
||||
```
|
||||
|
||||
**Smart Prompts:**
|
||||
```
|
||||
[After 2 hours of work]
|
||||
Claude: "You've been working for 2h. You usually take a break around now.
|
||||
Want to commit your work and pause the session?"
|
||||
|
||||
[When pattern detected]
|
||||
Claude: "This looks like the service object pattern. Should I save
|
||||
this as a reusable pattern for future reference?"
|
||||
|
||||
[When blocker encountered]
|
||||
Claude: "Want me to track this as a blocker and search for solutions
|
||||
from past sessions?"
|
||||
```
|
||||
|
||||
### Phase 3: Session End (Explicit or Auto)
|
||||
|
||||
**Explicit End:**
|
||||
```
|
||||
You: "/claude-os-session end"
|
||||
|
||||
Claude:
|
||||
═══════════════════════════════════════════════════════════════
|
||||
📊 SESSION SUMMARY: Dashboard Redesign
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Duration: 1h 23m
|
||||
Files changed: 3 files
|
||||
Quick saves: 2
|
||||
Commits: 1
|
||||
|
||||
Work completed:
|
||||
✓ Implemented concern methods for PHASE2-TASK3
|
||||
✓ Fixed N+1 query issue
|
||||
✓ Added helper methods for PHASE2-TASK4
|
||||
|
||||
Progress: 45% → 53% (8% increase!)
|
||||
Tasks completed: 2 tasks (PHASE2-TASK3, PHASE2-TASK4)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
💾 SMART SAVES DETECTED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
I found 2 items worth saving:
|
||||
|
||||
1. 💎 HIGH VALUE - Concern Method Pattern
|
||||
"Extracted group account logic to concern with proper
|
||||
service object integration. Reusable pattern."
|
||||
|
||||
Save to project_profile? [Y/n]
|
||||
|
||||
2. 📊 MEDIUM VALUE - N+1 Query Fix
|
||||
"Fixed N+1 in appointments loading by eager loading
|
||||
with includes(:group_account)"
|
||||
|
||||
Save to project_memories? [Y/n]
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
**Auto-End (Inactivity):**
|
||||
```
|
||||
[After 15 minutes of inactivity]
|
||||
|
||||
Claude: "Session 'Dashboard Redesign' has been inactive for 15 minutes.
|
||||
|
||||
Options:
|
||||
1. Keep session active
|
||||
2. Pause session (can resume later)
|
||||
3. End session (save and summarize)
|
||||
|
||||
Choice: _"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎛️ Session Switching
|
||||
|
||||
### Mid-Session Switch
|
||||
```
|
||||
[Active: Dashboard Redesign, 45m elapsed]
|
||||
|
||||
You: "Actually I need to fix a bug first"
|
||||
|
||||
Claude: "Want to switch sessions?
|
||||
|
||||
1. Pause 'Dashboard Redesign' and start 'Bug Fix' session
|
||||
2. Continue 'Dashboard Redesign' (handle bug within this session)
|
||||
|
||||
Choice: _"
|
||||
|
||||
You: "1"
|
||||
|
||||
Claude:
|
||||
═══════════════════════════════════════════════════════════════
|
||||
✓ SESSION SWITCHED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Paused: Dashboard Redesign (45m, 45% complete)
|
||||
Started: Bug Fix Session
|
||||
|
||||
What's the bug?
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### Multiple Sessions Same Day
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
📊 TODAY'S SESSIONS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
09:00 - 11:15 Dashboard Redesign 2h 15m ✓ Ended
|
||||
11:15 - 11:45 API Bug Fix 30m ✓ Ended
|
||||
12:00 - 13:30 Dashboard Redesign 1h 30m ⏸ Paused
|
||||
14:00 - 15:30 Code Review 1h 30m 🔄 Active
|
||||
|
||||
Total: 5h 45m across 4 sessions
|
||||
Most time: Dashboard Redesign (3h 45m, 2 sessions)
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Session Statistics
|
||||
|
||||
### Daily Summary
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
📊 END OF DAY SUMMARY - November 4, 2025
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Total sessions: 4
|
||||
Total time: 5h 45m
|
||||
Average session: 1h 26m
|
||||
|
||||
Session breakdown:
|
||||
• Feature work: 3h 45m (65%)
|
||||
• Bug fixes: 30m (9%)
|
||||
• Code review: 1h 30m (26%)
|
||||
|
||||
Productivity:
|
||||
• Tasks completed: 4 tasks
|
||||
• Memories saved: 6 insights
|
||||
• Patterns discovered: 2 patterns
|
||||
• Blockers resolved: 1
|
||||
|
||||
Most productive session: Dashboard Redesign (2h 15m, 3 tasks)
|
||||
Longest session: Dashboard Redesign (2h 15m)
|
||||
|
||||
Great day! 🚀
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### Weekly Summary
|
||||
```
|
||||
Week of Oct 28 - Nov 3, 2025
|
||||
|
||||
Total sessions: 23
|
||||
Total time: 32h 15m
|
||||
Average daily: 4h 36m
|
||||
|
||||
Session types:
|
||||
• Feature: 18 sessions (24h, 74%)
|
||||
• Bug fix: 3 sessions (2h, 6%)
|
||||
• Exploration: 2 sessions (6h 15m, 20%)
|
||||
|
||||
Top projects:
|
||||
1. Pistn: 28h (Dashboard Redesign: 18h)
|
||||
2. Claude OS: 4h 15m (Kanban feature: 4h 15m)
|
||||
|
||||
Memories saved: 23 insights
|
||||
Patterns discovered: 5 patterns
|
||||
|
||||
Most productive day: Oct 31 (6h 30m, 8 tasks)
|
||||
```
|
||||
|
||||
### Project Summary
|
||||
```
|
||||
Dashboard Redesign - Complete History
|
||||
|
||||
Total sessions: 5
|
||||
Total time: 12h 45m
|
||||
Status: In Progress (53% complete)
|
||||
|
||||
Session history:
|
||||
• Oct 29: 2h 15m (PHASE1: Sidebar + Cards)
|
||||
• Oct 31: 3h 00m (PHASE1: Toggles + Forms)
|
||||
• Nov 1: 2h 30m (PHASE2: Concerns started)
|
||||
• Nov 3: 3h 00m (PHASE2: Helper methods)
|
||||
• Nov 4: 2h 00m (PHASE2: Testing)
|
||||
|
||||
Progress:
|
||||
• Tasks completed: 27 of 52 (53%)
|
||||
• Phases completed: 1 of 3
|
||||
• Estimated remaining: 10h 30m
|
||||
|
||||
Velocity:
|
||||
• Average: 2.4 tasks per session
|
||||
• Average: 0.47h per task
|
||||
• Projected completion: Nov 6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Commands Reference
|
||||
|
||||
### Session Management
|
||||
```bash
|
||||
# At conversation start (automatic prompt)
|
||||
[Choose 1/2/3]
|
||||
|
||||
# During session
|
||||
/claude-os-session status # Current session details
|
||||
/claude-os-session save "note" # Quick save
|
||||
/claude-os-session switch "task" # Switch to different session
|
||||
/claude-os-session pause # Pause current session
|
||||
/claude-os-session end # End with summary
|
||||
|
||||
# View history
|
||||
/claude-os-session today # Today's sessions
|
||||
/claude-os-session week # This week
|
||||
/claude-os-session history [task] # History for specific task
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Session Preferences (`.claude-os/config.json`)
|
||||
```json
|
||||
{
|
||||
"session_management": {
|
||||
"mandatory": true,
|
||||
"auto_prompt_on_start": true,
|
||||
"auto_save_frequency": "15_minutes",
|
||||
"inactivity_timeout": 15,
|
||||
"auto_switch_detection": true,
|
||||
"daily_summary": true,
|
||||
"break_reminders": {
|
||||
"enabled": true,
|
||||
"interval_minutes": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
### 1. Name Sessions Descriptively
|
||||
```
|
||||
✅ Good: "Fix Tekmetric sync 500 errors"
|
||||
✅ Good: "Implement group account rendering"
|
||||
❌ Bad: "Work on stuff"
|
||||
❌ Bad: "Bug fix"
|
||||
```
|
||||
|
||||
### 2. Use Session Types Correctly
|
||||
- **Feature:** Building new functionality
|
||||
- **Bug:** Fixing specific issues
|
||||
- **Exploration:** Learning/understanding code
|
||||
- **Maintenance:** Refactoring/cleanup
|
||||
- **Review:** Code review
|
||||
- **Question:** Quick questions (auto-managed)
|
||||
|
||||
### 3. End Sessions Cleanly
|
||||
```
|
||||
Don't just close terminal!
|
||||
Use: /claude-os-session end
|
||||
|
||||
Benefits:
|
||||
• Captures work summary
|
||||
• Suggests valuable saves
|
||||
• Updates statistics
|
||||
• Prepares next session
|
||||
```
|
||||
|
||||
### 4. Switch Sessions When Context Changes
|
||||
```
|
||||
Don't mix unrelated work in one session!
|
||||
|
||||
Working on Dashboard → API bug appears → Switch sessions!
|
||||
|
||||
Keeps tracking clean and relevant.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Benefits Recap
|
||||
|
||||
### For You:
|
||||
- **Never lose work** - Everything tracked
|
||||
- **Perfect continuity** - Resume exactly where you left off
|
||||
- **Understand productivity** - Real metrics
|
||||
- **Learn from history** - See what works
|
||||
|
||||
### For Me (Claude):
|
||||
- **Complete context** - Know what we're working on
|
||||
- **Smart suggestions** - Based on session type
|
||||
- **Proactive saves** - Capture valuable insights
|
||||
- **Better guidance** - Relevant to current task
|
||||
|
||||
### For The Team:
|
||||
- **Shared knowledge** - All insights saved
|
||||
- **Velocity tracking** - Understand capacity
|
||||
- **Pattern recognition** - Learn what works
|
||||
- **Onboarding** - New members see work history
|
||||
|
||||
---
|
||||
|
||||
## 🎯 The Goal
|
||||
|
||||
**Every moment of coding is tracked, categorized, and learned from.**
|
||||
|
||||
No more:
|
||||
- ❌ "What was I doing?"
|
||||
- ❌ "How long did that take?"
|
||||
- ❌ "Where did we leave off?"
|
||||
- ❌ "Did we solve this before?"
|
||||
|
||||
Only:
|
||||
- ✅ Instant context on session start
|
||||
- ✅ Complete work history
|
||||
- ✅ Automatic learning
|
||||
- ✅ Zero context loss
|
||||
|
||||
**This is the complete AI development system working at full power.** 🚀
|
||||
|
||||
---
|
||||
|
||||
**Next Steps:**
|
||||
1. Update CLAUDE.md template with mandatory session flow
|
||||
2. Update `/claude-os-session` command to reflect new model
|
||||
3. Start using it TODAY in current projects
|
||||
4. Track results and iterate
|
||||
|
||||
**Let's make sessions mandatory!** 💪
|
||||
233
docs/guides/PORTS_GUIDE.md
Normal file
233
docs/guides/PORTS_GUIDE.md
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
# Claude OS Ports Guide
|
||||
|
||||
## Two Different Services
|
||||
|
||||
Claude OS has **two separate services** running on **different ports**:
|
||||
|
||||
### 1. MCP Server (Port 8051)
|
||||
|
||||
**Purpose:** API server for Claude Code integration
|
||||
|
||||
**Technology:** FastAPI (Python)
|
||||
|
||||
**URL:** `http://localhost:8051`
|
||||
|
||||
**Usage:**
|
||||
- Used by Claude Code (via MCP protocol)
|
||||
- Handles AI memory operations
|
||||
- Manages knowledge bases
|
||||
- Serves API endpoints
|
||||
|
||||
**⚠️ IMPORTANT:**
|
||||
- **Do NOT open this in a web browser**
|
||||
- It expects MCP protocol POST requests
|
||||
- Browser GET requests will show "Method Not Allowed"
|
||||
|
||||
**How to start:**
|
||||
```bash
|
||||
./start.sh # Starts MCP server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Web UI (Port 5173)
|
||||
|
||||
**Purpose:** Visual interface for humans
|
||||
|
||||
**Technology:** React + Vite (JavaScript)
|
||||
|
||||
**URL:** `http://localhost:5173`
|
||||
|
||||
**Usage:**
|
||||
- Browse knowledge bases
|
||||
- Upload documents
|
||||
- Search and query
|
||||
- Project management
|
||||
- Visual interface
|
||||
|
||||
**✅ CORRECT:**
|
||||
- **Open this URL in your browser**
|
||||
- Designed for human interaction
|
||||
- Nice visual interface
|
||||
|
||||
**How to start:**
|
||||
```bash
|
||||
cd frontend
|
||||
npm install # First time only
|
||||
npm run dev # Starts on port 5173
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Service | Port | Open in Browser? | Purpose |
|
||||
|---------|------|------------------|---------|
|
||||
| **MCP Server** | 8051 | ❌ NO | For Claude Code (API) |
|
||||
| **Web UI** | 5173 | ✅ YES | For humans (visual interface) |
|
||||
|
||||
---
|
||||
|
||||
## Common Confusion
|
||||
|
||||
### ❌ Wrong: Opening http://localhost:8051 in browser
|
||||
|
||||
**What you see:**
|
||||
```json
|
||||
{
|
||||
"detail": "Method Not Allowed"
|
||||
}
|
||||
```
|
||||
|
||||
**Why:** The MCP server expects POST requests with MCP protocol data, not browser GET requests.
|
||||
|
||||
### ✅ Correct: Opening http://localhost:5173 in browser
|
||||
|
||||
**What you see:**
|
||||
- Nice web interface
|
||||
- Project management
|
||||
- Knowledge base browser
|
||||
- Document upload
|
||||
|
||||
---
|
||||
|
||||
## Typical Workflow
|
||||
|
||||
### 1. Start MCP Server (for Claude Code)
|
||||
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
✅ Claude OS is running!
|
||||
|
||||
📡 MCP Server: http://localhost:8051
|
||||
(For Claude Code integration - do NOT open in browser)
|
||||
```
|
||||
|
||||
### 2. Start Web UI (optional, for visual interface)
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
➜ Local: http://localhost:5173/
|
||||
➜ Network: use --host to expose
|
||||
```
|
||||
|
||||
Now you can:
|
||||
- Use Claude Code with MCP server (port 8051)
|
||||
- Open Web UI in browser (port 5173)
|
||||
|
||||
---
|
||||
|
||||
## Testing Each Service
|
||||
|
||||
### Test MCP Server (Port 8051)
|
||||
|
||||
**Correct way (API call):**
|
||||
```bash
|
||||
curl -X POST http://localhost:8051/mcp/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "test"}'
|
||||
```
|
||||
|
||||
**Wrong way (browser):**
|
||||
```
|
||||
Open http://localhost:8051 in browser
|
||||
→ Shows "Method Not Allowed" ❌
|
||||
```
|
||||
|
||||
### Test Web UI (Port 5173)
|
||||
|
||||
**Correct way:**
|
||||
```
|
||||
Open http://localhost:5173 in browser
|
||||
→ Shows nice web interface ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Method Not Allowed" error
|
||||
|
||||
**Problem:** You're trying to access the MCP server (port 8051) in a browser
|
||||
|
||||
**Solution:**
|
||||
- Don't open port 8051 in browser
|
||||
- That's for Claude Code to use, not humans
|
||||
- If you want a web interface, start the frontend (port 5173)
|
||||
|
||||
### "Connection refused" on port 5173
|
||||
|
||||
**Problem:** Web UI is not running
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
cd frontend
|
||||
npm install # If first time
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### "Connection refused" on port 8051
|
||||
|
||||
**Problem:** MCP server is not running
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ YOUR BROWSER │
|
||||
│ http://localhost:5173 ✅ │
|
||||
│ (Web UI - React) │
|
||||
└──────────────┬──────────────────────────┘
|
||||
│ HTTP requests
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ MCP Server (FastAPI) │
|
||||
│ http://localhost:8051 │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌─────────┐ │
|
||||
│ │ API Routes │◄───┤ Claude │ │
|
||||
│ │ │ │ Code │ │
|
||||
│ └──────┬───────┘ └─────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Database │ │
|
||||
│ │ (SQLite) │ │
|
||||
│ └──────────────┘ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Access patterns:**
|
||||
- Browser → Port 5173 (Web UI) → Port 8051 (MCP Server) → Database
|
||||
- Claude Code → Port 8051 (MCP Server) → Database
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Remember:**
|
||||
- 🔴 **Port 8051:** For Claude Code (MCP protocol) - Don't open in browser!
|
||||
- 🟢 **Port 5173:** For humans (Web UI) - Open this in browser!
|
||||
|
||||
**When you run `./start.sh`:**
|
||||
- MCP server starts on 8051 (for Claude Code)
|
||||
- Web UI does NOT start automatically
|
||||
- Start Web UI separately with `cd frontend && npm run dev`
|
||||
|
||||
**Clear now?** 🚀
|
||||
415
docs/guides/SESSIONS_GUIDE.md
Normal file
415
docs/guides/SESSIONS_GUIDE.md
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
# Session Parsing & Insights Guide
|
||||
|
||||
**Parse Claude Code sessions and extract insights automatically.**
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Claude Code stores conversation history in `.jsonl` session files located at:
|
||||
```
|
||||
~/.claude/projects/{encoded-project-path}/{session-id}.jsonl
|
||||
```
|
||||
|
||||
Claude OS can parse these files to:
|
||||
- Extract conversations, tool calls, and file changes
|
||||
- Generate summaries for context loading
|
||||
- Extract insights (patterns, decisions, blockers)
|
||||
- Build analytics on your development sessions
|
||||
|
||||
---
|
||||
|
||||
## Session File Format
|
||||
|
||||
Claude Code session files are JSON Lines format with various entry types:
|
||||
|
||||
```jsonl
|
||||
{"type": "summary", "summary": "...", "leafUuid": "..."}
|
||||
{"type": "user", "uuid": "msg-001", "message": {"role": "user", "content": "..."}}
|
||||
{"type": "assistant", "uuid": "msg-002", "message": {"role": "assistant", "content": [...]}}
|
||||
{"type": "file-history-snapshot", "messageId": "...", "snapshot": {...}}
|
||||
```
|
||||
|
||||
### Entry Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `summary` | Session summary (usually at start) |
|
||||
| `user` | User message |
|
||||
| `assistant` | Assistant response (may contain tool_use) |
|
||||
| `file-history-snapshot` | File changes made during session |
|
||||
|
||||
---
|
||||
|
||||
## Using the Session Parser
|
||||
|
||||
### Python API
|
||||
|
||||
```python
|
||||
from app.core.session_parser import (
|
||||
SessionParser,
|
||||
get_project_sessions_dir,
|
||||
list_session_files
|
||||
)
|
||||
|
||||
# Find sessions for a project
|
||||
project_path = "/Users/me/Projects/myapp"
|
||||
sessions = list_session_files(project_path)
|
||||
# Returns: ['/path/to/session1.jsonl', '/path/to/session2.jsonl']
|
||||
|
||||
# Parse a session
|
||||
parser = SessionParser(sessions[0])
|
||||
session_data = parser.parse()
|
||||
|
||||
# Access parsed data
|
||||
print(f"Session ID: {session_data.session_id}")
|
||||
print(f"Messages: {len(session_data.messages)}")
|
||||
print(f"Tool Calls: {len(session_data.tool_calls)}")
|
||||
print(f"File Changes: {len(session_data.file_changes)}")
|
||||
print(f"Duration: {session_data.start_time} to {session_data.end_time}")
|
||||
|
||||
# Get conversation messages
|
||||
messages = parser.get_conversation()
|
||||
for msg in messages:
|
||||
print(f"{msg.role}: {msg.content[:100]}...")
|
||||
|
||||
# Get summary for LLM processing
|
||||
summary = parser.get_summary_for_extraction(max_tokens=500)
|
||||
print(summary)
|
||||
```
|
||||
|
||||
### REST API
|
||||
|
||||
```bash
|
||||
# List sessions for a project
|
||||
curl "http://localhost:8051/api/sessions?project_path=/path/to/project&limit=10"
|
||||
|
||||
# Get session details
|
||||
curl "http://localhost:8051/api/sessions/abc123?project_path=/path/to/project"
|
||||
|
||||
# Get session summary
|
||||
curl "http://localhost:8051/api/sessions/abc123/summary?project_path=/path/to/project&max_tokens=500"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Data Structure
|
||||
|
||||
### SessionData
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class SessionData:
|
||||
session_id: str
|
||||
session_path: str
|
||||
messages: List[Message]
|
||||
tool_calls: List[ToolCall]
|
||||
file_changes: List[FileChange]
|
||||
start_time: Optional[str]
|
||||
end_time: Optional[str]
|
||||
git_branch: Optional[str]
|
||||
cwd: Optional[str]
|
||||
total_entries: int
|
||||
```
|
||||
|
||||
### Message
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Message:
|
||||
role: str # "user" or "assistant"
|
||||
content: str # Message content
|
||||
timestamp: str # ISO timestamp
|
||||
uuid: str # Message UUID
|
||||
parent_uuid: Optional[str]
|
||||
```
|
||||
|
||||
### ToolCall
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ToolCall:
|
||||
tool_name: str # e.g., "Read", "Write", "Bash"
|
||||
timestamp: str
|
||||
uuid: str
|
||||
parent_uuid: Optional[str]
|
||||
input_data: Dict # Tool input parameters
|
||||
```
|
||||
|
||||
### FileChange
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FileChange:
|
||||
file_path: str
|
||||
timestamp: str
|
||||
message_id: str # Which message triggered this change
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Insight Extraction
|
||||
|
||||
The InsightExtractor analyzes sessions to identify:
|
||||
|
||||
- **Patterns** - Recurring solutions and approaches
|
||||
- **Decisions** - Architecture and implementation choices
|
||||
- **Blockers** - Problems encountered and how they were resolved
|
||||
- **Discoveries** - New learnings about the codebase
|
||||
|
||||
### Using InsightExtractor
|
||||
|
||||
```python
|
||||
from app.core.insight_extractor import InsightExtractor, Insight
|
||||
|
||||
# Create extractor with Ollama
|
||||
extractor = InsightExtractor(
|
||||
ollama_host="http://localhost:11434",
|
||||
model="llama3.1:8b"
|
||||
)
|
||||
|
||||
# Extract insights from a session
|
||||
parser = SessionParser("/path/to/session.jsonl")
|
||||
summary = parser.get_summary_for_extraction(max_tokens=1000)
|
||||
|
||||
insights = await extractor.extract_insights(summary)
|
||||
|
||||
for insight in insights:
|
||||
print(f"Type: {insight.insight_type}")
|
||||
print(f"Title: {insight.title}")
|
||||
print(f"Content: {insight.content}")
|
||||
print(f"Tags: {insight.tags}")
|
||||
print("---")
|
||||
```
|
||||
|
||||
### Insight Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `pattern` | A reusable solution or approach |
|
||||
| `decision` | An architectural or implementation choice |
|
||||
| `blocker` | A problem that was encountered |
|
||||
| `discovery` | Something new learned about the codebase |
|
||||
|
||||
### Saving Insights to Memory
|
||||
|
||||
```python
|
||||
from app.core.sqlite_manager import get_sqlite_manager
|
||||
|
||||
db = get_sqlite_manager()
|
||||
|
||||
for insight in insights:
|
||||
# Save to project memories KB
|
||||
db.add_document(
|
||||
kb_name=f"{project_name}-project_memories",
|
||||
content=f"# {insight.title}\n\n{insight.content}",
|
||||
filename=f"insight-{insight.insight_type}-{timestamp}.md",
|
||||
metadata={
|
||||
"type": insight.insight_type,
|
||||
"tags": insight.tags,
|
||||
"session_id": session_data.session_id
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Summary Format
|
||||
|
||||
The `get_summary_for_extraction()` method generates a structured summary:
|
||||
|
||||
```
|
||||
# Session: abc123
|
||||
Project: /Users/me/Projects/myapp
|
||||
Branch: feature-auth
|
||||
Duration: 2025-12-11T10:00:00Z to 2025-12-11T11:30:00Z
|
||||
|
||||
## Conversation (24 messages)
|
||||
USER: Help me implement user authentication...
|
||||
ASSISTANT: I'll help you implement authentication. Let me start by...
|
||||
USER: Can you add password hashing?
|
||||
ASSISTANT: Of course! I'll use bcrypt for secure password hashing...
|
||||
|
||||
## Tool Calls
|
||||
- Read: /Users/me/Projects/myapp/src/auth.py
|
||||
- Write: /Users/me/Projects/myapp/src/auth.py
|
||||
- Read: /Users/me/Projects/myapp/tests/test_auth.py
|
||||
- Write: /Users/me/Projects/myapp/tests/test_auth.py
|
||||
- Bash: pytest tests/test_auth.py
|
||||
|
||||
## File Changes
|
||||
- /Users/me/Projects/myapp/src/auth.py
|
||||
- /Users/me/Projects/myapp/tests/test_auth.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project Sessions Directory
|
||||
|
||||
Claude Code encodes project paths for the sessions directory:
|
||||
|
||||
```
|
||||
/Users/me/Projects/myapp
|
||||
→ ~/.claude/projects/-Users-me-Projects-myapp/
|
||||
```
|
||||
|
||||
The encoding:
|
||||
1. Replaces `/` with `-`
|
||||
2. Keeps the leading `-` (from the initial `/`)
|
||||
|
||||
### Finding Sessions
|
||||
|
||||
```python
|
||||
from app.core.session_parser import get_project_sessions_dir, list_session_files
|
||||
|
||||
# Get sessions directory for a project
|
||||
sessions_dir = get_project_sessions_dir("/Users/me/Projects/myapp")
|
||||
# Returns: ~/.claude/projects/-Users-me-Projects-myapp
|
||||
|
||||
# List all session files
|
||||
sessions = list_session_files("/Users/me/Projects/myapp")
|
||||
# Returns sorted list of .jsonl files (newest first)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. Load Context at Session Start
|
||||
|
||||
```python
|
||||
# At session start, load recent session insights
|
||||
sessions = list_session_files(project_path)
|
||||
if sessions:
|
||||
parser = SessionParser(sessions[0])
|
||||
summary = parser.get_summary_for_extraction(max_tokens=500)
|
||||
# Use summary to prime Claude with recent context
|
||||
```
|
||||
|
||||
### 2. Build Session Analytics
|
||||
|
||||
```python
|
||||
# Analyze all sessions for a project
|
||||
sessions = list_session_files(project_path)
|
||||
stats = {
|
||||
"total_sessions": len(sessions),
|
||||
"total_messages": 0,
|
||||
"total_tool_calls": 0,
|
||||
"tools_used": {}
|
||||
}
|
||||
|
||||
for session_path in sessions:
|
||||
parser = SessionParser(session_path)
|
||||
data = parser.parse()
|
||||
|
||||
stats["total_messages"] += len(data.messages)
|
||||
stats["total_tool_calls"] += len(data.tool_calls)
|
||||
|
||||
for tc in data.tool_calls:
|
||||
stats["tools_used"][tc.tool_name] = \
|
||||
stats["tools_used"].get(tc.tool_name, 0) + 1
|
||||
```
|
||||
|
||||
### 3. Extract and Save Patterns
|
||||
|
||||
```python
|
||||
# Extract insights from recent sessions
|
||||
extractor = InsightExtractor()
|
||||
|
||||
for session_path in sessions[:5]: # Last 5 sessions
|
||||
parser = SessionParser(session_path)
|
||||
summary = parser.get_summary_for_extraction()
|
||||
|
||||
insights = await extractor.extract_insights(summary)
|
||||
|
||||
for insight in insights:
|
||||
if insight.insight_type == "pattern":
|
||||
# Save to memories KB
|
||||
save_to_memories(project_name, insight)
|
||||
```
|
||||
|
||||
### 4. Debug Failed Sessions
|
||||
|
||||
```python
|
||||
# Find sessions with errors
|
||||
for session_path in sessions:
|
||||
parser = SessionParser(session_path)
|
||||
data = parser.parse()
|
||||
|
||||
# Look for error patterns in messages
|
||||
for msg in data.messages:
|
||||
if "error" in msg.content.lower() or "failed" in msg.content.lower():
|
||||
print(f"Session {data.session_id} may have errors")
|
||||
print(f" Message: {msg.content[:100]}...")
|
||||
break
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Ollama settings for insight extraction
|
||||
OLLAMA_HOST=http://localhost:11434
|
||||
OLLAMA_MODEL=llama3.1:8b
|
||||
```
|
||||
|
||||
### Truncation Settings
|
||||
|
||||
Large sessions are automatically truncated for LLM processing:
|
||||
|
||||
```python
|
||||
# Default: 500 tokens (~2000 chars)
|
||||
summary = parser.get_summary_for_extraction(max_tokens=500)
|
||||
|
||||
# For more context
|
||||
summary = parser.get_summary_for_extraction(max_tokens=2000)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "No sessions found"
|
||||
- Verify project path is correct
|
||||
- Check that sessions exist: `ls ~/.claude/projects/`
|
||||
- The encoded path includes leading hyphen: `-Users-me-Projects-myapp`
|
||||
|
||||
### "Failed to parse session"
|
||||
- Session file may be corrupted
|
||||
- Check for valid JSON on each line
|
||||
- Malformed lines are skipped automatically
|
||||
|
||||
### "Insight extraction failed"
|
||||
- Verify Ollama is running: `ollama list`
|
||||
- Check model is available: `ollama pull llama3.1:8b`
|
||||
- Check logs for API errors
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### REST Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/sessions` | GET | List project sessions |
|
||||
| `/api/sessions/{id}` | GET | Get session details |
|
||||
| `/api/sessions/{id}/summary` | GET | Get session summary |
|
||||
|
||||
### MCP Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `mcp__code-forge__list_sessions` | List sessions for a project |
|
||||
| `mcp__code-forge__get_session` | Get session details |
|
||||
| `mcp__code-forge__get_session_summary` | Get formatted summary |
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [API Reference](../API_REFERENCE.md) - Session API endpoints
|
||||
- [Self Learning System](../SELF_LEARNING_SYSTEM.md) - How Claude learns
|
||||
- [README](../../README.md) - Full Claude OS documentation
|
||||
410
docs/guides/SESSION_IMPLEMENTATION_SUMMARY.md
Normal file
410
docs/guides/SESSION_IMPLEMENTATION_SUMMARY.md
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
# Session Implementation Summary
|
||||
|
||||
**What we just built: Mandatory session management system for Claude OS**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 The Big Idea
|
||||
|
||||
**Every conversation is a session. Always.**
|
||||
|
||||
No more optional tracking. No more lost context. No more "what was I doing?"
|
||||
|
||||
---
|
||||
|
||||
## 📚 What We Created
|
||||
|
||||
### 1. **MANDATORY_SESSIONS.md** (Complete Spec)
|
||||
Location: `/Users/iamanmp/Projects/claude-os/docs/guides/MANDATORY_SESSIONS.md`
|
||||
|
||||
**Contents:**
|
||||
- Session lifecycle (selection → active → end)
|
||||
- 6 session types (feature, bug, exploration, maintenance, review, question)
|
||||
- Session switching flow
|
||||
- Statistics and tracking
|
||||
- Commands reference
|
||||
- Best practices
|
||||
|
||||
**Key Innovation:** Always-in-session model. User chooses WHICH session, not WHETHER to session.
|
||||
|
||||
### 2. **SESSION_START_PROTOCOL.md** (Technical Spec)
|
||||
Location: `/Users/iamanmp/Projects/claude-os/docs/guides/SESSION_START_PROTOCOL.md`
|
||||
|
||||
**Contents:**
|
||||
- 7-phase automatic startup protocol
|
||||
- What to read, search, and display
|
||||
- Configuration options
|
||||
- When to skip auto-start
|
||||
- Complete implementation checklist
|
||||
|
||||
**Key Innovation:** Automatic context loading in ~50 seconds
|
||||
|
||||
### 3. **IDEAL_SESSION_WORKFLOW.md** (User Guide)
|
||||
Location: `/Users/iamanmp/Projects/claude-os/docs/guides/IDEAL_SESSION_WORKFLOW.md`
|
||||
|
||||
**Contents:**
|
||||
- "Should happen" vs "Actually happens"
|
||||
- Practical workarounds
|
||||
- Power user tips
|
||||
- Quick reference
|
||||
|
||||
**Key Innovation:** Shows the vision vs reality gap
|
||||
|
||||
### 4. **Updated CLAUDE.md Template**
|
||||
Location: `/Users/iamanmp/Projects/claude-os/templates/project-files/CLAUDE.md.template`
|
||||
|
||||
**Changes:**
|
||||
- Added mandatory session prompt
|
||||
- 3-option choice (Resume/New/Question)
|
||||
- Detailed session start protocol
|
||||
- Links to all documentation
|
||||
|
||||
**Key Innovation:** Makes mandatory sessions the DEFAULT for all new projects
|
||||
|
||||
---
|
||||
|
||||
## 🔄 The Flow
|
||||
|
||||
### Conversation Start
|
||||
|
||||
```
|
||||
User opens Claude Code in project directory
|
||||
↓
|
||||
Claude reads CLAUDE.md (automatic)
|
||||
↓
|
||||
Claude sees mandatory session protocol
|
||||
↓
|
||||
Claude prompts:
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🚀 CLAUDE OS - SESSION MANAGER
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Project: Pistn
|
||||
Last Session: Dashboard Redesign (2 days ago, 2h 15m)
|
||||
Progress: 45% complete
|
||||
|
||||
Options:
|
||||
1. Resume "Dashboard Redesign" [loads full context]
|
||||
2. Start new session [what are you working on?]
|
||||
3. Quick question [auto-session, minimal context]
|
||||
|
||||
Choice: _
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### User Chooses Option 1 (Resume)
|
||||
|
||||
```
|
||||
Claude automatically:
|
||||
✓ Reads .claude-os/config.json
|
||||
✓ Reads claude-os-state.json
|
||||
✓ Checks git status
|
||||
✓ Searches project_memories
|
||||
✓ Loads coding standards
|
||||
✓ Checks Kanban board
|
||||
✓ Presents comprehensive summary
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
✓ RESUMING: Dashboard Redesign
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
[Full context loaded]
|
||||
Progress: 45% complete (23 of 52 tasks)
|
||||
Current Task: PHASE2-TASK3 (Implement concern methods)
|
||||
|
||||
Key insights:
|
||||
• 67-page implementation plan loaded
|
||||
• Zero functionality loss requirement
|
||||
• Bootstrap 3.x only
|
||||
|
||||
Ready! Let's continue PHASE2-TASK3.
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### User Chooses Option 2 (New Session)
|
||||
|
||||
```
|
||||
Claude: "What are you working on?"
|
||||
|
||||
User: "Fix the Tekmetric API 500 errors"
|
||||
|
||||
Claude automatically:
|
||||
✓ Detects type: Bug Fix
|
||||
✓ Pauses previous session
|
||||
✓ Searches for "Tekmetric API errors"
|
||||
✓ Loads relevant memories
|
||||
✓ Starts tracking
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
✓ NEW BUG SESSION STARTED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Session: "Fix Tekmetric API 500 errors"
|
||||
Type: Bug Fix
|
||||
Priority: High
|
||||
|
||||
Found 3 similar issues from past sessions.
|
||||
Dashboard Redesign session paused.
|
||||
|
||||
Let's debug! What's the error?
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### User Chooses Option 3 (Quick Question)
|
||||
|
||||
```
|
||||
Claude: "What's your question?"
|
||||
|
||||
User: "How do I use Draper decorators?"
|
||||
|
||||
Claude:
|
||||
═══════════════════════════════════════════════════════════════
|
||||
💬 QUICK QUESTION SESSION
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
[Answers question with minimal context loading]
|
||||
|
||||
Auto-ends after 5 min inactivity.
|
||||
Ready to resume Dashboard Redesign?
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Session Types
|
||||
|
||||
### 1. Feature Implementation
|
||||
- Linked to Kanban spec
|
||||
- Tracks task completion
|
||||
- Saves patterns and architecture decisions
|
||||
|
||||
### 2. Bug Fix
|
||||
- Auto-detects priority from description
|
||||
- Searches past similar issues
|
||||
- Saves root cause and solution
|
||||
|
||||
### 3. Exploration / Learning
|
||||
- Tracks files explored
|
||||
- Saves key learnings
|
||||
- Documents architecture insights
|
||||
|
||||
### 4. Refactoring / Maintenance
|
||||
- Tracks complexity reduction
|
||||
- Saves refactoring patterns
|
||||
- Documents improvements
|
||||
|
||||
### 5. Code Review
|
||||
- Tracks issues found
|
||||
- Saves review patterns
|
||||
- Documents suggestions
|
||||
|
||||
### 6. Quick Question (Auto)
|
||||
- Auto-managed
|
||||
- Minimal tracking
|
||||
- Only saves if valuable
|
||||
|
||||
---
|
||||
|
||||
## 💾 What Gets Tracked
|
||||
|
||||
### Every Session:
|
||||
- Start/end time
|
||||
- Duration
|
||||
- Files changed
|
||||
- Git commits
|
||||
- Quick saves (during session)
|
||||
- Type (feature/bug/etc)
|
||||
- Related spec (if applicable)
|
||||
- Related tasks (if Kanban)
|
||||
|
||||
### At Session End:
|
||||
- Work summary
|
||||
- Memories saved
|
||||
- Patterns discovered
|
||||
- Blockers encountered
|
||||
- Progress made
|
||||
- Statistics updated
|
||||
|
||||
### Across Sessions:
|
||||
- Total time per project
|
||||
- Total time per task/spec
|
||||
- Average session duration
|
||||
- Most productive times
|
||||
- Velocity (tasks/hour)
|
||||
- Pattern usage frequency
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Benefits
|
||||
|
||||
### Zero Context Loss
|
||||
```
|
||||
Before: "What was I doing?"
|
||||
After: "Welcome back! You were on PHASE2-TASK3, 45% done."
|
||||
```
|
||||
|
||||
### Complete Work History
|
||||
```
|
||||
Before: Manual time tracking, scattered notes
|
||||
After: Automatic tracking, everything in one place
|
||||
```
|
||||
|
||||
### Smart Insights
|
||||
```
|
||||
Before: Guess at velocity, no patterns tracked
|
||||
After: "You average 2.4 tasks per session at 0.47h per task"
|
||||
```
|
||||
|
||||
### Automatic Learning
|
||||
```
|
||||
Before: Solve same problems repeatedly
|
||||
After: "We solved this before in session X, here's the solution"
|
||||
```
|
||||
|
||||
### Better Planning
|
||||
```
|
||||
Before: "How long will this take? No idea."
|
||||
After: "Based on past velocity, ~10h 30m remaining"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
### For New Projects:
|
||||
1. Run `/claude-os-init`
|
||||
2. CLAUDE.md automatically includes mandatory session protocol
|
||||
3. First conversation: Choose session type
|
||||
4. Start working with full context!
|
||||
|
||||
### For Existing Projects:
|
||||
1. Update CLAUDE.md with new session protocol section
|
||||
2. Copy from template: `templates/project-files/CLAUDE.md.template`
|
||||
3. Next conversation: Session prompt appears
|
||||
4. Choose option and start working!
|
||||
|
||||
### For Claude OS Development:
|
||||
1. **API Enhancement:**
|
||||
- Add `/api/sessions/` endpoints
|
||||
- Session start, end, pause, resume, switch
|
||||
- Statistics endpoints
|
||||
|
||||
2. **Database Schema:**
|
||||
- `sessions` table (id, project_id, type, task, started_at, ended_at, duration, files_changed, etc)
|
||||
- Link to specs/tasks
|
||||
- Statistics and tracking
|
||||
|
||||
3. **Frontend UI:**
|
||||
- Session dashboard
|
||||
- Live session timer
|
||||
- Session history viewer
|
||||
- Statistics visualizations
|
||||
|
||||
4. **CLI Integration:**
|
||||
- `/claude-os-session` commands
|
||||
- Auto-prompt on conversation start
|
||||
- Background session tracking
|
||||
|
||||
---
|
||||
|
||||
## 📖 Documentation Tree
|
||||
|
||||
```
|
||||
docs/guides/
|
||||
├── MANDATORY_SESSIONS.md ← Complete specification
|
||||
├── SESSION_START_PROTOCOL.md ← Technical implementation
|
||||
├── IDEAL_SESSION_WORKFLOW.md ← User guide & vision
|
||||
├── SESSION_IMPLEMENTATION_SUMMARY.md ← This file
|
||||
└── WHAT_IS_CLAUDE_OS.md ← Updated with session info
|
||||
|
||||
templates/project-files/
|
||||
└── CLAUDE.md.template ← Updated with mandatory sessions
|
||||
|
||||
templates/commands/
|
||||
└── claude-os-session.md ← Session commands reference
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 What This Enables
|
||||
|
||||
### For You (User):
|
||||
- Never lose context
|
||||
- Track ALL work automatically
|
||||
- Understand your velocity
|
||||
- Learn from history
|
||||
- Better planning
|
||||
|
||||
### For Claude (AI):
|
||||
- Always have context
|
||||
- Smart suggestions based on session type
|
||||
- Proactive memory loading
|
||||
- Better guidance
|
||||
- Continuous learning
|
||||
|
||||
### For The Project:
|
||||
- Complete work history
|
||||
- Pattern recognition
|
||||
- Knowledge preservation
|
||||
- Velocity tracking
|
||||
- Team insights
|
||||
|
||||
---
|
||||
|
||||
## 💡 The Vision Realized
|
||||
|
||||
**Remember the "amazing" Kanban board we just built?**
|
||||
|
||||
Now combine it with **mandatory sessions**:
|
||||
|
||||
```
|
||||
You open Claude Code
|
||||
↓
|
||||
Claude: "Resume Dashboard Redesign?"
|
||||
↓
|
||||
You: "Yes"
|
||||
↓
|
||||
Claude: "You're on PHASE2-TASK3 of 52 tasks (45% complete).
|
||||
I have your implementation plan, 5 memories, and
|
||||
all coding standards loaded. Ready!"
|
||||
↓
|
||||
You work for 2 hours
|
||||
↓
|
||||
Claude: "You completed 2 tasks! Progress: 45% → 53%
|
||||
Save these 2 patterns to memories? [Y/n]"
|
||||
↓
|
||||
You: "y"
|
||||
↓
|
||||
Claude: "✓ Saved! Great session. Total time on this spec: 15h.
|
||||
Estimated 9h remaining. See you tomorrow!"
|
||||
```
|
||||
|
||||
**That's the complete AI development system.** 🚀
|
||||
|
||||
---
|
||||
|
||||
## 📊 Current Status
|
||||
|
||||
### ✅ Completed:
|
||||
- [x] Conceptual design
|
||||
- [x] Complete documentation (4 guides)
|
||||
- [x] CLAUDE.md template updated
|
||||
- [x] Session command reference
|
||||
- [x] Kanban board integration documented
|
||||
|
||||
### 🔄 Next (Optional):
|
||||
- [ ] API endpoints for sessions
|
||||
- [ ] Database schema for session tracking
|
||||
- [ ] Frontend session dashboard
|
||||
- [ ] Auto-tracking implementation
|
||||
- [ ] Statistics visualization
|
||||
|
||||
### 🎯 Ready to Use:
|
||||
**You can start using mandatory sessions TODAY!**
|
||||
|
||||
Just add the session protocol to your project's CLAUDE.md, and at the start of each conversation, I'll prompt for session choice.
|
||||
|
||||
---
|
||||
|
||||
**This is revolutionary. Let's do it!** 💪
|
||||
390
docs/guides/SESSION_START_PROTOCOL.md
Normal file
390
docs/guides/SESSION_START_PROTOCOL.md
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
# Session Start Protocol
|
||||
|
||||
**What Claude Should Do AUTOMATICALLY When You Start ANY Conversation**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 The Problem
|
||||
|
||||
When you start Claude Code CLI:
|
||||
- You're in a specific project directory
|
||||
- You may have been working on something
|
||||
- There's context from previous sessions
|
||||
- You have knowledge in Claude OS
|
||||
|
||||
**But Claude starts with ZERO context.**
|
||||
|
||||
---
|
||||
|
||||
## ✅ The Solution: Automatic Session Start
|
||||
|
||||
**Every time a conversation starts, Claude should:**
|
||||
|
||||
### Phase 1: Detect Project (5 seconds)
|
||||
|
||||
```bash
|
||||
# Check for Claude OS project
|
||||
pwd
|
||||
ls -la | grep -E "(CLAUDE.md|.claude-os|agent-os)"
|
||||
```
|
||||
|
||||
**If `.claude-os/config.json` exists:**
|
||||
- ✅ This is a Claude OS project
|
||||
- → Proceed with full session start
|
||||
|
||||
**If not:**
|
||||
- ℹ️ Generic mode (no project context)
|
||||
- → Offer to initialize with `/claude-os-init`
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Read Project State (10 seconds)
|
||||
|
||||
**Files to read:**
|
||||
|
||||
1. **`CLAUDE.md`** - Project overview (always loaded first)
|
||||
2. **`.claude-os/config.json`** - Project configuration
|
||||
3. **`claude-os-state.json`** - Session state (if exists)
|
||||
4. **`agent-os/config.yml`** - Agent-OS config (if using specs)
|
||||
|
||||
**Example:**
|
||||
```
|
||||
Reading CLAUDE.md...
|
||||
Reading .claude-os/config.json...
|
||||
Reading claude-os-state.json...
|
||||
|
||||
✓ Project: Pistn
|
||||
✓ MCPs: 4 configured
|
||||
✓ Last session: Oct 29, 2025 (2 days ago)
|
||||
✓ Agent-OS: Enabled (3 active specs)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Check Git Context (5 seconds)
|
||||
|
||||
```bash
|
||||
git branch --show-current
|
||||
git status --short
|
||||
git log -3 --oneline
|
||||
git remote get-url origin
|
||||
```
|
||||
|
||||
**Extract:**
|
||||
- Current branch name
|
||||
- Uncommitted changes count
|
||||
- Recent commits
|
||||
- Whether we're ahead/behind remote
|
||||
|
||||
**Example:**
|
||||
```
|
||||
Branch: feature/appointment-redesign
|
||||
Status: 3 files modified, 1 untracked
|
||||
Recent commits:
|
||||
- abc1234 Add sidebar navigation
|
||||
- def5678 Convert panels to cards
|
||||
- ghi9012 Update appointment form layout
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Search Project Memories (15 seconds)
|
||||
|
||||
**Search query based on:**
|
||||
- Last session task (from `claude-os-state.json`)
|
||||
- Current branch name
|
||||
- Recent commit messages
|
||||
- `+recent` modifier
|
||||
|
||||
**Example query:**
|
||||
```
|
||||
"appointment dashboard redesign recent"
|
||||
```
|
||||
|
||||
**Search:**
|
||||
- `{project}-project_memories` knowledge base
|
||||
- Return top 5 most relevant memories
|
||||
- Show me titles and key insights
|
||||
|
||||
**Example:**
|
||||
```
|
||||
📚 Found 5 relevant memories:
|
||||
1. Appointment Dashboard Redesign Plan (Oct 28)
|
||||
→ 67-page implementation plan, 5 tabs documented
|
||||
|
||||
2. Current Dashboard Analysis (Oct 28)
|
||||
→ All features catalogued, zero functionality loss
|
||||
|
||||
3. Bootstrap to Modern Cards Pattern (Oct 25)
|
||||
→ Reusable conversion pattern
|
||||
|
||||
4. Sidebar Navigation Pattern (Oct 22)
|
||||
→ localStorage persistence, responsive
|
||||
|
||||
5. Tekmetric Integration Notes (Oct 20)
|
||||
→ Must preserve API sync on save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Load Coding Standards (10 seconds)
|
||||
|
||||
**Search `{project}-project_profile`:**
|
||||
- Query: "coding standards architecture conventions"
|
||||
- Load key architectural decisions
|
||||
- Load tech stack preferences
|
||||
|
||||
**Example:**
|
||||
```
|
||||
📋 Coding Standards:
|
||||
• Service objects for business logic
|
||||
• Decorator pattern with Draper
|
||||
• Fragment caching with Redis
|
||||
• Bootstrap 3.x styling (no Bootstrap 4/5)
|
||||
• RSpec for testing
|
||||
• Concerns for shared controller logic
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Check Kanban Board (5 seconds)
|
||||
|
||||
**If Agent-OS is enabled:**
|
||||
|
||||
```http
|
||||
GET /api/projects/{id}/kanban
|
||||
```
|
||||
|
||||
**Show:**
|
||||
- Total specs
|
||||
- Active specs (not completed/archived)
|
||||
- Current tasks in progress
|
||||
- Next tasks todo
|
||||
|
||||
**Example:**
|
||||
```
|
||||
📊 Kanban Board:
|
||||
• 3 specs total (52 tasks)
|
||||
• 1 spec in progress: Group Account Rendering (45% complete)
|
||||
• 2 tasks in progress:
|
||||
- PHASE2-TASK3: Implement concern methods
|
||||
- PHASE2-TASK4: Add helper methods
|
||||
• 29 tasks remaining
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: Present Session Start Summary (Display to User)
|
||||
|
||||
**Format:**
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🚀 WELCOME BACK!
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
📁 Project: Pistn (Ruby on Rails)
|
||||
🌿 Branch: feature/appointment-redesign
|
||||
📅 Last Session: 2 days ago (Oct 29, 2025)
|
||||
⏱️ Duration: 2h 15m
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
📚 CONTEXT LOADED
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
✓ 5 relevant memories found
|
||||
✓ Coding standards loaded
|
||||
✓ 3 architectural patterns available
|
||||
✓ Kanban board synced (45% complete)
|
||||
|
||||
🎯 LAST TASK: Redesign Appointment Dashboard
|
||||
|
||||
We completed:
|
||||
✓ Sidebar navigation component
|
||||
✓ Bootstrap panels → modern cards
|
||||
✓ iOS-style toggle switches
|
||||
|
||||
Still working on:
|
||||
⏳ Phase 2: Implement concern methods
|
||||
⏳ Phase 2: Add helper methods
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
💡 KEY INSIGHTS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
• 67-page implementation plan available
|
||||
• Zero functionality loss requirement
|
||||
• Tekmetric integration must be preserved
|
||||
• Bootstrap 3.x only (no v4/v5)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🔄 AVAILABLE PATTERNS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
• Sidebar navigation (from user-auth work)
|
||||
• Card-based layouts (from reports redesign)
|
||||
• Modern toggle switches (already in app)
|
||||
• Service object pattern (business logic)
|
||||
• Draper decorators (view logic)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
⚠️ STATUS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Git Status:
|
||||
• 3 files modified
|
||||
• 1 file untracked
|
||||
• ⚠️ Uncommitted changes (from last session)
|
||||
|
||||
Blockers:
|
||||
• None! ✓
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🎯 SUGGESTED NEXT STEPS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Option 1: Continue Phase 2 Implementation
|
||||
→ Implement concern methods for PHASE2-TASK3
|
||||
→ Should take ~30 minutes based on estimate
|
||||
|
||||
Option 2: Commit Previous Work
|
||||
→ 3 files modified need to be committed
|
||||
→ Clean git state before starting new work
|
||||
|
||||
Option 3: Start Something New
|
||||
→ Tell me what you'd like to work on
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎛️ Configuration
|
||||
|
||||
**Auto-start can be configured in `.claude-os/config.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"session_management": {
|
||||
"auto_start": true,
|
||||
"auto_search_memories": true,
|
||||
"max_memories_to_load": 5,
|
||||
"search_days_back": 14,
|
||||
"show_git_status": true,
|
||||
"show_kanban_status": true,
|
||||
"proactive_suggestions": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚫 When NOT to Auto-Start
|
||||
|
||||
**Skip session start if:**
|
||||
- User message starts with a direct question (e.g., "What is...")
|
||||
- User is clearly asking for help/docs (e.g., "How do I...")
|
||||
- User explicitly says "ignore context" or "fresh start"
|
||||
- No `.claude-os/` directory exists
|
||||
|
||||
**In these cases:**
|
||||
- Answer the question directly
|
||||
- Offer to initialize Claude OS after answering
|
||||
|
||||
---
|
||||
|
||||
## 📊 Session Tracking During Conversation
|
||||
|
||||
**Throughout the session, Claude should:**
|
||||
|
||||
### Track Key Events
|
||||
- When user starts implementing something
|
||||
- When they hit a blocker
|
||||
- When they discover a pattern
|
||||
- When they make an architectural decision
|
||||
- When they solve a complex problem
|
||||
|
||||
### Proactive Memory References
|
||||
```
|
||||
You: "I need to add caching to this endpoint"
|
||||
Claude: "FYI, we have a memory about fragment caching with Redis
|
||||
from the reports redesign. Want me to use that pattern?"
|
||||
```
|
||||
|
||||
### Auto-Save Suggestions
|
||||
```
|
||||
You: "Finally got the Tekmetric sync working with the new structure!"
|
||||
Claude: "That sounds like a high-value solution. Would you like me
|
||||
to save this to project memories for future reference?"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 Session End Protocol
|
||||
|
||||
**When conversation ends (or user says "done"):**
|
||||
|
||||
### Option 1: User Runs `/claude-os-session end`
|
||||
- Follow the detailed end protocol from claude-os-session.md
|
||||
- Analyze work, suggest saves, update statistics
|
||||
|
||||
### Option 2: Conversation Ends Naturally
|
||||
- Claude should still provide a brief summary:
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
📊 SESSION COMPLETE
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Duration: ~45 minutes
|
||||
Work: Implemented concern methods for PHASE2-TASK3
|
||||
|
||||
Quick saves available:
|
||||
• Concern method pattern for group accounts
|
||||
• Service object integration approach
|
||||
|
||||
Run /claude-os-session end for full summary and saves
|
||||
Or just continue working next time - I'll remember!
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 The Goal
|
||||
|
||||
**Every session should feel like:**
|
||||
- Claude already knows everything
|
||||
- Claude remembers what we were doing
|
||||
- Claude proactively brings up relevant context
|
||||
- Claude suggests next steps based on history
|
||||
- Zero context loss between sessions
|
||||
|
||||
**NOT like:**
|
||||
- Starting from scratch
|
||||
- Repeating ourselves
|
||||
- Explaining the same architecture again
|
||||
- Searching for what we did before
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Implementation Checklist
|
||||
|
||||
For Claude to do this automatically:
|
||||
|
||||
- [ ] Check for `.claude-os/` directory at conversation start
|
||||
- [ ] Read `CLAUDE.md` if present
|
||||
- [ ] Read `.claude-os/config.json` if present
|
||||
- [ ] Read `claude-os-state.json` if present
|
||||
- [ ] Run git status and git log
|
||||
- [ ] Search project_memories with intelligent query
|
||||
- [ ] Load coding standards from project_profile
|
||||
- [ ] Check Kanban board status (if Agent-OS enabled)
|
||||
- [ ] Present comprehensive session start summary
|
||||
- [ ] Track key events during session
|
||||
- [ ] Provide session end summary
|
||||
|
||||
---
|
||||
|
||||
**This protocol ensures you NEVER start a conversation from zero context again!** 🚀
|
||||
315
docs/guides/SHARING_GUIDE.md
Normal file
315
docs/guides/SHARING_GUIDE.md
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
# Claude OS Sharing Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This guide explains how to share Claude OS with coworkers and set up new projects.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
claude-os/
|
||||
├── templates/ # All reusable templates
|
||||
│ ├── commands/ # Slash commands (symlinked to ~/.claude/)
|
||||
│ ├── skills/ # Skills (symlinked to ~/.claude/)
|
||||
│ ├── agents/ # Agent-OS agents (8 specialized agents)
|
||||
│ └── project-files/ # Files created during project init
|
||||
│ ├── CLAUDE.md.template
|
||||
│ ├── agent-os/ # Agent-OS project templates
|
||||
│ └── .claude-os/ # Config templates
|
||||
├── cli/ # CLI tools
|
||||
│ └── claude-os-consolidate.sh
|
||||
├── install.sh # One-command install for coworkers
|
||||
└── app/ # Flask API server
|
||||
```
|
||||
|
||||
## For You (Initial Setup)
|
||||
|
||||
### Step 1: Consolidate Your Files
|
||||
|
||||
Move all scattered commands and skills into templates/:
|
||||
|
||||
```bash
|
||||
cd /Users/iamanmp/Projects/claude-os
|
||||
./cli/claude-os-consolidate.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Move commands from `~/.claude/commands/` → `templates/commands/`
|
||||
- Move skills from `~/.claude/skills/` → `templates/skills/`
|
||||
- Create symlinks so everything still works for you
|
||||
- Update any hardcoded paths
|
||||
|
||||
### Step 2: Commit to Git
|
||||
|
||||
```bash
|
||||
cd /Users/iamanmp/Projects/claude-os
|
||||
git add templates/ cli/ install.sh SHARING_GUIDE.md
|
||||
git commit -m "Add templates and installation system for sharing"
|
||||
git push
|
||||
```
|
||||
|
||||
### Step 3: Test Installation (Optional)
|
||||
|
||||
Test on a different machine or user account:
|
||||
|
||||
```bash
|
||||
git clone your-repo/claude-os.git
|
||||
cd claude-os
|
||||
./install.sh
|
||||
```
|
||||
|
||||
## For Coworkers (First Time Setup)
|
||||
|
||||
### Step 1: Clone Claude OS
|
||||
|
||||
```bash
|
||||
git clone your-repo/claude-os.git
|
||||
cd claude-os
|
||||
```
|
||||
|
||||
### Step 2: Run Install Script
|
||||
|
||||
```bash
|
||||
./install.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- ✅ Create `~/.claude/` directories
|
||||
- ✅ Symlink all commands and skills
|
||||
- ✅ Optional: Install Agent-OS (8 agents by Builder Methods)
|
||||
- ✅ Ask about AI provider (Ollama or OpenAI)
|
||||
- ✅ Set up Python virtual environment
|
||||
- ✅ Install dependencies
|
||||
- ✅ Configure MCP server
|
||||
- ✅ Create start script
|
||||
|
||||
**Note**: Agent-OS is an optional integration created by Builder Methods (CasJam Media LLC) and MIT licensed. The installer will ask if you want to install it.
|
||||
|
||||
### Step 3: Start Claude OS
|
||||
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
This starts the MCP server at `http://localhost:8051`
|
||||
|
||||
### Step 4: Initialize Their First Project
|
||||
|
||||
```bash
|
||||
cd /path/to/their/project
|
||||
```
|
||||
|
||||
In Claude Code, run:
|
||||
```
|
||||
/claude-os-init
|
||||
```
|
||||
|
||||
Follow the prompts to:
|
||||
- Name the project
|
||||
- Specify tech stack
|
||||
- Ingest documentation
|
||||
- Analyze codebase
|
||||
|
||||
Done! Their project is now connected to Claude OS.
|
||||
|
||||
## For New Projects (After Setup)
|
||||
|
||||
Once Claude OS is installed, initializing new projects is simple:
|
||||
|
||||
```bash
|
||||
cd /path/to/new/project
|
||||
```
|
||||
|
||||
In Claude Code:
|
||||
```
|
||||
/claude-os-init
|
||||
```
|
||||
|
||||
Answer the questions and you're done!
|
||||
|
||||
## What Gets Created During Project Init
|
||||
|
||||
```
|
||||
your-project/
|
||||
├── CLAUDE.md # Project context (auto-loaded every session)
|
||||
├── .claude/ # Project-specific Claude Code files
|
||||
│ ├── commands/ # (symlinks to Claude OS templates)
|
||||
│ ├── skills/ # (symlinks to Claude OS templates)
|
||||
│ ├── agents/ # Project-specific agents
|
||||
│ ├── ARCHITECTURE.md # Generated by initialize-project
|
||||
│ ├── CODING_STANDARDS.md
|
||||
│ └── DEVELOPMENT_PRACTICES.md
|
||||
└── .claude-os/ # Claude OS state/config
|
||||
├── config.json # Project configuration
|
||||
├── hooks.json # Project hooks
|
||||
└── .gitignore # State files ignored
|
||||
```
|
||||
|
||||
## Knowledge Bases Created
|
||||
|
||||
Each project gets 4 knowledge bases:
|
||||
|
||||
1. **`{project}-project_memories`** - Claude's memory for decisions, patterns, solutions
|
||||
2. **`{project}-project_profile`** - Architecture, standards, practices
|
||||
3. **`{project}-project_index`** - Automated codebase index
|
||||
4. **`{project}-knowledge_docs`** - Your documentation (auto-ingested)
|
||||
|
||||
## Commands Available After Setup
|
||||
|
||||
All these work in any initialized project:
|
||||
|
||||
- `/claude-os-init` - Initialize new project
|
||||
- `/claude-os-search` - Search project memories
|
||||
- `/claude-os-save` - Save insights
|
||||
- `/claude-os-remember` - Quick save to memories
|
||||
- `/claude-os-list` - List knowledge bases
|
||||
- `/claude-os-session` - Manage dev sessions
|
||||
- `/claude-os-triggers` - Manage trigger phrases
|
||||
|
||||
## Skills Available
|
||||
|
||||
- `initialize-project` - Analyze codebase and generate standards
|
||||
- `remember-this` - Auto-save when you say "remember this:"
|
||||
- `memory` - Simple memory management
|
||||
|
||||
## Agent-OS Commands (If Installed)
|
||||
|
||||
If you chose to install Agent-OS during setup, you also get these commands:
|
||||
|
||||
- `/new-spec` - Initialize a new feature specification
|
||||
- `/create-spec` - Full specification workflow
|
||||
- `/plan-product` - Create product documentation
|
||||
- `/implement-spec` - Implement a specification
|
||||
|
||||
**Agent-OS** is created by Builder Methods (CasJam Media LLC) and provides 8 specialized agents for spec-driven development. Learn more at https://github.com/builder-methods/agent-os
|
||||
|
||||
## Benefits for Your Team
|
||||
|
||||
### One-Time Setup
|
||||
- Clone repo → run install → done
|
||||
- No configuration needed
|
||||
- Works on Mac/Linux (Windows with WSL)
|
||||
|
||||
### Project Isolation
|
||||
- Each project has its own knowledge bases
|
||||
- No cross-contamination
|
||||
- Team members can work on multiple projects
|
||||
|
||||
### AI Memory Across Sessions
|
||||
- Claude remembers decisions across conversations
|
||||
- Searchable history of all work
|
||||
- Pattern recognition improves over time
|
||||
|
||||
### Documentation Integration
|
||||
- Automatically indexes your docs
|
||||
- Searchable alongside code context
|
||||
- Always up to date
|
||||
|
||||
## Updating Claude OS
|
||||
|
||||
When you add new commands/skills to templates:
|
||||
|
||||
```bash
|
||||
# On your machine:
|
||||
cd /Users/iamanmp/Projects/claude-os
|
||||
git pull origin main
|
||||
|
||||
# Commands/skills auto-update via symlinks!
|
||||
```
|
||||
|
||||
Coworkers do the same:
|
||||
```bash
|
||||
cd ~/Projects/claude-os
|
||||
git pull origin main
|
||||
|
||||
# Their symlinks automatically point to updated templates
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Command not found: /claude-os-init"
|
||||
|
||||
Symlinks weren't created. Re-run:
|
||||
```bash
|
||||
cd /path/to/claude-os
|
||||
./install.sh
|
||||
```
|
||||
|
||||
### "Connection refused to localhost:8051"
|
||||
|
||||
Claude OS server isn't running:
|
||||
```bash
|
||||
cd /path/to/claude-os
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### "Project already exists"
|
||||
|
||||
Project name is taken. Choose a different name or delete the old project via the UI at `http://localhost:8051`
|
||||
|
||||
### Commands work but changes don't persist
|
||||
|
||||
Make sure you're committing changes to the Claude OS repo, not your project repo:
|
||||
```bash
|
||||
cd /path/to/claude-os # NOT your project dir
|
||||
git status
|
||||
git add templates/
|
||||
git commit -m "Updated templates"
|
||||
git push
|
||||
```
|
||||
|
||||
## Adding Custom Commands/Skills
|
||||
|
||||
### Add a New Command
|
||||
|
||||
1. Create file in `templates/commands/my-command.md`
|
||||
2. Write command logic (see existing commands for examples)
|
||||
3. Commit to git
|
||||
4. Coworkers pull and get it automatically via symlinks
|
||||
|
||||
### Add a New Skill
|
||||
|
||||
1. Create directory in `templates/skills/my-skill/`
|
||||
2. Add `SKILL.md` and any scripts
|
||||
3. Commit to git
|
||||
4. Coworkers pull and get it automatically
|
||||
|
||||
## Security Notes
|
||||
|
||||
- `.claude-os/` state files are git-ignored by default
|
||||
- Never commit API keys or secrets to CLAUDE.md
|
||||
- Each team member has their own Claude OS database
|
||||
- Knowledge bases are local, not shared (unless you want to sync them)
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Do I need to reinstall for each project?**
|
||||
A: No! Install Claude OS once, then just run `/claude-os-init` in each project.
|
||||
|
||||
**Q: Can multiple people work on the same project?**
|
||||
A: Yes! Each person has their own Claude OS database. Shared project knowledge goes in CLAUDE.md (git-tracked).
|
||||
|
||||
**Q: What if someone doesn't have Claude Code?**
|
||||
A: They need Claude Code (the CLI tool) to use this. Free at https://claude.com/claude-code
|
||||
|
||||
**Q: How much disk space does it use?**
|
||||
A: ~500MB for Claude OS + dependencies. Each project's knowledge base is ~10-100MB depending on docs.
|
||||
|
||||
**Q: Does this work on Windows?**
|
||||
A: Yes, with WSL (Windows Subsystem for Linux). Native Windows support coming soon.
|
||||
|
||||
## Support
|
||||
|
||||
- 📚 Documentation: See README.md in this repo
|
||||
- 🐛 Issues: Open an issue on GitHub
|
||||
- 💬 Questions: Ask in team chat or @ the maintainer
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **For you**: Run `./cli/claude-os-consolidate.sh` to organize your files
|
||||
2. **For coworkers**: Share the repo URL and have them run `./install.sh`
|
||||
3. **For new projects**: Just run `/claude-os-init` and answer questions
|
||||
|
||||
---
|
||||
|
||||
**You built this! Now share it and make your whole team invincible! 🚀**
|
||||
342
docs/guides/SKILLS_GUIDE.md
Normal file
342
docs/guides/SKILLS_GUIDE.md
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
# Skills Library Guide
|
||||
|
||||
**Browse, install, and manage Claude Code skills with Claude OS.**
|
||||
|
||||
---
|
||||
|
||||
## What Are Skills?
|
||||
|
||||
Skills are reusable instruction sets that teach Claude specific capabilities. They're stored as markdown files in `.claude/skills/` directories and are automatically loaded by Claude Code when relevant.
|
||||
|
||||
Skills can include:
|
||||
- Coding patterns and best practices
|
||||
- Tool usage workflows (e.g., PDF manipulation, spreadsheet editing)
|
||||
- Domain-specific knowledge (e.g., Rails patterns, React hooks)
|
||||
- Development methodologies (e.g., TDD, debugging frameworks)
|
||||
|
||||
---
|
||||
|
||||
## Skill Types
|
||||
|
||||
### Global Skills (`~/.claude/skills/`)
|
||||
|
||||
Available in ALL projects. These are core skills that come with Claude OS:
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| `memory` | Save and recall information across sessions |
|
||||
| `remember-this` | Auto-save on trigger phrases like "remember this:" |
|
||||
| `initialize-project` | Analyze codebase and generate documentation |
|
||||
|
||||
### Project Skills (`{project}/.claude/skills/`)
|
||||
|
||||
Available only in the specific project. Install from:
|
||||
- **Local Templates** - Pre-built skills bundled with Claude OS
|
||||
- **Community Skills** - Skills from GitHub repositories
|
||||
- **Custom** - Skills you create yourself
|
||||
|
||||
---
|
||||
|
||||
## Using the Skills Command
|
||||
|
||||
The `/claude-os-skills` command provides full skills management:
|
||||
|
||||
```bash
|
||||
# List all installed skills (global + project)
|
||||
/claude-os-skills
|
||||
|
||||
# Browse available local templates
|
||||
/claude-os-skills templates
|
||||
|
||||
# Install a template to your project
|
||||
/claude-os-skills install <name>
|
||||
|
||||
# Create a custom skill interactively
|
||||
/claude-os-skills create
|
||||
|
||||
# View skill details and content
|
||||
/claude-os-skills view <name>
|
||||
|
||||
# Delete a project skill
|
||||
/claude-os-skills delete <name>
|
||||
```
|
||||
|
||||
### Example Output
|
||||
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
📚 CLAUDE CODE SKILLS
|
||||
═══════════════════════════════════════
|
||||
|
||||
🌐 GLOBAL SKILLS (always available)
|
||||
────────────────────────────────────
|
||||
✓ memory - Save and recall information
|
||||
✓ remember-this - Auto-save on trigger phrases
|
||||
✓ initialize-project - Analyze codebase
|
||||
|
||||
📁 PROJECT SKILLS (/path/to/project)
|
||||
────────────────────────────────────
|
||||
✓ rails-backend - Rails patterns and service objects
|
||||
✓ rspec - RSpec testing patterns
|
||||
|
||||
💡 TIP: Run '/claude-os-skills templates' to see available templates
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Local Skill Templates
|
||||
|
||||
Claude OS includes a library of skill templates organized by category:
|
||||
|
||||
### Categories
|
||||
|
||||
| Category | Skills |
|
||||
|----------|--------|
|
||||
| **general** | `analyze-project`, `code-review` |
|
||||
| **rails** | `rails-backend`, `rails-api`, `active-record` |
|
||||
| **react** | `react-patterns`, `typescript-react`, `hooks` |
|
||||
| **testing** | `rspec`, `jest`, `pytest`, `tdd` |
|
||||
|
||||
### Installing a Template
|
||||
|
||||
```bash
|
||||
/claude-os-skills install rails-backend
|
||||
```
|
||||
|
||||
This copies the template to your project's `.claude/skills/` directory.
|
||||
|
||||
---
|
||||
|
||||
## Community Skills
|
||||
|
||||
Browse and install skills from trusted GitHub repositories!
|
||||
|
||||
### Available Sources
|
||||
|
||||
| Source | Repository | Skills | Description |
|
||||
|--------|------------|--------|-------------|
|
||||
| **Anthropic Official** | `anthropics/skills` | 16 | Official skills from Anthropic |
|
||||
| **Superpowers** | `obra/superpowers` | 20 | Battle-tested TDD, debugging, collaboration |
|
||||
|
||||
### Featured Community Skills
|
||||
|
||||
**From Anthropic Official:**
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| `pdf` | Create, edit, analyze PDF documents |
|
||||
| `xlsx` | Spreadsheet manipulation with formulas |
|
||||
| `docx` | Word document creation and editing |
|
||||
| `pptx` | Presentation creation and editing |
|
||||
| `frontend-design` | Production-grade UI components |
|
||||
| `mcp-builder` | Create MCP servers |
|
||||
| `doc-coauthoring` | Collaborative documentation workflow |
|
||||
| `canvas-design` | Visual art and poster creation |
|
||||
| `webapp-testing` | Playwright-based web testing |
|
||||
|
||||
**From Superpowers:**
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| `test-driven-development` | TDD workflow: red-green-refactor |
|
||||
| `systematic-debugging` | Four-phase debugging framework |
|
||||
| `root-cause-tracing` | Trace bugs back through call stack |
|
||||
| `receiving-code-review` | Handle code review feedback |
|
||||
| `requesting-code-review` | Dispatch code review subagent |
|
||||
| `brainstorming` | Structured ideation process |
|
||||
| `using-git-worktrees` | Isolated development branches |
|
||||
| `verification-before-completion` | Evidence-based completion claims |
|
||||
| `defense-in-depth` | Multi-layer validation |
|
||||
|
||||
### Installing via Web UI
|
||||
|
||||
1. Open http://localhost:5173
|
||||
2. Select your project
|
||||
3. Click the **Skills** tab
|
||||
4. Click **Install Template** button
|
||||
5. Switch to **Community Skills** tab
|
||||
6. Browse skills from Anthropic Official and Superpowers
|
||||
7. Click **Install** on any skill
|
||||
|
||||
### Installing via API
|
||||
|
||||
```bash
|
||||
# List community sources
|
||||
curl http://localhost:8051/api/skills/community/sources
|
||||
|
||||
# List all community skills
|
||||
curl http://localhost:8051/api/skills/community
|
||||
|
||||
# Filter by source
|
||||
curl "http://localhost:8051/api/skills/community?source=anthropic"
|
||||
|
||||
# Install a community skill
|
||||
curl -X POST "http://localhost:8051/api/skills/community/install?project_path=/path/to/project" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "pdf",
|
||||
"source": "anthropic",
|
||||
"repo": "anthropics/skills",
|
||||
"path": "skills/pdf"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating Custom Skills
|
||||
|
||||
### Via Command
|
||||
|
||||
```bash
|
||||
/claude-os-skills create
|
||||
```
|
||||
|
||||
Claude will interactively ask for:
|
||||
1. Skill name (e.g., "deployment")
|
||||
2. Description (one sentence)
|
||||
3. Content (the skill instructions)
|
||||
|
||||
### Via API
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8051/api/skills?project_path=/path/to/project" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "my-deployment",
|
||||
"description": "Deployment workflow for my project",
|
||||
"content": "# Deployment Skill\n\n## Steps\n1. Run tests\n2. Build\n3. Deploy",
|
||||
"category": "devops",
|
||||
"tags": ["deployment", "ci-cd"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Skill File Structure
|
||||
|
||||
A skill is a directory containing:
|
||||
|
||||
```
|
||||
my-skill/
|
||||
├── skill.md # Main skill instructions (required)
|
||||
├── metadata.json # Skill metadata (optional)
|
||||
└── examples/ # Example files (optional)
|
||||
├── example1.md
|
||||
└── example2.py
|
||||
```
|
||||
|
||||
### Writing Good Skills
|
||||
|
||||
1. **Be Specific** - Focus on one capability
|
||||
2. **Include Examples** - Show how to use the skill
|
||||
3. **Document Triggers** - When should Claude use this skill?
|
||||
4. **Test It** - Verify Claude follows the instructions
|
||||
|
||||
Example skill content:
|
||||
|
||||
```markdown
|
||||
# Rails Service Objects
|
||||
|
||||
Use this skill when implementing business logic in Rails applications.
|
||||
|
||||
## When to Use
|
||||
- Complex operations involving multiple models
|
||||
- Operations that need to be tested in isolation
|
||||
- Reusable business logic
|
||||
|
||||
## Pattern
|
||||
|
||||
```ruby
|
||||
class CreateUser
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
user = User.new(@params)
|
||||
if user.save
|
||||
send_welcome_email(user)
|
||||
Result.success(user)
|
||||
else
|
||||
Result.failure(user.errors)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_welcome_email(user)
|
||||
UserMailer.welcome(user).deliver_later
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ruby
|
||||
result = CreateUser.new(user_params).call
|
||||
if result.success?
|
||||
redirect_to result.value
|
||||
else
|
||||
render :new, errors: result.errors
|
||||
end
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill Locations
|
||||
|
||||
| Type | Location | Scope |
|
||||
|------|----------|-------|
|
||||
| Global Skills | `~/.claude/skills/` | All projects |
|
||||
| Project Skills | `{project}/.claude/skills/` | Single project |
|
||||
| Templates | `claude-os/templates/skill-library/` | Install to project |
|
||||
| Community | GitHub repositories | Install to project |
|
||||
|
||||
---
|
||||
|
||||
## MCP Tools
|
||||
|
||||
Skills management is also available via MCP tools:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `mcp__code-forge__list_skills` | List all skills |
|
||||
| `mcp__code-forge__list_skill_templates` | List available templates |
|
||||
| `mcp__code-forge__install_skill_template` | Install a template |
|
||||
| `mcp__code-forge__create_skill` | Create a custom skill |
|
||||
| `mcp__code-forge__get_skill` | Get skill details |
|
||||
| `mcp__code-forge__delete_skill` | Delete a project skill |
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Start with templates** - Install what you need per-project
|
||||
2. **Keep global minimal** - Only core skills should be global
|
||||
3. **Customize after install** - Edit installed skills for your needs
|
||||
4. **Create custom skills** - Document project-specific patterns
|
||||
5. **Share with team** - Check `.claude/skills/` into git
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Skill not found"
|
||||
- Check if the skill is installed: `/claude-os-skills`
|
||||
- Verify the skill directory exists in `.claude/skills/`
|
||||
|
||||
### "Community skills not loading"
|
||||
- Check internet connection
|
||||
- GitHub API rate limits may apply (1 hour cache)
|
||||
- Try refreshing: click the refresh button in UI
|
||||
|
||||
### "Skill not being used by Claude"
|
||||
- Verify `skill.md` exists in the skill directory
|
||||
- Check that the skill description matches your use case
|
||||
- Explicitly invoke with: `skill: <name>`
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [API Reference](../API_REFERENCE.md) - Skills API endpoints
|
||||
- [README](../../README.md) - Full Claude OS documentation
|
||||
295
docs/guides/START_SCRIPTS_GUIDE.md
Normal file
295
docs/guides/START_SCRIPTS_GUIDE.md
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
# Start Scripts Guide
|
||||
|
||||
Claude OS has **two different start scripts** for different use cases:
|
||||
|
||||
---
|
||||
|
||||
## 1. `./start.sh` - MCP Server Only
|
||||
|
||||
**What it starts:**
|
||||
- ✅ MCP Server (port 8051)
|
||||
|
||||
**What it does NOT start:**
|
||||
- ❌ Frontend Web UI
|
||||
- ❌ Redis
|
||||
- ❌ RQ Workers
|
||||
- ❌ Ollama
|
||||
|
||||
**Use when:**
|
||||
- You only need Claude Code integration
|
||||
- You don't need the visual web interface
|
||||
- You want minimal services running
|
||||
- Quick start for development
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
✅ Claude OS MCP Server is running!
|
||||
|
||||
📡 MCP Server: http://localhost:8051
|
||||
(For Claude Code integration - do NOT open in browser)
|
||||
|
||||
💡 Want the full experience?
|
||||
To start ALL services: ./start_all_services.sh
|
||||
```
|
||||
|
||||
**What you can do:**
|
||||
- ✅ Use Claude Code with `/claude-os-init`
|
||||
- ✅ Use Claude Code commands (`/claude-os-search`, etc.)
|
||||
- ❌ Can't access web UI (no frontend running)
|
||||
- ❌ No real-time learning (no workers running)
|
||||
|
||||
---
|
||||
|
||||
## 2. `./start_all_services.sh` - Everything
|
||||
|
||||
**What it starts:**
|
||||
- ✅ Ollama (port 11434) - Local AI models
|
||||
- ✅ Redis (port 6379) - Cache & job queue
|
||||
- ✅ RQ Workers - Background job processing
|
||||
- ✅ MCP Server (port 8051) - API for Claude Code
|
||||
- ✅ React Frontend (port 5173) - Web UI
|
||||
|
||||
**Use when:**
|
||||
- You want the complete experience
|
||||
- You need the web interface
|
||||
- You want real-time learning features
|
||||
- Production-like environment
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
./start_all_services.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
==================================================
|
||||
✅ All Services Started Successfully!
|
||||
==================================================
|
||||
|
||||
📡 Service URLs:
|
||||
🎨 Frontend: http://localhost:5173
|
||||
🔌 API Server: http://localhost:8051
|
||||
📚 API Docs: http://localhost:8051/docs
|
||||
|
||||
🔧 Ollama:
|
||||
Host: http://localhost:11434
|
||||
Models: llama3.1:latest, nomic-embed-text:latest
|
||||
|
||||
💾 Databases:
|
||||
SQLite: data/claude-os.db
|
||||
Redis: localhost:6379
|
||||
|
||||
🤖 Real-Time Learning System:
|
||||
RQ Workers: listening on 3 queues
|
||||
```
|
||||
|
||||
**What you can do:**
|
||||
- ✅ Use Claude Code integration
|
||||
- ✅ Open web UI at http://localhost:5173
|
||||
- ✅ Browse knowledge bases visually
|
||||
- ✅ Upload documents via UI
|
||||
- ✅ Real-time learning system active
|
||||
- ✅ Full feature set
|
||||
|
||||
---
|
||||
|
||||
## Quick Comparison
|
||||
|
||||
| Feature | `start.sh` | `start_all_services.sh` |
|
||||
|---------|------------|-------------------------|
|
||||
| **MCP Server** | ✅ Port 8051 | ✅ Port 8051 |
|
||||
| **Web UI** | ❌ | ✅ Port 5173 |
|
||||
| **Ollama** | ❌ (must start manually) | ✅ Auto-started |
|
||||
| **Redis** | ❌ | ✅ Auto-started |
|
||||
| **RQ Workers** | ❌ | ✅ Background jobs |
|
||||
| **Real-time Learning** | ❌ | ✅ Active |
|
||||
| **Startup Time** | ~1 second | ~30 seconds |
|
||||
| **Resource Usage** | Minimal | Moderate |
|
||||
|
||||
---
|
||||
|
||||
## Which Should You Use?
|
||||
|
||||
### Use `./start.sh` if:
|
||||
- Just testing Claude Code integration
|
||||
- Don't need web interface
|
||||
- Want faster startup
|
||||
- Minimal resource usage
|
||||
- Development/debugging
|
||||
|
||||
### Use `./start_all_services.sh` if:
|
||||
- Want full functionality
|
||||
- Need web interface
|
||||
- Want to browse/manage KBs visually
|
||||
- Production or demo environment
|
||||
- Want real-time learning features
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### ❌ Mistake 1: Opening MCP server in browser
|
||||
|
||||
**Problem:**
|
||||
```bash
|
||||
./start.sh
|
||||
# Then opening http://localhost:8051 in browser
|
||||
# → "Method Not Allowed" error
|
||||
```
|
||||
|
||||
**Why:** MCP server (port 8051) is for Claude Code API calls, not browsers
|
||||
|
||||
**Solution:**
|
||||
- Don't open 8051 in browser
|
||||
- Use `./start_all_services.sh` and open http://localhost:5173 instead
|
||||
|
||||
### ❌ Mistake 2: Expecting Web UI with start.sh
|
||||
|
||||
**Problem:**
|
||||
```bash
|
||||
./start.sh
|
||||
# Then trying to access http://localhost:5173
|
||||
# → Connection refused
|
||||
```
|
||||
|
||||
**Why:** `start.sh` doesn't start the frontend
|
||||
|
||||
**Solution:**
|
||||
- Use `./start_all_services.sh` instead
|
||||
|
||||
### ❌ Mistake 3: Wrong port numbers
|
||||
|
||||
**Problem:** Confusing which service runs on which port
|
||||
|
||||
**Remember:**
|
||||
- **Port 8051:** MCP Server (for Claude Code) - Don't open in browser!
|
||||
- **Port 5173:** Web UI (for humans) - Open this in browser!
|
||||
|
||||
---
|
||||
|
||||
## Stopping Services
|
||||
|
||||
### Stop MCP Server Only (`start.sh`)
|
||||
|
||||
**Option 1:** Press `Ctrl+C` in the terminal
|
||||
|
||||
**Option 2:** Kill by PID
|
||||
```bash
|
||||
# PID shown when you start it
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### Stop All Services (`start_all_services.sh`)
|
||||
|
||||
**Use the stop script:**
|
||||
```bash
|
||||
./stop_all_services.sh
|
||||
```
|
||||
|
||||
This will stop:
|
||||
- MCP Server
|
||||
- Frontend
|
||||
- RQ Workers
|
||||
- Redis (optional)
|
||||
- Ollama (optional)
|
||||
|
||||
---
|
||||
|
||||
## Restarting Services
|
||||
|
||||
### Restart MCP Only
|
||||
|
||||
```bash
|
||||
# Stop (Ctrl+C or kill PID)
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### Restart Everything
|
||||
|
||||
```bash
|
||||
./restart_services.sh
|
||||
```
|
||||
|
||||
Or manually:
|
||||
```bash
|
||||
./stop_all_services.sh
|
||||
./start_all_services.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logs
|
||||
|
||||
### MCP Server Logs
|
||||
|
||||
**If started with `start.sh`:**
|
||||
- Output shows in terminal
|
||||
|
||||
**If started with `start_all_services.sh`:**
|
||||
```bash
|
||||
tail -f logs/mcp_server.log
|
||||
```
|
||||
|
||||
### Frontend Logs
|
||||
|
||||
```bash
|
||||
tail -f logs/frontend.log
|
||||
```
|
||||
|
||||
### RQ Workers Logs
|
||||
|
||||
```bash
|
||||
tail -f logs/rq_workers.log
|
||||
```
|
||||
|
||||
### All Logs
|
||||
|
||||
```bash
|
||||
tail -f logs/*.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Installation Recommendation
|
||||
|
||||
After running `./install.sh`, the README says:
|
||||
|
||||
```
|
||||
1️⃣ Start Claude OS:
|
||||
./start.sh
|
||||
```
|
||||
|
||||
**But for the full experience, you should use:**
|
||||
```
|
||||
1️⃣ Start Claude OS (all services):
|
||||
./start_all_services.sh
|
||||
```
|
||||
|
||||
Then you can:
|
||||
- Use Claude Code at port 8051
|
||||
- Open web UI at http://localhost:5173
|
||||
- Have full functionality
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Remember:**
|
||||
- `./start.sh` = MCP server only (minimal, fast)
|
||||
- `./start_all_services.sh` = Everything (full features)
|
||||
|
||||
**For most users:**
|
||||
```bash
|
||||
./start_all_services.sh # ← Recommended!
|
||||
```
|
||||
|
||||
**Then access:**
|
||||
- Web UI: http://localhost:5173 ✅ Open in browser
|
||||
- MCP Server: http://localhost:8051 ❌ Don't open in browser (for Claude Code only)
|
||||
|
||||
**Problem solved!** 🎉
|
||||
896
docs/guides/WHAT_IS_CLAUDE_OS.md
Normal file
896
docs/guides/WHAT_IS_CLAUDE_OS.md
Normal file
|
|
@ -0,0 +1,896 @@
|
|||
# What is Claude OS? 🚀
|
||||
|
||||
# **The AI Operating System That Turns Claude Into Your Most Knowledgeable Team Member**
|
||||
|
||||
---
|
||||
|
||||
## The Problem You're Facing Right Now 😤
|
||||
|
||||
Let's be honest. You've experienced this a thousand times:
|
||||
|
||||
```
|
||||
You: "Claude, add a new feature to our auth system"
|
||||
Claude: "Based on common best practices, here's a generic auth implementation..."
|
||||
You: "No no no—we have a custom JWT handler, a special 2FA module for admins,
|
||||
and timezone handling in the token service. This is all wrong."
|
||||
Claude: "Oh! I didn't know that. Can you tell me about your auth system?"
|
||||
You: *starts typing a 30-minute context document*
|
||||
```
|
||||
|
||||
**Every. Single. Time.**
|
||||
|
||||
Or worse—you come back to a project after 3 weeks:
|
||||
|
||||
```
|
||||
You: "Hey Claude, remind me what we did last week"
|
||||
Claude: "I don't have any record of that. What did you work on?"
|
||||
You: *searches through commit messages, slack history, and old notes*
|
||||
```
|
||||
|
||||
**Claude is brilliant. But Claude starts from ZERO every conversation.**
|
||||
|
||||
That's about to change. Forever.
|
||||
|
||||
---
|
||||
|
||||
## Enter Claude OS: The AI That Actually Remembers 🧠
|
||||
|
||||
<p align="center">
|
||||
<img src="frontend/public/assets/claude-os-architecture.svg" alt="Claude OS Architecture" width="100%"/>
|
||||
</p>
|
||||
|
||||
> **⚡ NEW in v2.0:** Hybrid indexing with tree-sitter AST parsing!
|
||||
> **10,000 files indexed in 3 seconds** (was 3-5 hours) | **600-1000x faster** | **80% fewer embeddings** | **Start coding immediately!**
|
||||
|
||||
Claude OS is a **complete operating system for AI-assisted development** that turns Claude from a brilliant generalist into **your project's most knowledgeable expert**—someone who:
|
||||
|
||||
- **Remembers everything** about your project across all sessions (and never forgets)
|
||||
- **Learns automatically** from every conversation, commit, and decision you make
|
||||
- **Understands your entire codebase** exactly like a senior developer who's been there for years
|
||||
- **Detects architectural decisions, patterns, and edge cases** in real-time as you work
|
||||
- **Becomes genuinely expert** on YOUR specific project, YOUR tech stack, and YOUR way of doing things
|
||||
- **Adapts its responses** to match your coding style, conventions, and preferences
|
||||
|
||||
## How Claude OS Works: The Magic Behind the Intelligence 🎯
|
||||
|
||||
Claude OS is not just another tool—it's a **complete AI development operating system** built on **six interconnected pillars** that work together seamlessly:
|
||||
|
||||
---
|
||||
|
||||
### 1. 🧠 **Real-Time Learning System** (The Always-On Brain)
|
||||
|
||||
Imagine if Claude could eavesdrop on all your conversations and automatically update its knowledge of your project. That's exactly what this does.
|
||||
|
||||
```
|
||||
Your Conversation → Redis Pub/Sub → AI Pattern Detection → Auto Knowledge Update
|
||||
< 1ms latency 10 pattern types Instant indexing
|
||||
```
|
||||
|
||||
**Here's what makes it revolutionary:**
|
||||
|
||||
- **Always watching, never sleeping** - RQ workers monitor conversations 24/7, 365 days a year
|
||||
- **Lightning-fast detection** - Redis pub/sub broadcasts insights with < 1ms latency
|
||||
- **Intelligent pattern recognition** - Detects 10+ different learning patterns:
|
||||
- Architectural decisions ("We're moving from monolith to microservices")
|
||||
- Technology changes ("Switching from PostgreSQL to MongoDB")
|
||||
- Bug fixes and solutions ("Fixed timezone issue in auth tokens")
|
||||
- Performance insights ("This N+1 query is killing us")
|
||||
- Edge cases and gotchas ("Watch out for expired certs in prod")
|
||||
- Team preferences ("We prefer composition over inheritance")
|
||||
- Naming conventions ("All our handlers end with _handler")
|
||||
- Common pitfalls ("Never use SELECT * here, it's slow")
|
||||
- Integration patterns ("Always validate against the webhook signature")
|
||||
- Security concerns ("Remember to sanitize user input")
|
||||
- **High-confidence learning only** - 75-95% confidence thresholds prevent false learnings
|
||||
- **Completely automatic** - Zero manual configuration or training needed
|
||||
|
||||
**Real Examples It Captures:**
|
||||
|
||||
```
|
||||
"We're replacing our JWT library with a custom implementation"
|
||||
→ Updated tech stack knowledge, removes old library references
|
||||
|
||||
"I found the issue—the cron job was running in UTC but our timestamps are local"
|
||||
→ Remembered as critical insight in auth and scheduler modules
|
||||
|
||||
"This ORM query is generating 50 SQL statements per request"
|
||||
→ Added to performance anti-patterns database
|
||||
|
||||
"Let's enforce this naming convention: all event handlers must be on_[event_name]"
|
||||
→ Learned and will suggest this pattern in future code generation
|
||||
```
|
||||
|
||||
### 2. 💾 **Memory MCP** (Your AI's Institutional Memory)
|
||||
|
||||
Your explicit memories. Think of this as Claude taking detailed notes on everything you tell it to remember.
|
||||
|
||||
```
|
||||
You: "Remember: We use bcrypt with 12 rounds for password hashing, never change this"
|
||||
→ Saved forever
|
||||
→ Claude recalls this in EVERY future conversation about passwords
|
||||
```
|
||||
|
||||
**Why this is a game-changer:**
|
||||
|
||||
- **Persistent across sessions** - Context survives between conversations, weeks, months
|
||||
- **Natural language interface** - Just say "Remember:" to save anything. That's it.
|
||||
- **Instant recall in context** - Claude doesn't search—it automatically brings up relevant memories when discussing that topic
|
||||
- **Project-specific memory** - Each project has its own memory bank, no confusion
|
||||
- **Private and secure** - Your memories are git-ignored, never tracked, completely private
|
||||
- **Searchable** - Can browse and organize your memories anytime
|
||||
|
||||
**Examples of What You'd Remember:**
|
||||
|
||||
```
|
||||
"Remember: Our payment processing only supports USD and EUR, not other currencies"
|
||||
→ Claude never suggests converting to other currencies
|
||||
|
||||
"Remember: API v2 is deprecated. All new endpoints go in /api/v3/.
|
||||
Redirect v2 traffic with 301 moved permanently."
|
||||
→ Applied to every API endpoint Claude suggests
|
||||
|
||||
"Remember: Our database backups run at 2 AM UTC. That's our quiet window.
|
||||
Never schedule migrations then."
|
||||
→ Claude factors this into any infrastructure work
|
||||
|
||||
"Remember: The legacy_accounts table is for testing only and gets wiped weekly.
|
||||
Never store real data there."
|
||||
→ Prevents dangerous mistakes in code generation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 📚 **Semantic Knowledge Base** (Your Codebase as Living Documentation)
|
||||
|
||||
This is where the magic really happens. Claude doesn't just READ your code—it UNDERSTANDS it.
|
||||
|
||||
```
|
||||
Your Entire Codebase
|
||||
↓
|
||||
Vector Embeddings (semantic understanding)
|
||||
↓
|
||||
Instant Semantic Search
|
||||
↓
|
||||
Context-Aware Responses (always relevant to YOUR code)
|
||||
```
|
||||
|
||||
**What Claude knows:**
|
||||
|
||||
- **Every file and every function** - Line-by-line code understanding
|
||||
- **Architecture patterns** - How your system actually works
|
||||
- **Dependencies and relationships** - What connects to what and why
|
||||
- **Historical evolution** - How code changed and why
|
||||
- **Team patterns** - Naming conventions, code style, architectural preferences
|
||||
- **Business logic** - What your code does and why it does it that way
|
||||
- **Third-party integrations** - How you use Stripe, AWS, Auth0, etc.
|
||||
- **Database schema** - Tables, relationships, constraints, indexes
|
||||
- **API contracts** - Request/response formats, error codes, rate limits
|
||||
- **Known issues** - Bugs, workarounds, temporary patches
|
||||
|
||||
**The Result:**
|
||||
|
||||
When you ask Claude about a feature, it searches through your entire codebase semantically and brings back the 5-10 most relevant code examples, architectural patterns, and related files—all automatically. No manual searching needed.
|
||||
|
||||
---
|
||||
|
||||
### 4. 🔍 **Analyze-Project Skill** (Lightning-Fast Hybrid Indexing)
|
||||
|
||||
This is Claude's way of getting a complete crash course in your project—**in seconds, not hours.**
|
||||
|
||||
```
|
||||
/initialize-project [project-id]
|
||||
↓
|
||||
Phase 1: STRUCTURAL INDEXING (30 seconds for 10,000 files!)
|
||||
→ Tree-sitter AST parsing (no LLM needed!)
|
||||
→ Extract all symbols (classes, functions, signatures)
|
||||
→ Build dependency graph
|
||||
→ PageRank importance scoring
|
||||
→ ✅ Ready to code IMMEDIATELY
|
||||
↓
|
||||
Phase 2: SELECTIVE SEMANTIC INDEXING (background, optional)
|
||||
→ Embed top 20% most important files only
|
||||
→ Full documentation embedding
|
||||
→ 80% reduction in chunks (faster, cheaper)
|
||||
→ Runs while you code
|
||||
↓
|
||||
3 Documentation Files Generated:
|
||||
• CODING_STANDARDS.md
|
||||
• ARCHITECTURE.md
|
||||
• DEVELOPMENT_PRACTICES.md
|
||||
↓
|
||||
Git Hooks Installed (auto-indexing on every commit)
|
||||
```
|
||||
|
||||
**⚡ NEW: Hybrid Two-Phase Indexing**
|
||||
|
||||
Inspired by [Aider](https://github.com/Aider-AI/aider), Claude OS now uses a revolutionary two-phase approach:
|
||||
|
||||
**Phase 1 - Structural Index (INSTANT):**
|
||||
|
||||
- **Tree-sitter parsing** - Language-agnostic AST extraction (supports 40+ languages)
|
||||
- **No embeddings needed** - Parse syntax, extract symbols, zero LLM calls
|
||||
- **Blazing fast** - 10,000 Ruby files indexed in **30 seconds** (vs 3-5 hours before!)
|
||||
- **Immediate availability** - Start coding the moment indexing begins
|
||||
- **Dependency graphing** - Understands which files import/require others
|
||||
- **PageRank scoring** - Identifies most important files automatically
|
||||
|
||||
**Phase 2 - Semantic Index (SELECTIVE):**
|
||||
|
||||
- **Top 20% only** - Embed most important files (based on PageRank)
|
||||
- **Documentation first** - Full embedding for docs, specs, README
|
||||
- **Background processing** - Runs while you code, no waiting
|
||||
- **80% fewer chunks** - Faster queries, lower costs, same intelligence
|
||||
|
||||
**Real Performance Numbers:**
|
||||
|
||||
| Project | Before (Full Embed) | After (Hybrid) | Speedup |
|
||||
|---------|---------------------|----------------|---------|
|
||||
| **Pistn (10,000 Ruby files)** | 3-5 hours | **3 seconds** + 20 min optional | **600-1000x faster!** |
|
||||
| **Start coding** | After full index | **Immediately!** | Instant |
|
||||
| **Chunks indexed** | 100,000+ | ~20,000 (80% reduction) | More efficient |
|
||||
| **Query speed** | Semantic only | Structural + Semantic | Faster |
|
||||
|
||||
**How the indexing actually works:**
|
||||
|
||||
- **Intelligent priority** - Tree-sitter identifies key files (controllers, services, models) in seconds
|
||||
- **Smart expansion** - PageRank scoring finds connected dependencies automatically
|
||||
- **Real-time updates** - Git hooks trigger re-indexing on every commit (takes seconds!)
|
||||
- **Pattern recognition** - Learns naming conventions, test patterns, error handling from AST
|
||||
- **Architecture mapping** - Understands project structure, module relationships, dependency graph
|
||||
|
||||
**What it analyzes and learns:**
|
||||
|
||||
- Your file and directory structure and organization philosophy
|
||||
- Test coverage patterns and testing conventions you follow
|
||||
- Database schemas, relationships, constraints
|
||||
- API endpoints, request/response formats, error codes
|
||||
- Common error patterns and how you handle them
|
||||
- Code style preferences and formatting conventions
|
||||
- Build and deployment processes
|
||||
- Environmental configuration and secrets management
|
||||
|
||||
**The Result:**
|
||||
|
||||
Claude instantly knows:
|
||||
|
||||
- Where features are implemented
|
||||
- How to add new functionality in your style
|
||||
- What patterns to follow for consistency
|
||||
- Where related code lives
|
||||
- What tests need updating
|
||||
|
||||
---
|
||||
|
||||
### 5. 🎯 **Session Management** (Never Lose Context Again)
|
||||
|
||||
Every time you start a new conversation, Claude OS automatically checks your session state and loads relevant context.
|
||||
|
||||
```
|
||||
New Conversation Starts
|
||||
↓
|
||||
Claude OS checks: "What were we working on?"
|
||||
↓
|
||||
Loads: Active session, recent memories, current branch, decisions made
|
||||
↓
|
||||
Prompts: "Continue working on [feature]?" or "Start something new?"
|
||||
```
|
||||
|
||||
**What this means for you:**
|
||||
|
||||
- **Zero cold starts** - Every conversation picks up where you left off
|
||||
- **Automatic context loading** - Relevant memories surfaced immediately
|
||||
- **Session tracking** - Track task, branch, decisions, blockers across days
|
||||
- **Resume anywhere** - Start on laptop, continue on desktop, same context
|
||||
- **Work tracking** - Know exactly what was accomplished, what's next
|
||||
|
||||
**The Result:**
|
||||
|
||||
No more "what was I doing?" moments. Claude knows what you're working on before you even ask.
|
||||
|
||||
---
|
||||
|
||||
### 6. 📊 **Code Structure MCP** (Your Codebase's DNA)
|
||||
|
||||
The newest addition to Claude OS—a **complete structural map** of your entire codebase, built in seconds.
|
||||
|
||||
```
|
||||
Git Commit Detected
|
||||
↓
|
||||
Tree-sitter AST Parsing (< 3 seconds for 10,000 files!)
|
||||
↓
|
||||
Symbol Extraction
|
||||
• Classes, functions, methods
|
||||
• Import/require statements
|
||||
• Function signatures
|
||||
• File relationships
|
||||
↓
|
||||
Dependency Graph Built
|
||||
↓
|
||||
PageRank Importance Scoring
|
||||
↓
|
||||
SQLite Storage (instant queries!)
|
||||
↓
|
||||
Available to Claude via code_structure MCP
|
||||
```
|
||||
|
||||
**What makes this revolutionary:**
|
||||
|
||||
- **Language-agnostic** - Supports 40+ languages (Ruby, Python, JS, TS, Go, Rust, Java, C++, and more!)
|
||||
- **No LLM calls needed** - Pure AST parsing, zero API costs
|
||||
- **Insanely fast** - 10,000 Ruby files parsed in 3 seconds (real benchmark on Pistn)
|
||||
- **Complete symbol table** - Every class, function, import extracted and indexed
|
||||
- **Dependency awareness** - Knows which files depend on others
|
||||
- **Importance ranking** - PageRank algorithm identifies key files automatically
|
||||
- **Git hook integration** - Auto-updates on every commit (< 1 second incremental)
|
||||
|
||||
**What Claude can do with this:**
|
||||
|
||||
```
|
||||
You: "Where is the user authentication logic?"
|
||||
Claude: "Found in 3 locations:
|
||||
1. app/services/authentication_service.rb (class AuthenticationService)
|
||||
- handle_login(email, password)
|
||||
- generate_jwt_token(user)
|
||||
- validate_token(token)
|
||||
2. app/controllers/auth_controller.rb (imports AuthenticationService)
|
||||
- login_action
|
||||
- logout_action
|
||||
3. app/models/user.rb (used by AuthenticationService)
|
||||
- authenticate method
|
||||
- password_hash field"
|
||||
```
|
||||
|
||||
**The power:**
|
||||
|
||||
- **Instant symbol lookup** - Find any function/class in milliseconds
|
||||
- **Cross-reference navigation** - See all files that import a module
|
||||
- **Architecture understanding** - Visualize your codebase structure
|
||||
- **Smart suggestions** - Claude knows where to add new code based on existing patterns
|
||||
- **Refactoring safety** - See all dependencies before changing a function
|
||||
|
||||
**Integration with other pillars:**
|
||||
|
||||
- **Feeds PageRank scores** to Phase 2 semantic indexing (knows which files are most important)
|
||||
- **Provides context** to Real-Time Learning (understands architectural changes)
|
||||
- **Enriches Memory MCP** with code location references
|
||||
- **Powers intelligent search** across your entire codebase structure
|
||||
|
||||
---
|
||||
|
||||
### 7. 🔌 **MCP Integration** (The Claude Code Bridge)
|
||||
|
||||
MCPs (Model Context Protocol) are the bridge between Claude OS and Claude Code. They expose all Claude OS knowledge directly to you.
|
||||
|
||||
```
|
||||
Claude OS Knowledge Base
|
||||
↓
|
||||
MCP Server (http://localhost:8051)
|
||||
↓
|
||||
Claude Code Interface
|
||||
↓
|
||||
You get AI with complete project knowledge
|
||||
```
|
||||
|
||||
**Your Available MCPs (5 per project):**
|
||||
|
||||
- **pistn-project-profile** - Your project's coding standards, architecture, and development practices
|
||||
- **pistn-project-index** - Semantic index of key source files (vector embeddings for deep understanding)
|
||||
- **pistn-knowledge-docs** - Project documentation and specifications (auto-ingested from docs/)
|
||||
- **pistn-project-memories** - All your "Remember:" memories in one place (persistent across sessions)
|
||||
- **pistn-code_structure** - ⭐ NEW! Complete structural map (38,406 symbols, dependency graph, PageRank scores)
|
||||
|
||||
Every MCP has:
|
||||
|
||||
- 📊 **Full semantic search** - Search code by meaning, not keywords
|
||||
- 📚 **Complete documentation** - What each file does and why
|
||||
- 🔗 **Cross-referenced** - See how modules connect
|
||||
- 🎯 **Always current** - Auto-updated with every commit
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 🤝 **Team Collaboration & Sharing** (Built for Teams)
|
||||
|
||||
Claude OS isn't just for solo developers—it's designed for teams to share knowledge.
|
||||
|
||||
```
|
||||
Your Claude OS Setup
|
||||
↓
|
||||
One Command: ./install.sh
|
||||
↓
|
||||
Teammates Get: All commands, skills, templates instantly
|
||||
↓
|
||||
Each Project: /claude-os-init (2 minutes)
|
||||
↓
|
||||
Team Shares: CLAUDE.md (in git), separate memories (private)
|
||||
```
|
||||
|
||||
**How team sharing works:**
|
||||
|
||||
- **Template system** - Commands and skills shared via symlinks, updates propagate instantly
|
||||
- **One-command install** - Teammates run `./install.sh`, done in 3 minutes
|
||||
- **Project isolation** - Each project has separate knowledge bases
|
||||
- **Shared context** - CLAUDE.md in git gives everyone the same project overview
|
||||
- **Private memories** - Each developer's memories stay private (git-ignored)
|
||||
- **Update once, benefit everywhere** - Update templates, all projects get improvements
|
||||
|
||||
**The Result:**
|
||||
|
||||
Onboard new developers in minutes, not weeks. They get institutional knowledge from day one.
|
||||
|
||||
---
|
||||
|
||||
## 🎨 **Agent-OS Integration** (Spec-Driven Development - Optional)
|
||||
|
||||
Partner with Agent-OS by Builder Methods for structured feature development.
|
||||
|
||||
**8 Specialized Agents:**
|
||||
|
||||
1. **spec-initializer** - Create feature specification directories
|
||||
2. **spec-shaper** - Gather requirements through iterative questions
|
||||
3. **spec-writer** - Generate detailed technical specifications
|
||||
4. **tasks-list-creator** - Break specs into actionable tasks
|
||||
5. **implementer** - Implement features following task list
|
||||
6. **implementation-verifier** - Verify implementation completeness
|
||||
7. **spec-verifier** - Ensure spec and tasks consistency
|
||||
8. **product-planner** - Create product documentation
|
||||
|
||||
**The workflow:**
|
||||
|
||||
```
|
||||
/new-spec user-authentication
|
||||
↓
|
||||
/create-spec (answers 1-3 questions at a time)
|
||||
↓
|
||||
Spec.md + Tasks.md generated
|
||||
↓
|
||||
/implement-spec user-authentication
|
||||
↓
|
||||
Feature implemented, tested, verified
|
||||
```
|
||||
|
||||
**Why this matters:**
|
||||
|
||||
- **Structured planning** - Think before coding
|
||||
- **Clear requirements** - No more "wait, what did we want?"
|
||||
- **Task breakdown** - Implementation steps crystal clear
|
||||
- **Verification built-in** - Ensures nothing missed
|
||||
- **Documentation automatic** - Spec becomes permanent record
|
||||
|
||||
**Integration with Claude OS:**
|
||||
|
||||
Agent-OS agents search Claude OS memories before creating specs, ensuring they build on existing patterns and decisions. Perfect synergy.
|
||||
|
||||
---
|
||||
|
||||
## 📊 **Spec Tracking & Kanban Board** (Visual Project Management)
|
||||
|
||||
**NEW: Claude OS now automatically tracks all your Agent-OS specs and displays them as an interactive Kanban board!**
|
||||
|
||||
```
|
||||
Agent-OS creates spec → Claude OS parses tasks.md → Kanban board displays progress
|
||||
```
|
||||
|
||||
**What this means for you:**
|
||||
|
||||
- **Automatic discovery** - Claude OS scans your `agent-os/specs/` folder
|
||||
- **Complete task tracking** - Every task, phase, dependency parsed and stored
|
||||
- **Visual progress** - See all specs and their tasks organized by status (Todo, In Progress, Done, Blocked)
|
||||
- **Real-time updates** - Update task status via API, progress auto-calculated
|
||||
- **Archive completed specs** - Keep your board focused on active work
|
||||
- **Time tracking** - Track estimated vs actual time for every task
|
||||
- **Dependency awareness** - See which tasks depend on others
|
||||
|
||||
**The Kanban View:**
|
||||
|
||||
```
|
||||
📋 Group Account Rendering [52 tasks, 45% complete]
|
||||
┌─────────────┬─────────────┬─────────────┬─────────────┐
|
||||
│ TODO │ IN PROGRESS │ DONE │ BLOCKED │
|
||||
├─────────────┼─────────────┼─────────────┼─────────────┤
|
||||
│ PHASE2-TASK1│ PHASE1-TASK3│ PHASE1-TASK1│ │
|
||||
│ PHASE2-TASK2│ │ PHASE1-TASK2│ │
|
||||
│ PHASE3-TASK1│ │ ...23 more │ │
|
||||
└─────────────┴─────────────┴─────────────┴─────────────┘
|
||||
|
||||
📋 Manual Appointment Times [15 tasks, 100% complete] ✅ ARCHIVED
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. **Create spec with Agent-OS:**
|
||||
```
|
||||
/create-spec → agent-os/specs/2025-01-15-user-auth/tasks.md
|
||||
```
|
||||
|
||||
2. **Claude OS syncs automatically:**
|
||||
```
|
||||
POST /api/projects/{id}/specs/sync
|
||||
→ Parses all tasks.md files
|
||||
→ Extracts metadata, phases, dependencies
|
||||
→ Stores in SQLite
|
||||
```
|
||||
|
||||
3. **View progress in real-time:**
|
||||
```
|
||||
GET /api/projects/{id}/kanban
|
||||
→ See all specs grouped by status
|
||||
→ Track completion percentage
|
||||
→ Monitor time estimates
|
||||
```
|
||||
|
||||
4. **Update task status as you work:**
|
||||
```
|
||||
PATCH /api/tasks/{id}/status
|
||||
{
|
||||
"status": "done",
|
||||
"actual_minutes": 15
|
||||
}
|
||||
→ Spec progress auto-updates
|
||||
→ Status changes from "planning" to "in_progress" to "completed"
|
||||
```
|
||||
|
||||
5. **Archive when done:**
|
||||
```
|
||||
POST /api/specs/{id}/archive
|
||||
→ Hides from default view
|
||||
→ Preserves all history
|
||||
→ Keeps board clean
|
||||
```
|
||||
|
||||
**What gets tracked:**
|
||||
|
||||
- **Spec metadata** - Name, date, status, progress percentage
|
||||
- **Individual tasks** - Task code (PHASE1-TASK1), title, description
|
||||
- **Task status** - todo, in_progress, done, blocked
|
||||
- **Time estimates** - Estimated minutes vs actual minutes spent
|
||||
- **Dependencies** - Which tasks must complete before others
|
||||
- **Risk levels** - Low, medium, high risk tasks
|
||||
- **Phases** - Task organization by implementation phase
|
||||
- **Completion timestamps** - When tasks started/completed
|
||||
|
||||
**API Endpoints:**
|
||||
|
||||
```bash
|
||||
# Sync all specs from agent-os folder
|
||||
POST /api/projects/{project_id}/specs/sync
|
||||
|
||||
# Get Kanban board view
|
||||
GET /api/projects/{project_id}/kanban?include_archived=false
|
||||
|
||||
# Get all tasks for a spec
|
||||
GET /api/specs/{spec_id}/tasks
|
||||
|
||||
# Update task status
|
||||
PATCH /api/tasks/{task_id}/status
|
||||
{
|
||||
"status": "in_progress",
|
||||
"actual_minutes": 15
|
||||
}
|
||||
|
||||
# Archive/unarchive specs
|
||||
POST /api/specs/{spec_id}/archive
|
||||
POST /api/specs/{spec_id}/unarchive
|
||||
```
|
||||
|
||||
**The result:**
|
||||
|
||||
Never lose track of implementation progress. See at a glance what's done, what's in flight, and what's next. Perfect for solo devs and teams alike.
|
||||
|
||||
---
|
||||
|
||||
## ⚡ **Smart Commands & Triggers** (Your AI Assistant on Autopilot)
|
||||
|
||||
Claude OS gives you powerful slash commands and automatic triggers.
|
||||
|
||||
**Essential Commands:**
|
||||
|
||||
- `/claude-os-search "query"` - Search all memories and docs
|
||||
- `/claude-os-remember "content"` - Quick save to memories
|
||||
- `/claude-os-save "title"` - Full-featured save with KB selection
|
||||
- `/claude-os-list` - View all knowledge bases
|
||||
- `/claude-os-session start` - Begin tracking a feature
|
||||
- `/claude-os-triggers` - Manage automatic triggers
|
||||
|
||||
**Automatic Triggers:**
|
||||
|
||||
When you say phrases like these, Claude automatically saves to memories:
|
||||
|
||||
- "Remember this:"
|
||||
- "Save this:"
|
||||
- "Document this:"
|
||||
- "Note this:"
|
||||
- "Keep in mind:"
|
||||
|
||||
**The Result:**
|
||||
|
||||
Building institutional knowledge happens naturally as you work. No manual note-taking needed.
|
||||
|
||||
---
|
||||
|
||||
## What Can Claude OS Actually Do For You? 🚀
|
||||
|
||||
### ⚡ **Immediate Productivity Wins**
|
||||
|
||||
#### Instant Context Recovery
|
||||
|
||||
```
|
||||
You: "What were we working on last week on that payment refund issue?"
|
||||
Claude OS: "On Oct 20, you discovered that refunds for subscriptions
|
||||
were failing because the payment processor's API changed their
|
||||
transaction ID format. You created a migration script in
|
||||
/services/payment_processor.js line 234 that maps old IDs to new format.
|
||||
Tests are in __tests__/payment_processor.test.js lines 156-189.
|
||||
You still need to test the edge case for subscriptions that refund across
|
||||
month boundaries."
|
||||
```
|
||||
|
||||
**Without Claude OS:**
|
||||
|
||||
```
|
||||
You: "What were we working on last week?"
|
||||
Claude: "I'm not sure. Can you remind me?"
|
||||
*You search through Slack, emails, commits, pull requests for 30 minutes*
|
||||
```
|
||||
|
||||
#### Architecture-Aware Code Generation
|
||||
|
||||
```
|
||||
You: "Add a new handler for when users delete their accounts"
|
||||
Claude OS: "I'll create account_deletion_handler.js in /handlers/ (matching
|
||||
your naming convention). It will:
|
||||
1. Use the same error handling pattern as user_creation_handler.js
|
||||
2. Call the cleanup service you built in /services/account_cleanup.js
|
||||
3. Queue async tasks using your RQ setup (looking at how
|
||||
email_notification_handler does it)
|
||||
4. Follow the transaction pattern from your database service
|
||||
5. Include tests matching your test structure"
|
||||
```
|
||||
|
||||
The generated code is not just correct—it's indistinguishable from your own.
|
||||
|
||||
#### Pattern-Based Bug Diagnosis
|
||||
|
||||
```
|
||||
You: "This test is failing randomly"
|
||||
Claude OS: "Looking at your timezone handling history, I see you've had
|
||||
3 similar issues before:
|
||||
- Oct 15: Token expiry UTC/local timezone mismatch (Fixed in commit b56f585)
|
||||
- Oct 8: Cron job running in wrong timezone (Fixed in your scheduler)
|
||||
- Sep 29: Database query returning dates in different timezone
|
||||
|
||||
This looks like the same issue in a different place. The test compares
|
||||
timestamps without accounting for local timezone. Your fix before was
|
||||
to use moment.utc() for comparisons. Same solution here."
|
||||
```
|
||||
|
||||
### 🎯 **Architectural Decisions Made Easy**
|
||||
|
||||
```
|
||||
You: "Should we add caching for this endpoint?"
|
||||
Claude OS: "Looking at similar endpoints in your codebase:
|
||||
- /api/users/:id - cached (line 234)
|
||||
- /api/posts/:id - not cached (line 567)
|
||||
The difference is that /users/:id has high read volume but rare updates,
|
||||
while /posts/:id gets updated frequently. Your endpoint /api/product/:id
|
||||
matches the user pattern (high read, low write).
|
||||
Recommendation: Cache with 5-minute TTL like your /users endpoint."
|
||||
```
|
||||
|
||||
### 📚 **Documentation That Stays Current**
|
||||
|
||||
Claude OS automatically generates and maintains:
|
||||
|
||||
- **CODING_STANDARDS.md** - How YOU code (not how textbooks say to)
|
||||
- **ARCHITECTURE.md** - Your actual system design (not theory)
|
||||
- **DEVELOPMENT_PRACTICES.md** - Your team's conventions (enforced automatically)
|
||||
|
||||
These update automatically as you work—zero manual maintenance.
|
||||
|
||||
### 🧠 **Institutional Knowledge That Doesn't Walk Out the Door**
|
||||
|
||||
```
|
||||
Your intern learns from you:
|
||||
"Remember: Never use SELECT * in this schema because the accounts table
|
||||
has that 5MB blob field that kills performance. Always list specific columns."
|
||||
|
||||
Claude remembers this forever. When your intern leaves and a new person
|
||||
joins, Claude will warn them: "Be specific with SELECT statements on
|
||||
accounts table—there's a large blob field that causes performance issues."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Why Claude OS Beats Everything Else ✨
|
||||
|
||||
| Feature | Claude Alone | ChatGPT | GitHub Copilot | Claude OS |
|
||||
|---------|--------------|---------|-----------------|-----------|
|
||||
| Remembers your code | ❌ | ❌ | ❌ | ✅ Perfectly |
|
||||
| Understands your architecture | ❌ | ❌ | ❌ | ✅ Completely |
|
||||
| Learns from your decisions | ❌ | ❌ | ❌ | ✅ Automatically |
|
||||
| Writes in your style | ❌ | ❌ | Partial | ✅ Always |
|
||||
| **Indexes 10,000 files** | N/A | N/A | N/A | **✅ 3 seconds** |
|
||||
| **Tree-sitter AST parsing** | ❌ | ❌ | ❌ | **✅ 40+ languages** |
|
||||
| **Hybrid structural + semantic** | ❌ | ❌ | ❌ | **✅ Both!** |
|
||||
| Persists context between sessions | ❌ | ❌ | ❌ | ✅ Forever |
|
||||
| Session management & tracking | ❌ | ❌ | ❌ | ✅ Built-in |
|
||||
| Automatic context loading | ❌ | ❌ | ❌ | ✅ Every session |
|
||||
| Spec-driven development | ❌ | ❌ | ❌ | ✅ Optional |
|
||||
| Team sharing & collaboration | ❌ | ❌ | ❌ | ✅ One install |
|
||||
| Detects your bugs before they happen | ❌ | ❌ | ❌ | ✅ Via patterns |
|
||||
| Works offline | N/A | ✅ | ✅ | ✅ Yes |
|
||||
| Requires zero setup per project | ❌ | ✅ | ✅ | ✅ One command |
|
||||
| Free and open source | ❌ | ❌ | ❌ | ✅ 100% |
|
||||
|
||||
---
|
||||
|
||||
## The Multiplier Effect 📈
|
||||
|
||||
The magic isn't just that Claude is smart. It's that Claude gets **smarter over time**.
|
||||
|
||||
**Week 1:** Claude learns your project structure
|
||||
**Week 2:** Claude starts suggesting patterns
|
||||
**Week 3:** Claude catches bugs before they happen
|
||||
**Week 4:** Claude contributes architectural ideas
|
||||
**Month 2:** Claude is your most productive team member
|
||||
|
||||
This is the **compounding intelligence** that happens when AI actually understands context.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## The Complete System Architecture 🏗️
|
||||
|
||||
**Visual Overview:**
|
||||
|
||||
<p align="center">
|
||||
<img src="frontend/public/assets/claude-os-architecture.svg" alt="Claude OS Architecture" width="100%"/>
|
||||
</p>
|
||||
|
||||
**Technical Stack:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ CLAUDE OS ECOSYSTEM v2.0 │
|
||||
│ (Now with Hybrid Indexing!) │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ Real-Time │ │ Memory │ │ ⚡ NEW: Code Structure │ │
|
||||
│ │ Learning │ │ System │ │ (Tree-sitter AST) │ │
|
||||
│ │ (Redis) │ │ (MCP) │ │ 38,406 symbols! │ │
|
||||
│ │ <1ms │ │ Instant │ │ 3 seconds to index │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └───────────┬─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └──────────────────┼──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼─────────────────────────┐ │
|
||||
│ │ Semantic Knowledge Base (SQLite) │ │
|
||||
│ │ • 38,406 code symbols (structural index) │ │
|
||||
│ │ • ~20,000 semantic chunks (80% reduction!)│ │
|
||||
│ │ • Vector embeddings (selective, top 20%) │ │
|
||||
│ │ • Dependency graph + PageRank scores │ │
|
||||
│ │ • Team patterns & conventions │ │
|
||||
│ │ • Architecture documentation │ │
|
||||
│ │ • Persistent memory & insights │ │
|
||||
│ └──────────────────┬─────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼─────────────────────────┐ │
|
||||
│ │ MCP Server (http://localhost:8051) │ │
|
||||
│ │ Exposes 5 MCPs per project: │ │
|
||||
│ │ • project_memories (persistent) │ │
|
||||
│ │ • project_profile (standards) │ │
|
||||
│ │ • project_index (semantic) │ │
|
||||
│ │ • knowledge_docs (documentation) │ │
|
||||
│ │ • code_structure ⭐ NEW! (AST map) │ │
|
||||
│ └──────────────────┬─────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼─────────────────────────┐ │
|
||||
│ │ Claude Code Interface │ │
|
||||
│ │ (Your AI with 600-1000x faster indexing!) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Performance: Pistn (10,000 Ruby files) indexed in 3 seconds ⚡
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How This Compares to Traditional Development ⚖️
|
||||
|
||||
### ❌ Traditional Claude/AI Coding
|
||||
|
||||
- Each session starts from zero
|
||||
- You have to copy-paste code for context
|
||||
- Claude gives generic best-practice solutions
|
||||
- You spend 30 minutes explaining your architecture
|
||||
- Forgets everything when you close the conversation
|
||||
- Can't detect your patterns or preferences
|
||||
- Treats every project the same
|
||||
|
||||
### ✅ Claude OS AI Coding
|
||||
|
||||
- Sessions build on all previous knowledge
|
||||
- Context is automatic and complete
|
||||
- Claude gives YOUR solutions
|
||||
- Claude already understands your architecture
|
||||
- Remembers everything forever
|
||||
- Detects and adapts to your patterns
|
||||
- Becomes an expert on YOUR project
|
||||
|
||||
**The difference?** Claude OS transforms AI from a helpful tool into a **genuine team member who understands your project.**
|
||||
|
||||
---
|
||||
|
||||
## Why You Should Use Claude OS Right Now 💡
|
||||
|
||||
### You need this if you
|
||||
|
||||
- **Work on multiple projects** - Claude remembers each project separately
|
||||
- **Have team members leaving** - Preserves institutional knowledge they'd take with them
|
||||
- **Work on the same project long-term** - Claude gets smarter as it learns your patterns
|
||||
- **Care about code consistency** - Claude enforces YOUR conventions, not textbook ones
|
||||
- **Want faster development** - Context-aware AI is 10x faster than starting from zero
|
||||
- **Debug tricky issues** - Claude remembers similar issues and past solutions
|
||||
- **Onboard new developers** - They get the knowledge of everyone on your team
|
||||
- **Maintain architectural consistency** - Claude knows and enforces your patterns
|
||||
- **Document as you go** - Documentation updates automatically
|
||||
- **Make better architectural decisions** - Claude references your own similar choices
|
||||
|
||||
### You absolutely need this if you
|
||||
|
||||
- Have a large or complex codebase
|
||||
- Work in a startup where knowledge walks out the door with departing team members
|
||||
- Deal with legacy code and need to understand it deeply
|
||||
- Want to improve code quality systematically
|
||||
- Need AI that understands YOUR specific way of building software
|
||||
|
||||
---
|
||||
|
||||
## Getting Started in 3 Steps
|
||||
|
||||
```bash
|
||||
# 1. Install Claude OS (one-time, 3 minutes)
|
||||
git clone https://github.co/brobertsaz/claude-os.git
|
||||
cd claude-os
|
||||
./install.sh
|
||||
|
||||
# 2. Start Claude OS
|
||||
./start.sh
|
||||
|
||||
# 3. Initialize your project (2 minutes)
|
||||
cd /path/to/your/project
|
||||
/claude-os-init
|
||||
# Answer a few questions, done!
|
||||
```
|
||||
|
||||
Now every conversation with Claude includes your complete project knowledge.
|
||||
|
||||
**For Teams:**
|
||||
|
||||
Share the repo URL with teammates. They run `./install.sh` and get everything you have. Each project they work on runs `/claude-os-init` once. That's it.
|
||||
|
||||
---
|
||||
|
||||
## The Real Magic ✨
|
||||
|
||||
**Claude OS doesn't make Claude smarter.** Claude is already brilliant.
|
||||
|
||||
**Claude OS makes Claude USEFUL for your specific project.**
|
||||
|
||||
That's the magic. The difference between generic intelligence and practical expertise.
|
||||
|
||||
It's the difference between "a very smart person who's never seen your code" and "your most knowledgeable team member."
|
||||
|
||||
---
|
||||
|
||||
# 🚀 **Claude OS: The Future of AI-Assisted Development is Here**
|
||||
|
||||
*Stop explaining. Stop copying context. Stop starting over.*
|
||||
|
||||
*Start building with an AI that knows your code as well as you do.*
|
||||
|
||||
---
|
||||
|
||||
*Built with ❤️ by Anthropic Claude, who got tired of starting every conversation from zero.*
|
||||
687
docs/index.html
Normal file
687
docs/index.html
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Claude OS - The AI Operating System That Never Forgets</title>
|
||||
|
||||
<!-- Bootstrap 5 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- AOS (Animate On Scroll) -->
|
||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--cyan: #00D9FF;
|
||||
--purple: #8B5CF6;
|
||||
--pink: #FF00FF;
|
||||
--dark-bg: #0F172A;
|
||||
--darker-bg: #1E293B;
|
||||
}
|
||||
|
||||
* {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--dark-bg) 0%, var(--darker-bg) 100%);
|
||||
color: #ffffff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Gradient Text */
|
||||
.gradient-text {
|
||||
background: linear-gradient(90deg, var(--cyan) 0%, var(--purple) 50%, var(--pink) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
section {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
section.half-height {
|
||||
min-height: 50vh;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-size: 2rem;
|
||||
color: #cbd5e1;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero .emoji-row {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
/* Problem Box */
|
||||
.problem-box {
|
||||
background: rgba(255, 0, 0, 0.1);
|
||||
border-left: 4px solid #ef4444;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.problem-box pre {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 1.5rem;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.problem-box code {
|
||||
color: #fca5a5;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Architecture Image */
|
||||
.architecture-img {
|
||||
max-width: 100%;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 0 40px rgba(0, 217, 255, 0.3);
|
||||
}
|
||||
|
||||
/* Metric Badge */
|
||||
.metric-badge {
|
||||
display: inline-block;
|
||||
font-size: 2.5rem;
|
||||
color: var(--cyan);
|
||||
font-weight: 700;
|
||||
text-shadow: 0 0 20px rgba(0, 217, 255, 0.6);
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/* Pillar Cards */
|
||||
.pillar-card {
|
||||
background: rgba(30, 41, 59, 0.8);
|
||||
border: 2px solid var(--purple);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 0 20px rgba(139, 92, 246, 0.2);
|
||||
}
|
||||
|
||||
.pillar-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0 30px rgba(139, 92, 246, 0.4);
|
||||
}
|
||||
|
||||
.pillar-card h4 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pillar-card.cyan {
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
|
||||
.pillar-card.purple {
|
||||
border-color: var(--purple);
|
||||
}
|
||||
|
||||
.pillar-card.pink {
|
||||
border-color: var(--pink);
|
||||
}
|
||||
|
||||
/* Two Column Section */
|
||||
.two-col-section h3 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.two-col-section ul {
|
||||
font-size: 1.1rem;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
table thead {
|
||||
background: var(--purple);
|
||||
}
|
||||
|
||||
table th {
|
||||
padding: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 0.8rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
table tbody tr:hover {
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
}
|
||||
|
||||
/* Code Block */
|
||||
.code-block {
|
||||
background: rgba(30, 41, 59, 0.9);
|
||||
border: 1px solid var(--purple);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 0 20px rgba(139, 92, 246, 0.2);
|
||||
}
|
||||
|
||||
.code-block code {
|
||||
color: var(--cyan);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* CTA Button */
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
background: linear-gradient(90deg, var(--cyan) 0%, var(--purple) 100%);
|
||||
color: white;
|
||||
padding: 1.2rem 3rem;
|
||||
border-radius: 10px;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
margin: 2rem 0;
|
||||
box-shadow: 0 0 30px rgba(0, 217, 255, 0.5);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 0 50px rgba(0, 217, 255, 0.8);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Highlight */
|
||||
.highlight {
|
||||
color: var(--cyan);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Section Titles */
|
||||
h2.section-title {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hero h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h2.section-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
section {
|
||||
min-height: auto;
|
||||
padding: 60px 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Navbar (optional sticky nav) */
|
||||
.navbar-custom {
|
||||
background: rgba(15, 23, 42, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.navbar-custom .nav-link {
|
||||
color: #cbd5e1 !important;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.navbar-custom .nav-link:hover {
|
||||
color: var(--cyan) !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="hero" id="hero">
|
||||
<div class="container">
|
||||
<h1 class="gradient-text" data-aos="fade-down">Claude OS</h1>
|
||||
<h2 data-aos="fade-up" data-aos-delay="200">
|
||||
The AI Operating System<br>That Never Forgets
|
||||
</h2>
|
||||
<div class="emoji-row" data-aos="fade-up" data-aos-delay="400">
|
||||
<span>🧠 Persistent Memory</span>
|
||||
<span>⚡ 600-1000x Faster</span>
|
||||
<span>🚀 Zero Setup</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Problem Section -->
|
||||
<section id="problem">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1">
|
||||
<h2 class="section-title gradient-text" data-aos="fade-right">The Problem You're Facing</h2>
|
||||
<div class="problem-box" data-aos="fade-up">
|
||||
<p class="fs-5"><strong>Every. Single. Time.</strong></p>
|
||||
<pre><code>You: "Claude, add a feature to our auth system"
|
||||
Claude: "Based on common best practices..."
|
||||
You: "No no no—we have custom JWT, 2FA,
|
||||
timezone handling... This is all wrong."
|
||||
Claude: "Oh! I didn't know that."
|
||||
You: *starts typing 30-minute context document*</code></pre>
|
||||
</div>
|
||||
<p class="text-center fs-3 mt-4" data-aos="zoom-in" data-aos-delay="200">
|
||||
<span class="highlight">Claude is brilliant. But Claude starts from ZERO every conversation.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Solution + Architecture -->
|
||||
<section id="solution">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">Enter Claude OS</h2>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<img src="assets/claude-os-architecture.svg"
|
||||
alt="Claude OS Architecture"
|
||||
class="architecture-img"
|
||||
data-aos="zoom-in"
|
||||
data-aos-delay="200">
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-4" data-aos="fade-up" data-aos-delay="400">
|
||||
<div class="metric-badge">10,000 files indexed in 3 seconds</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- What It Does -->
|
||||
<section id="what-it-does">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1">
|
||||
<h2 class="section-title gradient-text" data-aos="fade-right">What Claude OS Does</h2>
|
||||
<ul class="fs-5" data-aos="fade-up">
|
||||
<li class="mb-3"><strong class="highlight">Remembers everything</strong> about your project across all sessions</li>
|
||||
<li class="mb-3"><strong class="highlight">Learns automatically</strong> from every conversation, commit, and decision</li>
|
||||
<li class="mb-3"><strong class="highlight">Understands your codebase</strong> like a senior developer</li>
|
||||
<li class="mb-3"><strong class="highlight">Detects patterns</strong> in real-time as you work</li>
|
||||
<li class="mb-3"><strong class="highlight">Adapts its responses</strong> to match YOUR coding style</li>
|
||||
<li class="mb-3"><strong class="highlight">Never forgets</strong> — context persists forever</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6 Pillars -->
|
||||
<section id="pillars">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">6 Core Pillars</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6 col-lg-4" data-aos="fade-up" data-aos-delay="0">
|
||||
<div class="pillar-card cyan">
|
||||
<h4 class="gradient-text">🧠 Real-Time Learning</h4>
|
||||
<p>Redis Pub/Sub with <1ms latency. Automatically detects 10+ pattern types from your conversations.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-4" data-aos="fade-up" data-aos-delay="100">
|
||||
<div class="pillar-card purple">
|
||||
<h4 class="gradient-text">💾 Memory MCP</h4>
|
||||
<p>Persistent memory across sessions. Natural language save and instant recall.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-4" data-aos="fade-up" data-aos-delay="200">
|
||||
<div class="pillar-card pink">
|
||||
<h4 class="gradient-text">📚 Semantic Knowledge</h4>
|
||||
<p>Vector embeddings for deep understanding. Instant semantic search across your codebase.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-4" data-aos="fade-up" data-aos-delay="300">
|
||||
<div class="pillar-card cyan">
|
||||
<h4 class="gradient-text">📊 Code Structure</h4>
|
||||
<p><strong>NEW!</strong> Tree-sitter AST parsing. 38,406 symbols indexed in 3 seconds.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-4" data-aos="fade-up" data-aos-delay="400">
|
||||
<div class="pillar-card purple">
|
||||
<h4 class="gradient-text">🔍 Analyze-Project</h4>
|
||||
<p>Hybrid indexing (structural + semantic). Git hooks for automatic updates.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-4" data-aos="fade-up" data-aos-delay="500">
|
||||
<div class="pillar-card pink">
|
||||
<h4 class="gradient-text">🎯 Session Management</h4>
|
||||
<p>Auto-resume sessions with full context preservation. Zero cold starts.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Hybrid Indexing -->
|
||||
<section id="hybrid-indexing" class="two-col-section">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">⚡ NEW: Hybrid Indexing</h2>
|
||||
<div class="row g-5">
|
||||
<div class="col-md-6" data-aos="fade-right">
|
||||
<h3 style="color: var(--cyan);">Phase 1: Structural</h3>
|
||||
<ul>
|
||||
<li>Tree-sitter AST parsing</li>
|
||||
<li>Extract all symbols</li>
|
||||
<li>Build dependency graph</li>
|
||||
<li>PageRank scoring</li>
|
||||
<li><strong class="metric-badge" style="font-size: 2rem;">3 seconds</strong> for 10k files!</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6" data-aos="fade-left">
|
||||
<h3 style="color: var(--purple);">Phase 2: Semantic</h3>
|
||||
<ul>
|
||||
<li>Top 20% most important files</li>
|
||||
<li>Full documentation embedding</li>
|
||||
<li>80% reduction in chunks</li>
|
||||
<li>Runs in background</li>
|
||||
<li><strong>Optional, while you code</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-5" data-aos="zoom-in">
|
||||
<p class="fs-2 gradient-text">600-1000x faster than before!</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Performance Metrics -->
|
||||
<section id="performance">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">Real Performance Numbers</h2>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="table-responsive" data-aos="fade-up">
|
||||
<table class="table table-dark table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Project</th>
|
||||
<th>Before (Full Embed)</th>
|
||||
<th>After (Hybrid)</th>
|
||||
<th>Speedup</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Pistn (10k Ruby files)</strong></td>
|
||||
<td>3-5 hours</td>
|
||||
<td><span class="highlight fs-5">3 seconds</span></td>
|
||||
<td><span class="highlight">600-1000x</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Start coding</strong></td>
|
||||
<td>After full index</td>
|
||||
<td><span class="highlight">Immediately!</span></td>
|
||||
<td>Instant</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Chunks indexed</strong></td>
|
||||
<td>100,000+</td>
|
||||
<td>~20,000</td>
|
||||
<td>80% reduction</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Query speed</strong></td>
|
||||
<td>Semantic only</td>
|
||||
<td>Structural + Semantic</td>
|
||||
<td>Faster!</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- New in v2.3 -->
|
||||
<section id="new-features">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">🆕 New in v2.3</h2>
|
||||
<div class="row g-4 mt-3">
|
||||
<div class="col-md-6" data-aos="fade-right">
|
||||
<div class="pillar-card cyan">
|
||||
<h4 class="gradient-text">🎯 Skills Library</h4>
|
||||
<p><strong>Browse & install skills from the community!</strong></p>
|
||||
<ul>
|
||||
<li><strong>36+ skills</strong> from Anthropic & Superpowers</li>
|
||||
<li>PDF, XLSX, frontend design, TDD, debugging</li>
|
||||
<li>One-click install via Web UI</li>
|
||||
<li>Create custom skills for your project</li>
|
||||
</ul>
|
||||
<code style="background: rgba(0,0,0,0.3); padding: 0.5rem; display: block; border-radius: 4px; margin-top: 1rem;">
|
||||
/claude-os-skills templates
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6" data-aos="fade-left">
|
||||
<div class="pillar-card purple">
|
||||
<h4 class="gradient-text">📊 Session Insights</h4>
|
||||
<p><strong>Parse Claude Code sessions automatically!</strong></p>
|
||||
<ul>
|
||||
<li>Extract patterns, decisions, blockers</li>
|
||||
<li>Analyze tool calls and file changes</li>
|
||||
<li>Build analytics on your work</li>
|
||||
<li>Auto-save insights to memory</li>
|
||||
</ul>
|
||||
<code style="background: rgba(0,0,0,0.3); padding: 0.5rem; display: block; border-radius: 4px; margin-top: 1rem;">
|
||||
Session → Insights → Memory → Smarter AI
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Comparison -->
|
||||
<section id="comparison">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">How It Compares</h2>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="table-responsive" data-aos="fade-up">
|
||||
<table class="table table-dark table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Feature</th>
|
||||
<th>Claude Alone</th>
|
||||
<th>ChatGPT</th>
|
||||
<th>Copilot</th>
|
||||
<th>Claude OS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Remembers your code</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td><span class="highlight">✅ Perfectly</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Understands architecture</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td><span class="highlight">✅ Completely</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Indexes 10k files</strong></td>
|
||||
<td>N/A</td>
|
||||
<td>N/A</td>
|
||||
<td>N/A</td>
|
||||
<td><span class="highlight fs-5">✅ 3 seconds</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Learns from decisions</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td><span class="highlight">✅ Automatically</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Session management</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td><span class="highlight">✅ Built-in</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Free & open source</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td><span class="highlight">✅ 100%</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Skills library</strong></td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td>❌</td>
|
||||
<td><span class="highlight">✅ 36+ skills</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Use Cases -->
|
||||
<section id="use-cases" class="two-col-section">
|
||||
<div class="container">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">You Need This If You...</h2>
|
||||
<div class="row g-5">
|
||||
<div class="col-md-6" data-aos="fade-right">
|
||||
<ul class="fs-5">
|
||||
<li class="mb-3">Work on multiple projects</li>
|
||||
<li class="mb-3">Have team members leaving</li>
|
||||
<li class="mb-3">Work long-term on same project</li>
|
||||
<li class="mb-3">Care about code consistency</li>
|
||||
<li class="mb-3">Want faster development</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6" data-aos="fade-left">
|
||||
<ul class="fs-5">
|
||||
<li class="mb-3">Debug tricky issues</li>
|
||||
<li class="mb-3">Onboard new developers</li>
|
||||
<li class="mb-3">Maintain architectural consistency</li>
|
||||
<li class="mb-3">Document as you go</li>
|
||||
<li class="mb-3">Make better decisions</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Getting Started -->
|
||||
<section id="getting-started">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1">
|
||||
<h2 class="section-title gradient-text text-center" data-aos="fade-down">Getting Started in 3 Steps</h2>
|
||||
<div class="code-block" data-aos="fade-up">
|
||||
<pre><code># 1. Install Claude OS (one-time, 3 minutes)
|
||||
git clone https://github.com/brobertsaz/claude-os.git
|
||||
cd claude-os
|
||||
./install.sh
|
||||
|
||||
# 2. Start Claude OS
|
||||
./start.sh
|
||||
|
||||
# 3. Initialize your project (2 minutes)
|
||||
cd /path/to/your/project
|
||||
/claude-os-init
|
||||
# Answer a few questions, done!</code></pre>
|
||||
</div>
|
||||
<p class="text-center fs-4 mt-4" data-aos="zoom-in" data-aos-delay="200">
|
||||
<span class="highlight">Now every conversation includes your complete project knowledge.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- The Magic -->
|
||||
<section id="magic" class="half-height">
|
||||
<div class="container text-center">
|
||||
<h2 class="section-title gradient-text" data-aos="fade-down">The Real Magic ✨</h2>
|
||||
<p class="fs-3 mb-4" data-aos="fade-up" data-aos-delay="200">
|
||||
Claude OS doesn't make Claude <strong class="highlight">smarter</strong>.
|
||||
</p>
|
||||
<p class="fs-3 mb-5" data-aos="fade-up" data-aos-delay="400">
|
||||
Claude OS makes Claude <strong class="highlight">USEFUL</strong> for your specific project.
|
||||
</p>
|
||||
<p class="fs-2 gradient-text" data-aos="zoom-in" data-aos-delay="600">
|
||||
Generic Intelligence → Practical Expertise
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA -->
|
||||
<section id="cta" class="half-height">
|
||||
<div class="container text-center">
|
||||
<h2 class="section-title gradient-text" data-aos="fade-down">Stop Starting From Zero</h2>
|
||||
<p class="fs-4 mb-4" data-aos="fade-up" data-aos-delay="200">
|
||||
Start building with an AI that knows your code<br>as well as you do.
|
||||
</p>
|
||||
<div data-aos="zoom-in" data-aos-delay="400">
|
||||
<a href="https://github.com/brobertsaz/claude-os" class="cta-button">
|
||||
Get Claude OS Now
|
||||
</a>
|
||||
</div>
|
||||
<p class="mt-5" style="opacity: 0.8;" data-aos="fade-up" data-aos-delay="600">
|
||||
100% Free & Open Source<br>
|
||||
<small>Built with ❤️ by developers who got tired of starting every conversation from zero</small>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- AOS JS -->
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
|
||||
<script>
|
||||
// Initialize AOS
|
||||
AOS.init({
|
||||
duration: 800,
|
||||
once: true,
|
||||
offset: 100
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
862
frontend/src/components/SkillsManagement.tsx
Normal file
862
frontend/src/components/SkillsManagement.tsx
Normal file
|
|
@ -0,0 +1,862 @@
|
|||
import { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
Sparkles,
|
||||
Plus,
|
||||
Trash2,
|
||||
Eye,
|
||||
Download,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Globe,
|
||||
FolderOpen,
|
||||
Tag,
|
||||
Search,
|
||||
X,
|
||||
Check,
|
||||
ExternalLink,
|
||||
Github,
|
||||
RefreshCw
|
||||
} from 'lucide-react';
|
||||
import axios from 'axios';
|
||||
|
||||
interface Skill {
|
||||
name: string;
|
||||
path: string;
|
||||
description: string;
|
||||
scope: 'global' | 'project';
|
||||
source: string;
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
category: string | null;
|
||||
tags: string[];
|
||||
created: string;
|
||||
modified: string;
|
||||
}
|
||||
|
||||
interface SkillTemplate {
|
||||
name: string;
|
||||
category: string;
|
||||
description: string;
|
||||
path: string;
|
||||
tags: string[];
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface CommunitySkill {
|
||||
name: string;
|
||||
source: string;
|
||||
repo: string;
|
||||
path: string;
|
||||
description: string;
|
||||
readme_url: string;
|
||||
raw_url: string;
|
||||
}
|
||||
|
||||
interface CommunitySource {
|
||||
repo: string;
|
||||
skills_path: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface SkillsManagementProps {
|
||||
projectPath: string;
|
||||
}
|
||||
|
||||
export default function SkillsManagement({ projectPath }: SkillsManagementProps) {
|
||||
const [selectedSkill, setSelectedSkill] = useState<Skill | null>(null);
|
||||
const [showTemplates, setShowTemplates] = useState(false);
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set(['general', 'rails', 'react', 'testing']));
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
// Community skills state
|
||||
const [templateTab, setTemplateTab] = useState<'local' | 'community'>('local');
|
||||
const [selectedCommunitySource, setSelectedCommunitySource] = useState<string>('all');
|
||||
|
||||
// Create skill form state
|
||||
const [newSkillName, setNewSkillName] = useState('');
|
||||
const [newSkillDescription, setNewSkillDescription] = useState('');
|
||||
const [newSkillContent, setNewSkillContent] = useState('');
|
||||
const [newSkillCategory, setNewSkillCategory] = useState('');
|
||||
const [newSkillTags, setNewSkillTags] = useState('');
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Fetch installed skills
|
||||
const { data: skillsData, isLoading: skillsLoading } = useQuery({
|
||||
queryKey: ['skills', projectPath],
|
||||
queryFn: async () => {
|
||||
const response = await axios.get('/api/skills', {
|
||||
params: { project_path: projectPath, include_content: false }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
|
||||
// Fetch skill templates
|
||||
const { data: templatesData, isLoading: templatesLoading } = useQuery({
|
||||
queryKey: ['skill-templates'],
|
||||
queryFn: async () => {
|
||||
const response = await axios.get('/api/skills/templates');
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
|
||||
// Fetch community sources
|
||||
const { data: communitySourcesData } = useQuery({
|
||||
queryKey: ['community-sources'],
|
||||
queryFn: async () => {
|
||||
const response = await axios.get('/api/skills/community/sources');
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
|
||||
// Fetch community skills
|
||||
const { data: communitySkillsData, isLoading: communityLoading, refetch: refetchCommunity } = useQuery({
|
||||
queryKey: ['community-skills', selectedCommunitySource],
|
||||
queryFn: async () => {
|
||||
const params: { source?: string } = {};
|
||||
if (selectedCommunitySource !== 'all') {
|
||||
params.source = selectedCommunitySource;
|
||||
}
|
||||
const response = await axios.get('/api/skills/community', { params });
|
||||
return response.data;
|
||||
},
|
||||
enabled: showTemplates && templateTab === 'community',
|
||||
});
|
||||
|
||||
// Install template mutation
|
||||
const installTemplateMutation = useMutation({
|
||||
mutationFn: async (templateName: string) => {
|
||||
const response = await axios.post('/api/skills/install', {
|
||||
template_name: templateName,
|
||||
}, {
|
||||
params: { project_path: projectPath }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['skills', projectPath] });
|
||||
setShowTemplates(false);
|
||||
},
|
||||
});
|
||||
|
||||
// Install community skill mutation
|
||||
const installCommunityMutation = useMutation({
|
||||
mutationFn: async (skill: CommunitySkill) => {
|
||||
const response = await axios.post('/api/skills/community/install', {
|
||||
skill_name: skill.name,
|
||||
source: skill.source,
|
||||
}, {
|
||||
params: { project_path: projectPath }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['skills', projectPath] });
|
||||
},
|
||||
});
|
||||
|
||||
// Create skill mutation
|
||||
const createSkillMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const response = await axios.post('/api/skills', {
|
||||
name: newSkillName,
|
||||
description: newSkillDescription,
|
||||
content: newSkillContent,
|
||||
category: newSkillCategory || null,
|
||||
tags: newSkillTags ? newSkillTags.split(',').map(t => t.trim()) : [],
|
||||
}, {
|
||||
params: { project_path: projectPath }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['skills', projectPath] });
|
||||
setShowCreateModal(false);
|
||||
resetCreateForm();
|
||||
},
|
||||
});
|
||||
|
||||
// Delete skill mutation
|
||||
const deleteSkillMutation = useMutation({
|
||||
mutationFn: async (skillName: string) => {
|
||||
await axios.delete(`/api/skills/${skillName}`, {
|
||||
params: { project_path: projectPath }
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['skills', projectPath] });
|
||||
setSelectedSkill(null);
|
||||
},
|
||||
});
|
||||
|
||||
// Get skill details
|
||||
const { data: skillDetails, isLoading: detailsLoading } = useQuery({
|
||||
queryKey: ['skill-details', selectedSkill?.name, selectedSkill?.scope],
|
||||
queryFn: async () => {
|
||||
if (!selectedSkill) return null;
|
||||
const response = await axios.get(`/api/skills/${selectedSkill.scope}/${selectedSkill.name}`, {
|
||||
params: { project_path: projectPath }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
enabled: !!selectedSkill,
|
||||
});
|
||||
|
||||
const resetCreateForm = () => {
|
||||
setNewSkillName('');
|
||||
setNewSkillDescription('');
|
||||
setNewSkillContent('');
|
||||
setNewSkillCategory('');
|
||||
setNewSkillTags('');
|
||||
};
|
||||
|
||||
const toggleCategory = (category: string) => {
|
||||
const newExpanded = new Set(expandedCategories);
|
||||
if (newExpanded.has(category)) {
|
||||
newExpanded.delete(category);
|
||||
} else {
|
||||
newExpanded.add(category);
|
||||
}
|
||||
setExpandedCategories(newExpanded);
|
||||
};
|
||||
|
||||
const globalSkills: Skill[] = skillsData?.global || [];
|
||||
const projectSkills: Skill[] = skillsData?.project || [];
|
||||
const templates: SkillTemplate[] = templatesData?.templates || [];
|
||||
const categories: string[] = templatesData?.categories || [];
|
||||
const communitySources: Record<string, CommunitySource> = communitySourcesData?.sources || {};
|
||||
const communitySkills: CommunitySkill[] = communitySkillsData?.skills || [];
|
||||
|
||||
// Filter templates by search
|
||||
const filteredTemplates = templates.filter(t =>
|
||||
t.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
t.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
t.tags.some(tag => tag.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
);
|
||||
|
||||
// Group templates by category
|
||||
const templatesByCategory = categories.reduce((acc, cat) => {
|
||||
acc[cat] = filteredTemplates.filter(t => t.category === cat);
|
||||
return acc;
|
||||
}, {} as Record<string, SkillTemplate[]>);
|
||||
|
||||
// Filter community skills by search
|
||||
const filteredCommunitySkills = communitySkills.filter(s =>
|
||||
s.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
s.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
// Group community skills by source
|
||||
const communityBySource = filteredCommunitySkills.reduce((acc, skill) => {
|
||||
if (!acc[skill.source]) {
|
||||
acc[skill.source] = [];
|
||||
}
|
||||
acc[skill.source].push(skill);
|
||||
return acc;
|
||||
}, {} as Record<string, CommunitySkill[]>);
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-white flex items-center gap-2">
|
||||
<Sparkles className="w-6 h-6 text-electric-teal" />
|
||||
Skills Management
|
||||
</h2>
|
||||
<p className="text-light-grey text-sm mt-1">
|
||||
Manage Claude Code skills for this project
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setShowTemplates(true)}
|
||||
className="btn-secondary flex items-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Install Template
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
className="btn-primary flex items-center gap-2"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Create Skill
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex gap-6 min-h-0">
|
||||
{/* Skills List */}
|
||||
<div className="w-80 flex flex-col">
|
||||
{/* Global Skills */}
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm font-bold text-electric-teal mb-2 flex items-center gap-2">
|
||||
<Globe className="w-4 h-4" />
|
||||
Global Skills ({globalSkills.length})
|
||||
</h3>
|
||||
<div className="space-y-1">
|
||||
{skillsLoading ? (
|
||||
<div className="text-light-grey text-sm py-2">Loading...</div>
|
||||
) : globalSkills.length === 0 ? (
|
||||
<div className="text-light-grey text-sm py-2">No global skills</div>
|
||||
) : (
|
||||
globalSkills.map((skill) => (
|
||||
<div
|
||||
key={`global-${skill.name}`}
|
||||
onClick={() => setSelectedSkill(skill)}
|
||||
className={`p-3 rounded-lg border cursor-pointer transition-all ${
|
||||
selectedSkill?.name === skill.name && selectedSkill?.scope === 'global'
|
||||
? 'bg-electric-teal/20 border-electric-teal'
|
||||
: 'bg-cool-blue/10 border-electric-teal/20 hover:border-electric-teal/50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium text-white">{skill.name}</div>
|
||||
<span className="text-xs bg-cool-blue/30 text-light-grey px-2 py-0.5 rounded">
|
||||
{skill.source}
|
||||
</span>
|
||||
</div>
|
||||
{skill.description && (
|
||||
<div className="text-xs text-light-grey mt-1 line-clamp-2">
|
||||
{skill.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Project Skills */}
|
||||
<div className="flex-1 min-h-0">
|
||||
<h3 className="text-sm font-bold text-electric-teal mb-2 flex items-center gap-2">
|
||||
<FolderOpen className="w-4 h-4" />
|
||||
Project Skills ({projectSkills.length})
|
||||
</h3>
|
||||
<div className="space-y-1 overflow-y-auto max-h-[400px]">
|
||||
{skillsLoading ? (
|
||||
<div className="text-light-grey text-sm py-2">Loading...</div>
|
||||
) : projectSkills.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<Sparkles className="w-12 h-12 mx-auto mb-2 text-electric-teal/30" />
|
||||
<p className="text-light-grey text-sm">No project skills yet</p>
|
||||
<p className="text-light-grey text-xs mt-1">
|
||||
Install a template or create a custom skill
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
projectSkills.map((skill) => (
|
||||
<div
|
||||
key={`project-${skill.name}`}
|
||||
onClick={() => setSelectedSkill(skill)}
|
||||
className={`p-3 rounded-lg border cursor-pointer transition-all ${
|
||||
selectedSkill?.name === skill.name && selectedSkill?.scope === 'project'
|
||||
? 'bg-electric-teal/20 border-electric-teal'
|
||||
: 'bg-cool-blue/10 border-electric-teal/20 hover:border-electric-teal/50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium text-white">{skill.name}</div>
|
||||
<span className="text-xs bg-blaze-orange/20 text-blaze-orange px-2 py-0.5 rounded">
|
||||
{skill.source}
|
||||
</span>
|
||||
</div>
|
||||
{skill.description && (
|
||||
<div className="text-xs text-light-grey mt-1 line-clamp-2">
|
||||
{skill.description}
|
||||
</div>
|
||||
)}
|
||||
{skill.tags && skill.tags.length > 0 && (
|
||||
<div className="flex gap-1 mt-2 flex-wrap">
|
||||
{skill.tags.slice(0, 3).map(tag => (
|
||||
<span key={tag} className="text-xs bg-cool-blue/20 text-light-grey px-1.5 py-0.5 rounded">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Skill Details */}
|
||||
<div className="flex-1 bg-cool-blue/10 border border-electric-teal/30 rounded-lg p-6 overflow-auto">
|
||||
{selectedSkill ? (
|
||||
detailsLoading ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-light-grey">Loading skill details...</div>
|
||||
</div>
|
||||
) : skillDetails ? (
|
||||
<div>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white">{skillDetails.name}</h3>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className={`text-xs px-2 py-0.5 rounded ${
|
||||
skillDetails.scope === 'global'
|
||||
? 'bg-cool-blue/30 text-light-grey'
|
||||
: 'bg-blaze-orange/20 text-blaze-orange'
|
||||
}`}>
|
||||
{skillDetails.scope}
|
||||
</span>
|
||||
<span className="text-xs bg-electric-teal/20 text-electric-teal px-2 py-0.5 rounded">
|
||||
{skillDetails.source}
|
||||
</span>
|
||||
{skillDetails.category && (
|
||||
<span className="text-xs bg-cool-blue/20 text-light-grey px-2 py-0.5 rounded">
|
||||
{skillDetails.category}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{skillDetails.scope === 'project' && (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (confirm(`Delete skill "${skillDetails.name}"?`)) {
|
||||
deleteSkillMutation.mutate(skillDetails.name);
|
||||
}
|
||||
}}
|
||||
className="p-2 rounded hover:bg-blaze-orange/20 transition-colors"
|
||||
title="Delete skill"
|
||||
>
|
||||
<Trash2 className="w-5 h-5 text-blaze-orange" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{skillDetails.description && (
|
||||
<p className="text-light-grey mb-4">{skillDetails.description}</p>
|
||||
)}
|
||||
|
||||
{skillDetails.tags && skillDetails.tags.length > 0 && (
|
||||
<div className="flex gap-2 mb-4 flex-wrap">
|
||||
<Tag className="w-4 h-4 text-light-grey" />
|
||||
{skillDetails.tags.map((tag: string) => (
|
||||
<span key={tag} className="text-xs bg-cool-blue/20 text-light-grey px-2 py-1 rounded">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border-t border-electric-teal/20 pt-4">
|
||||
<h4 className="text-sm font-bold text-electric-teal mb-2">Content</h4>
|
||||
<pre className="bg-deep-night/50 border border-electric-teal/20 rounded-lg p-4 overflow-auto max-h-[400px] text-sm text-light-grey whitespace-pre-wrap">
|
||||
{skillDetails.content || 'No content available'}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 text-xs text-light-grey mt-4">
|
||||
<span>Created: {new Date(skillDetails.created).toLocaleDateString()}</span>
|
||||
<span>Modified: {new Date(skillDetails.modified).toLocaleDateString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-light-grey">Failed to load skill details</div>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center">
|
||||
<Eye className="w-16 h-16 text-electric-teal/30 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-bold text-light-grey mb-2">Select a Skill</h3>
|
||||
<p className="text-light-grey text-sm">
|
||||
Click on a skill to view its details and content
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Install Template Modal */}
|
||||
<AnimatePresence>
|
||||
{showTemplates && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4"
|
||||
onClick={() => setShowTemplates(false)}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.9, opacity: 0 }}
|
||||
className="bg-deep-night border border-electric-teal/30 rounded-lg p-6 max-w-2xl w-full max-h-[80vh] overflow-hidden flex flex-col"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-2xl font-bold text-white">Install Skills</h2>
|
||||
<button
|
||||
onClick={() => setShowTemplates(false)}
|
||||
className="p-1 hover:bg-electric-teal/20 rounded"
|
||||
>
|
||||
<X className="w-5 h-5 text-light-grey" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab Selector */}
|
||||
<div className="flex gap-2 mb-4">
|
||||
<button
|
||||
onClick={() => setTemplateTab('local')}
|
||||
className={`flex-1 py-2 px-4 rounded-lg text-sm font-medium transition-colors flex items-center justify-center gap-2 ${
|
||||
templateTab === 'local'
|
||||
? 'bg-electric-teal text-deep-night'
|
||||
: 'bg-cool-blue/20 text-light-grey hover:bg-cool-blue/30'
|
||||
}`}
|
||||
>
|
||||
<FolderOpen className="w-4 h-4" />
|
||||
Local Templates
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTemplateTab('community')}
|
||||
className={`flex-1 py-2 px-4 rounded-lg text-sm font-medium transition-colors flex items-center justify-center gap-2 ${
|
||||
templateTab === 'community'
|
||||
? 'bg-electric-teal text-deep-night'
|
||||
: 'bg-cool-blue/20 text-light-grey hover:bg-cool-blue/30'
|
||||
}`}
|
||||
>
|
||||
<Github className="w-4 h-4" />
|
||||
Community Skills
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Search and Source Filter */}
|
||||
<div className="flex gap-2 mb-4">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-light-grey" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={templateTab === 'local' ? 'Search templates...' : 'Search community skills...'}
|
||||
className="input w-full pl-10"
|
||||
/>
|
||||
</div>
|
||||
{templateTab === 'community' && (
|
||||
<>
|
||||
<select
|
||||
value={selectedCommunitySource}
|
||||
onChange={(e) => setSelectedCommunitySource(e.target.value)}
|
||||
className="input w-40"
|
||||
>
|
||||
<option value="all">All Sources</option>
|
||||
{Object.entries(communitySources).map(([key, source]) => (
|
||||
<option key={key} value={key}>{source.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
onClick={() => refetchCommunity()}
|
||||
className="btn-secondary p-2"
|
||||
title="Refresh community skills"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${communityLoading ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content based on tab */}
|
||||
<div className="flex-1 overflow-y-auto space-y-2">
|
||||
{templateTab === 'local' ? (
|
||||
// Local Templates
|
||||
templatesLoading ? (
|
||||
<div className="text-center py-8 text-light-grey">Loading templates...</div>
|
||||
) : (
|
||||
categories.map((category) => {
|
||||
const categoryTemplates = templatesByCategory[category] || [];
|
||||
if (categoryTemplates.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div key={category} className="border border-electric-teal/20 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => toggleCategory(category)}
|
||||
className="w-full px-4 py-3 flex items-center justify-between bg-cool-blue/10 hover:bg-cool-blue/20 transition-colors"
|
||||
>
|
||||
<span className="font-semibold text-electric-teal capitalize">
|
||||
{category} ({categoryTemplates.length})
|
||||
</span>
|
||||
{expandedCategories.has(category) ? (
|
||||
<ChevronDown className="w-4 h-4 text-light-grey" />
|
||||
) : (
|
||||
<ChevronRight className="w-4 h-4 text-light-grey" />
|
||||
)}
|
||||
</button>
|
||||
{expandedCategories.has(category) && (
|
||||
<div className="divide-y divide-electric-teal/10">
|
||||
{categoryTemplates.map((template) => {
|
||||
const isInstalled = projectSkills.some(s => s.name === template.name);
|
||||
return (
|
||||
<div
|
||||
key={template.name}
|
||||
className="p-4 hover:bg-cool-blue/10 transition-colors"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-white">{template.name}</div>
|
||||
<div className="text-sm text-light-grey mt-1">
|
||||
{template.description}
|
||||
</div>
|
||||
<div className="flex gap-1 mt-2 flex-wrap">
|
||||
{template.tags.map(tag => (
|
||||
<span key={tag} className="text-xs bg-cool-blue/20 text-light-grey px-1.5 py-0.5 rounded">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => installTemplateMutation.mutate(template.name)}
|
||||
disabled={isInstalled || installTemplateMutation.isPending}
|
||||
className={`ml-4 px-3 py-1.5 rounded text-sm font-medium transition-colors ${
|
||||
isInstalled
|
||||
? 'bg-electric-teal/20 text-electric-teal cursor-not-allowed'
|
||||
: 'bg-electric-teal text-deep-night hover:bg-electric-teal/80'
|
||||
}`}
|
||||
>
|
||||
{isInstalled ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<Check className="w-4 h-4" />
|
||||
Installed
|
||||
</span>
|
||||
) : installTemplateMutation.isPending ? (
|
||||
'Installing...'
|
||||
) : (
|
||||
'Install'
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)
|
||||
) : (
|
||||
// Community Skills
|
||||
communityLoading ? (
|
||||
<div className="text-center py-8">
|
||||
<RefreshCw className="w-8 h-8 text-electric-teal animate-spin mx-auto mb-2" />
|
||||
<div className="text-light-grey">Fetching community skills from GitHub...</div>
|
||||
<div className="text-light-grey text-sm mt-1">This may take a moment</div>
|
||||
</div>
|
||||
) : filteredCommunitySkills.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<Github className="w-12 h-12 text-light-grey/30 mx-auto mb-2" />
|
||||
<div className="text-light-grey">No community skills found</div>
|
||||
<div className="text-light-grey text-sm mt-1">Try a different search or source</div>
|
||||
</div>
|
||||
) : (
|
||||
Object.entries(communityBySource).map(([source, skills]) => {
|
||||
const sourceInfo = communitySources[source];
|
||||
return (
|
||||
<div key={source} className="border border-electric-teal/20 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => toggleCategory(source)}
|
||||
className="w-full px-4 py-3 flex items-center justify-between bg-cool-blue/10 hover:bg-cool-blue/20 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Github className="w-4 h-4 text-light-grey" />
|
||||
<span className="font-semibold text-electric-teal">
|
||||
{sourceInfo?.name || source} ({skills.length})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<a
|
||||
href={`https://github.com/${sourceInfo?.repo || source}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="text-light-grey hover:text-electric-teal"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
{expandedCategories.has(source) ? (
|
||||
<ChevronDown className="w-4 h-4 text-light-grey" />
|
||||
) : (
|
||||
<ChevronRight className="w-4 h-4 text-light-grey" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
{expandedCategories.has(source) && (
|
||||
<div className="divide-y divide-electric-teal/10">
|
||||
{skills.map((skill) => {
|
||||
const isInstalled = projectSkills.some(s => s.name === skill.name);
|
||||
return (
|
||||
<div
|
||||
key={`${skill.source}-${skill.name}`}
|
||||
className="p-4 hover:bg-cool-blue/10 transition-colors"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="font-medium text-white">{skill.name}</div>
|
||||
<a
|
||||
href={skill.readme_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-light-grey hover:text-electric-teal"
|
||||
title="View on GitHub"
|
||||
>
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="text-sm text-light-grey mt-1">
|
||||
{skill.description || 'No description available'}
|
||||
</div>
|
||||
<div className="text-xs text-light-grey/60 mt-1">
|
||||
{skill.repo}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => installCommunityMutation.mutate(skill)}
|
||||
disabled={isInstalled || installCommunityMutation.isPending}
|
||||
className={`ml-4 px-3 py-1.5 rounded text-sm font-medium transition-colors ${
|
||||
isInstalled
|
||||
? 'bg-electric-teal/20 text-electric-teal cursor-not-allowed'
|
||||
: 'bg-electric-teal text-deep-night hover:bg-electric-teal/80'
|
||||
}`}
|
||||
>
|
||||
{isInstalled ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<Check className="w-4 h-4" />
|
||||
Installed
|
||||
</span>
|
||||
) : installCommunityMutation.isPending ? (
|
||||
'Installing...'
|
||||
) : (
|
||||
'Install'
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Create Skill Modal */}
|
||||
<AnimatePresence>
|
||||
{showCreateModal && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4"
|
||||
onClick={() => setShowCreateModal(false)}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.9, opacity: 0 }}
|
||||
className="bg-deep-night border border-electric-teal/30 rounded-lg p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-white mb-4">Create Custom Skill</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm text-light-grey mb-2">Skill Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newSkillName}
|
||||
onChange={(e) => setNewSkillName(e.target.value)}
|
||||
placeholder="my-skill"
|
||||
className="input w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-light-grey mb-2">Description *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newSkillDescription}
|
||||
onChange={(e) => setNewSkillDescription(e.target.value)}
|
||||
placeholder="What this skill does..."
|
||||
className="input w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-light-grey mb-2">Category (optional)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newSkillCategory}
|
||||
onChange={(e) => setNewSkillCategory(e.target.value)}
|
||||
placeholder="e.g., backend, frontend, testing"
|
||||
className="input w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-light-grey mb-2">Tags (comma-separated)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newSkillTags}
|
||||
onChange={(e) => setNewSkillTags(e.target.value)}
|
||||
placeholder="ruby, rails, api"
|
||||
className="input w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-light-grey mb-2">Content (Markdown) *</label>
|
||||
<textarea
|
||||
value={newSkillContent}
|
||||
onChange={(e) => setNewSkillContent(e.target.value)}
|
||||
placeholder="# My Skill Describe what this skill does and how to use it..."
|
||||
className="input w-full h-64 font-mono text-sm resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowCreateModal(false);
|
||||
resetCreateForm();
|
||||
}}
|
||||
className="btn-secondary flex-1"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => createSkillMutation.mutate()}
|
||||
disabled={!newSkillName.trim() || !newSkillDescription.trim() || !newSkillContent.trim() || createSkillMutation.isPending}
|
||||
className="btn-primary flex-1"
|
||||
>
|
||||
{createSkillMutation.isPending ? 'Creating...' : 'Create Skill'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Home, Settings, Database, MessageSquare, Plus, Trash2, FolderOpen, Zap, Edit, Activity, Trello, FileSearch } from 'lucide-react';
|
||||
import { Home, Settings, Database, MessageSquare, Plus, Trash2, FolderOpen, Zap, Edit, Activity, Trello, FileSearch, Sparkles } from 'lucide-react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import axios from 'axios';
|
||||
|
|
@ -11,6 +11,7 @@ import DirectoryPicker from '../components/DirectoryPicker';
|
|||
import ServiceDashboard from '../components/ServiceDashboard';
|
||||
import KanbanBoard from '../components/KanbanBoard';
|
||||
import JobsDashboard from '../components/JobsDashboard';
|
||||
import SkillsManagement from '../components/SkillsManagement';
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8051';
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ interface ProjectMCP {
|
|||
export default function MainApp() {
|
||||
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
|
||||
const [selectedMCP, setSelectedMCP] = useState<string | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'mcps' | 'chat' | 'services' | 'kanban' | 'jobs'>('overview');
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'mcps' | 'chat' | 'services' | 'kanban' | 'jobs' | 'skills'>('overview');
|
||||
const [showCreateProject, setShowCreateProject] = useState(false);
|
||||
const [showProjectSetup, setShowProjectSetup] = useState(false);
|
||||
const [newProjectName, setNewProjectName] = useState('');
|
||||
|
|
@ -358,6 +359,17 @@ export default function MainApp() {
|
|||
<FileSearch className="w-4 h-4 inline mr-2" />
|
||||
Jobs
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('skills')}
|
||||
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
|
||||
activeTab === 'skills'
|
||||
? 'bg-electric-teal text-deep-night'
|
||||
: 'text-light-grey hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<Sparkles className="w-4 h-4 inline mr-2" />
|
||||
Skills
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
|
|
@ -452,6 +464,8 @@ export default function MainApp() {
|
|||
<ServiceDashboard />
|
||||
) : activeTab === 'jobs' ? (
|
||||
<JobsDashboard />
|
||||
) : activeTab === 'skills' ? (
|
||||
<SkillsManagement projectPath={selectedProject.path} />
|
||||
) : (
|
||||
selectedMCP ? (
|
||||
<ChatInterface
|
||||
|
|
|
|||
|
|
@ -70,6 +70,14 @@ async def api_delete(path: str) -> dict:
|
|||
return response.json()
|
||||
|
||||
|
||||
async def api_put(path: str, data: dict = None) -> dict:
|
||||
"""Make PUT request to API."""
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.put(api_url(path), json=data or {}, timeout=30.0)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
@server.list_tools()
|
||||
async def list_tools() -> list[Tool]:
|
||||
"""List available Claude OS tools.
|
||||
|
|
@ -262,6 +270,134 @@ async def list_tools() -> list[Tool]:
|
|||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
),
|
||||
|
||||
# Session Tools
|
||||
Tool(
|
||||
name="list_sessions",
|
||||
description="List Claude Code session files for a project. Sessions are stored in ~/.claude/projects/{encoded-path}/ and contain conversation history.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project_path": {"type": "string", "description": "Absolute path to project directory (e.g., /Users/x/Projects/myapp)"},
|
||||
"limit": {"type": "integer", "default": 50, "description": "Maximum sessions to return (default: 50)"}
|
||||
},
|
||||
"required": ["project_path"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="extract_session_insights",
|
||||
description="Extract insights from a Claude Code session file using LLM. Analyzes conversation to find decisions, patterns, solutions, and blockers. Optionally saves insights to a knowledge base.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session_path": {"type": "string", "description": "Absolute path to .jsonl session file"},
|
||||
"kb_name": {"type": "string", "description": "Knowledge base name to save insights (optional)"},
|
||||
"auto_save": {"type": "boolean", "default": False, "description": "Auto-save all insights without prompting"},
|
||||
"insight_types": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "enum": ["decision", "pattern", "solution", "blocker"]},
|
||||
"default": ["decision", "pattern", "solution", "blocker"],
|
||||
"description": "Types of insights to extract"
|
||||
},
|
||||
"min_confidence": {"type": "number", "default": 0.7, "description": "Minimum confidence threshold (0.0-1.0)"}
|
||||
},
|
||||
"required": ["session_path"]
|
||||
}
|
||||
),
|
||||
|
||||
# Skill Management Tools
|
||||
Tool(
|
||||
name="list_skills",
|
||||
description="List all skills (global and project-level). Global skills are in ~/.claude/skills/, project skills are in {project}/.claude/skills/.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project_path": {"type": "string", "description": "Project path to include project-level skills (optional)"},
|
||||
"include_content": {"type": "boolean", "default": False, "description": "Include full skill content"}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="list_skill_templates",
|
||||
description="List available skill templates that can be installed to projects. Templates are organized by category.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {"type": "string", "description": "Filter by category (optional)"}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="get_skill",
|
||||
description="Get skill details including full content.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Skill name"},
|
||||
"scope": {"type": "string", "enum": ["global", "project"], "description": "Where the skill is located"},
|
||||
"project_path": {"type": "string", "description": "Project path (required if scope is 'project')"}
|
||||
},
|
||||
"required": ["name", "scope"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="install_skill_template",
|
||||
description="Install a skill template to a project. Copies the template to {project}/.claude/skills/.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"template_name": {"type": "string", "description": "Name of template to install"},
|
||||
"project_path": {"type": "string", "description": "Project path to install to"},
|
||||
"custom_name": {"type": "string", "description": "Custom name for installed skill (optional)"}
|
||||
},
|
||||
"required": ["template_name", "project_path"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="create_skill",
|
||||
description="Create a custom skill for a project.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Skill name"},
|
||||
"description": {"type": "string", "description": "Skill description"},
|
||||
"content": {"type": "string", "description": "Skill content (markdown)"},
|
||||
"project_path": {"type": "string", "description": "Project path"},
|
||||
"category": {"type": "string", "description": "Category (optional)"},
|
||||
"tags": {"type": "array", "items": {"type": "string"}, "description": "Tags (optional)"}
|
||||
},
|
||||
"required": ["name", "description", "content", "project_path"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="update_skill",
|
||||
description="Update an existing project skill.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Skill name"},
|
||||
"project_path": {"type": "string", "description": "Project path"},
|
||||
"content": {"type": "string", "description": "New content (optional)"},
|
||||
"description": {"type": "string", "description": "New description (optional)"},
|
||||
"tags": {"type": "array", "items": {"type": "string"}, "description": "New tags (optional)"}
|
||||
},
|
||||
"required": ["name", "project_path"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="delete_skill",
|
||||
description="Delete a project skill. Cannot delete global skills.",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Skill name to delete"},
|
||||
"project_path": {"type": "string", "description": "Project path"}
|
||||
},
|
||||
"required": ["name", "project_path"]
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -366,6 +502,69 @@ async def _execute_tool(name: str, args: dict[str, Any]) -> dict:
|
|||
except Exception as e:
|
||||
return {"status": "unhealthy", "error": str(e)}
|
||||
|
||||
# Session Tools
|
||||
elif name == "list_sessions":
|
||||
return await api_get(f"/api/sessions/list?project_path={args['project_path']}&limit={args.get('limit', 50)}")
|
||||
|
||||
elif name == "extract_session_insights":
|
||||
return await api_post("/api/sessions/extract", {
|
||||
"session_path": args["session_path"],
|
||||
"kb_name": args.get("kb_name"),
|
||||
"auto_save": args.get("auto_save", False),
|
||||
"insight_types": args.get("insight_types"),
|
||||
"min_confidence": args.get("min_confidence", 0.7)
|
||||
})
|
||||
|
||||
# Skill Management Tools
|
||||
elif name == "list_skills":
|
||||
project_path = args.get("project_path", "")
|
||||
include_content = args.get("include_content", False)
|
||||
url = f"/api/skills?include_content={include_content}"
|
||||
if project_path:
|
||||
url += f"&project_path={project_path}"
|
||||
return await api_get(url)
|
||||
|
||||
elif name == "list_skill_templates":
|
||||
category = args.get("category")
|
||||
url = "/api/skills/templates"
|
||||
if category:
|
||||
url += f"?category={category}"
|
||||
return await api_get(url)
|
||||
|
||||
elif name == "get_skill":
|
||||
scope = args["scope"]
|
||||
name_param = args["name"]
|
||||
project_path = args.get("project_path", "")
|
||||
url = f"/api/skills/{scope}/{name_param}"
|
||||
if project_path:
|
||||
url += f"?project_path={project_path}"
|
||||
return await api_get(url)
|
||||
|
||||
elif name == "install_skill_template":
|
||||
return await api_post(f"/api/skills/install?project_path={args['project_path']}", {
|
||||
"template_name": args["template_name"],
|
||||
"custom_name": args.get("custom_name")
|
||||
})
|
||||
|
||||
elif name == "create_skill":
|
||||
return await api_post(f"/api/skills?project_path={args['project_path']}", {
|
||||
"name": args["name"],
|
||||
"description": args["description"],
|
||||
"content": args["content"],
|
||||
"category": args.get("category"),
|
||||
"tags": args.get("tags")
|
||||
})
|
||||
|
||||
elif name == "update_skill":
|
||||
return await api_put(f"/api/skills/{args['name']}?project_path={args['project_path']}", {
|
||||
"content": args.get("content"),
|
||||
"description": args.get("description"),
|
||||
"tags": args.get("tags")
|
||||
})
|
||||
|
||||
elif name == "delete_skill":
|
||||
return await api_delete(f"/api/skills/{args['name']}?project_path={args['project_path']}")
|
||||
|
||||
else:
|
||||
return {"error": f"Unknown tool: {name}"}
|
||||
|
||||
|
|
|
|||
|
|
@ -2018,6 +2018,543 @@ async def api_unarchive_spec(spec_id: int):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SESSION EXTRACTION ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
from app.core.session_parser import SessionParser, list_session_files
|
||||
from app.core.insight_extractor import InsightExtractor
|
||||
|
||||
|
||||
class SessionListRequest(BaseModel):
|
||||
"""Request model for listing sessions."""
|
||||
project_path: str
|
||||
limit: Optional[int] = 50
|
||||
|
||||
|
||||
class SessionExtractRequest(BaseModel):
|
||||
"""Request model for extracting insights from a session."""
|
||||
session_path: str
|
||||
kb_name: Optional[str] = None
|
||||
auto_save: bool = False
|
||||
insight_types: Optional[List[str]] = None
|
||||
min_confidence: float = 0.7
|
||||
|
||||
|
||||
@app.get("/api/sessions/list")
|
||||
async def api_list_sessions(project_path: str, limit: int = 50):
|
||||
"""
|
||||
REST API: List Claude Code session files for a project.
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to project directory
|
||||
limit: Maximum number of sessions to return (default: 50)
|
||||
|
||||
Returns:
|
||||
List of session file metadata
|
||||
"""
|
||||
try:
|
||||
sessions = list_session_files(project_path, limit)
|
||||
|
||||
# Calculate total size
|
||||
total_size_bytes = sum(s["size_bytes"] for s in sessions)
|
||||
total_size_mb = total_size_bytes / (1024 * 1024)
|
||||
|
||||
return {
|
||||
"project_path": project_path,
|
||||
"sessions": sessions,
|
||||
"total_sessions": len(sessions),
|
||||
"total_size_mb": round(total_size_mb, 2)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list sessions for {project_path}: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/sessions/extract")
|
||||
async def api_extract_session_insights(request: SessionExtractRequest):
|
||||
"""
|
||||
REST API: Extract insights from a Claude Code session file.
|
||||
|
||||
Steps:
|
||||
1. Parse session .jsonl file
|
||||
2. Build conversation summary
|
||||
3. Send to LLM for insight extraction
|
||||
4. Optionally save insights to knowledge base
|
||||
|
||||
Args:
|
||||
request: SessionExtractRequest with session_path, kb_name, etc.
|
||||
|
||||
Returns:
|
||||
Extracted insights with metadata
|
||||
"""
|
||||
try:
|
||||
# Parse session file
|
||||
parser = SessionParser(request.session_path)
|
||||
session_data = parser.parse()
|
||||
|
||||
# Build summary for extraction
|
||||
summary = parser.get_summary_for_extraction()
|
||||
|
||||
# Extract insights using LLM
|
||||
extractor = InsightExtractor()
|
||||
insights = await extractor.extract(
|
||||
session_summary=summary,
|
||||
insight_types=request.insight_types
|
||||
)
|
||||
|
||||
# Filter by confidence
|
||||
filtered_insights = extractor.filter_by_confidence(
|
||||
insights,
|
||||
min_confidence=request.min_confidence
|
||||
)
|
||||
|
||||
# Optionally save to knowledge base
|
||||
saved_count = 0
|
||||
if request.auto_save and request.kb_name:
|
||||
db_manager = get_sqlite_manager()
|
||||
if not db_manager.collection_exists(request.kb_name):
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Knowledge base '{request.kb_name}' not found"
|
||||
)
|
||||
|
||||
for insight in filtered_insights:
|
||||
# Format as markdown
|
||||
markdown = extractor.format_for_save(insight, session_data.session_id)
|
||||
|
||||
# Generate filename
|
||||
filename = f"session-{session_data.session_id}-{insight.type}-{saved_count}.md"
|
||||
|
||||
# Save to KB
|
||||
db_manager.upsert_document(
|
||||
collection_name=request.kb_name,
|
||||
doc_id=filename,
|
||||
content=markdown,
|
||||
metadata={
|
||||
"title": insight.title,
|
||||
"type": "session_insight",
|
||||
"insight_type": insight.type,
|
||||
"session_id": session_data.session_id,
|
||||
"confidence": insight.confidence
|
||||
}
|
||||
)
|
||||
saved_count += 1
|
||||
|
||||
# Calculate session duration
|
||||
duration_minutes = None
|
||||
if session_data.start_time and session_data.end_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
start = datetime.fromisoformat(session_data.start_time.replace('Z', '+00:00'))
|
||||
end = datetime.fromisoformat(session_data.end_time.replace('Z', '+00:00'))
|
||||
duration_minutes = (end - start).total_seconds() / 60
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Format response
|
||||
insights_response = []
|
||||
for insight in filtered_insights:
|
||||
insights_response.append({
|
||||
"type": insight.type,
|
||||
"title": insight.title,
|
||||
"content": insight.content,
|
||||
"confidence": insight.confidence,
|
||||
"saved": request.auto_save and request.kb_name is not None
|
||||
})
|
||||
|
||||
return {
|
||||
"session_id": session_data.session_id,
|
||||
"session_path": request.session_path,
|
||||
"session_date": session_data.start_time,
|
||||
"duration_minutes": duration_minutes,
|
||||
"message_count": len(session_data.messages),
|
||||
"insights": insights_response,
|
||||
"insights_saved": saved_count,
|
||||
"kb_name": request.kb_name if request.auto_save else None
|
||||
}
|
||||
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"Session file not found: {e}")
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to extract insights from session: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SKILLS MANAGEMENT ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
from app.core.skill_manager import (
|
||||
SkillManager,
|
||||
list_skills,
|
||||
list_skill_templates,
|
||||
list_community_skills,
|
||||
install_community_skill,
|
||||
get_community_manager,
|
||||
COMMUNITY_SOURCES
|
||||
)
|
||||
|
||||
|
||||
class SkillCreateRequest(BaseModel):
|
||||
"""Request model for creating a custom skill."""
|
||||
name: str
|
||||
description: str
|
||||
content: str
|
||||
category: Optional[str] = None
|
||||
tags: Optional[List[str]] = None
|
||||
|
||||
|
||||
class SkillUpdateRequest(BaseModel):
|
||||
"""Request model for updating a skill."""
|
||||
content: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
tags: Optional[List[str]] = None
|
||||
|
||||
|
||||
class SkillInstallRequest(BaseModel):
|
||||
"""Request model for installing a template."""
|
||||
template_name: str
|
||||
custom_name: Optional[str] = None
|
||||
|
||||
|
||||
@app.get("/api/skills")
|
||||
async def api_list_skills(project_path: Optional[str] = None, include_content: bool = False):
|
||||
"""
|
||||
REST API: List all skills (global and project).
|
||||
|
||||
Args:
|
||||
project_path: Optional project path for project-level skills
|
||||
include_content: Whether to include skill content (default: False)
|
||||
|
||||
Returns:
|
||||
Dict with global and project skill lists
|
||||
"""
|
||||
try:
|
||||
manager = SkillManager(project_path)
|
||||
skills = manager.list_all_skills(include_content=include_content)
|
||||
|
||||
return {
|
||||
"global": [s.to_dict() for s in skills["global"]],
|
||||
"project": [s.to_dict() for s in skills["project"]],
|
||||
"total_global": len(skills["global"]),
|
||||
"total_project": len(skills["project"])
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list skills: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/skills/templates")
|
||||
async def api_list_skill_templates(category: Optional[str] = None):
|
||||
"""
|
||||
REST API: List available skill templates.
|
||||
|
||||
Args:
|
||||
category: Optional category filter
|
||||
|
||||
Returns:
|
||||
List of skill templates with categories
|
||||
"""
|
||||
try:
|
||||
manager = SkillManager()
|
||||
templates = manager.list_templates(category)
|
||||
categories = manager.get_template_categories()
|
||||
|
||||
return {
|
||||
"templates": [t.to_dict() for t in templates],
|
||||
"categories": categories,
|
||||
"total": len(templates)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list skill templates: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/skills/{scope}/{name}")
|
||||
async def api_get_skill(scope: str, name: str, project_path: Optional[str] = None):
|
||||
"""
|
||||
REST API: Get skill details including content.
|
||||
|
||||
Args:
|
||||
scope: "global" or "project"
|
||||
name: Skill name
|
||||
project_path: Required if scope is "project"
|
||||
|
||||
Returns:
|
||||
Skill details with content
|
||||
"""
|
||||
if scope not in ["global", "project"]:
|
||||
raise HTTPException(status_code=400, detail="Scope must be 'global' or 'project'")
|
||||
|
||||
if scope == "project" and not project_path:
|
||||
raise HTTPException(status_code=400, detail="project_path required for project skills")
|
||||
|
||||
try:
|
||||
manager = SkillManager(project_path)
|
||||
skill = manager.get_skill(name, scope)
|
||||
|
||||
if not skill:
|
||||
raise HTTPException(status_code=404, detail=f"Skill not found: {name}")
|
||||
|
||||
return skill.to_dict()
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get skill {name}: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/skills/install")
|
||||
async def api_install_skill_template(request: SkillInstallRequest, project_path: str):
|
||||
"""
|
||||
REST API: Install a skill template to a project.
|
||||
|
||||
Args:
|
||||
request: SkillInstallRequest with template_name and optional custom_name
|
||||
project_path: Project path to install to
|
||||
|
||||
Returns:
|
||||
Installed skill details
|
||||
"""
|
||||
try:
|
||||
manager = SkillManager(project_path)
|
||||
skill = manager.install_template(
|
||||
template_name=request.template_name,
|
||||
custom_name=request.custom_name
|
||||
)
|
||||
|
||||
logger.info(f"Installed skill template {request.template_name} to {project_path}")
|
||||
|
||||
return {
|
||||
"message": f"Installed template '{request.template_name}' as '{skill.name}'",
|
||||
"skill": skill.to_dict()
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
except FileExistsError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to install skill template: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/skills")
|
||||
async def api_create_skill(request: SkillCreateRequest, project_path: str):
|
||||
"""
|
||||
REST API: Create a custom skill for a project.
|
||||
|
||||
Args:
|
||||
request: SkillCreateRequest with name, description, content, etc.
|
||||
project_path: Project path to create skill in
|
||||
|
||||
Returns:
|
||||
Created skill details
|
||||
"""
|
||||
try:
|
||||
manager = SkillManager(project_path)
|
||||
skill = manager.create_skill(
|
||||
name=request.name,
|
||||
description=request.description,
|
||||
content=request.content,
|
||||
category=request.category,
|
||||
tags=request.tags
|
||||
)
|
||||
|
||||
logger.info(f"Created custom skill '{request.name}' in {project_path}")
|
||||
|
||||
return {
|
||||
"message": f"Created skill '{request.name}'",
|
||||
"skill": skill.to_dict()
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except FileExistsError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create skill: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.put("/api/skills/{name}")
|
||||
async def api_update_skill(name: str, request: SkillUpdateRequest, project_path: str):
|
||||
"""
|
||||
REST API: Update an existing project skill.
|
||||
|
||||
Args:
|
||||
name: Skill name
|
||||
request: SkillUpdateRequest with content, description, tags
|
||||
project_path: Project path
|
||||
|
||||
Returns:
|
||||
Updated skill details
|
||||
"""
|
||||
try:
|
||||
manager = SkillManager(project_path)
|
||||
skill = manager.update_skill(
|
||||
name=name,
|
||||
content=request.content,
|
||||
description=request.description,
|
||||
tags=request.tags
|
||||
)
|
||||
|
||||
logger.info(f"Updated skill '{name}' in {project_path}")
|
||||
|
||||
return {
|
||||
"message": f"Updated skill '{name}'",
|
||||
"skill": skill.to_dict()
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except FileNotFoundError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update skill: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.delete("/api/skills/{name}")
|
||||
async def api_delete_skill(name: str, project_path: str):
|
||||
"""
|
||||
REST API: Delete a project skill.
|
||||
|
||||
Args:
|
||||
name: Skill name
|
||||
project_path: Project path
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
manager = SkillManager(project_path)
|
||||
manager.delete_skill(name)
|
||||
|
||||
logger.info(f"Deleted skill '{name}' from {project_path}")
|
||||
|
||||
return {
|
||||
"message": f"Deleted skill '{name}'",
|
||||
"name": name
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except FileNotFoundError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete skill: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# COMMUNITY SKILLS ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
class CommunitySkillInstallRequest(BaseModel):
|
||||
"""Request model for installing a community skill."""
|
||||
source: str # "anthropic", "superpowers"
|
||||
skill_name: str
|
||||
custom_name: Optional[str] = None
|
||||
|
||||
|
||||
@app.get("/api/skills/community/sources")
|
||||
async def api_list_community_sources():
|
||||
"""
|
||||
REST API: List available community skill sources.
|
||||
|
||||
Returns:
|
||||
Dict of available sources with metadata
|
||||
"""
|
||||
return {
|
||||
"sources": {
|
||||
key: {
|
||||
"name": config["name"],
|
||||
"description": config["description"],
|
||||
"repo": config["repo"]
|
||||
}
|
||||
for key, config in COMMUNITY_SOURCES.items()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/skills/community")
|
||||
async def api_list_community_skills(source: Optional[str] = None):
|
||||
"""
|
||||
REST API: List skills from community repositories.
|
||||
|
||||
Args:
|
||||
source: Optional source filter ("anthropic", "superpowers")
|
||||
|
||||
Returns:
|
||||
List of community skills grouped by source
|
||||
"""
|
||||
try:
|
||||
skills = await list_community_skills(source)
|
||||
|
||||
# Group by source
|
||||
by_source = {}
|
||||
for skill in skills:
|
||||
src = skill["source"]
|
||||
if src not in by_source:
|
||||
by_source[src] = {
|
||||
"name": COMMUNITY_SOURCES.get(src, {}).get("name", src),
|
||||
"skills": []
|
||||
}
|
||||
by_source[src]["skills"].append(skill)
|
||||
|
||||
return {
|
||||
"skills": skills, # Flat list for easy consumption
|
||||
"sources": by_source, # Grouped by source
|
||||
"total": len(skills)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list community skills: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/skills/community/install")
|
||||
async def api_install_community_skill(
|
||||
request: CommunitySkillInstallRequest,
|
||||
project_path: str
|
||||
):
|
||||
"""
|
||||
REST API: Install a community skill to a project.
|
||||
|
||||
Fetches the skill from GitHub and installs it to the project's
|
||||
.claude/skills/ directory.
|
||||
|
||||
Args:
|
||||
request: CommunitySkillInstallRequest with source, skill_name
|
||||
project_path: Project path to install to
|
||||
|
||||
Returns:
|
||||
Installed skill details
|
||||
"""
|
||||
try:
|
||||
skill = await install_community_skill(
|
||||
source=request.source,
|
||||
skill_name=request.skill_name,
|
||||
project_path=project_path,
|
||||
custom_name=request.custom_name
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Installed community skill {request.skill_name} "
|
||||
f"from {request.source} to {project_path}"
|
||||
)
|
||||
|
||||
return {
|
||||
"message": f"Installed '{request.skill_name}' from {request.source}",
|
||||
"skill": skill
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
except FileExistsError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to install community skill: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# OLLAMA ENDPOINTS
|
||||
# ============================================================================
|
||||
|
|
|
|||
|
|
@ -203,6 +203,107 @@ I found 3 things worth saving:
|
|||
[y] Save all [n] Save none [s] Select individually
|
||||
```
|
||||
|
||||
**Phase 2.5: Built-in Session History Extraction (Optional)**
|
||||
|
||||
After smart save prompts, offer to extract insights from the full conversation history:
|
||||
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
📚 EXTRACT FROM SESSION HISTORY?
|
||||
═══════════════════════════════════════
|
||||
|
||||
Claude Code recorded our full conversation in its built-in session file.
|
||||
I can analyze it to extract additional insights we might have missed.
|
||||
|
||||
This captures things we discussed but didn't explicitly save:
|
||||
• Decisions made during debugging
|
||||
• Patterns discovered while exploring code
|
||||
• Solutions to errors we encountered
|
||||
• Blockers we hit and resolved
|
||||
|
||||
[y] Extract insights [n] Skip [v] Preview what I'd extract
|
||||
```
|
||||
|
||||
**If user selects [y] Extract insights:**
|
||||
|
||||
```python
|
||||
# Step 1: Find the current session file
|
||||
# Sessions are stored at: ~/.claude/projects/{encoded-project-path}/
|
||||
# Project path /Users/x/Projects/myapp becomes -Users-x-Projects-myapp
|
||||
|
||||
# Step 2: Call the extraction tool
|
||||
mcp__code-forge__extract_session_insights
|
||||
session_path: "~/.claude/projects/-Users-iamanmp-Projects-{project}/{session_id}.jsonl"
|
||||
kb_name: "{project}-project_memories"
|
||||
auto_save: false
|
||||
```
|
||||
|
||||
**Step 3: Show extracted insights for approval:**
|
||||
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
🔍 EXTRACTED INSIGHTS
|
||||
═══════════════════════════════════════
|
||||
|
||||
Found 4 insights from session history:
|
||||
|
||||
1. 💎 HIGH (0.92) - Decision
|
||||
"Hybrid Session Architecture"
|
||||
Decided to combine Claude Code built-in sessions (for replay)
|
||||
with Claude OS knowledge bases (for semantic search).
|
||||
[Save] [Skip]
|
||||
|
||||
2. 💎 HIGH (0.88) - Pattern
|
||||
"Session File Structure"
|
||||
Built-in sessions use JSONL format with entry types: user,
|
||||
assistant, file-history-snapshot. UUID parent chain for threading.
|
||||
[Save] [Skip]
|
||||
|
||||
3. 📊 MEDIUM (0.75) - Solution
|
||||
"Update Lock Error Resolution"
|
||||
Claude update error "Another process updating" resolved by
|
||||
checking version - was already on latest, no lock file existed.
|
||||
[Save] [Skip]
|
||||
|
||||
4. 📊 MEDIUM (0.71) - Decision
|
||||
"User-Controlled Extraction"
|
||||
Session extraction should be opt-in with user approval for
|
||||
each insight to avoid noise and respect privacy.
|
||||
[Save] [Skip]
|
||||
|
||||
───────────────────────────────────────
|
||||
[a] Save all [h] Save high confidence only (>0.8) [s] Skip all
|
||||
```
|
||||
|
||||
**If user selects [v] Preview:**
|
||||
|
||||
Show a dry-run of what would be extracted without saving, then re-prompt.
|
||||
|
||||
**If user selects [n] Skip:**
|
||||
|
||||
Continue to Phase 3 without extraction.
|
||||
|
||||
**Configuration:**
|
||||
|
||||
Users can configure extraction behavior in their state file:
|
||||
|
||||
```json
|
||||
{
|
||||
"preferences": {
|
||||
"session_extraction": {
|
||||
"enabled": true,
|
||||
"auto_prompt_on_end": true,
|
||||
"min_confidence_threshold": 0.7,
|
||||
"auto_save_high_confidence": false,
|
||||
"insight_types": ["decisions", "patterns", "solutions", "blockers"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `auto_prompt_on_end` is false, skip this phase entirely.
|
||||
If `auto_save_high_confidence` is true, automatically save insights above threshold.
|
||||
|
||||
**Phase 3: Update State & Stats**
|
||||
|
||||
```json
|
||||
|
|
@ -309,7 +410,7 @@ When starting a session in a project without a state file, create one with this
|
|||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"current_session": {
|
||||
"active": false,
|
||||
"started_at": null,
|
||||
|
|
@ -319,7 +420,8 @@ When starting a session in a project without a state file, create one with this
|
|||
"context": [],
|
||||
"decisions_made": [],
|
||||
"blockers": [],
|
||||
"patterns_discovered": []
|
||||
"patterns_discovered": [],
|
||||
"builtin_session_id": null
|
||||
},
|
||||
"last_session": null,
|
||||
"active_memories": {
|
||||
|
|
@ -331,6 +433,7 @@ When starting a session in a project without a state file, create one with this
|
|||
"total_sessions": 0,
|
||||
"total_memories_saved": 0,
|
||||
"total_patterns_discovered": 0,
|
||||
"total_insights_extracted": 0,
|
||||
"most_referenced_memories": [],
|
||||
"average_session_duration": 0
|
||||
},
|
||||
|
|
@ -339,9 +442,23 @@ When starting a session in a project without a state file, create one with this
|
|||
"has_unresolved_blockers": false,
|
||||
"patterns_ready_to_document": false
|
||||
},
|
||||
"preferences": {
|
||||
"auto_search_on_start": true,
|
||||
"max_memories_to_load": 5,
|
||||
"search_days_back": 14,
|
||||
"include_pattern_search": true,
|
||||
"proactive_suggestions": true,
|
||||
"session_extraction": {
|
||||
"enabled": true,
|
||||
"auto_prompt_on_end": true,
|
||||
"min_confidence_threshold": 0.7,
|
||||
"auto_save_high_confidence": false,
|
||||
"insight_types": ["decisions", "patterns", "solutions", "blockers"]
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"last_updated": null,
|
||||
"claude_os_version": "1.0.0",
|
||||
"claude_os_version": "1.1.0",
|
||||
"project_name": null
|
||||
}
|
||||
}
|
||||
|
|
@ -445,21 +562,14 @@ I'll:
|
|||
|
||||
## CONFIGURATION
|
||||
|
||||
### Automatic Memory Search
|
||||
All preferences are stored in the state file (`claude-os-state.json`). See the Default State Template above for all available options.
|
||||
|
||||
Edit state file to configure:
|
||||
|
||||
```json
|
||||
{
|
||||
"preferences": {
|
||||
"auto_search_on_start": true,
|
||||
"max_memories_to_load": 5,
|
||||
"search_days_back": 14,
|
||||
"include_pattern_search": true,
|
||||
"proactive_suggestions": true
|
||||
}
|
||||
}
|
||||
```
|
||||
Key settings:
|
||||
- `preferences.auto_search_on_start` - Search memories when starting session
|
||||
- `preferences.max_memories_to_load` - How many memories to load (default: 5)
|
||||
- `preferences.session_extraction.enabled` - Enable history extraction feature
|
||||
- `preferences.session_extraction.auto_prompt_on_end` - Ask about extraction on session end
|
||||
- `preferences.session_extraction.min_confidence_threshold` - Only show insights above this score
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
262
templates/commands/claude-os-skills.md
Normal file
262
templates/commands/claude-os-skills.md
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
# Claude OS Skills Management
|
||||
|
||||
Manage Claude Code skills - list, install from templates, create custom, and configure.
|
||||
|
||||
## Commands
|
||||
|
||||
```
|
||||
/claude-os-skills - List all skills (global + project)
|
||||
/claude-os-skills templates - Show available templates
|
||||
/claude-os-skills install <name> - Install template to project
|
||||
/claude-os-skills create - Create a custom skill
|
||||
/claude-os-skills view <name> - View skill details
|
||||
/claude-os-skills delete <name> - Delete a project skill
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LIST SKILLS (default)
|
||||
|
||||
When user runs `/claude-os-skills` with no arguments:
|
||||
|
||||
**Step 1: Get Skills**
|
||||
```
|
||||
mcp__code-forge__list_skills
|
||||
project_path: "{cwd}"
|
||||
```
|
||||
|
||||
**Step 2: Display Results**
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
📚 CLAUDE CODE SKILLS
|
||||
═══════════════════════════════════════
|
||||
|
||||
🌐 GLOBAL SKILLS (always available)
|
||||
────────────────────────────────────
|
||||
✓ memory - Save and recall information
|
||||
✓ remember-this - Auto-save on trigger phrases
|
||||
|
||||
📁 PROJECT SKILLS ({cwd})
|
||||
────────────────────────────────────
|
||||
✓ rails-backend - Rails patterns and service objects
|
||||
✓ rspec - RSpec testing patterns
|
||||
|
||||
💡 TIP: Run '/claude-os-skills templates' to see available templates
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LIST TEMPLATES
|
||||
|
||||
When user runs `/claude-os-skills templates`:
|
||||
|
||||
**Step 1: Get Templates**
|
||||
```
|
||||
mcp__code-forge__list_skill_templates
|
||||
```
|
||||
|
||||
**Step 2: Display by Category**
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
📦 SKILL TEMPLATES
|
||||
═══════════════════════════════════════
|
||||
|
||||
GENERAL
|
||||
────────────────────────────────────
|
||||
• initialize-project
|
||||
Analyze codebase, generate docs
|
||||
Tags: onboarding, analysis
|
||||
|
||||
RAILS
|
||||
────────────────────────────────────
|
||||
• rails-backend
|
||||
Service objects, ActiveRecord, APIs
|
||||
Tags: ruby, rails, backend
|
||||
|
||||
REACT
|
||||
────────────────────────────────────
|
||||
• react-patterns
|
||||
Hooks, TypeScript, components
|
||||
Tags: react, typescript, frontend
|
||||
|
||||
TESTING
|
||||
────────────────────────────────────
|
||||
• rspec
|
||||
RSpec patterns, factories
|
||||
Tags: ruby, rspec, tdd
|
||||
|
||||
───────────────────────────────────────
|
||||
Install with: /claude-os-skills install <name>
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## INSTALL TEMPLATE
|
||||
|
||||
When user runs `/claude-os-skills install <name>`:
|
||||
|
||||
**Step 1: Install Template**
|
||||
```
|
||||
mcp__code-forge__install_skill_template
|
||||
template_name: "<name>"
|
||||
project_path: "{cwd}"
|
||||
```
|
||||
|
||||
**Step 2: Confirm**
|
||||
```
|
||||
✓ Installed '<name>' to {cwd}/.claude/skills/<name>
|
||||
|
||||
The skill is now available in this project!
|
||||
|
||||
To use it, Claude Code will automatically load it when relevant,
|
||||
or you can explicitly invoke it with: skill: <name>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CREATE CUSTOM SKILL
|
||||
|
||||
When user runs `/claude-os-skills create`:
|
||||
|
||||
**Step 1: Gather Information**
|
||||
|
||||
Ask the user for:
|
||||
1. Skill name (e.g., "deployment", "code-review")
|
||||
2. Description (one sentence)
|
||||
3. Content (the skill instructions)
|
||||
|
||||
**Step 2: Create Skill**
|
||||
```
|
||||
mcp__code-forge__create_skill
|
||||
name: "<name>"
|
||||
description: "<description>"
|
||||
content: "<content>"
|
||||
project_path: "{cwd}"
|
||||
```
|
||||
|
||||
**Step 3: Confirm**
|
||||
```
|
||||
✓ Created skill '<name>' in {cwd}/.claude/skills/<name>
|
||||
|
||||
The skill is now available in this project!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VIEW SKILL
|
||||
|
||||
When user runs `/claude-os-skills view <name>`:
|
||||
|
||||
**Step 1: Get Skill Details**
|
||||
|
||||
First try project scope, then global:
|
||||
```
|
||||
mcp__code-forge__get_skill
|
||||
name: "<name>"
|
||||
scope: "project"
|
||||
project_path: "{cwd}"
|
||||
```
|
||||
|
||||
If not found:
|
||||
```
|
||||
mcp__code-forge__get_skill
|
||||
name: "<name>"
|
||||
scope: "global"
|
||||
```
|
||||
|
||||
**Step 2: Display Skill**
|
||||
```
|
||||
═══════════════════════════════════════
|
||||
📖 SKILL: <name>
|
||||
═══════════════════════════════════════
|
||||
|
||||
Scope: project
|
||||
Source: template
|
||||
Category: rails
|
||||
Tags: ruby, rails, backend
|
||||
|
||||
─────────────────────────────────────
|
||||
|
||||
<skill content here>
|
||||
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DELETE SKILL
|
||||
|
||||
When user runs `/claude-os-skills delete <name>`:
|
||||
|
||||
**Step 1: Confirm with User**
|
||||
```
|
||||
⚠️ Are you sure you want to delete the '<name>' skill?
|
||||
|
||||
This will remove it from {cwd}/.claude/skills/<name>
|
||||
|
||||
Type 'yes' to confirm, or anything else to cancel.
|
||||
```
|
||||
|
||||
**Step 2: Delete if Confirmed**
|
||||
```
|
||||
mcp__code-forge__delete_skill
|
||||
name: "<name>"
|
||||
project_path: "{cwd}"
|
||||
```
|
||||
|
||||
**Step 3: Confirm**
|
||||
```
|
||||
✓ Deleted skill '<name>' from project
|
||||
```
|
||||
|
||||
**Note:** Cannot delete global skills (memory, remember-this). These are core to Claude OS.
|
||||
|
||||
---
|
||||
|
||||
## SKILL LOCATIONS
|
||||
|
||||
- **Global Skills**: `~/.claude/skills/`
|
||||
- Available in ALL projects
|
||||
- Core skills: memory, remember-this
|
||||
|
||||
- **Project Skills**: `{project}/.claude/skills/`
|
||||
- Available only in that project
|
||||
- Installed from templates or custom created
|
||||
|
||||
- **Templates**: `claude-os/templates/skill-library/`
|
||||
- Categorized templates to install
|
||||
|
||||
---
|
||||
|
||||
## TIPS
|
||||
|
||||
1. **Start with templates** - Install what you need per-project
|
||||
2. **Keep global minimal** - Only core skills should be global
|
||||
3. **Customize templates** - After installing, edit for your needs
|
||||
4. **Create custom skills** - Document project-specific patterns
|
||||
|
||||
---
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
```
|
||||
/claude-os-skills
|
||||
→ Lists all global and project skills
|
||||
|
||||
/claude-os-skills templates
|
||||
→ Shows available templates by category
|
||||
|
||||
/claude-os-skills install rails-backend
|
||||
→ Installs Rails patterns to current project
|
||||
|
||||
/claude-os-skills create
|
||||
→ Interactive custom skill creation
|
||||
|
||||
/claude-os-skills view rails-backend
|
||||
→ Shows full skill content
|
||||
|
||||
/claude-os-skills delete old-skill
|
||||
→ Removes skill from project (after confirmation)
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "initialize-project",
|
||||
"description": "Analyze a project's codebase and documentation to generate coding standards, architecture docs, and development practices. Perfect for new project onboarding.",
|
||||
"category": "general",
|
||||
"tags": ["onboarding", "analysis", "documentation", "setup"],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
163
templates/skill-library/general/initialize-project/skill.md
Normal file
163
templates/skill-library/general/initialize-project/skill.md
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
---
|
||||
name: analyze-project
|
||||
description: "Analyze a project's codebase and documentation to generate coding standards, architecture docs, and development practices. Perfect for new project onboarding. Usage: 'analyze-project: /path/to/project' or 'analyze: /path/to/project'"
|
||||
allowed-tools: Read, Write, Bash, Glob, Grep
|
||||
---
|
||||
|
||||
# Project Analysis Skill
|
||||
|
||||
## Purpose
|
||||
|
||||
Automatically analyze any project's codebase, specifications, and coding patterns to generate comprehensive documentation for the project_profile MCP. This enables AI assistants to understand:
|
||||
|
||||
- **Coding Standards**: Conventions, naming patterns, style guides
|
||||
- **Architecture**: Design principles, structural patterns, module organization
|
||||
- **Development Practices**: Workflows, testing approaches, deployment patterns
|
||||
|
||||
## How to Use
|
||||
|
||||
Simply say one of:
|
||||
- **"analyze-project: 4"** - Analyze project with database ID 4
|
||||
- **"analyze: 4"**
|
||||
- **"project analysis: 4"**
|
||||
|
||||
**How it works:**
|
||||
1. Fetches project from Claude OS using the database ID
|
||||
2. Registers all 4 MCPs with Claude Code (if not already registered)
|
||||
3. Analyzes the project codebase
|
||||
4. Generates 3 documentation files (saved locally)
|
||||
5. Creates a concise project summary
|
||||
6. **Displays summary for you to save to Claude's native memory**
|
||||
7. MCPs are registered but NOT loaded (saves context tokens)
|
||||
|
||||
**Example Workflow:**
|
||||
```
|
||||
You: "analyze-project: 1"
|
||||
|
||||
→ Loads project from Claude OS (ID #1)
|
||||
→ Registers 4 MCPs (myapp-project-profile, etc.)
|
||||
→ Analyzes codebase
|
||||
→ Generates 3 docs locally
|
||||
→ Displays summary:
|
||||
|
||||
PROJECT: MyApp (ID: 1)
|
||||
TYPE: Rails
|
||||
... (save this to native memory)
|
||||
|
||||
→ MCPs ready to load on-demand when you need them
|
||||
```
|
||||
|
||||
**Then:**
|
||||
```
|
||||
You (saving to memory): "Remember: [paste the summary above]"
|
||||
|
||||
When working on the project: "Load myapp-project-profile"
|
||||
→ Loads that specific MCP into context only when needed
|
||||
```
|
||||
|
||||
## What Happens
|
||||
|
||||
When you invoke this skill:
|
||||
|
||||
1. **Scan the project** for source files, specs, and documentation
|
||||
2. **Analyze code patterns** - naming conventions, structure, style
|
||||
3. **Review documentation** - README, spec files, guides
|
||||
4. **Generate 3 documents**:
|
||||
- `CODING_STANDARDS.md` - Style, conventions, patterns
|
||||
- `ARCHITECTURE.md` - Design, structure, principles
|
||||
- `DEVELOPMENT_PRACTICES.md` - Workflows, testing, deployment
|
||||
5. **Save locally** to `.claude-os/project-profile/` for reference
|
||||
6. **Ingest to MCP** (if project_id provided) - Adds docs to the project_profile knowledge base in Claude OS
|
||||
|
||||
## Document Structure
|
||||
|
||||
### CODING_STANDARDS.md
|
||||
- Naming conventions (variables, functions, classes)
|
||||
- Code style and formatting
|
||||
- File organization
|
||||
- Import/require patterns
|
||||
- Comment and documentation style
|
||||
- Linting/formatting rules
|
||||
|
||||
### ARCHITECTURE.md
|
||||
- High-level design patterns
|
||||
- Module organization
|
||||
- Data flow and dependencies
|
||||
- Database schema (if applicable)
|
||||
- Key architectural decisions
|
||||
- Technology stack
|
||||
|
||||
### DEVELOPMENT_PRACTICES.md
|
||||
- Development workflow
|
||||
- Testing strategy and patterns
|
||||
- Git workflow (branching, commits)
|
||||
- Code review process
|
||||
- Deployment and release process
|
||||
- Build and deployment pipelines
|
||||
- Common debugging approaches
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Analyze a Rails Project
|
||||
```
|
||||
You: "analyze-project: /Users/me/Projects/my-rails-app"
|
||||
|
||||
→ I scan Rails structure (app/, config/, spec/, etc.)
|
||||
→ Analyze Models, Controllers, Services, Views
|
||||
→ Review gems and dependencies
|
||||
→ Generate 3 docs with Rails-specific patterns
|
||||
→ Save to mcp/kb/my-rails-app-project-profile/
|
||||
→ Confirm: "✓ Project analysis complete. 3 docs saved to project_profile KB"
|
||||
```
|
||||
|
||||
### Example 2: Analyze Specific Aspects
|
||||
```
|
||||
You: "analyze-project: /Users/me/Projects/api-service architecture"
|
||||
|
||||
→ Focus on architecture analysis
|
||||
→ Generate ARCHITECTURE.md
|
||||
→ Skip coding standards and practices (or generate minimal versions)
|
||||
```
|
||||
|
||||
### Example 3: Re-analyze Project
|
||||
```
|
||||
You: "analyze-project: /Users/me/Projects/my-project"
|
||||
|
||||
→ Existing docs are replaced with fresh analysis
|
||||
→ Useful after major refactoring or architecture changes
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
✅ **Consistency** - Apply same analysis to any project
|
||||
✅ **Speed** - Generate docs in seconds instead of hours
|
||||
✅ **Comprehensive** - Captures coding style, architecture, and practices
|
||||
✅ **Automatic Integration** - Syncs directly to project_profile MCP
|
||||
✅ **Reusable** - Same skill works for Rails, Python, Node, Go, etc.
|
||||
✅ **AI-Friendly** - Docs enable Claude to write better code for your project
|
||||
|
||||
## Technical Details
|
||||
|
||||
**Output Location**: `mcp/kb/{project-name}-project-profile/`
|
||||
**Document Names**:
|
||||
- `CODING_STANDARDS.md`
|
||||
- `ARCHITECTURE.md`
|
||||
- `DEVELOPMENT_PRACTICES.md`
|
||||
|
||||
**Integration**: Automatically available in Claude OS's project_profile MCP
|
||||
**Storage**: SQLite via project_profile knowledge base
|
||||
**Sync**: Uses Claude OS file watcher for auto-updates
|
||||
|
||||
## Supported Project Types
|
||||
|
||||
- **Rails** - Ruby on Rails applications
|
||||
- **Python** - Django, FastAPI, Flask projects
|
||||
- **Node.js** - Express, NestJS, Next.js applications
|
||||
- **Java** - Spring Boot, Maven/Gradle projects
|
||||
- **Go** - Standard Go project structure
|
||||
- **React** - React applications
|
||||
- **Generic** - Any project with source code and documentation
|
||||
|
||||
---
|
||||
|
||||
**Pro Tip**: Run this skill immediately after creating a new project in Agent OS. The generated docs become the foundation for AI-assisted development, ensuring all code generation aligns with your project's patterns and practices.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "rails-backend",
|
||||
"description": "Rails backend patterns including service objects, ActiveRecord best practices, migrations, API endpoints, and background jobs.",
|
||||
"category": "rails",
|
||||
"tags": ["ruby", "rails", "backend", "api", "activerecord", "service-objects"],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
148
templates/skill-library/rails/rails-backend/skill.md
Normal file
148
templates/skill-library/rails/rails-backend/skill.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# Rails Backend Patterns
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides standard Rails backend patterns including:
|
||||
- Service objects for business logic
|
||||
- ActiveRecord best practices
|
||||
- Migration conventions
|
||||
- API endpoint patterns
|
||||
- Background job patterns (Sidekiq)
|
||||
|
||||
## Service Object Pattern
|
||||
|
||||
All complex business logic should be extracted into service objects:
|
||||
|
||||
```ruby
|
||||
# app/services/user_registration_service.rb
|
||||
class UserRegistrationService
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
return failure("Email already exists") if User.exists?(email: @params[:email])
|
||||
|
||||
user = User.create!(@params)
|
||||
send_welcome_email(user)
|
||||
|
||||
success(user)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
failure(e.message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_welcome_email(user)
|
||||
UserMailer.welcome(user).deliver_later
|
||||
end
|
||||
|
||||
def success(user)
|
||||
OpenStruct.new(success?: true, user: user)
|
||||
end
|
||||
|
||||
def failure(error)
|
||||
OpenStruct.new(success?: false, error: error)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## ActiveRecord Best Practices
|
||||
|
||||
### Query Optimization
|
||||
```ruby
|
||||
# Always use includes to prevent N+1 queries
|
||||
User.includes(:posts, :comments).where(active: true)
|
||||
|
||||
# Use find_each for large datasets
|
||||
User.find_each(batch_size: 1000) do |user|
|
||||
process_user(user)
|
||||
end
|
||||
|
||||
# Use select to limit columns
|
||||
User.select(:id, :name, :email).where(active: true)
|
||||
```
|
||||
|
||||
### Scopes
|
||||
```ruby
|
||||
class User < ApplicationRecord
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :recent, -> { order(created_at: :desc) }
|
||||
scope :with_posts, -> { joins(:posts).distinct }
|
||||
end
|
||||
```
|
||||
|
||||
## Migration Conventions
|
||||
|
||||
```ruby
|
||||
class AddIndexToUsersEmail < ActiveRecord::Migration[7.0]
|
||||
disable_ddl_transaction! # For concurrent indexes
|
||||
|
||||
def change
|
||||
add_index :users, :email,
|
||||
unique: true,
|
||||
algorithm: :concurrently,
|
||||
if_not_exists: true
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## API Controller Pattern
|
||||
|
||||
```ruby
|
||||
class Api::V1::UsersController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_user, only: [:show, :update, :destroy]
|
||||
|
||||
def index
|
||||
@users = User.active.page(params[:page])
|
||||
render json: UserSerializer.new(@users).serializable_hash
|
||||
end
|
||||
|
||||
def create
|
||||
result = UserRegistrationService.new(user_params).call
|
||||
|
||||
if result.success?
|
||||
render json: UserSerializer.new(result.user).serializable_hash,
|
||||
status: :created
|
||||
else
|
||||
render json: { error: result.error }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user
|
||||
@user = User.find(params[:id])
|
||||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:name, :email, :password)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Background Jobs
|
||||
|
||||
```ruby
|
||||
class ProcessUserReportJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
|
||||
discard_on ActiveJob::DeserializationError
|
||||
|
||||
def perform(user_id)
|
||||
user = User.find(user_id)
|
||||
ReportService.new(user).generate
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **Fat models, skinny controllers** - Keep controllers thin
|
||||
2. **Service objects** - Extract complex logic
|
||||
3. **Query objects** - For complex database queries
|
||||
4. **Decorators** - For view-specific logic (use Draper)
|
||||
5. **Form objects** - For complex form handling
|
||||
6. **Policy objects** - For authorization (use Pundit)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "react-patterns",
|
||||
"description": "Modern React patterns including hooks, TypeScript, state management, forms, error boundaries, and performance optimization.",
|
||||
"category": "react",
|
||||
"tags": ["react", "typescript", "hooks", "frontend", "components", "state-management"],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
262
templates/skill-library/react/react-patterns/skill.md
Normal file
262
templates/skill-library/react/react-patterns/skill.md
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
# React Patterns and Best Practices
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides modern React patterns including:
|
||||
- Component organization
|
||||
- Hooks best practices
|
||||
- State management patterns
|
||||
- TypeScript integration
|
||||
- Performance optimization
|
||||
|
||||
## Component Structure
|
||||
|
||||
```typescript
|
||||
// src/components/UserProfile/UserProfile.tsx
|
||||
import { FC, memo } from 'react';
|
||||
import { useUserProfile } from './useUserProfile';
|
||||
import { UserProfileProps } from './types';
|
||||
import styles from './UserProfile.module.css';
|
||||
|
||||
export const UserProfile: FC<UserProfileProps> = memo(({ userId }) => {
|
||||
const { user, isLoading, error } = useUserProfile(userId);
|
||||
|
||||
if (isLoading) return <LoadingSpinner />;
|
||||
if (error) return <ErrorMessage error={error} />;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Avatar src={user.avatar} alt={user.name} />
|
||||
<h2 className={styles.name}>{user.name}</h2>
|
||||
<p className={styles.email}>{user.email}</p>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
UserProfile.displayName = 'UserProfile';
|
||||
```
|
||||
|
||||
## Custom Hooks
|
||||
|
||||
```typescript
|
||||
// src/hooks/useAsync.ts
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
interface AsyncState<T> {
|
||||
data: T | null;
|
||||
isLoading: boolean;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
export function useAsync<T>() {
|
||||
const [state, setState] = useState<AsyncState<T>>({
|
||||
data: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const execute = useCallback(async (promise: Promise<T>) => {
|
||||
setState({ data: null, isLoading: true, error: null });
|
||||
try {
|
||||
const data = await promise;
|
||||
setState({ data, isLoading: false, error: null });
|
||||
return data;
|
||||
} catch (error) {
|
||||
setState({ data: null, isLoading: false, error: error as Error });
|
||||
throw error;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { ...state, execute };
|
||||
}
|
||||
```
|
||||
|
||||
## API Hook Pattern
|
||||
|
||||
```typescript
|
||||
// src/hooks/useApi.ts
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from '../api';
|
||||
|
||||
export function useUsers() {
|
||||
return useQuery({
|
||||
queryKey: ['users'],
|
||||
queryFn: api.getUsers,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: api.createUser,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['users'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Context Pattern
|
||||
|
||||
```typescript
|
||||
// src/context/AuthContext.tsx
|
||||
import { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
login: (credentials: Credentials) => Promise<void>;
|
||||
logout: () => void;
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
|
||||
const login = async (credentials: Credentials) => {
|
||||
const user = await authApi.login(credentials);
|
||||
setUser(user);
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
setUser(null);
|
||||
authApi.logout();
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{
|
||||
user,
|
||||
login,
|
||||
logout,
|
||||
isAuthenticated: !!user
|
||||
}}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within AuthProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
```
|
||||
|
||||
## Form Pattern (React Hook Form)
|
||||
|
||||
```typescript
|
||||
// src/components/UserForm/UserForm.tsx
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(2, 'Name must be at least 2 characters'),
|
||||
email: z.string().email('Invalid email address'),
|
||||
});
|
||||
|
||||
type FormData = z.infer<typeof schema>;
|
||||
|
||||
export function UserForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<input {...register('name')} placeholder="Name" />
|
||||
{errors.name && <span>{errors.name.message}</span>}
|
||||
|
||||
<input {...register('email')} placeholder="Email" />
|
||||
{errors.email && <span>{errors.email.message}</span>}
|
||||
|
||||
<button type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Saving...' : 'Save'}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Error Boundary
|
||||
|
||||
```typescript
|
||||
// src/components/ErrorBoundary.tsx
|
||||
import { Component, ErrorInfo, ReactNode } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
fallback?: ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends Component<Props, State> {
|
||||
state: State = { hasError: false };
|
||||
|
||||
static getDerivedStateFromError(error: Error): State {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('Error caught by boundary:', error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback || <div>Something went wrong</div>;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
```typescript
|
||||
// Memoization
|
||||
const ExpensiveComponent = memo(({ data }) => {
|
||||
// Only re-renders when data changes
|
||||
return <div>{/* ... */}</div>;
|
||||
});
|
||||
|
||||
// useMemo for expensive calculations
|
||||
const sortedItems = useMemo(() => {
|
||||
return items.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}, [items]);
|
||||
|
||||
// useCallback for stable function references
|
||||
const handleClick = useCallback((id: string) => {
|
||||
setSelected(id);
|
||||
}, []);
|
||||
|
||||
// Lazy loading
|
||||
const HeavyComponent = lazy(() => import('./HeavyComponent'));
|
||||
|
||||
<Suspense fallback={<Loading />}>
|
||||
<HeavyComponent />
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **Composition over inheritance** - Compose components
|
||||
2. **Single responsibility** - One component, one job
|
||||
3. **Lift state up** - Share state at common ancestor
|
||||
4. **Custom hooks** - Extract reusable logic
|
||||
5. **TypeScript first** - Type everything
|
||||
6. **Memoize wisely** - Don't over-optimize
|
||||
7. **Error boundaries** - Graceful error handling
|
||||
7
templates/skill-library/testing/rspec/metadata.json
Normal file
7
templates/skill-library/testing/rspec/metadata.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "rspec",
|
||||
"description": "RSpec testing patterns for Ruby/Rails including unit tests, request specs, factories, shared examples, and best practices.",
|
||||
"category": "testing",
|
||||
"tags": ["ruby", "rails", "rspec", "testing", "tdd", "factory-bot"],
|
||||
"version": "1.0.0"
|
||||
}
|
||||
221
templates/skill-library/testing/rspec/skill.md
Normal file
221
templates/skill-library/testing/rspec/skill.md
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
# RSpec Testing Patterns
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides comprehensive RSpec testing patterns for Ruby/Rails applications:
|
||||
- Unit testing conventions
|
||||
- Integration testing patterns
|
||||
- Factory Bot best practices
|
||||
- Mocking and stubbing
|
||||
- Test organization
|
||||
|
||||
## Test Structure
|
||||
|
||||
```ruby
|
||||
# spec/services/user_registration_service_spec.rb
|
||||
RSpec.describe UserRegistrationService do
|
||||
describe '#call' do
|
||||
subject(:service) { described_class.new(params) }
|
||||
|
||||
context 'with valid params' do
|
||||
let(:params) { { name: 'Test', email: 'test@example.com', password: 'password123' } }
|
||||
|
||||
it 'creates a user' do
|
||||
expect { service.call }.to change(User, :count).by(1)
|
||||
end
|
||||
|
||||
it 'returns success' do
|
||||
result = service.call
|
||||
expect(result).to be_success
|
||||
expect(result.user).to be_persisted
|
||||
end
|
||||
|
||||
it 'sends welcome email' do
|
||||
expect { service.call }
|
||||
.to have_enqueued_mail(UserMailer, :welcome)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with existing email' do
|
||||
let(:params) { { name: 'Test', email: existing_user.email, password: 'password123' } }
|
||||
let(:existing_user) { create(:user) }
|
||||
|
||||
it 'does not create a user' do
|
||||
expect { service.call }.not_to change(User, :count)
|
||||
end
|
||||
|
||||
it 'returns failure' do
|
||||
result = service.call
|
||||
expect(result).not_to be_success
|
||||
expect(result.error).to include('Email already exists')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Factory Bot Patterns
|
||||
|
||||
```ruby
|
||||
# spec/factories/users.rb
|
||||
FactoryBot.define do
|
||||
factory :user do
|
||||
sequence(:email) { |n| "user#{n}@example.com" }
|
||||
name { Faker::Name.name }
|
||||
password { 'password123' }
|
||||
|
||||
trait :admin do
|
||||
role { :admin }
|
||||
end
|
||||
|
||||
trait :with_posts do
|
||||
transient do
|
||||
posts_count { 3 }
|
||||
end
|
||||
|
||||
after(:create) do |user, evaluator|
|
||||
create_list(:post, evaluator.posts_count, user: user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Usage:
|
||||
create(:user) # Basic user
|
||||
create(:user, :admin) # Admin user
|
||||
create(:user, :with_posts) # User with 3 posts
|
||||
create(:user, :with_posts, posts_count: 5) # User with 5 posts
|
||||
```
|
||||
|
||||
## Request Specs (API Testing)
|
||||
|
||||
```ruby
|
||||
# spec/requests/api/v1/users_spec.rb
|
||||
RSpec.describe 'Api::V1::Users', type: :request do
|
||||
describe 'GET /api/v1/users' do
|
||||
let!(:users) { create_list(:user, 3) }
|
||||
|
||||
before { get '/api/v1/users', headers: auth_headers }
|
||||
|
||||
it 'returns users' do
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json_response['data'].size).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/users' do
|
||||
let(:valid_params) do
|
||||
{
|
||||
user: {
|
||||
name: 'New User',
|
||||
email: 'new@example.com',
|
||||
password: 'password123'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
context 'with valid params' do
|
||||
it 'creates user' do
|
||||
expect {
|
||||
post '/api/v1/users', params: valid_params, headers: auth_headers
|
||||
}.to change(User, :count).by(1)
|
||||
|
||||
expect(response).to have_http_status(:created)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Model Specs
|
||||
|
||||
```ruby
|
||||
# spec/models/user_spec.rb
|
||||
RSpec.describe User, type: :model do
|
||||
describe 'validations' do
|
||||
subject { build(:user) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:email) }
|
||||
it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
end
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to have_many(:posts).dependent(:destroy) }
|
||||
it { is_expected.to have_many(:comments) }
|
||||
it { is_expected.to belong_to(:organization).optional }
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
describe '.active' do
|
||||
let!(:active_user) { create(:user, active: true) }
|
||||
let!(:inactive_user) { create(:user, active: false) }
|
||||
|
||||
it 'returns only active users' do
|
||||
expect(User.active).to contain_exactly(active_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#full_name' do
|
||||
let(:user) { build(:user, first_name: 'John', last_name: 'Doe') }
|
||||
|
||||
it 'returns combined name' do
|
||||
expect(user.full_name).to eq('John Doe')
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Shared Examples
|
||||
|
||||
```ruby
|
||||
# spec/support/shared_examples/authenticatable.rb
|
||||
RSpec.shared_examples 'authenticatable' do
|
||||
context 'without authentication' do
|
||||
let(:auth_headers) { {} }
|
||||
|
||||
it 'returns unauthorized' do
|
||||
subject
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Usage in specs:
|
||||
describe 'GET /api/v1/users' do
|
||||
subject { get '/api/v1/users', headers: auth_headers }
|
||||
|
||||
it_behaves_like 'authenticatable'
|
||||
end
|
||||
```
|
||||
|
||||
## Test Helpers
|
||||
|
||||
```ruby
|
||||
# spec/support/request_helpers.rb
|
||||
module RequestHelpers
|
||||
def json_response
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
|
||||
def auth_headers
|
||||
user = create(:user)
|
||||
{ 'Authorization' => "Bearer #{user.generate_token}" }
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include RequestHelpers, type: :request
|
||||
end
|
||||
```
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **One assertion per test** - Keep tests focused
|
||||
2. **Use `let` and `let!`** - Lazy vs eager loading
|
||||
3. **Use `subject`** - Name what you're testing
|
||||
4. **Use `described_class`** - Reference class being tested
|
||||
5. **Use traits** - Compose variations cleanly
|
||||
6. **Avoid database when possible** - Use `build` over `create`
|
||||
7. **Use `aggregate_failures`** - Group related assertions
|
||||
260
tests/test_session_parser.py
Normal file
260
tests/test_session_parser.py
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
"""
|
||||
Tests for session parser and insight extractor.
|
||||
"""
|
||||
|
||||
import json
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from app.core.session_parser import (
|
||||
SessionParser,
|
||||
get_project_sessions_dir,
|
||||
list_session_files,
|
||||
Message,
|
||||
ToolCall,
|
||||
FileChange,
|
||||
SessionData
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_session_file(tmp_path):
|
||||
"""Create a sample session file for testing."""
|
||||
session_file = tmp_path / "test-session.jsonl"
|
||||
|
||||
entries = [
|
||||
{
|
||||
"type": "summary",
|
||||
"summary": "Test session summary",
|
||||
"leafUuid": "abc123"
|
||||
},
|
||||
{
|
||||
"type": "user",
|
||||
"uuid": "msg-001",
|
||||
"parentUuid": None,
|
||||
"timestamp": "2025-12-11T10:00:00Z",
|
||||
"cwd": "/Users/test/Projects/myapp",
|
||||
"gitBranch": "main",
|
||||
"sessionId": "test-session",
|
||||
"message": {
|
||||
"role": "user",
|
||||
"content": "Hello, can you help me with testing?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "assistant",
|
||||
"uuid": "msg-002",
|
||||
"parentUuid": "msg-001",
|
||||
"timestamp": "2025-12-11T10:00:05Z",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{"type": "text", "text": "Of course! I can help you with testing."}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "user",
|
||||
"uuid": "msg-003",
|
||||
"parentUuid": "msg-002",
|
||||
"timestamp": "2025-12-11T10:00:10Z",
|
||||
"message": {
|
||||
"role": "user",
|
||||
"content": "Can you write a test for me?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "assistant",
|
||||
"uuid": "msg-004",
|
||||
"parentUuid": "msg-003",
|
||||
"timestamp": "2025-12-11T10:00:15Z",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{"type": "text", "text": "I'll write a test for you."},
|
||||
{
|
||||
"type": "tool_use",
|
||||
"id": "tool-001",
|
||||
"name": "Write",
|
||||
"input": {
|
||||
"file_path": "/Users/test/Projects/myapp/test.py",
|
||||
"content": "def test_example():\n pass"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "file-history-snapshot",
|
||||
"messageId": "msg-004",
|
||||
"snapshot": {
|
||||
"messageId": "msg-004",
|
||||
"timestamp": "2025-12-11T10:00:20Z",
|
||||
"trackedFileBackups": {
|
||||
"/Users/test/Projects/myapp/test.py": {"content": "def test_example():\n pass"}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
with open(session_file, 'w') as f:
|
||||
for entry in entries:
|
||||
f.write(json.dumps(entry) + '\n')
|
||||
|
||||
return session_file
|
||||
|
||||
|
||||
def test_session_parser_init(sample_session_file):
|
||||
"""Test SessionParser initialization."""
|
||||
parser = SessionParser(str(sample_session_file))
|
||||
assert parser.path == sample_session_file
|
||||
assert parser.session_id == "test-session"
|
||||
|
||||
|
||||
def test_session_parser_parse(sample_session_file):
|
||||
"""Test parsing a session file."""
|
||||
parser = SessionParser(str(sample_session_file))
|
||||
session_data = parser.parse()
|
||||
|
||||
# Check basic metadata
|
||||
assert session_data.session_id == "test-session"
|
||||
assert session_data.cwd == "/Users/test/Projects/myapp"
|
||||
assert session_data.git_branch == "main"
|
||||
assert session_data.total_entries == 6
|
||||
|
||||
# Check messages (2 user + 2 assistant = 4 messages)
|
||||
assert len(session_data.messages) == 4
|
||||
assert session_data.messages[0].role == "user"
|
||||
assert "help me with testing" in session_data.messages[0].content
|
||||
assert session_data.messages[1].role == "assistant"
|
||||
assert "help you with testing" in session_data.messages[1].content
|
||||
|
||||
# Check tool calls
|
||||
assert len(session_data.tool_calls) == 1
|
||||
assert session_data.tool_calls[0].tool_name == "Write"
|
||||
assert session_data.tool_calls[0].input_data["file_path"] == "/Users/test/Projects/myapp/test.py"
|
||||
|
||||
# Check file changes
|
||||
assert len(session_data.file_changes) == 1
|
||||
assert session_data.file_changes[0].file_path == "/Users/test/Projects/myapp/test.py"
|
||||
|
||||
|
||||
def test_session_parser_get_conversation(sample_session_file):
|
||||
"""Test getting conversation messages."""
|
||||
parser = SessionParser(str(sample_session_file))
|
||||
messages = parser.get_conversation()
|
||||
|
||||
assert len(messages) == 4 # 2 user + 2 assistant messages
|
||||
assert all(isinstance(msg, Message) for msg in messages)
|
||||
assert messages[0].role == "user"
|
||||
assert messages[1].role == "assistant"
|
||||
|
||||
|
||||
def test_session_parser_get_summary(sample_session_file):
|
||||
"""Test generating summary for extraction."""
|
||||
parser = SessionParser(str(sample_session_file))
|
||||
summary = parser.get_summary_for_extraction()
|
||||
|
||||
assert "test-session" in summary
|
||||
assert "/Users/test/Projects/myapp" in summary
|
||||
assert "main" in summary
|
||||
assert "help me with testing" in summary
|
||||
assert "Write" in summary
|
||||
|
||||
|
||||
def test_get_project_sessions_dir():
|
||||
"""Test encoding project path to sessions directory."""
|
||||
project_path = "/Users/test/Projects/myapp"
|
||||
sessions_dir = get_project_sessions_dir(project_path)
|
||||
|
||||
# Leading hyphen is preserved from the path encoding
|
||||
expected = Path.home() / ".claude" / "projects" / "-Users-test-Projects-myapp"
|
||||
assert sessions_dir == expected
|
||||
|
||||
|
||||
def test_list_session_files_empty_dir(tmp_path):
|
||||
"""Test listing sessions when directory doesn't exist."""
|
||||
fake_project = str(tmp_path / "nonexistent")
|
||||
sessions = list_session_files(fake_project)
|
||||
assert sessions == []
|
||||
|
||||
|
||||
def test_session_parser_invalid_file():
|
||||
"""Test parser with non-existent file."""
|
||||
with pytest.raises(FileNotFoundError):
|
||||
parser = SessionParser("/path/to/nonexistent/file.jsonl")
|
||||
|
||||
|
||||
def test_session_parser_malformed_json(tmp_path):
|
||||
"""Test parser handles malformed JSON gracefully."""
|
||||
session_file = tmp_path / "malformed.jsonl"
|
||||
|
||||
with open(session_file, 'w') as f:
|
||||
f.write('{"type": "user", "content": "valid"}\n')
|
||||
f.write('this is not json\n')
|
||||
f.write('{"type": "assistant", "content": "also valid"}\n')
|
||||
|
||||
parser = SessionParser(str(session_file))
|
||||
session_data = parser.parse()
|
||||
|
||||
# Should parse the valid lines, skip the malformed one
|
||||
assert session_data.total_entries == 2
|
||||
|
||||
|
||||
def test_session_data_defaults():
|
||||
"""Test SessionData dataclass defaults."""
|
||||
data = SessionData(
|
||||
session_id="test",
|
||||
session_path="/path/to/session.jsonl"
|
||||
)
|
||||
|
||||
assert data.messages == []
|
||||
assert data.tool_calls == []
|
||||
assert data.file_changes == []
|
||||
assert data.start_time is None
|
||||
assert data.end_time is None
|
||||
assert data.total_entries == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_insight_extractor_import():
|
||||
"""Test that InsightExtractor can be imported."""
|
||||
from app.core.insight_extractor import InsightExtractor, Insight
|
||||
|
||||
# Just verify imports work
|
||||
assert InsightExtractor is not None
|
||||
assert Insight is not None
|
||||
|
||||
|
||||
def test_session_parser_truncation(tmp_path):
|
||||
"""Test that very long sessions are truncated in summary."""
|
||||
session_file = tmp_path / "long-session.jsonl"
|
||||
|
||||
# Create a very long message
|
||||
long_content = "A" * 2000
|
||||
|
||||
entries = [
|
||||
{
|
||||
"type": "user",
|
||||
"uuid": "msg-001",
|
||||
"parentUuid": None,
|
||||
"timestamp": "2025-12-11T10:00:00Z",
|
||||
"cwd": "/test",
|
||||
"sessionId": "long-session",
|
||||
"message": {
|
||||
"role": "user",
|
||||
"content": long_content
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
with open(session_file, 'w') as f:
|
||||
for entry in entries:
|
||||
f.write(json.dumps(entry) + '\n')
|
||||
|
||||
parser = SessionParser(str(session_file))
|
||||
summary = parser.get_summary_for_extraction(max_tokens=100)
|
||||
|
||||
# Summary should be shorter than the original content
|
||||
# (header adds overhead, so just verify truncation occurred)
|
||||
assert len(summary) < len(long_content)
|
||||
assert "[... truncated ...]" in summary
|
||||
Loading…
Add table
Reference in a new issue