Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/interface/cli/ast_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,17 +315,14 @@ def _read_file(
return None, 2


def ast_parse(
file_path: str, json_output: bool = True, root: str | None = None, quiet: bool = False
) -> int:
def ast_parse(file_path: str, root: str | None = None, quiet: bool = False) -> int:
"""Parse a markdown file and output the AST as JSON.

Reads the file, parses it with JerryDocument, and prints a JSON object
containing the file path, the flat token list, and the full AST tree.

Args:
file_path: Path to the markdown file to parse.
json_output: Unused flag kept for API symmetry; output is always JSON.
root: Optional ``--root`` containment override (BUG-010 Option C).
See ``_read_file`` for semantics.
quiet: Suppress stderr transparency notes and broad-root warnings
Expand Down Expand Up @@ -497,7 +494,6 @@ def ast_validate(
def ast_query(
file_path: str,
selector: str,
json_output: bool = True,
root: str | None = None,
quiet: bool = False,
) -> int:
Expand All @@ -510,7 +506,6 @@ def ast_query(
Args:
file_path: Path to the markdown file to query.
selector: The node type string to query (e.g., "heading", "blockquote").
json_output: Unused flag kept for API symmetry; output is always JSON.
root: Optional ``--root`` containment override (BUG-010 Option C).
See ``_read_file`` for semantics.
quiet: Suppress stderr transparency notes and broad-root warnings
Expand Down
9 changes: 4 additions & 5 deletions src/interface/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def main() -> int:
elif args.namespace == "context":
return _handle_context(args)
elif args.namespace == "ast":
return _handle_ast(args, json_output)
return _handle_ast(args)
elif args.namespace == "agents":
return _handle_agents(args, json_output)
elif args.namespace == "ci":
Expand Down Expand Up @@ -390,7 +390,7 @@ def _handle_context(args: Any) -> int:
return 1


def _handle_ast(args: Any, json_output: bool) -> int:
def _handle_ast(args: Any) -> int:
"""Route ast namespace commands.

Does not require the CLIAdapter; calls the domain layer directly via
Expand All @@ -403,7 +403,6 @@ def _handle_ast(args: Any, json_output: bool) -> int:
``getattr(args, "root"/"quiet", None/False)`` and passed
through to every command, matching the existing defensive
pattern already used for ``schema``/``nav``.
json_output: Whether JSON output was requested (passed through to commands).

Returns:
Exit code: 0 (success), 1 (validation failure), 2 (parse error).
Expand Down Expand Up @@ -432,7 +431,7 @@ def _handle_ast(args: Any, json_output: bool) -> int:
quiet = getattr(args, "quiet", False)

if args.command == "parse":
return ast_parse(args.file, json_output, root=root, quiet=quiet)
return ast_parse(args.file, root=root, quiet=quiet)
elif args.command == "render":
return ast_render(args.file, root=root, quiet=quiet)
elif args.command == "validate":
Expand All @@ -444,7 +443,7 @@ def _handle_ast(args: Any, json_output: bool) -> int:
quiet=quiet,
)
elif args.command == "query":
return ast_query(args.file, args.selector, json_output, root=root, quiet=quiet)
return ast_query(args.file, args.selector, root=root, quiet=quiet)
elif args.command == "frontmatter":
return ast_frontmatter(args.file, root=root, quiet=quiet)
elif args.command == "modify":
Expand Down
34 changes: 32 additions & 2 deletions tests/unit/interface/cli/test_ast_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from __future__ import annotations

import inspect
import json
import sys
import tempfile
Expand Down Expand Up @@ -1175,7 +1176,7 @@ class FakeArgs:
file = str(tmp_md_file)

with patch("sys.stdout", new_callable=StringIO):
result = _handle_ast(FakeArgs(), json_output=False)
result = _handle_ast(FakeArgs())
assert result == 1

def test_handle_ast_unknown_command_prints_error_to_stderr_only(
Expand All @@ -1191,7 +1192,7 @@ class FakeArgs:
command = "nonexistent"
file = str(tmp_md_file)

result = _handle_ast(FakeArgs(), json_output=False)
result = _handle_ast(FakeArgs())
captured = capsys.readouterr()
assert result == 1
assert captured.out == ""
Expand Down Expand Up @@ -2054,3 +2055,32 @@ def test_ast_commands_module_when_imported_then_get_repo_root_is_not_defined(sel
not from this legacy single-root convenience accessor. Guard
against silent reintroduction."""
assert not hasattr(ast_commands_module, "_get_repo_root")


# =============================================================================
# GH #378: unused json_output parameter removed from the ast command path
# =============================================================================
#
# ast_parse and ast_query always serialize their output as JSON; the
# json_output parameter they accepted was never read, and _handle_ast only
# existed to forward the global --json flag into them. These guards prevent
# silent reintroduction of the dead parameter.
# =============================================================================


class TestJsonOutputParameterRemoved:
"""GH #378: guard against reintroduction of the unused json_output parameter."""

def test_ast_parse_when_inspected_then_has_no_json_output_parameter(self) -> None:
"""ast_parse must not expose json_output -- it always emits JSON."""
assert "json_output" not in inspect.signature(ast_parse).parameters

def test_ast_query_when_inspected_then_has_no_json_output_parameter(self) -> None:
"""ast_query must not expose json_output -- it always emits JSON."""
assert "json_output" not in inspect.signature(ast_query).parameters

def test_handle_ast_when_inspected_then_has_no_json_output_parameter(self) -> None:
"""_handle_ast must not accept json_output -- no ast command reads it."""
from src.interface.cli.main import _handle_ast

assert "json_output" not in inspect.signature(_handle_ast).parameters