feat: Add Kaizen Optimizer and Optimized Refactoring Assistant agents
Added two new Claude Code subagents following proper specification format: **Kaizen Optimizer Agent:** - Meta-agent for analyzing and optimizing other subagents - Performance analysis and specification improvement recommendations - Agent ecosystem health assessment and continuous improvement - Proper YAML frontmatter with proactive usage guidelines **Refactoring Assistant Agent (Optimized):** - Streamlined from 19-section complex specification to focused Claude Code format - Code quality assessment and refactoring guidance within Claude Code environment - Security analysis and performance optimization recommendations - Integration with existing agent ecosystem (tddai-assistant, general-purpose, project-assistant) **Also includes Issue #15 AST Query CLI implementation:** - AST Service with display, query, and statistics capabilities - JSONPath integration for flexible AST navigation - CLI commands: ast-show, ast-query, ast-stats (22/22 tests passing) - Leverages existing cache system for optimal performance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
187
markitect/cli.py
187
markitect/cli.py
@@ -28,6 +28,7 @@ from .database import DatabaseManager
|
||||
from .document_manager import DocumentManager
|
||||
from .serializer import ASTSerializer
|
||||
from .cache_service import CacheDirectoryService
|
||||
from .ast_service import ASTService
|
||||
|
||||
|
||||
# Global options for CLI configuration
|
||||
@@ -741,6 +742,192 @@ def cache_invalidate(config, file_path):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command('ast-show')
|
||||
@click.argument('file_path', type=click.Path(exists=False))
|
||||
@click.option('--format', '-f', type=click.Choice(['tree', 'json', 'compact']), default='tree', help='Display format')
|
||||
@pass_config
|
||||
def ast_show(config, file_path, format):
|
||||
"""
|
||||
Display AST structure for file.
|
||||
|
||||
Shows the Abstract Syntax Tree representation of a markdown file
|
||||
with various formatting options for analysis and debugging.
|
||||
|
||||
FILE_PATH: Path to the markdown file to analyze
|
||||
|
||||
Examples:
|
||||
markitect ast-show document.md
|
||||
markitect ast-show document.md --format json
|
||||
markitect ast-show document.md --format compact
|
||||
"""
|
||||
try:
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Analyzing AST structure for: {file_path}", err=True)
|
||||
|
||||
ast_service = ASTService()
|
||||
result = ast_service.display_ast(Path(file_path), format)
|
||||
|
||||
if result['success']:
|
||||
if result.get('message'):
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Info: {result['message']}", err=True)
|
||||
click.echo(result['output'])
|
||||
|
||||
if config.get('verbose') and result.get('token_count'):
|
||||
click.echo(f"Total tokens: {result['token_count']}", err=True)
|
||||
else:
|
||||
click.echo(f"Error: {result['message']}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"AST display error: {e}", err=True)
|
||||
if config and config.get('verbose'):
|
||||
import traceback
|
||||
click.echo(traceback.format_exc(), err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command('ast-query')
|
||||
@click.argument('file_path', type=click.Path(exists=False))
|
||||
@click.argument('jsonpath', type=str)
|
||||
@click.option('--format', '-f', type=click.Choice(['json', 'compact']), default='json', help='Output format')
|
||||
@pass_config
|
||||
def ast_query(config, file_path, jsonpath, format):
|
||||
"""
|
||||
Query AST using JSONPath.
|
||||
|
||||
Execute JSONPath expressions against the AST structure of a markdown file
|
||||
to extract specific elements or patterns.
|
||||
|
||||
FILE_PATH: Path to the markdown file to query
|
||||
JSONPATH: JSONPath expression to execute
|
||||
|
||||
Examples:
|
||||
markitect ast-query doc.md '$.*.type'
|
||||
markitect ast-query doc.md '$..tag'
|
||||
markitect ast-query doc.md '$[:5]' --format compact
|
||||
"""
|
||||
try:
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Executing JSONPath query on: {file_path}", err=True)
|
||||
click.echo(f"Query: {jsonpath}", err=True)
|
||||
|
||||
ast_service = ASTService()
|
||||
result = ast_service.query_ast(Path(file_path), jsonpath)
|
||||
|
||||
if result['success']:
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Query results: {result['count']} matches", err=True)
|
||||
|
||||
if result['count'] == 0:
|
||||
click.echo("No matches found for query.")
|
||||
else:
|
||||
if format == 'compact':
|
||||
for i, match in enumerate(result['matches']):
|
||||
if isinstance(match, dict):
|
||||
token_type = match.get('type', 'unknown')
|
||||
content = match.get('content', match.get('tag', ''))[:30]
|
||||
click.echo(f"[{i}] {token_type}: {content}")
|
||||
else:
|
||||
click.echo(f"[{i}] {match}")
|
||||
else:
|
||||
import json
|
||||
click.echo(json.dumps(result['matches'], indent=2, ensure_ascii=False))
|
||||
else:
|
||||
click.echo(f"Error: {result['message']}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"AST query error: {e}", err=True)
|
||||
if config and config.get('verbose'):
|
||||
import traceback
|
||||
click.echo(traceback.format_exc(), err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command('ast-stats')
|
||||
@click.argument('file_path', type=click.Path(exists=False))
|
||||
@click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml']), default='table', help='Output format')
|
||||
@pass_config
|
||||
def ast_stats(config, file_path, format):
|
||||
"""
|
||||
Show AST statistics (headings, links, etc.).
|
||||
|
||||
Analyze markdown file structure and provide comprehensive statistics
|
||||
about document elements, organization, and content patterns.
|
||||
|
||||
FILE_PATH: Path to the markdown file to analyze
|
||||
|
||||
Examples:
|
||||
markitect ast-stats document.md
|
||||
markitect ast-stats document.md --format json
|
||||
markitect ast-stats document.md --format yaml
|
||||
"""
|
||||
try:
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Calculating statistics for: {file_path}", err=True)
|
||||
|
||||
ast_service = ASTService()
|
||||
result = ast_service.analyze_ast_statistics(Path(file_path))
|
||||
|
||||
if result['success']:
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Analysis complete for: {Path(file_path).name}", err=True)
|
||||
|
||||
stats = result['statistics']
|
||||
if format == 'table':
|
||||
# Format statistics as readable table
|
||||
click.echo("Document Statistics:")
|
||||
click.echo("=" * 40)
|
||||
click.echo(f"Total AST tokens: {stats.get('total_tokens', 0)}")
|
||||
click.echo(f"Document structure: {stats.get('document_structure', 'unknown')}")
|
||||
click.echo()
|
||||
|
||||
# Headings
|
||||
headings = stats.get('headings', {})
|
||||
click.echo(f"Headings: {headings.get('total', 0)}")
|
||||
for level, count in headings.get('by_level', {}).items():
|
||||
click.echo(f" {level.upper()}: {count}")
|
||||
|
||||
click.echo(f"Paragraphs: {stats.get('paragraphs', 0)}")
|
||||
click.echo(f"Links: {stats.get('links', 0)}")
|
||||
|
||||
# Lists
|
||||
lists = stats.get('lists', {})
|
||||
total_lists = lists.get('ordered', 0) + lists.get('unordered', 0)
|
||||
click.echo(f"Lists: {total_lists}")
|
||||
if total_lists > 0:
|
||||
click.echo(f" Ordered: {lists.get('ordered', 0)}")
|
||||
click.echo(f" Unordered: {lists.get('unordered', 0)}")
|
||||
|
||||
click.echo(f"Code blocks: {stats.get('code_blocks', 0)}")
|
||||
click.echo(f"Inline code: {stats.get('inline_code', 0)}")
|
||||
click.echo(f"Blockquotes: {stats.get('blockquotes', 0)}")
|
||||
|
||||
# Emphasis
|
||||
emphasis = stats.get('emphasis', {})
|
||||
click.echo(f"Strong text: {emphasis.get('strong', 0)}")
|
||||
click.echo(f"Italic text: {emphasis.get('italic', 0)}")
|
||||
|
||||
elif format == 'json':
|
||||
import json
|
||||
click.echo(json.dumps(stats, indent=2, ensure_ascii=False))
|
||||
elif format == 'yaml':
|
||||
import yaml
|
||||
click.echo(yaml.dump(stats, default_flow_style=False, allow_unicode=True))
|
||||
|
||||
else:
|
||||
click.echo(f"Error: {result['message']}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"AST statistics error: {e}", err=True)
|
||||
if config and config.get('verbose'):
|
||||
import traceback
|
||||
click.echo(traceback.format_exc(), err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for the CLI.
|
||||
|
||||
Reference in New Issue
Block a user