SuperClaude/setup/services/claude_md.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

335 lines
12 KiB
Python

"""
CLAUDE.md Manager for preserving user customizations while managing framework imports
"""
import re
from pathlib import Path
from typing import List, Set, Dict, Optional
from ..utils.logger import get_logger
class CLAUDEMdService:
"""Manages CLAUDE.md file updates while preserving user customizations"""
def __init__(self, install_dir: Path):
"""
Initialize CLAUDEMdService
Args:
install_dir: Installation directory (typically ~/.claude/superclaude)
"""
self.install_dir = install_dir
# CLAUDE.md is always in parent directory (~/.claude/)
self.claude_md_path = install_dir.parent / "CLAUDE.md"
self.logger = get_logger()
def read_existing_imports(self) -> Set[str]:
"""
Parse CLAUDE.md for existing @import statements
Returns:
Set of already imported filenames (without @)
"""
existing_imports = set()
if not self.claude_md_path.exists():
return existing_imports
try:
with open(self.claude_md_path, "r", encoding="utf-8") as f:
content = f.read()
# Find all @import statements using regex
# Supports both @superclaude/file.md and @file.md (legacy)
import_pattern = r"^@(?:superclaude/)?([^\s\n]+\.md)\s*$"
matches = re.findall(import_pattern, content, re.MULTILINE)
existing_imports.update(matches)
self.logger.debug(f"Found existing imports: {existing_imports}")
except Exception as e:
self.logger.warning(f"Could not read existing CLAUDE.md imports: {e}")
return existing_imports
def read_existing_content(self) -> str:
"""
Read existing CLAUDE.md content
Returns:
Existing content or empty string if file doesn't exist
"""
if not self.claude_md_path.exists():
return ""
try:
with open(self.claude_md_path, "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
self.logger.warning(f"Could not read existing CLAUDE.md: {e}")
return ""
def extract_user_content(self, content: str) -> str:
"""
Extract user content (everything before framework imports section)
Args:
content: Full CLAUDE.md content
Returns:
User content without framework imports
"""
# Look for framework imports section marker
framework_marker = "# ===================================================\n# SuperClaude Framework Components"
if framework_marker in content:
user_content = content.split(framework_marker)[0].rstrip()
else:
# If no framework section exists, preserve all content
user_content = content.rstrip()
return user_content
def organize_imports_by_category(
self, files_by_category: Dict[str, List[str]]
) -> str:
"""
Organize imports into categorized sections
Args:
files_by_category: Dict mapping category names to lists of files
Returns:
Formatted import sections
"""
if not files_by_category:
return ""
sections = []
# Framework imports section header
sections.append("# ===================================================")
sections.append("# SuperClaude Framework Components")
sections.append("# ===================================================")
sections.append("")
# Add each category
for category, files in files_by_category.items():
if files:
sections.append(f"# {category}")
for file in sorted(files):
# Add superclaude/ prefix for all imports
sections.append(f"@superclaude/{file}")
sections.append("")
return "\n".join(sections)
def add_imports(self, files: List[str], category: str = "Framework") -> bool:
"""
Add new imports with duplicate checking and user content preservation
Args:
files: List of filenames to import
category: Category name for organizing imports
Returns:
True if successful, False otherwise
"""
try:
# Check if CLAUDE.md exists (DO NOT create it)
if not self.ensure_claude_md_exists():
self.logger.info("Skipping CLAUDE.md update (file does not exist)")
return False
# Read existing content and imports
existing_content = self.read_existing_content()
existing_imports = self.read_existing_imports()
# Filter out files already imported
new_files = [f for f in files if f not in existing_imports]
if not new_files:
self.logger.info("All files already imported, no changes needed")
return True
self.logger.info(
f"Adding {len(new_files)} new imports to category '{category}': {new_files}"
)
# Extract user content (preserve everything before framework section)
user_content = self.extract_user_content(existing_content)
# Parse existing framework imports by category
existing_framework_imports = self._parse_existing_framework_imports(
existing_content
)
# Add new files to the specified category
if category not in existing_framework_imports:
existing_framework_imports[category] = []
existing_framework_imports[category].extend(new_files)
# Build new content
new_content_parts = []
# Add user content
if user_content.strip():
new_content_parts.append(user_content)
new_content_parts.append("") # Add blank line before framework section
# Add organized framework imports
framework_section = self.organize_imports_by_category(
existing_framework_imports
)
if framework_section:
new_content_parts.append(framework_section)
# Write updated content
new_content = "\n".join(new_content_parts)
with open(self.claude_md_path, "w", encoding="utf-8") as f:
f.write(new_content)
self.logger.success(f"Updated CLAUDE.md with {len(new_files)} new imports")
return True
except Exception as e:
self.logger.error(f"Failed to update CLAUDE.md: {e}")
return False
def _parse_existing_framework_imports(self, content: str) -> Dict[str, List[str]]:
"""
Parse existing framework imports organized by category
Args:
content: Full CLAUDE.md content
Returns:
Dict mapping category names to lists of imported files
"""
imports_by_category = {}
# Look for framework imports section
framework_marker = "# ===================================================\n# SuperClaude Framework Components"
if framework_marker not in content:
return imports_by_category
# Extract framework section
framework_section = (
content.split(framework_marker)[1] if framework_marker in content else ""
)
# Parse categories and imports
lines = framework_section.split("\n")
current_category = None
for line in lines:
line = line.strip()
# Skip section header lines and empty lines
if line.startswith("# ===") or not line:
continue
# Category header (starts with # but not the section divider)
if line.startswith("# ") and not line.startswith("# ==="):
current_category = line[2:].strip() # Remove "# "
if current_category not in imports_by_category:
imports_by_category[current_category] = []
# Import line (starts with @)
elif line.startswith("@") and current_category:
import_file = line[1:].strip() # Remove "@"
# Remove superclaude/ prefix if present (normalize to filename only)
if import_file.startswith("superclaude/"):
import_file = import_file[len("superclaude/"):]
if import_file not in imports_by_category[current_category]:
imports_by_category[current_category].append(import_file)
return imports_by_category
def ensure_claude_md_exists(self) -> bool:
"""
Check if CLAUDE.md exists (DO NOT create it - Claude Code pure file)
Returns:
True if CLAUDE.md exists, False otherwise
"""
if self.claude_md_path.exists():
return True
# CLAUDE.md is a Claude Code pure file - NEVER create or modify it
self.logger.warning(
f"⚠️ CLAUDE.md not found at {self.claude_md_path}\n"
f" SuperClaude will NOT create this file automatically.\n"
f" Please manually add the following to your CLAUDE.md:\n\n"
f" # SuperClaude Framework Components\n"
f" @superclaude/FLAGS.md\n"
f" @superclaude/PRINCIPLES.md\n"
f" @superclaude/RULES.md\n"
f" (and other SuperClaude components)\n"
)
return False
def remove_imports(self, files: List[str]) -> bool:
"""
Remove specific imports from CLAUDE.md
Args:
files: List of filenames to remove from imports
Returns:
True if successful, False otherwise
"""
try:
if not self.claude_md_path.exists():
return True # Nothing to remove
existing_content = self.read_existing_content()
user_content = self.extract_user_content(existing_content)
existing_framework_imports = self._parse_existing_framework_imports(
existing_content
)
# Remove files from all categories
removed_any = False
for category, category_files in existing_framework_imports.items():
for file in files:
if file in category_files:
category_files.remove(file)
removed_any = True
# Remove empty categories
existing_framework_imports = {
k: v for k, v in existing_framework_imports.items() if v
}
if not removed_any:
return True # Nothing was removed
# Rebuild content
new_content_parts = []
if user_content.strip():
new_content_parts.append(user_content)
new_content_parts.append("")
framework_section = self.organize_imports_by_category(
existing_framework_imports
)
if framework_section:
new_content_parts.append(framework_section)
# Write updated content
new_content = "\n".join(new_content_parts)
with open(self.claude_md_path, "w", encoding="utf-8") as f:
f.write(new_content)
self.logger.info(f"Removed {len(files)} imports from CLAUDE.md")
return True
except Exception as e:
self.logger.error(f"Failed to remove imports from CLAUDE.md: {e}")
return False