SuperClaude/setup/utils/logger.py
kazuki nakai 882a0d8356
refactor: PM Agent complete independence from external MCP servers (#439)
* 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>
2025-10-17 05:43:06 +05:30

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)