From 704374cb4cd96862633f81ee5c459ca6ed6dec86 Mon Sep 17 00:00:00 2001 From: Elifarley Date: Thu, 19 Dec 2024 21:21:26 +0000 Subject: [PATCH 1/3] Fix license badge --- .gitignore | 3 +++ README.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3983192..8c6ae29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ /src/version/_version.py src/*.egg-info +__pycache__ dist build out +.venv +.env .coverage .*.history* *.tags.cache* diff --git a/README.md b/README.md index e594511..cda89bc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI version](https://badge.fury.io/py/cedarscript-editor.svg)](https://pypi.org/project/cedarscript-editor/) [![Python Versions](https://img.shields.io/pypi/pyversions/cedarscript-editor.svg)](https://pypi.org/project/cedarscript-editor/) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) `CEDARScript Editor (Python)` is a [CEDARScript](https://bit.ly/cedarscript) runtime for interpreting `CEDARScript` scripts and performing code analysis and modification operations on a codebase. From 3d0d7973e411a1c9d6d3f7141fda6161911a874c Mon Sep 17 00:00:00 2001 From: Elifarley C Date: Fri, 21 Nov 2025 14:35:51 -0300 Subject: [PATCH 2/3] Add CLI interface for CEDARScript Editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create src/cedarscript_editor/cli.py with Click framework - Support three input methods: direct command, file, and STDIN - Add --root, --quiet, and --check options - Update pyproject.toml with Click dependency and console script entry point - Add comprehensive CLI documentation to README.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 149 ++++++++++++++++++++--- pyproject.toml | 4 + src/cedarscript_editor/__init__.py | 19 +-- src/cedarscript_editor/cli.py | 185 +++++++++++++++++++++++++++++ 4 files changed, 331 insertions(+), 26 deletions(-) create mode 100644 src/cedarscript_editor/cli.py diff --git a/README.md b/README.md index cda89bc..6418004 100644 --- a/README.md +++ b/README.md @@ -42,30 +42,141 @@ pip install cedarscript-editor ## Usage -Here's a quick example of how to use `CEDARScript` Editor: +### Python Library + +Here's a quick example of how to use `CEDARScript` Editor as a Python library: ```python -from cedarscript_editor import CEDARScriptEdior - -parser = CEDARScriptEdior() -code = """ -CREATE FILE "example.py" -UPDATE FILE "example.py" - INSERT AT END OF FILE - CONTENT - print("Hello, World!") - END CONTENT -END UPDATE +from cedarscript_editor import CEDARScriptEditor + +editor = CEDARScriptEditor("/path/to/project") + +# Parse and execute CEDARScript commands +cedarscript = """```CEDARScript +CREATE FILE "example.py" WITH +""" +print("Hello, World!") +""" +```""" + +# Apply commands to the codebase +results = editor.apply_cedarscript(cedarscript) +print(results) +``` + +### Command Line Interface + +`cedarscript-editor` also provides a CLI for executing CEDARScript commands directly from the command line. + +#### Installation + +After installing via pip, the `cedarscript` command will be available: + +```bash +pip install cedarscript-editor +``` + +#### Basic Usage + +```bash +# Execute CEDARScript directly +cedarscript 'CREATE FILE "example.py" WITH "print(\"Hello World\")"' + +# Read CEDARScript from file +cedarscript -f commands.cedar +cedarscript --file commands.cedar + +# Read from STDIN +cat commands.cedar | cedarscript +echo 'UPDATE FILE "test.py" INSERT LINE 1 "import os"' | cedarscript + +# Specify base directory +cedarscript --root /path/to/project -f commands.cedar + +# Quiet mode for scripting +cedarscript --quiet -f commands.cedar + +# Syntax check only +cedarscript --check -f commands.cedar +``` + +#### CLI Options + +- `-f, --file FILENAME`: Read CEDARScript commands from file +- `--root DIRECTORY`: Base directory for file operations (default: current directory) +- `-q, --quiet`: Minimal output (for scripting) +- `--check`: Syntax check only - parse commands without executing +- `COMMAND`: Direct CEDARScript command (alternative to file input) + +#### CEDARScript File Format + +CEDARScript commands must be enclosed in fenced code blocks: + +````markdown +```CEDARScript +CREATE FILE "example.py" WITH +""" +print("Hello, World!") +""" +``` +```` + +Or use the `` tag for direct command execution: + +```cedarscript + +CREATE FILE "example.py" WITH +""" +print("Hello, World!") +""" +``` + +#### Examples + +**Create a new file:** +```bash +cedarscript '```CEDARScript +CREATE FILE "utils.py" WITH +""" +def hello(): + print("Hello from utils!") """ +```' +``` -commands, errors = parser.parse_script(code) +**Update an existing file:** +```bash +cat > update_commands.cedar << 'EOF' +```CEDARScript +UPDATE FILE "app.py" +INSERT LINE 1 + """Application module""" +INSERT AFTER "import os" + import sys +```' +EOF + +cedarscript -f update_commands.cedar +``` -if errors: - for error in errors: - print(f"Error: {error}") -else: - for command in commands: - print(f"Parsed command: {command}") +**Multi-file operations:** +```bash +cat > refactor.cedar << 'EOF' +```CEDARScript +# Move method to top level +UPDATE CLASS "DataProcessor" +FROM FILE "data.py" +MOVE METHOD "process" + +# Update call sites +UPDATE FUNCTION "main" +FROM FILE "main.py" +REPLACE LINE 5 + result = process(data) +```' +EOF + +cedarscript --root ./my-project -f refactor.cedar ``` ## Contributing diff --git a/pyproject.toml b/pyproject.toml index da22f44..0542c25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ # https://github.com/grantjenks/py-tree-sitter-languages/issues/64 "tree-sitter==0.21.3", # 0.22 breaks tree-sitter-languages "tree-sitter-languages==1.10.2", + "click>=8.0.0", ] requires-python = ">=3.11" @@ -38,6 +39,9 @@ Documentation = "https://github.com/CEDARScript/cedarscript-editor-python#readme Repository = "https://github.com/CEDARScript/cedarscript-editor-python.git" "Bug Tracker" = "https://github.com/CEDARScript/cedarscript-editor-python/issues" +[project.scripts] +cedarscript = "cedarscript_editor.cli:main" + [project.optional-dependencies] dev = [ "pytest>=7.0", diff --git a/src/cedarscript_editor/__init__.py b/src/cedarscript_editor/__init__.py index c7b0617..7ca7dfd 100644 --- a/src/cedarscript_editor/__init__.py +++ b/src/cedarscript_editor/__init__.py @@ -9,17 +9,22 @@ # TODO Move to cedarscript-ast-parser -def find_commands(content: str): +def find_commands(content: str, require_fenced: bool = True): # Regex pattern to match CEDARScript blocks pattern = r'```CEDARScript\n(.*?)```' cedar_script_blocks = re.findall(pattern, content, re.DOTALL) print(f'[find_cedar_commands] Script block count: {len(cedar_script_blocks)}') - if len(cedar_script_blocks) == 0 and not content.strip().endswith(''): - raise ValueError( - "No CEDARScript block detected. " - "Perhaps you forgot to enclose the block using ```CEDARScript and ``` ? " - "Or was that intentional? If so, just write tag as the last line" - ) + if require_fenced: + if len(cedar_script_blocks) == 0 and not content.strip().endswith(''): + raise ValueError( + "No CEDARScript block detected. " + "Perhaps you forgot to enclose the block using ```CEDARScript and ``` ? " + "Or was that intentional? If so, just write tag as the last line" + ) + else: + # New behavior - treat entire content as single block + cedar_script_blocks = [content] + print(f'[find_cedar_commands] Processing entire input as single CEDARScript block') cedarscript_parser = CEDARScriptASTParser() for cedar_script in cedar_script_blocks: parsed_commands, parse_errors = cedarscript_parser.parse_script(cedar_script) diff --git a/src/cedarscript_editor/cli.py b/src/cedarscript_editor/cli.py new file mode 100644 index 0000000..726a2ab --- /dev/null +++ b/src/cedarscript_editor/cli.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +""" +CEDARScript Editor CLI Interface + +Provides command-line interface for executing CEDARScript commands +on code files using the CEDARScript Editor library. +""" + +import sys +from pathlib import Path +from typing import Optional + +import click +from cedarscript_ast_parser import CEDARScriptASTParser + +from .cedarscript_editor import CEDARScriptEditor, CEDARScriptEditorException +from . import find_commands + + +@click.command(help="Execute CEDARScript commands on code files") +@click.option( + '--file', '-f', + type=click.File('r'), + help='Read CEDARScript commands from file' +) +@click.option( + '--root', '-r', + type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path), + default=Path.cwd(), + help='Base directory for file operations (default: current directory)' +) +@click.option( + '--quiet', '-q', + is_flag=True, + help='Minimal output (for scripting)' +) +@click.option( + '--check', '-c', + is_flag=True, + help='Syntax check only - parse commands without executing' +) +@click.option( + '--fenced', '-F', + is_flag=True, + default=False, + help='Require CEDARScript blocks to be fenced with ```CEDARScript (default: treat entire input as single block)' +) +@click.argument('command', required=False) +def main( + file: Optional[click.File], + root: Path, + quiet: bool, + check: bool, + fenced: bool, + command: Optional[str] +) -> None: + """ + CEDARScript Editor CLI + + Execute CEDARScript commands to transform code files. + + Examples: + \b + # Direct command + cedarscript "UPDATE myfile.py SET imports.append('import os')" + + # From file (both short and long forms) + cedarscript -f commands.cedar + cedarscript --file commands.cedar + + # From STDIN + cat commands.cedar | cedarscript + + # With custom base directory + cedarscript -r /path/to/project -f commands.cedar + cedarscript --root /path/to/project -f commands.cedar + + # Quiet mode for scripting + cedarscript -q -f commands.cedar + cedarscript --quiet -f commands.cedar + + # Syntax check only + cedarscript -c -f commands.cedar + cedarscript --check -f commands.cedar + + # With fenced requirement + cedarscript -F -f commands.cedar + cedarscript --fenced -f commands.cedar + + # Mixed short and long flags + cedarscript -r /path/to -c -F --file commands.cedar + """ + try: + # Determine command source + commands = _get_commands_input(file, command, quiet) + + if not commands.strip(): + _echo_error("No CEDARScript commands provided", quiet) + sys.exit(1) + + # Parse commands + if not quiet: + click.echo("Parsing CEDARScript commands...") + + try: + parsed_commands = list(find_commands(commands, require_fenced=fenced)) + except Exception as e: + _echo_error(f"Failed to parse CEDARScript: {e}", quiet) + sys.exit(1) + + if not quiet: + click.echo(f"Parsed {len(parsed_commands)} command(s)") + + # If syntax check only, exit here + if check: + if not quiet: + click.echo("Syntax check passed - commands are valid") + sys.exit(0) + + # Execute commands + if not quiet: + click.echo(f"Executing commands in directory: {root}") + + editor = CEDARScriptEditor(str(root)) + results = editor.apply_commands(parsed_commands) + + # Output results + if not quiet: + click.echo("\nResults:") + for result in results: + click.echo(f" {result}") + else: + # Quiet mode - just show success/failure + click.echo(f"Applied {len(results)} command(s)") + + except CEDARScriptEditorException as e: + _echo_error(f"CEDARScript execution error: {e}", quiet) + sys.exit(2) + except KeyboardInterrupt: + _echo_error("Operation cancelled by user", quiet) + sys.exit(130) + except Exception as e: + _echo_error(f"Unexpected error: {e}", quiet) + sys.exit(3) + + +def _get_commands_input( + file: Optional[click.File], + command: Optional[str], + quiet: bool +) -> str: + """ + Determine the source of CEDARScript commands based on provided inputs. + + Priority: command argument > file option > STDIN + """ + if command: + return command + + if file: + return file.read() + + # Check if STDIN has data (not a TTY) + if not sys.stdin.isatty(): + try: + return sys.stdin.read() + except Exception as e: + _echo_error(f"Error reading from STDIN: {e}", quiet) + sys.exit(1) + + return "" + + +def _echo_error(message: str, quiet: bool) -> None: + """Echo error message with appropriate formatting.""" + if quiet: + # In quiet mode, send errors to stderr but keep them brief + click.echo(f"Error: {message}", err=True) + else: + # In normal mode, use styled error output + click.echo(click.style(f"Error: {message}", fg='red'), err=True) + + +if __name__ == '__main__': + main() \ No newline at end of file From 624235d42b45f9befbe8ee2f2bb51345f3773ed6 Mon Sep 17 00:00:00 2001 From: "Elifarley C." <519940+elifarley@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:47:53 -0300 Subject: [PATCH 3/3] Include reference to Serena --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6418004..6473b80 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,10 @@ cedarscript --root ./my-project -f refactor.cedar Contributions are welcome! Please feel free to submit a Pull Request. +## See Also + +- https://github.com/oraios/serena + ## License This project is licensed under the MIT License.