mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-18 10:16:49 +00:00
* refactor: PM Agent complete independence from external MCP servers ## Summary Implement graceful degradation to ensure PM Agent operates fully without any MCP server dependencies. MCP servers now serve as optional enhancements rather than required components. ## Changes ### Responsibility Separation (NEW) - **PM Agent**: Development workflow orchestration (PDCA cycle, task management) - **mindbase**: Memory management (long-term, freshness, error learning) - **Built-in memory**: Session-internal context (volatile) ### 3-Layer Memory Architecture with Fallbacks 1. **Built-in Memory** [OPTIONAL]: Session context via MCP memory server 2. **mindbase** [OPTIONAL]: Long-term semantic search via airis-mcp-gateway 3. **Local Files** [ALWAYS]: Core functionality in docs/memory/ ### Graceful Degradation Implementation - All MCP operations marked with [ALWAYS] or [OPTIONAL] - Explicit IF/ELSE fallback logic for every MCP call - Dual storage: Always write to local files + optionally to mindbase - Smart lookup: Semantic search (if available) → Text search (always works) ### Key Fallback Strategies **Session Start**: - mindbase available: search_conversations() for semantic context - mindbase unavailable: Grep docs/memory/*.jsonl for text-based lookup **Error Detection**: - mindbase available: Semantic search for similar past errors - mindbase unavailable: Grep docs/mistakes/ + solutions_learned.jsonl **Knowledge Capture**: - Always: echo >> docs/memory/patterns_learned.jsonl (persistent) - Optional: mindbase.store() for semantic search enhancement ## Benefits - ✅ Zero external dependencies (100% functionality without MCP) - ✅ Enhanced capabilities when MCPs available (semantic search, freshness) - ✅ No functionality loss, only reduced search intelligence - ✅ Transparent degradation (no error messages, automatic fallback) ## Related Research - Serena MCP investigation: Exposes tools (not resources), memory = markdown files - mindbase superiority: PostgreSQL + pgvector > Serena memory features - Best practices alignment: /Users/kazuki/github/airis-mcp-gateway/docs/mcp-best-practices.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: add PR template and pre-commit config - Add structured PR template with Git workflow checklist - Add pre-commit hooks for secret detection and Conventional Commits - Enforce code quality gates (YAML/JSON/Markdown lint, shellcheck) NOTE: Execute pre-commit inside Docker container to avoid host pollution: docker compose exec workspace uv tool install pre-commit docker compose exec workspace pre-commit run --all-files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: update PM Agent context with token efficiency architecture - Add Layer 0 Bootstrap (150 tokens, 95% reduction) - Document Intent Classification System (5 complexity levels) - Add Progressive Loading strategy (5-layer) - Document mindbase integration incentive (38% savings) - Update with 2025-10-17 redesign details * refactor: PM Agent command with progressive loading - Replace auto-loading with User Request First philosophy - Add 5-layer progressive context loading - Implement intent classification system - Add workflow metrics collection (.jsonl) - Document graceful degradation strategy * fix: installer improvements Update installer logic for better reliability * docs: add comprehensive development documentation - Add architecture overview - Add PM Agent improvements analysis - Add parallel execution architecture - Add CLI install improvements - Add code style guide - Add project overview - Add install process analysis * docs: add research documentation Add LLM agent token efficiency research and analysis * docs: add suggested commands reference * docs: add session logs and testing documentation - Add session analysis logs - Add testing documentation * feat: migrate CLI to typer + rich for modern UX ## What Changed ### New CLI Architecture (typer + rich) - Created `superclaude/cli/` module with modern typer-based CLI - Replaced custom UI utilities with rich native features - Added type-safe command structure with automatic validation ### Commands Implemented - **install**: Interactive installation with rich UI (progress, panels) - **doctor**: System diagnostics with rich table output - **config**: API key management with format validation ### Technical Improvements - Dependencies: Added typer>=0.9.0, rich>=13.0.0, click>=8.0.0 - Entry Point: Updated pyproject.toml to use `superclaude.cli.app:cli_main` - Tests: Added comprehensive smoke tests (11 passed) ### User Experience Enhancements - Rich formatted help messages with panels and tables - Automatic input validation with retry loops - Clear error messages with actionable suggestions - Non-interactive mode support for CI/CD ## Testing ```bash uv run superclaude --help # ✓ Works uv run superclaude doctor # ✓ Rich table output uv run superclaude config show # ✓ API key management pytest tests/test_cli_smoke.py # ✓ 11 passed, 1 skipped ``` ## Migration Path - ✅ P0: Foundation complete (typer + rich + smoke tests) - 🔜 P1: Pydantic validation models (next sprint) - 🔜 P2: Enhanced error messages (next sprint) - 🔜 P3: API key retry loops (next sprint) ## Performance Impact - **Code Reduction**: Prepared for -300 lines (custom UI → rich) - **Type Safety**: Automatic validation from type hints - **Maintainability**: Framework primitives vs custom code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: consolidate documentation directories Merged claudedocs/ into docs/research/ for consistent documentation structure. Changes: - Moved all claudedocs/*.md files to docs/research/ - Updated all path references in documentation (EN/KR) - Updated RULES.md and research.md command templates - Removed claudedocs/ directory - Removed ClaudeDocs/ from .gitignore Benefits: - Single source of truth for all research reports - PEP8-compliant lowercase directory naming - Clearer documentation organization - Prevents future claudedocs/ directory creation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * perf: reduce /sc:pm command output from 1652 to 15 lines - Remove 1637 lines of documentation from command file - Keep only minimal bootstrap message - 99% token reduction on command execution - Detailed specs remain in superclaude/agents/pm-agent.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * perf: split PM Agent into execution workflows and guide - Reduce pm-agent.md from 735 to 429 lines (42% reduction) - Move philosophy/examples to docs/agents/pm-agent-guide.md - Execution workflows (PDCA, file ops) stay in pm-agent.md - Guide (examples, quality standards) read once when needed Token savings: - Agent loading: ~6K → ~3.5K tokens (42% reduction) - Total with pm.md: 71% overall reduction 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: consolidate PM Agent optimization and pending changes PM Agent optimization (already committed separately): - superclaude/commands/pm.md: 1652→14 lines - superclaude/agents/pm-agent.md: 735→429 lines - docs/agents/pm-agent-guide.md: new guide file Other pending changes: - setup: framework_docs, mcp, logger, remove ui.py - superclaude: __main__, cli/app, cli/commands/install - tests: test_ui updates - scripts: workflow metrics analysis tools - docs/memory: session state updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: simplify MCP installer to unified gateway with legacy mode ## Changes ### MCP Component (setup/components/mcp.py) - Simplified to single airis-mcp-gateway by default - Added legacy mode for individual official servers (sequential-thinking, context7, magic, playwright) - Dynamic prerequisites based on mode: - Default: uv + claude CLI only - Legacy: node (18+) + npm + claude CLI - Removed redundant server definitions ### CLI Integration - Added --legacy flag to setup/cli/commands/install.py - Added --legacy flag to superclaude/cli/commands/install.py - Config passes legacy_mode to component installer ## Benefits - ✅ Simpler: 1 gateway vs 9+ individual servers - ✅ Lighter: No Node.js/npm required (default mode) - ✅ Unified: All tools in one gateway (sequential-thinking, context7, magic, playwright, serena, morphllm, tavily, chrome-devtools, git, puppeteer) - ✅ Flexible: --legacy flag for official servers if needed ## Usage ```bash superclaude install # Default: airis-mcp-gateway (推奨) superclaude install --legacy # Legacy: individual official servers ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: rename CoreComponent to FrameworkDocsComponent and add PM token tracking ## Changes ### Component Renaming (setup/components/) - Renamed CoreComponent → FrameworkDocsComponent for clarity - Updated all imports in __init__.py, agents.py, commands.py, mcp_docs.py, modes.py - Better reflects the actual purpose (framework documentation files) ### PM Agent Enhancement (superclaude/commands/pm.md) - Added token usage tracking instructions - PM Agent now reports: 1. Current token usage from system warnings 2. Percentage used (e.g., "27% used" for 54K/200K) 3. Status zone: 🟢 <75% | 🟡 75-85% | 🔴 >85% - Helps prevent token exhaustion during long sessions ### UI Utilities (setup/utils/ui.py) - Added new UI utility module for installer - Provides consistent user interface components ## Benefits - ✅ Clearer component naming (FrameworkDocs vs Core) - ✅ PM Agent token awareness for efficiency - ✅ Better visual feedback with status zones 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(pm-agent): minimize output verbosity (471→284 lines, 40% reduction) **Problem**: PM Agent generated excessive output with redundant explanations - "System Status Report" with decorative formatting - Repeated "Common Tasks" lists user already knows - Verbose session start/end protocols - Duplicate file operations documentation **Solution**: Compress without losing functionality - Session Start: Reduced to symbol-only status (🟢 branch | nM nD | token%) - Session End: Compressed to essential actions only - File Operations: Consolidated from 2 sections to 1 line reference - Self-Improvement: 5 phases → 1 unified workflow - Output Rules: Explicit constraints to prevent Claude over-explanation **Quality Preservation**: - ✅ All core functions retained (PDCA, memory, patterns, mistakes) - ✅ PARALLEL Read/Write preserved (performance critical) - ✅ Workflow unchanged (session lifecycle intact) - ✅ Added output constraints (prevents verbose generation) **Reduction Method**: - Deleted: Explanatory text, examples, redundant sections - Retained: Action definitions, file paths, core workflows - Added: Explicit output constraints to enforce minimalism **Token Impact**: 40% reduction in agent documentation size **Before**: Verbose multi-section report with task lists **After**: Single line status: 🟢 integration | 15M 17D | 36% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: consolidate MCP integration to unified gateway **Changes**: - Remove individual MCP server docs (superclaude/mcp/*.md) - Remove MCP server configs (superclaude/mcp/configs/*.json) - Delete MCP docs component (setup/components/mcp_docs.py) - Simplify installer (setup/core/installer.py) - Update components for unified gateway approach **Rationale**: - Unified gateway (airis-mcp-gateway) provides all MCP servers - Individual docs/configs no longer needed (managed centrally) - Reduces maintenance burden and file count - Simplifies installation process **Files Removed**: 17 MCP files (docs + configs) **Installer Changes**: Removed legacy MCP installation logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: update version and component metadata - Bump version (pyproject.toml, setup/__init__.py) - Update CLAUDE.md import service references - Reflect component structure changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: kazuki <kazuki@kazukinoMacBook-Air.local> Co-authored-by: Claude <noreply@anthropic.com>
336 lines
10 KiB
Python
336 lines
10 KiB
Python
"""
|
|
Logging system for SuperClaude installation suite
|
|
"""
|
|
|
|
import logging
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, Any
|
|
from enum import Enum
|
|
|
|
from rich.console import Console
|
|
from .symbols import symbols
|
|
from .paths import get_home_directory
|
|
|
|
# Rich console for colored output
|
|
console = Console()
|
|
|
|
|
|
class LogLevel(Enum):
|
|
"""Log levels"""
|
|
|
|
DEBUG = logging.DEBUG
|
|
INFO = logging.INFO
|
|
WARNING = logging.WARNING
|
|
ERROR = logging.ERROR
|
|
CRITICAL = logging.CRITICAL
|
|
|
|
|
|
class Logger:
|
|
"""Enhanced logger with console and file output"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str = "superclaude",
|
|
log_dir: Optional[Path] = None,
|
|
console_level: LogLevel = LogLevel.INFO,
|
|
file_level: LogLevel = LogLevel.DEBUG,
|
|
):
|
|
"""
|
|
Initialize logger
|
|
|
|
Args:
|
|
name: Logger name
|
|
log_dir: Directory for log files (defaults to ~/.claude/logs)
|
|
console_level: Minimum level for console output
|
|
file_level: Minimum level for file output
|
|
"""
|
|
self.name = name
|
|
self.log_dir = log_dir or (get_home_directory() / ".claude" / "logs")
|
|
self.console_level = console_level
|
|
self.file_level = file_level
|
|
self.session_start = datetime.now()
|
|
|
|
# Create logger
|
|
self.logger = logging.getLogger(name)
|
|
self.logger.setLevel(logging.DEBUG) # Accept all levels, handlers will filter
|
|
|
|
# Remove existing handlers to avoid duplicates
|
|
self.logger.handlers.clear()
|
|
|
|
# Setup handlers
|
|
self._setup_console_handler()
|
|
self._setup_file_handler()
|
|
|
|
self.log_counts: Dict[str, int] = {
|
|
"debug": 0,
|
|
"info": 0,
|
|
"warning": 0,
|
|
"error": 0,
|
|
"critical": 0,
|
|
}
|
|
|
|
def _setup_console_handler(self) -> None:
|
|
"""Setup colorized console handler using rich"""
|
|
from rich.logging import RichHandler
|
|
|
|
handler = RichHandler(
|
|
console=console,
|
|
show_time=False,
|
|
show_path=False,
|
|
markup=True,
|
|
rich_tracebacks=True,
|
|
tracebacks_show_locals=False,
|
|
)
|
|
handler.setLevel(self.console_level.value)
|
|
|
|
# Simple formatter (rich handles coloring)
|
|
formatter = logging.Formatter("%(message)s")
|
|
handler.setFormatter(formatter)
|
|
|
|
self.logger.addHandler(handler)
|
|
|
|
def _setup_file_handler(self) -> None:
|
|
"""Setup file handler with rotation"""
|
|
try:
|
|
# Ensure log directory exists
|
|
self.log_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Create timestamped log file
|
|
timestamp = self.session_start.strftime("%Y%m%d_%H%M%S")
|
|
log_file = self.log_dir / f"{self.name}_{timestamp}.log"
|
|
|
|
handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
handler.setLevel(self.file_level.value)
|
|
|
|
# Detailed formatter for files
|
|
formatter = logging.Formatter(
|
|
"%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
handler.setFormatter(formatter)
|
|
|
|
self.logger.addHandler(handler)
|
|
self.log_file = log_file
|
|
|
|
# Clean up old log files (keep last 10)
|
|
self._cleanup_old_logs()
|
|
|
|
except Exception as e:
|
|
# If file logging fails, continue with console only
|
|
console.print(f"[yellow][!] Could not setup file logging: {e}[/yellow]")
|
|
self.log_file = None
|
|
|
|
def _cleanup_old_logs(self, keep_count: int = 10) -> None:
|
|
"""Clean up old log files"""
|
|
try:
|
|
# Get all log files for this logger
|
|
log_files = list(self.log_dir.glob(f"{self.name}_*.log"))
|
|
|
|
# Sort by modification time, newest first
|
|
log_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
|
|
|
|
# Remove old files
|
|
for old_file in log_files[keep_count:]:
|
|
try:
|
|
old_file.unlink()
|
|
except OSError:
|
|
pass # Ignore errors when cleaning up
|
|
|
|
except Exception:
|
|
pass # Ignore cleanup errors
|
|
|
|
def debug(self, message: str, **kwargs) -> None:
|
|
"""Log debug message"""
|
|
self.logger.debug(message, **kwargs)
|
|
self.log_counts["debug"] += 1
|
|
|
|
def info(self, message: str, **kwargs) -> None:
|
|
"""Log info message"""
|
|
self.logger.info(message, **kwargs)
|
|
self.log_counts["info"] += 1
|
|
|
|
def warning(self, message: str, **kwargs) -> None:
|
|
"""Log warning message"""
|
|
self.logger.warning(message, **kwargs)
|
|
self.log_counts["warning"] += 1
|
|
|
|
def error(self, message: str, **kwargs) -> None:
|
|
"""Log error message"""
|
|
self.logger.error(message, **kwargs)
|
|
self.log_counts["error"] += 1
|
|
|
|
def critical(self, message: str, **kwargs) -> None:
|
|
"""Log critical message"""
|
|
self.logger.critical(message, **kwargs)
|
|
self.log_counts["critical"] += 1
|
|
|
|
def success(self, message: str, **kwargs) -> None:
|
|
"""Log success message (info level with special formatting)"""
|
|
# Use rich markup for success messages
|
|
success_msg = f"[green]{symbols.checkmark} {message}[/green]"
|
|
self.logger.info(success_msg, **kwargs)
|
|
self.log_counts["info"] += 1
|
|
|
|
def step(self, step: int, total: int, message: str, **kwargs) -> None:
|
|
"""Log step progress"""
|
|
step_msg = f"[{step}/{total}] {message}"
|
|
self.info(step_msg, **kwargs)
|
|
|
|
def section(self, title: str, **kwargs) -> None:
|
|
"""Log section header"""
|
|
separator = "=" * min(50, len(title) + 4)
|
|
self.info(separator, **kwargs)
|
|
self.info(f" {title}", **kwargs)
|
|
self.info(separator, **kwargs)
|
|
|
|
def exception(self, message: str, exc_info: bool = True, **kwargs) -> None:
|
|
"""Log exception with traceback"""
|
|
self.logger.error(message, exc_info=exc_info, **kwargs)
|
|
self.log_counts["error"] += 1
|
|
|
|
def log_system_info(self, info: Dict[str, Any]) -> None:
|
|
"""Log system information"""
|
|
self.section("System Information")
|
|
for key, value in info.items():
|
|
self.info(f"{key}: {value}")
|
|
|
|
def log_operation_start(
|
|
self, operation: str, details: Optional[Dict[str, Any]] = None
|
|
) -> None:
|
|
"""Log start of operation"""
|
|
self.section(f"Starting: {operation}")
|
|
if details:
|
|
for key, value in details.items():
|
|
self.info(f"{key}: {value}")
|
|
|
|
def log_operation_end(
|
|
self,
|
|
operation: str,
|
|
success: bool,
|
|
duration: float,
|
|
details: Optional[Dict[str, Any]] = None,
|
|
) -> None:
|
|
"""Log end of operation"""
|
|
status = "SUCCESS" if success else "FAILED"
|
|
self.info(
|
|
f"Operation {operation} completed: {status} (Duration: {duration:.2f}s)"
|
|
)
|
|
|
|
if details:
|
|
for key, value in details.items():
|
|
self.info(f"{key}: {value}")
|
|
|
|
def get_statistics(self) -> Dict[str, Any]:
|
|
"""Get logging statistics"""
|
|
runtime = datetime.now() - self.session_start
|
|
|
|
return {
|
|
"session_start": self.session_start.isoformat(),
|
|
"runtime_seconds": runtime.total_seconds(),
|
|
"log_counts": self.log_counts.copy(),
|
|
"total_messages": sum(self.log_counts.values()),
|
|
"log_file": (
|
|
str(self.log_file)
|
|
if hasattr(self, "log_file") and self.log_file
|
|
else None
|
|
),
|
|
"has_errors": self.log_counts["error"] + self.log_counts["critical"] > 0,
|
|
}
|
|
|
|
def set_console_level(self, level: LogLevel) -> None:
|
|
"""Change console logging level"""
|
|
self.console_level = level
|
|
if self.logger.handlers:
|
|
self.logger.handlers[0].setLevel(level.value)
|
|
|
|
def set_file_level(self, level: LogLevel) -> None:
|
|
"""Change file logging level"""
|
|
self.file_level = level
|
|
if len(self.logger.handlers) > 1:
|
|
self.logger.handlers[1].setLevel(level.value)
|
|
|
|
def flush(self) -> None:
|
|
"""Flush all handlers"""
|
|
for handler in self.logger.handlers:
|
|
if hasattr(handler, "flush"):
|
|
handler.flush()
|
|
|
|
def close(self) -> None:
|
|
"""Close logger and handlers"""
|
|
self.section("Installation Session Complete")
|
|
stats = self.get_statistics()
|
|
|
|
self.info(f"Total runtime: {stats['runtime_seconds']:.1f} seconds")
|
|
self.info(f"Messages logged: {stats['total_messages']}")
|
|
if stats["has_errors"]:
|
|
self.warning(
|
|
f"Errors/warnings: {stats['log_counts']['error'] + stats['log_counts']['warning']}"
|
|
)
|
|
|
|
if stats["log_file"]:
|
|
self.info(f"Full log saved to: {stats['log_file']}")
|
|
|
|
# Close all handlers
|
|
for handler in self.logger.handlers[:]:
|
|
handler.close()
|
|
self.logger.removeHandler(handler)
|
|
|
|
|
|
# Global logger instance
|
|
_global_logger: Optional[Logger] = None
|
|
|
|
|
|
def get_logger(name: str = "superclaude") -> Logger:
|
|
"""Get or create global logger instance"""
|
|
global _global_logger
|
|
|
|
if _global_logger is None or _global_logger.name != name:
|
|
_global_logger = Logger(name)
|
|
|
|
return _global_logger
|
|
|
|
|
|
def setup_logging(
|
|
name: str = "superclaude",
|
|
log_dir: Optional[Path] = None,
|
|
console_level: LogLevel = LogLevel.INFO,
|
|
file_level: LogLevel = LogLevel.DEBUG,
|
|
) -> Logger:
|
|
"""Setup logging with specified configuration"""
|
|
global _global_logger
|
|
_global_logger = Logger(name, log_dir, console_level, file_level)
|
|
return _global_logger
|
|
|
|
|
|
# Convenience functions using global logger
|
|
def debug(message: str, **kwargs) -> None:
|
|
"""Log debug message using global logger"""
|
|
get_logger().debug(message, **kwargs)
|
|
|
|
|
|
def info(message: str, **kwargs) -> None:
|
|
"""Log info message using global logger"""
|
|
get_logger().info(message, **kwargs)
|
|
|
|
|
|
def warning(message: str, **kwargs) -> None:
|
|
"""Log warning message using global logger"""
|
|
get_logger().warning(message, **kwargs)
|
|
|
|
|
|
def error(message: str, **kwargs) -> None:
|
|
"""Log error message using global logger"""
|
|
get_logger().error(message, **kwargs)
|
|
|
|
|
|
def critical(message: str, **kwargs) -> None:
|
|
"""Log critical message using global logger"""
|
|
get_logger().critical(message, **kwargs)
|
|
|
|
|
|
def success(message: str, **kwargs) -> None:
|
|
"""Log success message using global logger"""
|
|
get_logger().success(message, **kwargs)
|