From d76e7f4f1537d1881a972c16e93c90d50ebb7e20 Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Fri, 17 Apr 2026 13:16:19 +0200 Subject: [PATCH 1/3] tests: Removed trailing commas from function calls Signed-off-by: Ole Herman Schumacher Elgesem --- tests/format/003_wrapping.expected.cf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/format/003_wrapping.expected.cf b/tests/format/003_wrapping.expected.cf index 546f1a2..82cc4f3 100644 --- a/tests/format/003_wrapping.expected.cf +++ b/tests/format/003_wrapping.expected.cf @@ -70,7 +70,7 @@ bundle agent function_calls "seven", "eight", "nine", - "ten", + "ten" ); } @@ -90,7 +90,7 @@ bundle agent nested_function_calls "variable_name" string => concat( "one", - concat("two", "three", "four", "five", "six", "seven", "eight", ""), + concat("two", "three", "four", "five", "six", "seven", "eight", "") ); "variable_name" @@ -98,7 +98,7 @@ bundle agent nested_function_calls "one", concat( "two", "three", "four", "five", "six", "seven", "eight", "nine", "" - ), + ) ); "variable_name" @@ -113,8 +113,8 @@ bundle agent nested_function_calls "seven", "eight", "nine", - "ten", - ), + "ten" + ) ); "variable_name" @@ -122,7 +122,7 @@ bundle agent nested_function_calls "one", "two", "three", - concat("two", "three", "four", "five", "six", "seven", "eight"), + concat("two", "three", "four", "five", "six", "seven", "eight") ); "variable_name" @@ -130,7 +130,7 @@ bundle agent nested_function_calls "one", "two", "three", - concat("two", "three", "four", "five", "six", "seven", "eight"), + concat("two", "three", "four", "five", "six", "seven", "eight") ); "variable_name" @@ -139,7 +139,7 @@ bundle agent nested_function_calls "two", "three", # comment a - concat("two", "three", "four", "five", "six", "seven", "eight"), + concat("two", "three", "four", "five", "six", "seven", "eight") # comment b ); } From 164ee35b9b381cfae2621cb870ba15674a965e0b Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Fri, 17 Apr 2026 13:16:33 +0200 Subject: [PATCH 2/3] Added initial CLAUDE.md Signed-off-by: Ole Herman Schumacher Elgesem --- CLAUDE.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9aa03b0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,40 @@ +# CFEngine CLI information for LLMs + +## Fix the implementation + +In general, when the prompter asks you to fix the implementation, this means that they have adjusted the tests already and they want you to fix the implementation. +Typically you should not touch the tests in this case, unless there is something obviously wrong in them, like a typo. +The first step to identify what is necessary should be to run the tests and see which ones are failing. + +## Running tests + +In general, the main command to run for testing is: + +```bash +make check +``` + +This will run all the test suites. + +## Running python tools + +This project uses `uv`. +That means that you should not run `python`, `python3`, `pip`, `pip3` directly. +Instead, run the appropriate uv command to ensure we're using the right python and the right dependencies. + +## Pointers for the source code + +When fixing issues, these are usually the files to look at: + +- The implementation of `cfengine format` is in `src/cfengine_cli/format.py`. +- The implementation of `cfengine lint` is in `src/cfengine_cli/lint.py`. + +## Test suites + +As mentioned above, the `make check` command runs all the tests. +We have different suites: + +- Unit tests in `tests/unit` test individual python functions. +- Formatting tests in `tests/format` test the formatter (`cfengine format`). +- Linting tests in `tests/lint` test the linter. +- Shell tests in `tests/shell` tests various subcommands and the tool as a whole in an end-to-end fashion. From beef613832e55bbd7a151e4b40a8557d0dc4cb62 Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Fri, 17 Apr 2026 13:23:23 +0200 Subject: [PATCH 3/3] cfengine format: Removed trailing commas where they're not supported In CFEngine, calls (to functions, bodies, bundles) don't support trailing commas (yet). Co-authored-by: Claude Opus 4.6 (1M context) Signed-off-by: Ole Herman Schumacher Elgesem --- src/cfengine_cli/format.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cfengine_cli/format.py b/src/cfengine_cli/format.py index 48539fa..81ac781 100644 --- a/src/cfengine_cli/format.py +++ b/src/cfengine_cli/format.py @@ -182,7 +182,9 @@ def split_generic_value(node: Node, indent: int, line_length: int) -> list[str]: return [stringify_single_line_node(node)] -def split_generic_list(middle: list[Node], indent: int, line_length: int) -> list[str]: +def split_generic_list( + middle: list[Node], indent: int, line_length: int, trailing_comma: bool = True +) -> list[str]: """Split list elements into one-per-line strings, each pre-indented.""" elements: list[str] = [] for element in middle: @@ -196,25 +198,27 @@ def split_generic_list(middle: list[Node], indent: int, line_length: int) -> lis lines = split_generic_value(element, indent, line_length) elements.append(" " * indent + lines[0]) elements.extend(lines[1:]) - # Always add a trailing comma on multi-line lists, on the last + # Ensure trailing comma state matches the desired setting, on the last # non-comment element (so it doesn't end up after a trailing comment). for i in range(len(elements) - 1, -1, -1): if elements[i].lstrip().startswith("#"): continue - if not elements[i].endswith(","): + if trailing_comma and not elements[i].endswith(","): elements[i] = elements[i] + "," + elif not trailing_comma and elements[i].endswith(","): + elements[i] = elements[i][:-1] break return elements def maybe_split_generic_list( - nodes: list[Node], indent: int, line_length: int + nodes: list[Node], indent: int, line_length: int, trailing_comma: bool = True ) -> list[str]: """Try a single-line rendering; fall back to split_generic_list if too long.""" string = " " * indent + stringify_single_line_nodes(nodes) if len(string) < line_length: return [string] - return split_generic_list(nodes, indent, line_length) + return split_generic_list(nodes, indent, line_length, trailing_comma) def split_rval_list(node: Node, indent: int, line_length: int) -> list[str]: @@ -236,7 +240,9 @@ def split_rval_call(node: Node, indent: int, line_length: int) -> list[str]: first = text(node.children[0]) + "(" last = " " * indent + text(node.children[-1]) middle = node.children[2:-1] - elements = maybe_split_generic_list(middle, indent + 2, line_length) + elements = maybe_split_generic_list( + middle, indent + 2, line_length, trailing_comma=False + ) return [first, *elements, last]