Initial commit
This commit is contained in:
158
skills/smart-init/README.md
Normal file
158
skills/smart-init/README.md
Normal 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
395
skills/smart-init/SKILL.md
Normal 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."**
|
||||
554
skills/smart-init/scripts/discover.py
Executable file
554
skills/smart-init/scripts/discover.py
Executable 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()
|
||||
Reference in New Issue
Block a user