Initial commit
This commit is contained in:
430
skills/plugin-slash-command/functions/command_generator.py
Normal file
430
skills/plugin-slash-command/functions/command_generator.py
Normal file
@@ -0,0 +1,430 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Command Generator - Generate Navigator slash command markdown files
|
||||
|
||||
Generates properly structured command files following Navigator conventions.
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def generate_command(
|
||||
name: str,
|
||||
description: str,
|
||||
complexity: str = "medium",
|
||||
sections: Optional[Dict] = None
|
||||
) -> str:
|
||||
"""
|
||||
Generate complete Navigator command markdown file.
|
||||
|
||||
Args:
|
||||
name: Command name (kebab-case, without /nav: prefix)
|
||||
description: One-line purpose description
|
||||
complexity: Command complexity level ("simple", "medium", "complex")
|
||||
sections: Dictionary of section content (optional, uses templates if not provided)
|
||||
|
||||
Returns:
|
||||
Complete markdown content for the command file
|
||||
|
||||
Example:
|
||||
>>> content = generate_command(
|
||||
... name="example",
|
||||
... description="Example command for testing",
|
||||
... complexity="simple"
|
||||
... )
|
||||
>>> "---" in content and "description:" in content
|
||||
True
|
||||
"""
|
||||
if sections is None:
|
||||
sections = {}
|
||||
|
||||
# Validate inputs
|
||||
valid, error = validate_command_name(name)
|
||||
if not valid:
|
||||
raise ValueError(f"Invalid command name: {error}")
|
||||
|
||||
if complexity not in ["simple", "medium", "complex"]:
|
||||
raise ValueError(f"Complexity must be 'simple', 'medium', or 'complex', got: {complexity}")
|
||||
|
||||
# Generate frontmatter
|
||||
frontmatter = f"""---
|
||||
description: {description}
|
||||
---"""
|
||||
|
||||
# Generate title
|
||||
title = f"# {format_title(name)}"
|
||||
|
||||
# Generate content based on complexity
|
||||
if complexity == "simple":
|
||||
content = generate_simple_command(name, description, sections)
|
||||
elif complexity == "medium":
|
||||
content = generate_medium_command(name, description, sections)
|
||||
else: # complex
|
||||
content = generate_complex_command(name, description, sections)
|
||||
|
||||
# Combine all parts
|
||||
return f"{frontmatter}\n\n{title}\n\n{content}"
|
||||
|
||||
|
||||
def generate_simple_command(name: str, description: str, sections: Dict) -> str:
|
||||
"""Generate content for a simple command."""
|
||||
what_this_does = sections.get("what_this_does", f"[Explain what /nav:{name} does in 2-3 sentences]")
|
||||
usage = sections.get("usage", f"/nav:{name}")
|
||||
when_to_use = sections.get("when_to_use", [
|
||||
"Scenario 1",
|
||||
"Scenario 2",
|
||||
"Scenario 3"
|
||||
])
|
||||
output_format = sections.get("output_format", "[Example output]")
|
||||
troubleshooting = sections.get("troubleshooting", {
|
||||
"Issue 1": "Solution 1",
|
||||
"Issue 2": "Solution 2"
|
||||
})
|
||||
|
||||
# Build when_to_use section
|
||||
when_to_use_content = "\n\n".join([
|
||||
f"**{scenario}**:\n```\n[Example]\n```" for scenario in when_to_use
|
||||
])
|
||||
|
||||
# Build troubleshooting section
|
||||
troubleshooting_content = "\n\n".join([
|
||||
f"### {issue}\n\n**Problem**: [Description]\n\n**Solution**:\n{solution}"
|
||||
for issue, solution in troubleshooting.items()
|
||||
])
|
||||
|
||||
return f"""## What This Does
|
||||
|
||||
{what_this_does}
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
{usage}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to Use
|
||||
|
||||
{when_to_use_content}
|
||||
|
||||
---
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
{output_format}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
{troubleshooting_content}
|
||||
|
||||
---
|
||||
|
||||
**[Closing statement about the command]** 🚀"""
|
||||
|
||||
|
||||
def generate_medium_command(name: str, description: str, sections: Dict) -> str:
|
||||
"""Generate content for a medium complexity command."""
|
||||
overview = sections.get("overview", f"You are using Navigator's `/nav:{name}` command.\n\n[Explain context and purpose]")
|
||||
what_this_does = sections.get("what_this_does", "[Detailed explanation with comparisons]")
|
||||
when_to_use = sections.get("when_to_use", [f"Scenario {i+1}" for i in range(5)])
|
||||
execution_steps = sections.get("execution_steps", [f"Step {i+1}" for i in range(3)])
|
||||
troubleshooting = sections.get("troubleshooting", {f"Issue {i+1}": f"Solution {i+1}" for i in range(4)})
|
||||
|
||||
# Build when_to_use section
|
||||
when_to_use_content = "\n\n".join([
|
||||
f"**{scenario}**:\n```\n[Example]\n```" for scenario in when_to_use
|
||||
])
|
||||
|
||||
# Build execution steps
|
||||
execution_content = "\n\n".join([
|
||||
f"### {step}\n\n[Instructions for this step]\n\n**Expected outcome**: [What happens]"
|
||||
for step in execution_steps
|
||||
])
|
||||
|
||||
# Build troubleshooting
|
||||
troubleshooting_content = "\n\n".join([
|
||||
f"### {issue}\n\n**Problem**: [Description]\n\n**Solutions**:\n1. {solution}\n2. [Additional solution]\n3. [Additional solution]"
|
||||
for issue, solution in troubleshooting.items()
|
||||
])
|
||||
|
||||
return f"""{overview}
|
||||
|
||||
---
|
||||
|
||||
## What This Does
|
||||
|
||||
{what_this_does}
|
||||
|
||||
---
|
||||
|
||||
## When to Use
|
||||
|
||||
{when_to_use_content}
|
||||
|
||||
---
|
||||
|
||||
## Execution Steps
|
||||
|
||||
{execution_content}
|
||||
|
||||
---
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
[Expected output format]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- [Best practice 1]
|
||||
- [Best practice 2]
|
||||
- [Best practice 3]
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
{troubleshooting_content}
|
||||
|
||||
---
|
||||
|
||||
**[Closing statement emphasizing key benefit]** 🚀"""
|
||||
|
||||
|
||||
def generate_complex_command(name: str, description: str, sections: Dict) -> str:
|
||||
"""Generate content for a complex command."""
|
||||
return f"""You are executing the `/nav:{name}` command.
|
||||
|
||||
[Comprehensive overview explaining the command's role in Navigator workflow]
|
||||
|
||||
---
|
||||
|
||||
## What This Does
|
||||
|
||||
[Detailed explanation with comparisons to alternative approaches]
|
||||
|
||||
**Traditional approach**: [Manual process]
|
||||
|
||||
**With `/nav:{name}`**:
|
||||
- [Benefit 1]
|
||||
- [Benefit 2]
|
||||
- [Benefit 3]
|
||||
|
||||
---
|
||||
|
||||
## EXECUTION PLAN
|
||||
|
||||
You will execute these steps in order. Each step has explicit outcomes.
|
||||
|
||||
---
|
||||
|
||||
### Step 1: Pre-Flight Checks
|
||||
|
||||
[Validation and preparation steps]
|
||||
|
||||
**Checks**:
|
||||
- [ ] Check 1
|
||||
- [ ] Check 2
|
||||
- [ ] Check 3
|
||||
|
||||
---
|
||||
|
||||
### Step 2: [Main Operation]
|
||||
|
||||
[Detailed implementation instructions]
|
||||
|
||||
**Process**:
|
||||
1. [Substep 1]
|
||||
2. [Substep 2]
|
||||
3. [Substep 3]
|
||||
|
||||
**Expected outcome**: [What should happen]
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Validation
|
||||
|
||||
[Verification steps]
|
||||
|
||||
**Verify**:
|
||||
- [ ] Verification 1
|
||||
- [ ] Verification 2
|
||||
- [ ] Verification 3
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Completion
|
||||
|
||||
[Finalization and user feedback]
|
||||
|
||||
**Show summary**:
|
||||
```
|
||||
✅ [Success message]
|
||||
|
||||
[Summary of what was accomplished]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Notes
|
||||
|
||||
[How this command integrates with other Navigator features or external tools]
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
**This command succeeds when**:
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
- [ ] Criterion 3
|
||||
- [ ] Criterion 4
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issue 1
|
||||
|
||||
**Error**: [Error message or symptom]
|
||||
|
||||
**Solution**:
|
||||
[Detailed solution with commands]
|
||||
|
||||
### Common Issue 2
|
||||
|
||||
**Error**: [Error message or symptom]
|
||||
|
||||
**Solution**:
|
||||
[Detailed solution]
|
||||
|
||||
### Edge Case 1
|
||||
|
||||
**Scenario**: [When this happens]
|
||||
|
||||
**Handling**:
|
||||
[How to handle this case]
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
[Any performance considerations, optimization tips, or scalability notes]
|
||||
|
||||
---
|
||||
|
||||
**[Comprehensive closing statement]** 🚀"""
|
||||
|
||||
|
||||
def validate_command_name(name: str) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Validate command name follows Navigator conventions.
|
||||
|
||||
Args:
|
||||
name: Command name to validate
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, error_message)
|
||||
|
||||
Example:
|
||||
>>> validate_command_name("my-command")
|
||||
(True, None)
|
||||
>>> validate_command_name("MyCommand")
|
||||
(False, 'Command name must be kebab-case')
|
||||
"""
|
||||
import re
|
||||
|
||||
if not name:
|
||||
return False, "Command name cannot be empty"
|
||||
|
||||
if not re.match(r'^[a-z][a-z0-9]*(-[a-z0-9]+)*$', name):
|
||||
return False, "Command name must be kebab-case (lowercase, hyphens only)"
|
||||
|
||||
if len(name) > 50:
|
||||
return False, "Command name too long (max 50 characters)"
|
||||
|
||||
# Reserved names
|
||||
reserved = ["help", "clear", "reset"]
|
||||
if name in reserved:
|
||||
return False, f"Command name '{name}' is reserved"
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def format_title(name: str) -> str:
|
||||
"""
|
||||
Format command name as title.
|
||||
|
||||
Args:
|
||||
name: Command name (kebab-case)
|
||||
|
||||
Returns:
|
||||
Formatted title string
|
||||
|
||||
Example:
|
||||
>>> format_title("update-doc")
|
||||
'Update Doc - Navigator'
|
||||
>>> format_title("marker")
|
||||
'Marker - Navigator'
|
||||
"""
|
||||
# Convert kebab-case to Title Case
|
||||
title = name.replace('-', ' ').title()
|
||||
|
||||
# Add Navigator branding
|
||||
return f"{title} - Navigator"
|
||||
|
||||
|
||||
def generate_description(name: str, purpose: str) -> str:
|
||||
"""
|
||||
Generate command description for YAML frontmatter.
|
||||
|
||||
Args:
|
||||
name: Command name
|
||||
purpose: Brief purpose statement
|
||||
|
||||
Returns:
|
||||
Formatted description (under 100 chars)
|
||||
|
||||
Example:
|
||||
>>> desc = generate_description("marker", "save conversation state")
|
||||
>>> len(desc) < 100
|
||||
True
|
||||
"""
|
||||
# Ensure it starts with a verb and is concise
|
||||
if len(purpose) > 90:
|
||||
purpose = purpose[:87] + "..."
|
||||
|
||||
return purpose
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("Generating simple command...")
|
||||
simple = generate_command(
|
||||
name="example",
|
||||
description="Example command for demonstration",
|
||||
complexity="simple"
|
||||
)
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print(simple[:500] + "...")
|
||||
print("=" * 50)
|
||||
|
||||
# Validate names
|
||||
test_names = ["my-command", "MyCommand", "my_command", "valid-name-123"]
|
||||
print("\nValidation Tests:")
|
||||
for name in test_names:
|
||||
valid, error = validate_command_name(name)
|
||||
status = "✅" if valid else "❌"
|
||||
print(f"{status} {name}: {error or 'Valid'}")
|
||||
323
skills/plugin-slash-command/functions/command_validator.py
Normal file
323
skills/plugin-slash-command/functions/command_validator.py
Normal file
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Command Validator - Validate Navigator slash command files
|
||||
|
||||
Validates command markdown files follow Navigator conventions and standards.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import List, Tuple, Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def validate_command_file(file_path: str) -> Tuple[bool, List[str]]:
|
||||
"""
|
||||
Validate complete command markdown file.
|
||||
|
||||
Args:
|
||||
file_path: Path to command .md file
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, list_of_errors)
|
||||
|
||||
Example:
|
||||
>>> valid, errors = validate_command_file("commands/marker.md")
|
||||
>>> valid or len(errors) > 0
|
||||
True
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Check file exists
|
||||
path = Path(file_path)
|
||||
if not path.exists():
|
||||
return False, [f"File not found: {file_path}"]
|
||||
|
||||
# Read content
|
||||
try:
|
||||
content = path.read_text()
|
||||
except Exception as e:
|
||||
return False, [f"Cannot read file: {e}"]
|
||||
|
||||
# Validate sections
|
||||
errors.extend(validate_frontmatter(content))
|
||||
errors.extend(validate_structure(content))
|
||||
errors.extend(validate_formatting(content))
|
||||
errors.extend(validate_style(content))
|
||||
|
||||
return len(errors) == 0, errors
|
||||
|
||||
|
||||
def validate_frontmatter(content: str) -> List[str]:
|
||||
"""
|
||||
Validate YAML frontmatter.
|
||||
|
||||
Args:
|
||||
content: File content
|
||||
|
||||
Returns:
|
||||
List of errors (empty if valid)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Check frontmatter exists
|
||||
if not content.startswith("---"):
|
||||
errors.append("Missing YAML frontmatter (must start with '---')")
|
||||
return errors
|
||||
|
||||
# Extract frontmatter
|
||||
parts = content.split("---", 2)
|
||||
if len(parts) < 3:
|
||||
errors.append("Invalid frontmatter structure (must be surrounded by '---')")
|
||||
return errors
|
||||
|
||||
frontmatter = parts[1].strip()
|
||||
|
||||
# Check description field
|
||||
if "description:" not in frontmatter:
|
||||
errors.append("Missing 'description' field in frontmatter")
|
||||
else:
|
||||
# Extract description value
|
||||
desc_match = re.search(r'description:\s*(.+)', frontmatter)
|
||||
if desc_match:
|
||||
desc = desc_match.group(1).strip()
|
||||
if not desc:
|
||||
errors.append("Description field is empty")
|
||||
elif len(desc) > 150:
|
||||
errors.append(f"Description too long ({len(desc)} chars, max 150)")
|
||||
else:
|
||||
errors.append("Cannot parse description field")
|
||||
|
||||
# Check for invalid fields (Navigator commands use minimal frontmatter)
|
||||
valid_fields = ["description", "author", "version", "deprecated"]
|
||||
for line in frontmatter.split("\n"):
|
||||
if ":" in line:
|
||||
field = line.split(":")[0].strip()
|
||||
if field and field not in valid_fields:
|
||||
errors.append(f"Unexpected frontmatter field: '{field}'")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_structure(content: str) -> List[str]:
|
||||
"""
|
||||
Validate document structure and required sections.
|
||||
|
||||
Args:
|
||||
content: File content
|
||||
|
||||
Returns:
|
||||
List of errors (empty if valid)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Extract markdown body (after frontmatter)
|
||||
parts = content.split("---", 2)
|
||||
if len(parts) < 3:
|
||||
return ["Cannot extract markdown body"]
|
||||
|
||||
body = parts[2].strip()
|
||||
|
||||
# Check for title (# heading)
|
||||
if not body.startswith("#"):
|
||||
errors.append("Missing main title (must start with # heading)")
|
||||
else:
|
||||
title_match = re.match(r'^#\s+(.+)$', body.split("\n")[0])
|
||||
if not title_match:
|
||||
errors.append("Invalid title format")
|
||||
else:
|
||||
title = title_match.group(1)
|
||||
# Navigator commands typically end with " - Navigator" or context
|
||||
if "navigator" not in title.lower() and "jitd" not in title.lower():
|
||||
errors.append(f"Title should include Navigator branding: '{title}'")
|
||||
|
||||
# Check for required sections (vary by complexity, so we check for minimum)
|
||||
required_keywords = ["what", "usage", "when"]
|
||||
for keyword in required_keywords:
|
||||
if keyword.lower() not in body.lower():
|
||||
errors.append(f"Missing section with '{keyword}' (recommended sections: What This Does, Usage, When to Use)")
|
||||
|
||||
# Check for code blocks (commands should have examples)
|
||||
if "```" not in body:
|
||||
errors.append("No code blocks found (commands should include examples)")
|
||||
|
||||
# Check for closing statement
|
||||
last_line = body.strip().split("\n")[-1]
|
||||
if not last_line.startswith("**") or not last_line.endswith("**"):
|
||||
errors.append("Missing closing statement (should be bold text at end)")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_formatting(content: str) -> List[str]:
|
||||
"""
|
||||
Validate markdown formatting and syntax.
|
||||
|
||||
Args:
|
||||
content: File content
|
||||
|
||||
Returns:
|
||||
List of errors (empty if valid)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Check for proper heading hierarchy
|
||||
headings = re.findall(r'^(#{1,6})\s+(.+)$', content, re.MULTILINE)
|
||||
prev_level = 0
|
||||
for heading, text in headings:
|
||||
level = len(heading)
|
||||
if level > prev_level + 1:
|
||||
errors.append(f"Heading hierarchy skip: {heading} {text} (jumped from h{prev_level} to h{level})")
|
||||
prev_level = level
|
||||
|
||||
# Check for unclosed code blocks
|
||||
code_block_count = content.count("```")
|
||||
if code_block_count % 2 != 0:
|
||||
errors.append(f"Unclosed code block (found {code_block_count} backticks, must be even)")
|
||||
|
||||
# Check for proper list formatting
|
||||
lines = content.split("\n")
|
||||
in_list = False
|
||||
for i, line in enumerate(lines, 1):
|
||||
if re.match(r'^\s*[-*+]\s+', line):
|
||||
in_list = True
|
||||
# Check indentation consistency
|
||||
if not re.match(r'^( |\t)?[-*+]\s+\S', line):
|
||||
errors.append(f"Line {i}: Improper list item format (needs space after bullet)")
|
||||
elif in_list and line.strip() and not line.startswith(" ") and not line.startswith("\t"):
|
||||
in_list = False
|
||||
|
||||
# Check for broken links (markdown links with empty href)
|
||||
broken_links = re.findall(r'\[([^\]]+)\]\(\s*\)', content)
|
||||
if broken_links:
|
||||
errors.append(f"Broken markdown links found: {broken_links}")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_style(content: str) -> List[str]:
|
||||
"""
|
||||
Validate Navigator style conventions.
|
||||
|
||||
Args:
|
||||
content: File content
|
||||
|
||||
Returns:
|
||||
List of errors (empty if valid)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Check for 2nd person perspective (Navigator style)
|
||||
first_person = ["I am", "I will", "I have", "we are", "we will", "we have"]
|
||||
for phrase in first_person:
|
||||
if phrase.lower() in content.lower():
|
||||
errors.append(f"Use 2nd person perspective ('you are') not 1st person ('{phrase}')")
|
||||
|
||||
# Check emoji usage (Navigator commands use emojis sparingly)
|
||||
# Common Navigator emojis: ✅ ❌ 📖 🚀 ⚠️ 💡 🔹
|
||||
emoji_count = len(re.findall(r'[\U0001F300-\U0001F9FF]', content))
|
||||
if emoji_count > 15:
|
||||
errors.append(f"Too many emojis ({emoji_count} found, keep under 15 for professionalism)")
|
||||
|
||||
# Check for proper bash/shell syntax in code blocks
|
||||
bash_blocks = re.findall(r'```(?:bash|shell|sh)\n(.*?)\n```', content, re.DOTALL)
|
||||
for block in bash_blocks:
|
||||
if "$(" in block and not re.search(r'\)\s*$', block, re.MULTILINE):
|
||||
errors.append("Potential unclosed command substitution in bash block")
|
||||
|
||||
# Check for Navigator command references (should use /nav: prefix)
|
||||
nav_cmds = re.findall(r'`/(?:jitd|nav):([a-z-]+)`', content)
|
||||
jitd_cmds = re.findall(r'`/jitd:([a-z-]+)`', content)
|
||||
if len(jitd_cmds) > len(nav_cmds) * 0.5: # More than 50% use old prefix
|
||||
errors.append("Prefer /nav: prefix over /jitd: (for consistency)")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_example_realism(content: str) -> List[str]:
|
||||
"""
|
||||
Check if examples are realistic (not placeholders).
|
||||
|
||||
Args:
|
||||
content: File content
|
||||
|
||||
Returns:
|
||||
List of warnings (empty if examples look good)
|
||||
"""
|
||||
warnings = []
|
||||
|
||||
# Check for common placeholder patterns
|
||||
placeholders = [
|
||||
r'\[.*?\]', # [placeholder]
|
||||
r'<.*?>', # <placeholder>
|
||||
r'\.\.\.+', # ...
|
||||
r'TODO',
|
||||
r'FIXME',
|
||||
r'XXX',
|
||||
]
|
||||
|
||||
code_blocks = re.findall(r'```.*?\n(.*?)\n```', content, re.DOTALL)
|
||||
for block in code_blocks:
|
||||
for pattern in placeholders:
|
||||
if re.search(pattern, block):
|
||||
warnings.append(f"Code block contains placeholder-like content: {pattern}")
|
||||
break # One warning per block
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
def quick_validate(file_path: str) -> bool:
|
||||
"""
|
||||
Quick validation check (returns only boolean).
|
||||
|
||||
Args:
|
||||
file_path: Path to command file
|
||||
|
||||
Returns:
|
||||
True if valid, False otherwise
|
||||
"""
|
||||
valid, _ = validate_command_file(file_path)
|
||||
return valid
|
||||
|
||||
|
||||
def print_validation_report(file_path: str):
|
||||
"""
|
||||
Print formatted validation report.
|
||||
|
||||
Args:
|
||||
file_path: Path to command file
|
||||
"""
|
||||
valid, errors = validate_command_file(file_path)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Validation Report: {Path(file_path).name}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
if valid:
|
||||
print("✅ All validations passed!")
|
||||
print("\nFile is ready to use.")
|
||||
else:
|
||||
print(f"❌ Found {len(errors)} issue(s):\n")
|
||||
for i, error in enumerate(errors, 1):
|
||||
print(f"{i}. {error}")
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("Fix these issues before using the command.")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python command_validator.py <command-file.md>")
|
||||
print("\nExample:")
|
||||
print(" python command_validator.py commands/marker.md")
|
||||
sys.exit(1)
|
||||
|
||||
file_path = sys.argv[1]
|
||||
print_validation_report(file_path)
|
||||
|
||||
# Exit with error code if validation failed
|
||||
if not quick_validate(file_path):
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user