170 lines
4.9 KiB
Python
Executable File
170 lines
4.9 KiB
Python
Executable File
#!/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()
|