Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 17:51:59 +08:00
commit 38e80921c8
89 changed files with 20480 additions and 0 deletions

View File

@@ -0,0 +1,448 @@
#!/usr/bin/env python3
"""
Navigator CLAUDE.md Updater
Extracts customizations and generates updated CLAUDE.md with v3.1 template
"""
import sys
import json
import re
from pathlib import Path
from typing import Dict, List, Optional
from urllib import request
from urllib.error import URLError, HTTPError
def get_plugin_version() -> Optional[str]:
"""
Get installed Navigator plugin version from plugin.json.
Returns:
Version string (e.g., "4.3.0") or None if not found
"""
possible_paths = [
Path.home() / '.claude' / 'plugins' / 'marketplaces' / 'navigator-marketplace' / '.claude-plugin' / 'plugin.json',
Path.home() / '.config' / 'claude' / 'plugins' / 'navigator' / '.claude-plugin' / 'plugin.json',
Path.home() / '.claude' / 'plugins' / 'navigator' / '.claude-plugin' / 'plugin.json',
]
for path in possible_paths:
if path.exists():
try:
with open(path, 'r') as f:
data = json.load(f)
return data.get('version')
except (json.JSONDecodeError, FileNotFoundError, PermissionError):
continue
return None
def fetch_template_from_github(version: Optional[str] = None) -> Optional[str]:
"""
Fetch CLAUDE.md template from GitHub releases.
Priority:
1. Specified version (e.g., 'v4.3.0' or '4.3.0')
2. Detected plugin version
3. Returns None (caller should use bundled fallback)
Args:
version: Specific version to fetch (optional)
Returns:
Template content as string, or None if fetch fails
"""
if not version:
version = get_plugin_version()
if not version:
return None
# Ensure version has 'v' prefix for GitHub URL
if not version.startswith('v'):
version = f'v{version}'
github_url = f"https://raw.githubusercontent.com/alekspetrov/navigator/{version}/templates/CLAUDE.md"
try:
req = request.Request(github_url)
req.add_header('User-Agent', 'Navigator-CLAUDE-Updater')
with request.urlopen(req, timeout=10) as response:
if response.status == 200:
content = response.read().decode('utf-8')
return content
except (URLError, HTTPError, TimeoutError) as e:
# Silent fail - caller will use bundled template
print(f"⚠️ Could not fetch template from GitHub ({version}): {e}", file=sys.stderr)
print(f" Falling back to bundled template", file=sys.stderr)
return None
return None
def get_template_path(bundled_template_dir: str, version: Optional[str] = None) -> tuple[str, bool]:
"""
Get template path, preferring GitHub source over bundled.
Args:
bundled_template_dir: Path to bundled templates directory
version: Optional specific version to fetch
Returns:
Tuple of (template_path_or_content, is_from_github)
"""
# Try GitHub first
github_template = fetch_template_from_github(version)
if github_template:
# Write to temporary file
import tempfile
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False)
temp_file.write(github_template)
temp_file.close()
detected_version = version or get_plugin_version()
print(f"✓ Using template from GitHub ({detected_version})", file=sys.stderr)
return (temp_file.name, True)
# Fallback to bundled
bundled_path = Path(bundled_template_dir) / 'CLAUDE.md'
if bundled_path.exists():
bundled_version = get_plugin_version() or "unknown"
print(f"✓ Using bundled template (v{bundled_version})", file=sys.stderr)
return (str(bundled_path), False)
raise FileNotFoundError(f"No template found (GitHub failed, bundled not at {bundled_path})")
def extract_section(content: str, header: str, next_headers: List[str]) -> Optional[str]:
"""Extract content between header and next section header"""
# Find header (supports ## or # with various markdown formats)
header_pattern = r'^#{1,2}\s+' + re.escape(header) + r'.*?$'
match = re.search(header_pattern, content, re.MULTILINE | re.IGNORECASE)
if not match:
return None
start = match.end()
# Find next header
next_pattern = r'^#{1,2}\s+(' + '|'.join(re.escape(h) for h in next_headers) + r').*?$'
next_match = re.search(next_pattern, content[start:], re.MULTILINE | re.IGNORECASE)
if next_match:
end = start + next_match.start()
else:
end = len(content)
section = content[start:end].strip()
return section if section else None
def extract_customizations(claude_md_path: str) -> Dict:
"""Extract project-specific customizations from CLAUDE.md"""
with open(claude_md_path, 'r', encoding='utf-8') as f:
content = f.read()
customizations = {
"project_name": "",
"description": "",
"tech_stack": [],
"code_standards": [],
"forbidden_actions": [],
"pm_tool": "none",
"custom_sections": {}
}
# Extract project name (first # header)
title_match = re.search(r'^#\s+(.+?)\s*-\s*Claude Code Configuration', content, re.MULTILINE)
if title_match:
customizations["project_name"] = title_match.group(1).strip()
# Extract description from Context section
context = extract_section(content, "Context", [
"Navigator Quick Start", "Quick Start", "Project-Specific", "Code Standards",
"Forbidden Actions", "Documentation", "Project Management"
])
if context:
# Extract brief description (text before tech stack, excluding brackets)
lines = context.split('\n')
desc_lines = []
for line in lines:
line = line.strip()
if line and not line.startswith('**Tech Stack') and not line.startswith('['):
desc_lines.append(line)
if line.startswith('**Tech Stack'):
break
if desc_lines:
customizations["description"] = ' '.join(desc_lines)
# Extract tech stack
tech_match = re.search(r'\*\*Tech Stack\*\*:\s*(.+?)(?:\n|$)', context)
if tech_match:
tech_text = tech_match.group(1).strip()
# Remove brackets and split by comma
tech_text = re.sub(r'\[|\]', '', tech_text)
customizations["tech_stack"] = [t.strip() for t in tech_text.split(',')]
# Extract code standards
standards_section = extract_section(content, "Project-Specific Code Standards", [
"Forbidden", "Documentation", "Project Management", "Configuration",
"Commit Guidelines", "Success Metrics"
])
if not standards_section:
standards_section = extract_section(content, "Code Standards", [
"Forbidden", "Documentation", "Project Management", "Configuration"
])
if standards_section:
# Extract custom rules (lines that aren't in default template)
default_rules = [
"KISS, DRY, SOLID",
"TypeScript",
"Strict mode",
"Line Length",
"Max 100",
"Testing",
"Framework-Specific",
"General Standards",
"Architecture"
]
lines = standards_section.split('\n')
for line in lines:
line = line.strip()
# Skip empty lines, headers, and default rules
if not line or line.startswith('#') or line.startswith('**'):
continue
# Check if it's a custom rule
is_default = any(rule in line for rule in default_rules)
if not is_default:
if line.startswith('-') or line.startswith('*'):
customizations["code_standards"].append(line.lstrip('-*').strip())
elif ':' in line: # Format like "Custom rule: Always use hooks"
customizations["code_standards"].append(line)
# Extract forbidden actions
forbidden_section = extract_section(content, "Forbidden Actions", [
"Documentation", "Project Management", "Configuration",
"Commit Guidelines", "Success Metrics"
])
if forbidden_section:
# Extract custom forbidden actions (not in default template)
default_forbidden = [
"NEVER wait for explicit commit",
"NEVER leave tickets open",
"NEVER skip documentation",
"NEVER load all `.agent/`",
"NEVER load all .agent",
"NEVER skip reading DEVELOPMENT-README",
"No Claude Code mentions",
"No package.json modifications",
"Never commit secrets",
"Don't delete tests",
"NEVER skip tests"
]
lines = forbidden_section.split('\n')
for line in lines:
line = line.strip()
# Skip empty lines and headers
if not line or line.startswith('#') or line.startswith('**'):
continue
if line.startswith('') or line.startswith('-'):
action = line.lstrip('❌- ').strip()
# Check if it's truly custom
is_default = any(df in action for df in default_forbidden)
if action and not is_default:
# Remove any leading emoji that might remain
action = action.lstrip('')
customizations["forbidden_actions"].append(action)
# Extract PM tool configuration
pm_section = extract_section(content, "Project Management", [
"Configuration", "Commit Guidelines", "Success Metrics"
])
if pm_section:
# Look for configured tool
tool_match = re.search(r'\*\*Configured Tool\*\*:\s*(\w+)', pm_section, re.IGNORECASE)
if tool_match:
tool = tool_match.group(1).lower()
if tool in ['linear', 'github', 'jira', 'gitlab']:
customizations["pm_tool"] = tool
# Extract custom sections (not in standard template)
standard_sections = [
"Context", "Navigator", "Quick Start", "Code Standards",
"Project-Specific Code Standards", "Forbidden Actions",
"Documentation Structure", "Project Management",
"Configuration", "Commit Guidelines", "Success Metrics"
]
# Find all ## headers
headers = re.findall(r'^##\s+(.+?)$', content, re.MULTILINE)
for header in headers:
if header.strip() not in standard_sections:
section_content = extract_section(content, header, standard_sections + headers)
if section_content:
customizations["custom_sections"][header.strip()] = section_content
return customizations
def generate_updated_claude_md(customizations: Dict, template_path: str, output_path: str):
"""Generate updated CLAUDE.md using v3.1 template and customizations"""
with open(template_path, 'r', encoding='utf-8') as f:
template = f.read()
# Replace project name
if customizations["project_name"]:
template = template.replace('[Project Name]', customizations["project_name"])
# Replace description
if customizations["description"]:
template = template.replace(
'[Brief project description - explain what this project does]',
customizations["description"]
)
# Replace tech stack
if customizations["tech_stack"]:
tech_stack = ', '.join(customizations["tech_stack"])
template = template.replace(
'[List your technologies, e.g., Next.js, TypeScript, PostgreSQL]',
tech_stack
)
# Append custom code standards
if customizations["code_standards"]:
standards_marker = "[Add project-specific violations here]"
if standards_marker in template:
custom_standards = "\n\n### Additional Project Standards\n\n"
for standard in customizations["code_standards"]:
custom_standards += f"- {standard}\n"
template = template.replace(standards_marker, custom_standards + "\n" + standards_marker)
# Append custom forbidden actions
if customizations["forbidden_actions"]:
forbidden_marker = "[Add project-specific violations here]"
if forbidden_marker in template:
custom_forbidden = "\n### Additional Forbidden Actions\n\n"
for action in customizations["forbidden_actions"]:
custom_forbidden += f"- ❌ {action}\n"
# Find the marker and append after it
template = template.replace(forbidden_marker, custom_forbidden)
# Update PM tool
if customizations["pm_tool"] != "none":
template = template.replace(
'**Configured Tool**: [Linear / GitHub Issues / Jira / GitLab / None]',
f'**Configured Tool**: {customizations["pm_tool"].title()}'
)
# Update config JSON
template = template.replace(
'"project_management": "none"',
f'"project_management": "{customizations["pm_tool"]}"'
)
# Append custom sections at the end
if customizations["custom_sections"]:
template += "\n\n---\n\n## Custom Project Sections\n\n"
for section_name, section_content in customizations["custom_sections"].items():
template += f"### {section_name}\n\n{section_content}\n\n"
# Write updated file
with open(output_path, 'w', encoding='utf-8') as f:
f.write(template)
def main():
if len(sys.argv) < 3:
print("Usage:", file=sys.stderr)
print(" Extract: python3 claude_updater.py extract CLAUDE.md > customizations.json", file=sys.stderr)
print(" Generate: python3 claude_updater.py generate --customizations file.json --template template.md --output CLAUDE.md", file=sys.stderr)
sys.exit(1)
command = sys.argv[1]
if command == "extract":
claude_md_path = sys.argv[2]
if not Path(claude_md_path).exists():
print(f"Error: File not found: {claude_md_path}", file=sys.stderr)
sys.exit(1)
try:
customizations = extract_customizations(claude_md_path)
print(json.dumps(customizations, indent=2))
except Exception as e:
print(f"Error extracting customizations: {e}", file=sys.stderr)
sys.exit(2)
elif command == "generate":
# Parse arguments
args = {
'customizations': None,
'template': None,
'output': None
}
i = 2
while i < len(sys.argv):
if sys.argv[i] == '--customizations' and i + 1 < len(sys.argv):
args['customizations'] = sys.argv[i + 1]
i += 2
elif sys.argv[i] == '--template' and i + 1 < len(sys.argv):
args['template'] = sys.argv[i + 1]
i += 2
elif sys.argv[i] == '--output' and i + 1 < len(sys.argv):
args['output'] = sys.argv[i + 1]
i += 2
else:
i += 1
if not all(args.values()):
print("Error: Missing required arguments", file=sys.stderr)
print("Required: --customizations, --template, --output", file=sys.stderr)
sys.exit(1)
try:
with open(args['customizations'], 'r') as f:
customizations = json.load(f)
# Use get_template_path for GitHub fetch with bundled fallback
# If --template is a directory, treat it as bundled_template_dir
# Otherwise, use it directly as a file path
template_arg = args['template']
if Path(template_arg).is_dir():
# Directory provided - use get_template_path for smart fetching
template_path, is_github = get_template_path(template_arg)
elif Path(template_arg).is_file():
# File provided directly - use as-is (backward compatibility)
template_path = template_arg
is_github = False
else:
# Try parent directory for get_template_path
template_dir = str(Path(template_arg).parent)
template_path, is_github = get_template_path(template_dir)
generate_updated_claude_md(customizations, template_path, args['output'])
print(f"✓ Generated {args['output']}", file=sys.stderr)
# Cleanup temp file if from GitHub
if is_github and Path(template_path).exists():
Path(template_path).unlink()
except Exception as e:
print(f"Error generating CLAUDE.md: {e}", file=sys.stderr)
sys.exit(2)
else:
print(f"Error: Unknown command: {command}", file=sys.stderr)
print("Valid commands: extract, generate", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,169 @@
#!/usr/bin/env python3
"""
Navigator CLAUDE.md Version Detector
Detects if CLAUDE.md is outdated, current (v3.1), or unknown
"""
import sys
import re
from pathlib import Path
from typing import Literal
VersionStatus = Literal["outdated", "current", "unknown"]
def detect_version(claude_md_path: str) -> VersionStatus:
"""Detect CLAUDE.md version status"""
if not Path(claude_md_path).exists():
print(f"Error: File not found: {claude_md_path}", file=sys.stderr)
sys.exit(1)
with open(claude_md_path, 'r', encoding='utf-8') as f:
content = f.read()
# Check for version marker
version_match = re.search(r'Navigator Version[:\s]+(\d+\.\d+\.\d+)', content, re.IGNORECASE)
if version_match:
version_str = version_match.group(1)
major, minor, patch = map(int, version_str.split('.'))
# Version 3.1+ is current
if major > 3 or (major == 3 and minor >= 1):
# Double-check for natural language (should have it in v3+)
if has_natural_language_examples(content):
return "current"
else:
# Has v3.1 marker but no natural language - partial migration
return "outdated"
# Version 3.0 - check for natural language
elif major == 3 and minor == 0:
if has_natural_language_examples(content) and not has_slash_commands(content):
return "current"
else:
return "outdated"
# Version < 3.0 is definitely outdated
else:
return "outdated"
# No version marker - use heuristics
return detect_by_heuristics(content)
def has_slash_commands(content: str) -> bool:
"""Check if content has slash command references"""
slash_patterns = [
r'/nav:start',
r'/nav:init',
r'/nav:doc',
r'/nav:marker',
r'/nav:markers',
r'/nav:compact',
r'/jitd:',
]
for pattern in slash_patterns:
if re.search(pattern, content):
return True
return False
def has_natural_language_examples(content: str) -> bool:
"""Check if content has natural language command examples"""
natural_language_patterns = [
r'"Start my Navigator session"',
r'"Initialize Navigator in this project"',
r'"Archive TASK-\w+ documentation"',
r'"Create an SOP for',
r'"Clear context and preserve markers"',
r'"Start my session"',
r'"Load the navigator"',
]
matches = 0
for pattern in natural_language_patterns:
if re.search(pattern, content, re.IGNORECASE):
matches += 1
# Need at least 2 natural language examples to be considered current
return matches >= 2
def has_skills_explanation(content: str) -> bool:
"""Check if content explains skills architecture"""
skills_markers = [
r'skills-only architecture',
r'skills that auto-invoke',
r'How Claude Discovers.*Skills',
r'Progressive disclosure.*skills',
]
for pattern in skills_markers:
if re.search(pattern, content, re.IGNORECASE | re.DOTALL):
return True
return False
def has_navigator_markers(content: str) -> bool:
"""Check if content has any Navigator-specific markers"""
navigator_markers = [
r'Navigator',
r'\.agent/',
r'DEVELOPMENT-README\.md',
r'nav-start',
r'nav-task',
r'nav-compact',
r'context markers',
r'token optimization',
]
matches = 0
for pattern in navigator_markers:
if re.search(pattern, content, re.IGNORECASE):
matches += 1
# Need at least 3 Navigator markers to be considered Navigator-related
return matches >= 3
def detect_by_heuristics(content: str) -> VersionStatus:
"""Detect version using heuristics when no version marker present"""
# Check if it's Navigator-related at all
if not has_navigator_markers(content):
return "unknown"
# Has slash commands → definitely outdated
if has_slash_commands(content):
return "outdated"
# Has natural language + skills explanation → current
if has_natural_language_examples(content) and has_skills_explanation(content):
return "current"
# Has natural language but no skills explanation → partial migration
if has_natural_language_examples(content):
return "outdated"
# Has Navigator markers but no natural language → old version
if has_navigator_markers(content):
return "outdated"
# Can't determine
return "unknown"
def main():
if len(sys.argv) < 2:
print("Usage: python3 version_detector.py CLAUDE.md", file=sys.stderr)
sys.exit(1)
claude_md_path = sys.argv[1]
try:
status = detect_version(claude_md_path)
print(status)
except Exception as e:
print(f"Error detecting version: {e}", file=sys.stderr)
sys.exit(2)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,362 @@
---
name: nav-update-claude
description: Update project CLAUDE.md to latest Navigator version, preserving customizations. Use when user says "update CLAUDE.md", "migrate to v3", or when detecting outdated Navigator configuration.
allowed-tools: Read, Write, Edit, Bash
version: 1.0.0
---
# Navigator CLAUDE.md Updater Skill
Update project's CLAUDE.md to latest Navigator version (v3.1) while preserving project-specific customizations.
## When to Invoke
Invoke this skill when the user:
- Says "update my CLAUDE.md", "migrate CLAUDE.md to v3"
- Says "update Navigator configuration", "fix my CLAUDE.md"
- Mentions outdated commands like "/nav:start" and wants to upgrade
- Complains that Claude doesn't understand Navigator workflow
**DO NOT invoke** if:
- CLAUDE.md already references v3.1 and natural language commands
- User is editing CLAUDE.md for project-specific reasons (not Navigator updates)
- Working on plugin's root CLAUDE.md (not user projects)
## Execution Steps
### Step 1: Detect Current CLAUDE.md Version
Check if CLAUDE.md exists and detect version:
```bash
if [ ! -f "CLAUDE.md" ]; then
echo "❌ No CLAUDE.md found in current directory"
echo ""
echo "Run 'Initialize Navigator in this project' first."
exit 1
fi
```
Use `version_detector.py` to analyze CLAUDE.md:
```bash
python3 "$SKILL_BASE_DIR/functions/version_detector.py" CLAUDE.md
```
This script checks for:
- Version markers (e.g., "Navigator Version: 3.1.0")
- Slash command references (`/nav:start`, `/nav:doc`, etc.)
- Skills vs commands language
- Natural language examples
**Outputs**:
- `outdated` - Has `/nav:` commands or v1/v2 markers
- `current` - Already v3.1 with natural language
- `unknown` - Can't determine (custom/non-Navigator file)
**If `current`**:
```
✅ CLAUDE.md is already up to date (v3.1)
No migration needed.
```
Exit successfully.
**If `unknown`**:
```
⚠️ CLAUDE.md doesn't appear to be a Navigator file
This might be a custom configuration. Manual review recommended.
Proceed with migration anyway? [y/N]
```
If user declines, exit. If accepts, continue.
### Step 2: Backup Current CLAUDE.md
Always create backup before modifying:
```bash
cp CLAUDE.md CLAUDE.md.backup
echo "📦 Backup created: CLAUDE.md.backup"
```
### Step 3: Extract Project-Specific Customizations
Use `claude_updater.py` to parse current CLAUDE.md:
```bash
python3 "$SKILL_BASE_DIR/functions/claude_updater.py" extract CLAUDE.md > /tmp/nav-customizations.json
```
This extracts:
- **Project name** (from title)
- **Project description** (from Context section)
- **Tech stack** (languages, frameworks)
- **Code standards** (custom rules beyond Navigator defaults)
- **Forbidden actions** (project-specific restrictions)
- **PM tool configuration** (Linear, GitHub, Jira, etc.)
- **Custom sections** (anything not in Navigator template)
### Step 4: Generate Updated CLAUDE.md
Apply latest template with extracted customizations:
```bash
# Template fetching now automatic via get_template_path():
# 1. Tries GitHub (version-matched)
# 2. Falls back to bundled if offline
python3 "$SKILL_BASE_DIR/functions/claude_updater.py" generate \
--customizations /tmp/nav-customizations.json \
--template "$SKILL_BASE_DIR/../../templates/CLAUDE.md" \
--output CLAUDE.md
```
**Template Source Priority**:
1. **GitHub** (version-matched): Fetches from `https://raw.githubusercontent.com/alekspetrov/navigator/v{version}/templates/CLAUDE.md`
- Matches installed plugin version (e.g., v4.3.0)
- Always up-to-date with release
- Works with pre-releases
2. **Bundled** (fallback): Uses `templates/CLAUDE.md` from installed plugin
- Offline fallback
- Guaranteed availability
**What this does**:
1. Loads template (GitHub or bundled)
2. Replaces placeholders with extracted data
3. Preserves custom sections
4. Updates Navigator workflow to natural language
5. Removes slash command references
6. Adds skills explanation
### Step 5: Show Diff and Confirm
Display changes for user review:
```bash
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 CHANGES TO CLAUDE.MD"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Show unified diff
diff -u CLAUDE.md.backup CLAUDE.md || true
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
```
### Step 6: Verify and Commit
Show summary of changes:
```
✅ CLAUDE.md Updated to v3.1
Key changes:
✓ Removed slash command references (e.g., /nav:start)
✓ Added natural language examples ("Start my Navigator session")
✓ Added skills architecture explanation
✓ Updated Navigator workflow section
✓ Preserved your project-specific customizations:
- Tech stack: [list]
- Code standards: [count] custom rules
- Forbidden actions: [count] custom rules
Backup saved: CLAUDE.md.backup
Next steps:
1. Review changes: git diff CLAUDE.md
2. Test: "Start my Navigator session" should work
3. Commit: git add CLAUDE.md && git commit -m "chore: update CLAUDE.md to Navigator v3.1"
4. Remove backup: rm CLAUDE.md.backup
Rollback if needed: mv CLAUDE.md.backup CLAUDE.md
```
### Step 7: Optional - Update .nav-config.json
If config exists, check version:
```bash
if [ -f ".agent/.nav-config.json" ]; then
version=$(jq -r '.version' .agent/.nav-config.json)
if [ "$version" != "4.5.0" ]; then
echo ""
echo "💡 .nav-config.json is version $version"
echo " Update to 4.5.0? [Y/n]"
read -r response
if [[ "$response" =~ ^([yY][eE][sS]|[yY]|)$ ]]; then
jq '.version = "4.5.0"' .agent/.nav-config.json > /tmp/nav-config.tmp
mv /tmp/nav-config.tmp .agent/.nav-config.json
echo " ✓ Updated config to v4.5.0"
fi
fi
fi
```
## Predefined Functions
### functions/version_detector.py
**Purpose**: Detect CLAUDE.md version (outdated, current, unknown)
**Usage**:
```bash
python3 version_detector.py CLAUDE.md
```
**Output**: Prints one of: `outdated`, `current`, `unknown`
**Exit codes**:
- 0: Success (version detected)
- 1: File not found
- 2: Parse error
**Detection logic**:
1. Check for version marker: `Navigator Version: X.X.X`
2. Check for slash commands: `/nav:start`, `/jitd:`, etc.
3. Check for natural language examples: `"Start my Navigator session"`
4. Check for skills section
**Heuristics**:
- Has `/nav:` → outdated
- Version < 3.0 → outdated
- Version >= 3.0 + natural language → current
- No version + no Navigator markers → unknown
### functions/claude_updater.py
**Purpose**: Extract customizations and generate updated CLAUDE.md
**Usage**:
```bash
# Extract customizations
python3 claude_updater.py extract CLAUDE.md > customizations.json
# Generate updated file
python3 claude_updater.py generate \
--customizations customizations.json \
--template ../../templates/CLAUDE.md \
--output CLAUDE.md
```
**Extract mode** outputs JSON:
```json
{
"project_name": "MyApp",
"description": "Brief project description",
"tech_stack": ["Next.js", "TypeScript", "PostgreSQL"],
"code_standards": ["Custom rule 1", "Custom rule 2"],
"forbidden_actions": ["Custom restriction 1"],
"pm_tool": "github",
"custom_sections": {
"Deployment": "Custom deployment instructions..."
}
}
```
**Generate mode**:
1. Loads template
2. Replaces `[Project Name]` with `project_name`
3. Replaces `[Brief project description]` with `description`
4. Replaces `[List your technologies...]` with `tech_stack`
5. Appends custom code standards
6. Appends custom forbidden actions
7. Inserts custom sections at end
## Error Handling
**No CLAUDE.md found**:
```
❌ No CLAUDE.md found in current directory
This project doesn't appear to have Navigator initialized.
Run "Initialize Navigator in this project" first.
```
**Backup failed**:
```
❌ Failed to create backup: CLAUDE.md.backup
Check file permissions and disk space.
```
**Parse error**:
```
❌ Failed to parse CLAUDE.md
The file might be corrupted or have unusual formatting.
Manual review required.
Backup saved at: CLAUDE.md.backup
```
**Template not found**:
```
❌ Navigator template not found
This might be a plugin installation issue.
Try reinstalling Navigator plugin: /plugin update navigator
```
## Success Criteria
Migration is successful when:
- [ ] CLAUDE.md backed up successfully
- [ ] Version detected correctly
- [ ] Customizations extracted
- [ ] New file generated with v3.1 template
- [ ] Project-specific content preserved
- [ ] Diff shown to user for review
- [ ] Commit instructions provided
## Rollback Procedure
If migration fails or user is unhappy:
```bash
# Restore backup
mv CLAUDE.md.backup CLAUDE.md
# Or compare and manually fix
diff CLAUDE.md.backup CLAUDE.md
```
## Notes
This skill:
- **Preserves all customizations** (tech stack, standards, restrictions)
- **Non-destructive** (always creates backup)
- **Idempotent** (running multiple times is safe)
- **Transparent** (shows diff before finalizing)
**What gets updated**:
- Navigator version marker
- Slash commands → natural language
- Workflow examples
- Skills vs commands explanation
- Token optimization strategy
**What gets preserved**:
- Project name and description
- Tech stack
- Code standards
- Forbidden actions
- PM tool configuration
- Custom sections
## Related Skills
- **nav-init**: Initialize Navigator in new project (creates CLAUDE.md from scratch)
- **nav-start**: Start session (uses updated CLAUDE.md)
- **nav-task**: Task documentation (benefits from updated workflow)
## Examples
### Example 1: Simple Update
```
User: "Update my CLAUDE.md to v3.1"