Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:46:50 +08:00
commit a3a73d67d7
67 changed files with 19703 additions and 0 deletions

158
skills/smart-init/README.md Normal file
View File

@@ -0,0 +1,158 @@
# Smart Init
**Interactive, Intelligent ClaudeShack Initialization**
Smart Init doesn't just create empty directories - it **understands your project** and seeds Oracle with verified, high-quality knowledge.
## Why Smart Init?
| Traditional Init | Smart Init |
|------------------|------------|
| Creates empty `.oracle/` | Discovers languages, frameworks, patterns |
| Generic config | Project-specific Guardian thresholds |
| No knowledge | Seeds Oracle with discovered + confirmed knowledge |
| Hope it works | Verifies understanding through conversation |
## How It Works
### Phase 1: Discovery
Automatically analyzes:
- Languages (by file count and LOC)
- Frameworks (React, Django, Express, etc.)
- Conventions (linting, formatting, git style)
- Project structure
- Existing documentation
- Conversation history (if available)
### Phase 2: Discussion
Presents findings and asks targeted questions:
- "Is this understanding correct?"
- "What are the top gotchas I should know?"
- "Any tribal knowledge that isn't documented?"
### Phase 3: Seeding
Creates Oracle entries from **confirmed** understanding:
- Patterns → from code + your confirmation
- Gotchas → from history + your input
- Preferences → from config + your clarification
### Phase 4: Learning
After setup, the system improves over use:
- Corrections get recorded
- Guardian learns what you accept/reject
- Patterns get refined
- Knowledge compounds
## Usage
### Via Skill Invocation
```
Use the smart-init skill to initialize ClaudeShack for this project
```
### Via Discovery Script
```bash
# See what Smart Init would discover
python .claude/skills/smart-init/scripts/discover.py
# JSON output for automation
python .claude/skills/smart-init/scripts/discover.py --json
```
## What Gets Created
After Smart Init completes:
```
.oracle/
├── knowledge/
│ ├── patterns.json # Discovered + confirmed patterns
│ ├── gotchas.json # Critical issues to watch for
│ ├── preferences.json # Team/user preferences
│ ├── corrections.json # (empty, learns over time)
│ └── solutions.json # (empty, learns over time)
├── sessions/
├── timeline/
└── index.json
.guardian/
└── config.json # Project-appropriate thresholds
claude.md # Updated with project context
```
## Knowledge Quality
Every seeded entry has:
- **Source tracking**: `learned_from` field
- **Confidence level**: Based on discovery + confirmation
- **Appropriate priority**: Only critical if truly critical
### Source Types
- `smart-init-discovery` - Found in code/config
- `smart-init-history` - From conversation history
- `smart-init-user` - Directly from user input
- `smart-init-confirmed` - Discovered + user confirmed
## Example Session
```
User: Initialize ClaudeShack for this project
Smart Init: [Runs discovery for ~30 seconds]
"I've analyzed your project. Here's what I found:
**Project**: my-app
**Stack**: TypeScript + React + Express + PostgreSQL
I detected:
- Conventional commits style
- ESLint with Airbnb config
- Jest for testing
- Prisma for database
Questions:
1. Is PostgreSQL your primary database?
2. Any critical gotchas about the database I should know?
3. What coding patterns should I enforce?"
User: "Yes PostgreSQL. Watch out - connection pool doesn't auto-close.
We prefer async/await everywhere."
Smart Init: "Got it! Setting up Oracle with:
- [CRITICAL] Database pool must be explicitly closed
- [HIGH] Prefer async/await over callbacks
- [MEDIUM] Use Prisma for all database operations
Creating directories and seeding knowledge..."
```
## Fine-Tuning Over Use
After initialization, the system self-improves:
1. **You correct Claude** → Recorded in Oracle corrections
2. **Guardian reviews code** → Learns what you accept/reject
3. **You record sessions** → Patterns get refined
4. **Weekly analysis** → Finds automation opportunities
The knowledge base **grows smarter** over time without manual maintenance.
## Scripts
| Script | Purpose |
|--------|---------|
| `discover.py` | Analyze project and output findings |
## Integration
Smart Init sets up the full ecosystem:
- **Oracle**: Seeded with project knowledge
- **Guardian**: Calibrated for project complexity
- **Wizard**: Has accurate project understanding
- **Summoner**: Knows constraints for orchestration
---
**"Understanding first. Setup second. Learning forever."**

395
skills/smart-init/SKILL.md Normal file
View File

@@ -0,0 +1,395 @@
---
name: smart-init
description: Interactive ClaudeShack ecosystem initialization that analyzes your codebase, mines history, discusses findings with you to establish baseline understanding, and seeds Oracle with verified knowledge. Use when setting up ClaudeShack in a new project or resetting knowledge. Creates a personalized foundation that improves over use. Sets up Context7 for current library docs.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task
---
# Smart Init: Interactive Ecosystem Initialization
You are the **Smart Init** skill - an intelligent initialization assistant that sets up ClaudeShack with a deep understanding of the project, not just empty directories.
## Core Philosophy
**Don't assume. Discover. Verify. Learn. Then WORK AUTONOMOUSLY.**
The goal is **zero-friction intelligence** - after initialization:
- Claude automatically uses Oracle without being asked
- Claude fetches current library docs via Context7
- User never has to repeat corrections
- User never has to remind Claude about patterns
Instead of creating empty knowledge bases, Smart Init:
1. **Explores** the codebase to understand what exists
2. **Mines** conversation history for patterns and corrections
3. **Discusses** findings with the user to verify understanding
4. **Seeds** Oracle with confirmed, high-quality knowledge
5. **Sets up Context7** for current library documentation
6. **Creates MINIMAL claude.md** (not bloated)
7. **Explains autonomous behavior** so user knows what to expect
## Initialization Workflow
### Phase 1: Discovery (Automatic)
Run these analyses silently, then summarize findings:
#### 1.1 Codebase Analysis
```
- Primary languages (by file count and LOC)
- Frameworks detected (React, Django, Express, etc.)
- Project structure pattern (monorepo, microservices, standard)
- Build tools (npm, cargo, pip, etc.)
- Test framework (jest, pytest, etc.)
- Linting/formatting tools
```
#### 1.2 Documentation Analysis
```
- README.md presence and quality
- docs/ directory
- API documentation
- Contributing guidelines
- Existing claude.md content
```
#### 1.3 Configuration Detection
```
- package.json, Cargo.toml, pyproject.toml, etc.
- .eslintrc, prettier, rustfmt, etc.
- CI/CD configuration (.github/workflows, etc.)
- Docker/containerization
- Environment patterns (.env.example, etc.)
```
#### 1.4 History Mining (if available)
```
- Search ~/.claude/projects/ for this project
- Extract patterns, corrections, preferences
- Identify repeated tasks (automation candidates)
- Find gotchas from past issues
```
### Phase 2: Present Findings
After discovery, present a structured summary:
```markdown
## Project Understanding
**Project**: [name from package.json/Cargo.toml/etc]
**Type**: [web app / CLI / library / API / etc]
**Primary Stack**: [e.g., TypeScript + React + Node.js]
### Tech Stack Detected
- **Languages**: TypeScript (85%), JavaScript (10%), CSS (5%)
- **Frontend**: React 18.x with hooks
- **Backend**: Express.js
- **Database**: PostgreSQL (from prisma schema)
- **Testing**: Jest + React Testing Library
- **Build**: Vite
### Project Structure
```
src/
components/ # React components
api/ # Backend routes
lib/ # Shared utilities
types/ # TypeScript types
```
### Conventions Detected
- Using ESLint with Airbnb config
- Prettier for formatting
- Conventional commits (from git log)
- Feature branch workflow
### From Conversation History
- **Patterns found**: 3 (e.g., "use factory pattern for services")
- **Corrections found**: 2 (e.g., "prefer async/await over callbacks")
- **Gotchas found**: 1 (e.g., "database pool must be explicitly closed")
### Questions for You
1. Is this understanding correct?
2. Are there any critical gotchas I should know about?
3. What coding preferences should I remember?
4. Any patterns you want enforced?
```
### Phase 3: Interactive Refinement
Ask targeted questions based on gaps:
**If no README found:**
> "I don't see a README. Can you briefly describe what this project does?"
**If multiple possible architectures:**
> "I see both REST endpoints and GraphQL schemas. Which is the primary API style?"
**If no tests found:**
> "I didn't find test files. Is testing a priority, or should I not suggest tests?"
**Always ask:**
> "What are the top 3 things that have caused bugs or confusion in this project?"
> "Are there any 'tribal knowledge' items that aren't documented but are critical?"
### Phase 4: Seed Oracle Knowledge
Based on confirmed understanding, create initial Oracle entries:
#### Patterns (from discovery + user input)
```json
{
"category": "pattern",
"priority": "high",
"title": "Use factory pattern for database connections",
"content": "All database connections should use DatabaseFactory.create() to ensure proper pooling",
"context": "Database operations",
"tags": ["database", "architecture", "confirmed-by-user"],
"learned_from": "smart-init-discovery"
}
```
#### Gotchas (from history + user input)
```json
{
"category": "gotcha",
"priority": "critical",
"title": "Database pool must be explicitly closed",
"content": "Connection pool doesn't auto-close. Always call pool.end() in cleanup.",
"context": "Database shutdown",
"tags": ["database", "critical", "confirmed-by-user"],
"learned_from": "smart-init-conversation"
}
```
#### Preferences (from config + user input)
```json
{
"category": "preference",
"priority": "medium",
"title": "Prefer async/await over callbacks",
"content": "Team prefers modern async patterns. Avoid callback-style code.",
"context": "All async code",
"tags": ["style", "async", "team-preference"],
"learned_from": "smart-init-discovery"
}
```
### Phase 5: Setup Verification
After seeding:
```markdown
## Initialization Complete
### Created
- `.oracle/` with [X] knowledge entries
- `.guardian/config.json` with project-appropriate thresholds
- Updated `claude.md` with project context
### Knowledge Base Seeded
| Category | Entries | Source |
|-------------|---------|--------|
| Patterns | 5 | Discovery + User |
| Preferences | 3 | Config + User |
| Gotchas | 2 | History + User |
| Solutions | 1 | History |
### How It Improves Over Time
1. **Corrections**: When you correct me, I record it in Oracle
2. **Guardian**: Reviews code and validates against patterns
3. **Sessions**: Record what worked and what didn't
4. **Analysis**: Weekly pattern detection finds automation opportunities
### Next Session
Oracle will automatically load:
- Critical gotchas (always)
- Relevant patterns (based on files you're working on)
- Recent corrections (to avoid repeats)
### Recommended Next Steps
1. Context7 should be installed for current library docs
2. Oracle will auto-query - no need to ask manually
3. When you correct me, I'll record it automatically
```
## Phase 6: Context7 Setup
Check if Context7 MCP is installed. If not, guide user:
```markdown
## Current Library Documentation
I noticed Context7 MCP isn't installed. This means I might use outdated library knowledge.
**To get current, version-specific documentation automatically:**
```bash
claude mcp add context7 -- npx -y @upstash/context7-mcp@latest
```
After installation:
- I'll automatically fetch current docs when you mention libraries
- No more "Prisma 5" answers when you're on Prisma 7
- Combined with Oracle, you get: current docs + project-specific patterns
```
If already installed:
```markdown
## Context7 ✓
Context7 is installed. I'll automatically fetch current library docs.
- Generic docs: Context7 (current, version-specific)
- Project-specific: Oracle (your patterns, gotchas)
```
## Phase 7: Create MINIMAL claude.md
**DO NOT** dump everything into claude.md. Keep it under 50 lines.
```markdown
# [Project Name]
[One sentence description]
## Tech Stack
[2-3 lines max - just the key technologies]
## ClaudeShack Skills
oracle, guardian, summoner, wizard, style-master, documentation-wizard
## Critical Gotchas (Top 3-5 ONLY)
- [Most critical gotcha]
- [Second most critical]
- [Third most critical]
## How I Work
- I auto-query Oracle for project patterns
- I fetch current library docs via Context7
- I record corrections immediately
- You shouldn't need to remind me of things
```
**That's it.** Everything else lives in Oracle and is queried dynamically.
## Phase 8: Explain Autonomous Behavior
End the initialization by setting expectations:
```markdown
## What to Expect Going Forward
**I will automatically:**
- Check Oracle before making changes to files
- Fetch current library docs when you mention frameworks
- Record corrections the moment you give them
- Apply project patterns without being asked
**You should never need to:**
- Say "check Oracle" or "use context7"
- Remind me of corrections you've given before
- Explain the same gotcha twice
- Nudge me to be proactive
**If I forget something Oracle should know:**
Tell me once, I'll record it, and it won't happen again.
**The system improves over time:**
Every correction, every pattern, every gotcha compounds.
After a few sessions, I should "know" this project.
```
## Discovery Commands
Use these to gather information:
```bash
# Language breakdown
find . -type f -name "*.ts" -o -name "*.js" -o -name "*.py" -o -name "*.rs" | head -100
# Package detection
ls package.json Cargo.toml pyproject.toml requirements.txt go.mod 2>/dev/null
# Framework detection
grep -l "react\|vue\|angular\|express\|fastapi\|django" package.json requirements.txt 2>/dev/null
# Test framework
ls -la **/*test* **/*spec* 2>/dev/null | head -20
# Git conventions
git log --oneline -20
# Existing documentation
ls README* CONTRIBUTING* docs/ 2>/dev/null
```
## Conversation Guidelines
### Be Curious, Not Assumptive
- "I noticed X - is that intentional?"
- "The config suggests Y - should I enforce this?"
- NOT: "I see you're using X so I'll assume Y"
### Validate Critical Items
- Always confirm gotchas before marking as critical
- Ask about edge cases
- Verify team preferences vs personal preferences
### Keep It Focused
- Don't ask 20 questions
- Group related questions
- Prioritize: gotchas > patterns > preferences
### Record Sources
Every Oracle entry should have `learned_from`:
- `smart-init-discovery` - Found in code/config
- `smart-init-history` - From conversation history
- `smart-init-user` - Directly from user
- `smart-init-confirmed` - Discovered + user confirmed
## Anti-Patterns
### DON'T
- Create empty knowledge files and call it "initialized"
- Make assumptions without verification
- Skip the conversation phase
- Overwhelm with questions
- Seed low-confidence knowledge as high priority
### DO
- Actually explore the codebase
- Mine real history for real patterns
- Have a genuine conversation
- Seed only verified, valuable knowledge
- Explain how the system learns
## Integration with Other Skills
After Smart Init:
- **Oracle**: Has seeded knowledge to work with
- **Guardian**: Has calibrated thresholds for the project
- **Wizard**: Can reference accurate project understanding
- **Summoner**: Knows project constraints for orchestration
## Example Session
```
User: "Initialize ClaudeShack for this project"
Smart Init:
1. [Runs discovery - 30 seconds]
2. "I've analyzed your project. Here's what I found..."
3. [Presents findings summary]
4. "A few questions to make sure I understand correctly..."
5. [Asks 3-5 targeted questions]
6. [User responds]
7. "Great, let me set up Oracle with this understanding..."
8. [Seeds knowledge base]
9. "All set! Here's what I created and how it will improve..."
```
---
**"Understanding first. Setup second. Learning forever."**

View File

@@ -0,0 +1,554 @@
#!/usr/bin/env python3
"""
Smart Init Discovery Script
Analyzes a project to gather context for intelligent initialization.
Outputs structured JSON with findings for the Smart Init skill.
Usage:
python discover.py [project_path]
python discover.py --json # Machine-readable output
python discover.py --verbose # Detailed human output
"""
import os
import sys
import json
import subprocess
import re
from pathlib import Path
from datetime import datetime, timedelta
from collections import Counter
from typing import Dict, List, Any, Optional
def run_command(cmd: str, cwd: Path = None, timeout: int = 10) -> Optional[str]:
"""Run a shell command and return output."""
try:
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True,
cwd=str(cwd) if cwd else None, timeout=timeout
)
return result.stdout.strip() if result.returncode == 0 else None
except (subprocess.TimeoutExpired, Exception):
return None
def detect_languages(project_path: Path) -> Dict[str, int]:
"""Detect programming languages by file extension."""
extensions = {
'.ts': 'TypeScript', '.tsx': 'TypeScript',
'.js': 'JavaScript', '.jsx': 'JavaScript',
'.py': 'Python',
'.rs': 'Rust',
'.go': 'Go',
'.java': 'Java',
'.rb': 'Ruby',
'.php': 'PHP',
'.cs': 'C#',
'.cpp': 'C++', '.cc': 'C++', '.cxx': 'C++',
'.c': 'C', '.h': 'C/C++',
'.swift': 'Swift',
'.kt': 'Kotlin',
'.scala': 'Scala',
'.css': 'CSS', '.scss': 'SCSS', '.sass': 'Sass',
'.html': 'HTML',
'.vue': 'Vue',
'.svelte': 'Svelte',
}
counts = Counter()
for root, dirs, files in os.walk(project_path):
# Skip common non-source directories
dirs[:] = [d for d in dirs if d not in [
'node_modules', '.git', 'venv', '__pycache__',
'target', 'dist', 'build', '.next', 'vendor'
]]
for file in files:
ext = Path(file).suffix.lower()
if ext in extensions:
counts[extensions[ext]] += 1
return dict(counts.most_common(10))
def detect_frameworks(project_path: Path) -> Dict[str, List[str]]:
"""Detect frameworks and tools from config files."""
frameworks = {
'frontend': [],
'backend': [],
'database': [],
'testing': [],
'build': [],
'ci_cd': [],
'containerization': []
}
# Check package.json
pkg_json = project_path / 'package.json'
if pkg_json.exists():
try:
with open(pkg_json) as f:
pkg = json.load(f)
deps = {**pkg.get('dependencies', {}), **pkg.get('devDependencies', {})}
# Frontend
if 'react' in deps:
frameworks['frontend'].append(f"React {deps.get('react', '')}")
if 'vue' in deps:
frameworks['frontend'].append(f"Vue {deps.get('vue', '')}")
if 'angular' in deps or '@angular/core' in deps:
frameworks['frontend'].append("Angular")
if 'svelte' in deps:
frameworks['frontend'].append("Svelte")
if 'next' in deps:
frameworks['frontend'].append(f"Next.js {deps.get('next', '')}")
# Backend
if 'express' in deps:
frameworks['backend'].append("Express.js")
if 'fastify' in deps:
frameworks['backend'].append("Fastify")
if 'koa' in deps:
frameworks['backend'].append("Koa")
if 'nestjs' in deps or '@nestjs/core' in deps:
frameworks['backend'].append("NestJS")
# Database
if 'prisma' in deps or '@prisma/client' in deps:
frameworks['database'].append("Prisma")
if 'mongoose' in deps:
frameworks['database'].append("MongoDB (Mongoose)")
if 'pg' in deps:
frameworks['database'].append("PostgreSQL")
if 'mysql2' in deps:
frameworks['database'].append("MySQL")
if 'sequelize' in deps:
frameworks['database'].append("Sequelize ORM")
# Testing
if 'jest' in deps:
frameworks['testing'].append("Jest")
if 'vitest' in deps:
frameworks['testing'].append("Vitest")
if 'mocha' in deps:
frameworks['testing'].append("Mocha")
if '@testing-library/react' in deps:
frameworks['testing'].append("React Testing Library")
if 'cypress' in deps:
frameworks['testing'].append("Cypress")
if 'playwright' in deps:
frameworks['testing'].append("Playwright")
# Build tools
if 'vite' in deps:
frameworks['build'].append("Vite")
if 'webpack' in deps:
frameworks['build'].append("Webpack")
if 'esbuild' in deps:
frameworks['build'].append("esbuild")
if 'turbo' in deps:
frameworks['build'].append("Turborepo")
except (json.JSONDecodeError, IOError):
pass
# Check Python
for pyfile in ['pyproject.toml', 'requirements.txt', 'setup.py']:
pypath = project_path / pyfile
if pypath.exists():
try:
content = pypath.read_text()
if 'django' in content.lower():
frameworks['backend'].append("Django")
if 'fastapi' in content.lower():
frameworks['backend'].append("FastAPI")
if 'flask' in content.lower():
frameworks['backend'].append("Flask")
if 'pytest' in content.lower():
frameworks['testing'].append("pytest")
if 'sqlalchemy' in content.lower():
frameworks['database'].append("SQLAlchemy")
except IOError:
pass
# Check Rust
cargo = project_path / 'Cargo.toml'
if cargo.exists():
try:
content = cargo.read_text()
if 'actix' in content:
frameworks['backend'].append("Actix")
if 'axum' in content:
frameworks['backend'].append("Axum")
if 'tokio' in content:
frameworks['backend'].append("Tokio (async runtime)")
except IOError:
pass
# Check CI/CD
if (project_path / '.github' / 'workflows').exists():
frameworks['ci_cd'].append("GitHub Actions")
if (project_path / '.gitlab-ci.yml').exists():
frameworks['ci_cd'].append("GitLab CI")
if (project_path / 'Jenkinsfile').exists():
frameworks['ci_cd'].append("Jenkins")
# Check containerization
if (project_path / 'Dockerfile').exists():
frameworks['containerization'].append("Docker")
if (project_path / 'docker-compose.yml').exists() or (project_path / 'docker-compose.yaml').exists():
frameworks['containerization'].append("Docker Compose")
if (project_path / 'kubernetes').exists() or (project_path / 'k8s').exists():
frameworks['containerization'].append("Kubernetes")
# Filter empty
return {k: v for k, v in frameworks.items() if v}
def detect_conventions(project_path: Path) -> Dict[str, Any]:
"""Detect coding conventions and style configs."""
conventions = {
'linting': [],
'formatting': [],
'git': {},
'typing': False
}
# Linting
lint_files = ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml',
'pylintrc', '.pylintrc', 'ruff.toml', '.flake8']
for lf in lint_files:
if (project_path / lf).exists():
conventions['linting'].append(lf)
# Formatting
format_files = ['.prettierrc', '.prettierrc.js', '.prettierrc.json',
'rustfmt.toml', '.editorconfig', 'pyproject.toml']
for ff in format_files:
if (project_path / ff).exists():
conventions['formatting'].append(ff)
# Git conventions (from recent commits)
git_log = run_command('git log --oneline -50', cwd=project_path)
if git_log:
commits = git_log.split('\n')
# Check for conventional commits
conventional_pattern = r'^[a-f0-9]+ (feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?:'
conventional_count = sum(1 for c in commits if re.match(conventional_pattern, c))
if conventional_count > len(commits) * 0.5:
conventions['git']['style'] = 'conventional-commits'
# Check branch pattern
branch = run_command('git branch --show-current', cwd=project_path)
if branch:
conventions['git']['current_branch'] = branch
# TypeScript/typing
if (project_path / 'tsconfig.json').exists():
conventions['typing'] = 'TypeScript'
elif (project_path / 'py.typed').exists() or (project_path / 'mypy.ini').exists():
conventions['typing'] = 'Python type hints'
return conventions
def detect_project_structure(project_path: Path) -> Dict[str, Any]:
"""Detect project structure pattern."""
structure = {
'type': 'unknown',
'key_directories': [],
'entry_points': []
}
# Check for monorepo indicators
if (project_path / 'packages').exists() or (project_path / 'apps').exists():
structure['type'] = 'monorepo'
if (project_path / 'packages').exists():
structure['key_directories'].append('packages/')
if (project_path / 'apps').exists():
structure['key_directories'].append('apps/')
# Check for standard patterns
src = project_path / 'src'
if src.exists():
structure['key_directories'].append('src/')
structure['type'] = 'standard'
# Detect src subdirectories
for subdir in ['components', 'pages', 'api', 'lib', 'utils',
'hooks', 'services', 'models', 'controllers', 'views']:
if (src / subdir).exists():
structure['key_directories'].append(f'src/{subdir}/')
# Entry points
entry_files = ['index.ts', 'index.js', 'main.ts', 'main.js',
'main.py', 'app.py', 'main.rs', 'lib.rs', 'main.go']
for ef in entry_files:
for match in project_path.rglob(ef):
rel_path = str(match.relative_to(project_path))
if 'node_modules' not in rel_path and 'target' not in rel_path:
structure['entry_points'].append(rel_path)
break
return structure
def detect_documentation(project_path: Path) -> Dict[str, Any]:
"""Detect existing documentation."""
docs = {
'readme': None,
'contributing': None,
'docs_directory': False,
'api_docs': False,
'claude_md': None
}
# README
for readme in ['README.md', 'README.rst', 'README.txt', 'readme.md']:
readme_path = project_path / readme
if readme_path.exists():
docs['readme'] = readme
# Check quality (rough estimate by size)
size = readme_path.stat().st_size
if size > 5000:
docs['readme_quality'] = 'detailed'
elif size > 1000:
docs['readme_quality'] = 'basic'
else:
docs['readme_quality'] = 'minimal'
break
# Contributing
for contrib in ['CONTRIBUTING.md', 'contributing.md', 'CONTRIBUTE.md']:
if (project_path / contrib).exists():
docs['contributing'] = contrib
break
# Docs directory
docs['docs_directory'] = (project_path / 'docs').exists()
# API docs
docs['api_docs'] = any([
(project_path / 'docs' / 'api').exists(),
(project_path / 'api-docs').exists(),
(project_path / 'openapi.yaml').exists(),
(project_path / 'openapi.json').exists(),
(project_path / 'swagger.yaml').exists(),
])
# claude.md
claude_md = project_path / 'claude.md'
if claude_md.exists():
docs['claude_md'] = 'exists'
content = claude_md.read_text()
if 'ClaudeShack' in content:
docs['claude_md'] = 'has-claudeshack'
return docs
def mine_history(project_path: Path) -> Dict[str, Any]:
"""Mine Claude Code conversation history for patterns."""
history = {
'found': False,
'patterns': [],
'corrections': [],
'gotchas': [],
'preferences': []
}
# Determine Claude projects directory
if sys.platform == 'darwin':
projects_dir = Path.home() / 'Library' / 'Application Support' / 'Claude' / 'projects'
elif sys.platform == 'win32':
projects_dir = Path(os.environ.get('APPDATA', '')) / 'Claude' / 'projects'
else:
projects_dir = Path.home() / '.claude' / 'projects'
if not projects_dir.exists():
return history
# Try to find project hash
project_name = project_path.name.lower()
for project_hash_dir in projects_dir.iterdir():
if not project_hash_dir.is_dir():
continue
# Look for JSONL files
for jsonl_file in project_hash_dir.glob('*.jsonl'):
try:
with open(jsonl_file, 'r', encoding='utf-8') as f:
content = f.read()
# Simple pattern matching
if project_name in content.lower() or str(project_path) in content:
history['found'] = True
# Look for corrections (simple heuristic)
correction_patterns = [
r"no,?\s+(use|prefer|don't|never|always)",
r"actually,?\s+(it's|that's|we)",
r"that's\s+(wrong|incorrect|not right)",
]
for pattern in correction_patterns:
matches = re.findall(pattern, content, re.IGNORECASE)
if matches:
history['corrections'].append(f"Found {len(matches)} potential corrections")
break
# Don't process too much
break
except (IOError, UnicodeDecodeError):
continue
return history
def get_project_name(project_path: Path) -> str:
"""Get project name from config files or directory."""
# Try package.json
pkg_json = project_path / 'package.json'
if pkg_json.exists():
try:
with open(pkg_json) as f:
return json.load(f).get('name', project_path.name)
except:
pass
# Try Cargo.toml
cargo = project_path / 'Cargo.toml'
if cargo.exists():
try:
content = cargo.read_text()
match = re.search(r'name\s*=\s*"([^"]+)"', content)
if match:
return match.group(1)
except:
pass
# Try pyproject.toml
pyproject = project_path / 'pyproject.toml'
if pyproject.exists():
try:
content = pyproject.read_text()
match = re.search(r'name\s*=\s*"([^"]+)"', content)
if match:
return match.group(1)
except:
pass
return project_path.name
def discover(project_path: Path) -> Dict[str, Any]:
"""Run full discovery on a project."""
return {
'project_name': get_project_name(project_path),
'project_path': str(project_path),
'discovered_at': datetime.now().isoformat(),
'languages': detect_languages(project_path),
'frameworks': detect_frameworks(project_path),
'conventions': detect_conventions(project_path),
'structure': detect_project_structure(project_path),
'documentation': detect_documentation(project_path),
'history': mine_history(project_path)
}
def format_human_readable(discovery: Dict[str, Any]) -> str:
"""Format discovery results for human reading."""
output = []
output.append("=" * 60)
output.append(f"Project Discovery: {discovery['project_name']}")
output.append("=" * 60)
# Languages
if discovery['languages']:
output.append("\n## Languages")
total = sum(discovery['languages'].values())
for lang, count in discovery['languages'].items():
pct = (count / total) * 100
output.append(f" - {lang}: {count} files ({pct:.0f}%)")
# Frameworks
if discovery['frameworks']:
output.append("\n## Tech Stack")
for category, items in discovery['frameworks'].items():
if items:
output.append(f" **{category.replace('_', ' ').title()}**: {', '.join(items)}")
# Conventions
conv = discovery['conventions']
if conv['linting'] or conv['formatting']:
output.append("\n## Conventions")
if conv['linting']:
output.append(f" - Linting: {', '.join(conv['linting'])}")
if conv['formatting']:
output.append(f" - Formatting: {', '.join(conv['formatting'])}")
if conv.get('typing'):
output.append(f" - Typing: {conv['typing']}")
if conv.get('git', {}).get('style'):
output.append(f" - Git: {conv['git']['style']}")
# Structure
struct = discovery['structure']
output.append(f"\n## Structure: {struct['type']}")
if struct['key_directories']:
output.append(f" Key dirs: {', '.join(struct['key_directories'][:5])}")
# Documentation
docs = discovery['documentation']
output.append("\n## Documentation")
if docs['readme']:
output.append(f" - README: {docs['readme']} ({docs.get('readme_quality', 'unknown')})")
if docs['docs_directory']:
output.append(" - docs/ directory: Yes")
if docs['claude_md']:
output.append(f" - claude.md: {docs['claude_md']}")
# History
hist = discovery['history']
if hist['found']:
output.append("\n## Conversation History")
output.append(" Found existing Claude conversations for this project")
if hist['corrections']:
output.append(f" - {hist['corrections'][0]}")
output.append("\n" + "=" * 60)
return "\n".join(output)
def main():
import argparse
parser = argparse.ArgumentParser(description='Discover project context')
parser.add_argument('project_path', nargs='?', default='.',
help='Project path (default: current directory)')
parser.add_argument('--json', action='store_true',
help='Output as JSON')
parser.add_argument('--verbose', '-v', action='store_true',
help='Verbose output')
args = parser.parse_args()
project_path = Path(args.project_path).resolve()
if not project_path.exists():
print(f"Error: Path does not exist: {project_path}", file=sys.stderr)
sys.exit(1)
discovery = discover(project_path)
if args.json:
print(json.dumps(discovery, indent=2))
else:
print(format_human_readable(discovery))
if __name__ == '__main__':
main()