scripts-command-expert
""" Command Type Expert
Specializes in understanding what makes a document a "command" document. Command docs define slash commands with invocation syntax, parameters, and usage.
Key characteristics:
- Invocation syntax (/command-name)
- Parameter definitions
- Usage examples
- CLI-style documentation
- Return value specifications """
import re from typing import Dict, List from pathlib import Path import sys
sys.path.insert(0, str(Path(file).parent.parent)) from core.models import Document, AnalystVote from .base import TypeExpert, TypeAnalysis, ContentEnhancement
class CommandExpert(TypeExpert): """Expert in identifying and enhancing command documents."""
expert_type = "command"
strong_indicators = [
r'/\w+[-\w]*', # Slash command pattern
r'invocation',
r'usage:',
r'arguments?:',
r'parameters?:',
r'--\w+', # CLI flags
r'options?:',
r'syntax:',
]
analyst_expectations = {
'metadata': ["type: command in frontmatter", "component_type: command"],
'content': ["## Usage section", "## Parameters/Arguments", "Command examples"],
'structural': ["Path contains /commands/", "CLI documentation structure"],
'semantic': ["Imperative language", "Technical parameter descriptions"],
'pattern': ["Filename matches command patterns", "Slash command in title"],
}
def analyze(
self,
document: Document,
analyst_votes: List[AnalystVote]
) -> TypeAnalysis:
"""Analyze if document is truly a command definition."""
content = document.body or document.content
headings = self.extract_headings(content)
h2_texts = [h[1].lower() for h in headings if h[0] == 2]
evidence_for = []
evidence_against = []
# Check strong indicators
for indicator in self.strong_indicators:
if re.search(indicator, content, re.I):
evidence_for.append(f"Contains command indicator: '{indicator}'")
# Check for slash command pattern
slash_commands = re.findall(r'/[\w-]+', content)
if slash_commands:
evidence_for.append(f"Contains slash commands: {', '.join(set(slash_commands[:3]))}")
# Check for CLI flags
flags = re.findall(r'--[\w-]+', content)
if len(flags) >= 2:
evidence_for.append(f"Contains CLI flags: {', '.join(set(flags[:3]))}")
# Check for usage section
if any('usage' in h for h in h2_texts):
evidence_for.append("Has usage section")
if any('parameter' in h or 'argument' in h for h in h2_texts):
evidence_for.append("Has parameters/arguments section")
if any('example' in h for h in h2_texts):
evidence_for.append("Has examples section")
# Check for code blocks with command invocations
code_blocks = self.extract_code_blocks(content)
for lang, code in code_blocks:
if re.search(r'^[$/]', code.strip()):
evidence_for.append("Has command invocation examples")
break
# Check path
if '/command' in str(document.path).lower():
evidence_for.append("Located in commands directory")
# Evidence against
if any('phase' in h or 'stage' in h for h in h2_texts):
evidence_against.append("Has phase/stage sections - might be workflow")
if re.search(r'you are\b', content, re.I):
evidence_against.append("Has persona language - might be agent")
if any('prerequisites' in h for h in h2_texts):
evidence_against.append("Has prerequisites - might be guide")
confidence = min(0.98, len(evidence_for) * 0.13)
if evidence_against:
confidence -= len(evidence_against) * 0.1
is_command = len(evidence_for) >= 2 and confidence > 0.5
# Missing signals
missing = []
if not slash_commands:
missing.append('invocation')
if not any('usage' in h for h in h2_texts):
missing.append('usage')
if not any('parameter' in h or 'argument' in h or 'option' in h for h in h2_texts):
missing.append('parameters')
if not any('example' in h for h in h2_texts):
missing.append('examples')
disagreeing = self.identify_disagreeing_analysts(analyst_votes, 'command')
analysts_to_sway = {
name: f"Needs more command signals (invocation syntax, parameters) to classify as command"
for name in disagreeing
}
return TypeAnalysis(
is_this_type=is_command,
confidence=max(0, confidence),
evidence_for=evidence_for,
evidence_against=evidence_against,
semantic_purpose=self.analyze_semantic_purpose(document),
missing_signals=missing,
recommended_changes=[],
analysts_to_sway=analysts_to_sway,
expert_type=self.expert_type
)
def generate_enhancements(
self,
document: Document,
analysis: TypeAnalysis
) -> List[ContentEnhancement]:
"""Generate contextual command enhancements."""
enhancements = []
content = document.body or document.content
title = document.frontmatter.get('title', 'command')
# Extract command name from title or content
command_name = self._infer_command_name(title, content)
if 'invocation' in analysis.missing_signals:
enhancements.append(ContentEnhancement(
signal_type='invocation',
content=self._generate_invocation(command_name),
insertion_point='after_frontmatter',
reason="Command docs need clear invocation syntax",
expected_analyst_boost={'content': 0.20, 'pattern': 0.15},
priority=1
))
if 'usage' in analysis.missing_signals:
enhancements.append(ContentEnhancement(
signal_type='usage',
content=self._generate_usage(command_name, content),
insertion_point='after_invocation',
reason="Command docs need usage section",
expected_analyst_boost={'content': 0.15, 'structural': 0.10},
priority=1
))
if 'parameters' in analysis.missing_signals:
params = self._infer_parameters(content)
enhancements.append(ContentEnhancement(
signal_type='parameters',
content=self._generate_parameters(params),
insertion_point='after_usage',
reason="Command docs should specify parameters",
expected_analyst_boost={'content': 0.15, 'semantic': 0.10},
priority=1
))
if 'examples' in analysis.missing_signals:
enhancements.append(ContentEnhancement(
signal_type='examples',
content=self._generate_examples(command_name),
insertion_point='before_end',
reason="Command docs benefit from usage examples",
expected_analyst_boost={'content': 0.10, 'pattern': 0.10},
priority=2
))
return enhancements
def _infer_command_name(self, title: str, content: str) -> str:
"""Infer command name from title or content."""
# Look for /command pattern
match = re.search(r'/(\w+[-\w]*)', content)
if match:
return match.group(1)
# Convert title to command style
return title.lower().replace(' ', '-').replace('_', '-')
def _infer_parameters(self, content: str) -> List[Dict]:
"""Infer parameters from content."""
params = []
# Look for --flag patterns with descriptions
flag_matches = re.findall(r'(--[\w-]+)\s*[:\-]\s*(.+?)(?:\n|$)', content)
for flag, desc in flag_matches[:5]:
params.append({
'name': flag,
'type': 'boolean',
'description': desc.strip()[:100]
})
# Look for positional arguments
if re.search(r'path|file|directory', content, re.I):
params.append({
'name': 'path',
'type': 'string',
'description': 'Target path or file'
})
if not params:
params = [
{'name': '--help', 'type': 'boolean', 'description': 'Show help message'},
{'name': '--verbose', 'type': 'boolean', 'description': 'Enable verbose output'},
]
return params
def _generate_invocation(self, command_name: str) -> str:
"""Generate invocation section."""
return f"""
Invocation
/{command_name} [options] [arguments]
"""
def _generate_usage(self, command_name: str, content: str) -> str:
"""Generate usage section."""
# Try to infer what the command does
purpose = "Execute the specified operation"
if 'sync' in command_name or 'sync' in content.lower():
purpose = "Synchronize data or state"
elif 'create' in command_name or 'new' in command_name:
purpose = "Create a new resource"
elif 'delete' in command_name or 'remove' in command_name:
purpose = "Remove the specified resource"
elif 'list' in command_name or 'show' in command_name:
purpose = "Display information"
return f"""
Usage
{purpose}
Basic usage:
/{command_name}
With options:
/{command_name} --verbose
"""
def _generate_parameters(self, params: List[Dict]) -> str:
"""Generate parameters section."""
rows = "\n".join(
f"| `{p['name']}` | {p['type']} | {p['description']} |"
for p in params
)
return f"""
Parameters
| Parameter | Type | Description |
|---|---|---|
| {rows} | ||
| """ |
def _generate_examples(self, command_name: str) -> str:
"""Generate examples section."""
return f"""
Examples
Basic Example
/{command_name}
With Options
/{command_name} --verbose --output ./result
Advanced Usage
/{command_name} path/to/target --recursive --dry-run
"""