Initial commit
This commit is contained in:
169
skills/nav-update-claude/functions/version_detector.py
Executable file
169
skills/nav-update-claude/functions/version_detector.py
Executable 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()
|
||||
Reference in New Issue
Block a user