refactor: restructure core modules into context and memory packages

- Move pm_init components to dedicated packages
- context/: PM mode initialization and contracts
- memory/: Reflexion memory system
- Remove deprecated superclaude/core/pm_init/

Breaking change: Import paths updated
- Old: superclaude.core.pm_init.context_contract
- New: superclaude.context.contract

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kazuki
2025-10-20 03:52:27 +09:00
parent 328a0a016b
commit c5690e4a71
8 changed files with 41 additions and 55 deletions

View File

@@ -0,0 +1,12 @@
"""Project Context Management
Detects and manages project-specific configuration:
- Context Contract (project rules)
- Project structure detection
- Initialization
"""
from .contract import ContextContract
from .init import initialize_context
__all__ = ["ContextContract", "initialize_context"]

View File

@@ -0,0 +1,139 @@
"""Context Contract System
Auto-generates project-specific rules that must be enforced:
- Infrastructure patterns (Kong, Traefik, Infisical)
- Security policies (.env禁止, 秘密値管理)
- Runtime requirements
- Validation requirements
"""
from pathlib import Path
from typing import Dict, Any, List
import yaml
class ContextContract:
"""Manages project-specific Context Contract"""
def __init__(self, git_root: Path, structure: Dict[str, Any]):
self.git_root = git_root
self.structure = structure
self.contract_path = git_root / "docs" / "memory" / "context-contract.yaml"
def detect_principles(self) -> Dict[str, Any]:
"""Detect project-specific principles from structure"""
principles = {}
# Infisical detection
if self.structure.get("infrastructure", {}).get("infisical"):
principles["use_infisical_only"] = True
principles["no_env_files"] = True
else:
principles["use_infisical_only"] = False
principles["no_env_files"] = False
# Kong detection
if self.structure.get("infrastructure", {}).get("kong"):
principles["outbound_through"] = "kong"
# Traefik detection
elif self.structure.get("infrastructure", {}).get("traefik"):
principles["outbound_through"] = "traefik"
else:
principles["outbound_through"] = None
# Supabase detection
if self.structure.get("infrastructure", {}).get("supabase"):
principles["supabase_integration"] = True
else:
principles["supabase_integration"] = False
return principles
def detect_runtime(self) -> Dict[str, Any]:
"""Detect runtime requirements"""
runtime = {}
# Node.js
if "package.json" in self.structure.get("package_managers", {}).get("node", []):
if "pnpm-lock.yaml" in self.structure.get("package_managers", {}).get("node", []):
runtime["node"] = {
"manager": "pnpm",
"source": "lockfile-defined"
}
else:
runtime["node"] = {
"manager": "npm",
"source": "package-json-defined"
}
# Python
if "pyproject.toml" in self.structure.get("package_managers", {}).get("python", []):
if "uv.lock" in self.structure.get("package_managers", {}).get("python", []):
runtime["python"] = {
"manager": "uv",
"source": "lockfile-defined"
}
else:
runtime["python"] = {
"manager": "pip",
"source": "pyproject-defined"
}
return runtime
def detect_validators(self) -> List[str]:
"""Detect required validators"""
validators = [
"deps_exist_on_registry",
"tests_must_run"
]
principles = self.detect_principles()
if principles.get("use_infisical_only"):
validators.append("no_env_file_creation")
validators.append("no_hardcoded_secrets")
if principles.get("outbound_through"):
validators.append("outbound_through_proxy")
return validators
def generate_contract(self) -> Dict[str, Any]:
"""Generate Context Contract from detected structure"""
return {
"version": "1.0.0",
"generated_at": "auto",
"principles": self.detect_principles(),
"runtime": self.detect_runtime(),
"validators": self.detect_validators(),
"structure_snapshot": self.structure
}
def load_contract(self) -> Dict[str, Any]:
"""Load existing Context Contract"""
if not self.contract_path.exists():
return {}
with open(self.contract_path, "r") as f:
return yaml.safe_load(f)
def save_contract(self, contract: Dict[str, Any]) -> None:
"""Save Context Contract to disk"""
self.contract_path.parent.mkdir(parents=True, exist_ok=True)
with open(self.contract_path, "w") as f:
yaml.dump(contract, f, default_flow_style=False, sort_keys=False)
def generate_or_load(self) -> Dict[str, Any]:
"""Generate or load Context Contract"""
# Try to load existing
existing = self.load_contract()
# If exists and version matches, return it
if existing and existing.get("version") == "1.0.0":
return existing
# Otherwise, generate new contract
contract = self.generate_contract()
self.save_contract(contract)
return contract

134
superclaude/context/init.py Normal file
View File

@@ -0,0 +1,134 @@
"""Context Initialization
Runs at session start to:
1. Detect repository root and structure
2. Generate Context Contract
3. Load Reflexion Memory
4. Set up project context
"""
import os
import subprocess
from pathlib import Path
from typing import Optional, Dict, Any
import yaml
from .contract import ContextContract
from superclaude.memory import ReflexionMemory
class PMInitializer:
"""Initializes PM Mode with project context"""
def __init__(self, cwd: Optional[Path] = None):
self.cwd = cwd or Path.cwd()
self.git_root: Optional[Path] = None
self.config: Dict[str, Any] = {}
def detect_git_root(self) -> Optional[Path]:
"""Detect Git repository root"""
try:
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
cwd=self.cwd,
capture_output=True,
text=True,
check=False
)
if result.returncode == 0:
return Path(result.stdout.strip())
except Exception:
pass
return None
def scan_project_structure(self) -> Dict[str, Any]:
"""Lightweight scan of project structure (paths only, no content)"""
if not self.git_root:
return {}
structure = {
"docker_compose": [],
"infrastructure": {
"traefik": [],
"kong": [],
"supabase": [],
"infisical": []
},
"package_managers": {
"node": [],
"python": []
},
"config_files": []
}
# Docker Compose files
for pattern in ["docker-compose*.yml", "docker-compose*.yaml"]:
structure["docker_compose"].extend([
str(p.relative_to(self.git_root))
for p in self.git_root.glob(pattern)
])
# Infrastructure directories
for infra_type in ["traefik", "kong", "supabase", "infisical"]:
infra_path = self.git_root / "infra" / infra_type
if infra_path.exists():
structure["infrastructure"][infra_type].append(str(infra_path.relative_to(self.git_root)))
# Package managers
if (self.git_root / "package.json").exists():
structure["package_managers"]["node"].append("package.json")
if (self.git_root / "pnpm-lock.yaml").exists():
structure["package_managers"]["node"].append("pnpm-lock.yaml")
if (self.git_root / "pyproject.toml").exists():
structure["package_managers"]["python"].append("pyproject.toml")
if (self.git_root / "uv.lock").exists():
structure["package_managers"]["python"].append("uv.lock")
return structure
def initialize(self) -> Dict[str, Any]:
"""Main initialization routine"""
# Step 1: Detect Git root
self.git_root = self.detect_git_root()
if not self.git_root:
return {
"status": "not_git_repo",
"message": "Not a Git repository - PM Mode running in standalone mode"
}
# Step 2: Scan project structure (lightweight)
structure = self.scan_project_structure()
# Step 3: Generate or load Context Contract
contract = ContextContract(self.git_root, structure)
contract_data = contract.generate_or_load()
# Step 4: Load Reflexion Memory
memory = ReflexionMemory(self.git_root)
memory_data = memory.load()
# Step 5: Return initialization data
return {
"status": "initialized",
"git_root": str(self.git_root),
"structure": structure,
"context_contract": contract_data,
"reflexion_memory": memory_data,
"message": "PM Mode initialized successfully"
}
def initialize_context(cwd: Optional[Path] = None) -> Dict[str, Any]:
"""
Initialize project context.
This function runs at session start.
Args:
cwd: Current working directory (defaults to os.getcwd())
Returns:
Initialization status and configuration
"""
initializer = PMInitializer(cwd)
return initializer.initialize()