Skip to content

Commit 34b0e03

Browse files
committed
feat: add dry-run validation, structured error reporting, and expanded CLI command support
1 parent 7ac09da commit 34b0e03

13 files changed

Lines changed: 1244 additions & 92 deletions

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88

9+
## [0.6.0] - 2026-04-06
10+
11+
### Changed
12+
13+
- **Dependency bump**: requires `apcore >= 0.17.1` (was `>= 0.15.1`). Adds Execution Pipeline Strategy, Config Bus enhancements, Pipeline v2 declarative step metadata, `minimal` strategy preset.
14+
- **Schema parser**: Required schema properties now correctly enforced at CLI option level (was silently optional).
15+
- **Approval gate**: Fixed inverted logic in annotation type guard; `check_approval()` now accepts `timeout` parameter.
16+
17+
### Added
18+
19+
- **FE-11: Usability Enhancements** — 11 new capabilities:
20+
- `--dry-run` preflight mode via `Executor.validate()`. Standalone `validate` command.
21+
- System management commands: `health`, `usage`, `enable`, `disable`, `reload`, `config get`/`config set`. Graceful no-op when system modules unavailable.
22+
- Enhanced error output: structured JSON with `ai_guidance`, `suggestion`, `retryable`, `user_fixable`, `details`. TTY hides machine-only fields.
23+
- `--trace` pipeline visualization via `call_with_trace()`.
24+
- `CliApprovalHandler` class implementing apcore `ApprovalHandler` protocol, wired to `Executor.set_approval_handler()`. `--approval-timeout`, `--approval-token` flags.
25+
- `--stream` JSONL output via `Executor.stream()`.
26+
- Enhanced `list` command: `--search`, `--status`, `--annotation`, `--sort`, `--reverse`, `--deprecated`, `--deps`.
27+
- `--strategy` selection: `standard`, `internal`, `testing`, `performance`, `minimal`. `describe-pipeline` command.
28+
- Output format extensions: `--format csv|yaml|jsonl`, `--fields` dot-path field selection.
29+
- Multi-level grouping: `cli.group_depth` config key.
30+
- Custom command extension: `create_cli(extra_commands=[...])` with collision detection.
31+
- New error code: `CONFIG_ENV_MAP_CONFLICT`.
32+
- New config keys: `cli.approval_timeout` (60), `cli.strategy` ("standard"), `cli.group_depth` (1).
33+
- New environment variables: `APCORE_CLI_APPROVAL_TIMEOUT`, `APCORE_CLI_STRATEGY`, `APCORE_CLI_GROUP_DEPTH`.
34+
- New files: `system_cmd.py`, `strategy.py`.
35+
36+
---
37+
938
## [0.5.1] - 2026-04-03
1039

1140
### Added

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "apcore-cli"
7-
version = "0.5.1"
7+
version = "0.6.0"
88
description = "Terminal adapter for apcore — execute AI-Perceivable modules from the command line"
99
readme = "README.md"
1010
license = "Apache-2.0"
@@ -26,7 +26,7 @@ classifiers = [
2626
"Environment :: Console",
2727
]
2828
dependencies = [
29-
"apcore>=0.15.1",
29+
"apcore>=0.17.1",
3030
"click>=8.1",
3131
"jsonschema>=4.20",
3232
"rich>=13.0",

src/apcore_cli/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
"auto_approve": False,
2222
"help_text_max_length": 1000,
2323
"logging_level": "WARNING",
24+
"approval_timeout": 60,
25+
"strategy": "standard",
26+
"group_depth": 1,
2427
},
2528
)
2629
except (ImportError, AttributeError):

src/apcore_cli/__main__.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@
55
import logging
66
import os
77
import sys
8+
from importlib.metadata import PackageNotFoundError
9+
from importlib.metadata import version as _get_version
810
from typing import Any
911

1012
import click
1113

12-
from apcore_cli import __version__
1314
from apcore_cli.cli import GroupedModuleGroup, set_audit_logger, set_verbose_help
1415
from apcore_cli.config import ConfigResolver
1516
from apcore_cli.discovery import register_discovery_commands
1617
from apcore_cli.security.audit import AuditLogger
1718
from apcore_cli.shell import configure_man_help, register_shell_commands
1819

20+
try:
21+
__version__ = _get_version("apcore-cli")
22+
except PackageNotFoundError:
23+
__version__ = "unknown"
24+
1925
logger = logging.getLogger("apcore_cli")
2026

2127
EXIT_CONFIG_NOT_FOUND = 47
@@ -64,6 +70,7 @@ def create_cli(
6470
binding_path: str | None = None,
6571
registry: Any | None = None,
6672
executor: Any | None = None,
73+
extra_commands: list[Any] | None = None,
6774
) -> click.Group:
6875
"""Create the CLI application.
6976
@@ -223,6 +230,22 @@ def create_cli(
223230
except Exception as e:
224231
logger.warning("Failed to initialize audit logger: %s", e)
225232

233+
# Wire CliApprovalHandler to Executor (FE-11 §3.5)
234+
try:
235+
import contextlib
236+
237+
from apcore_cli.approval import CliApprovalHandler
238+
239+
approval_timeout = 60
240+
with contextlib.suppress(TypeError, ValueError):
241+
approval_timeout = int(config.resolve("cli.approval_timeout", env_var="APCORE_CLI_APPROVAL_TIMEOUT") or 60)
242+
handler = CliApprovalHandler(auto_approve=False, timeout=approval_timeout)
243+
if hasattr(executor, "set_approval_handler"):
244+
executor.set_approval_handler(handler)
245+
logger.debug("CliApprovalHandler wired to Executor (timeout=%ds).", approval_timeout)
246+
except Exception as e:
247+
logger.debug("Could not wire CliApprovalHandler: %s", e)
248+
226249
@click.group(
227250
cls=GroupedModuleGroup,
228251
registry=registry,
@@ -286,9 +309,24 @@ def cli(
286309
ctx.obj["extensions_dir"] = ext_dir
287310
ctx.obj["verbose_help"] = verbose_help
288311

289-
# Register discovery commands
312+
# Register discovery commands (list, describe)
290313
register_discovery_commands(cli, registry)
291314

315+
# Register validate command (FE-11 §3.1)
316+
from apcore_cli.discovery import register_validate_command
317+
318+
register_validate_command(cli, registry, executor)
319+
320+
# Register system management commands (FE-11 §3.2) — no-op if system modules unavailable
321+
from apcore_cli.system_cmd import register_system_commands
322+
323+
register_system_commands(cli, executor)
324+
325+
# Register pipeline introspection command (FE-11 §3.8)
326+
from apcore_cli.strategy import register_pipeline_command
327+
328+
register_pipeline_command(cli, executor)
329+
292330
# Register shell integration commands
293331
register_shell_commands(cli, prog_name=prog_name)
294332

@@ -300,6 +338,17 @@ def cli(
300338

301339
register_init_command(cli)
302340

341+
# Register extra commands from downstream projects (FE-11 §3.11)
342+
if extra_commands:
343+
from apcore_cli.cli import BUILTIN_COMMANDS
344+
345+
for cmd in extra_commands:
346+
cmd_name = getattr(cmd, "name", None)
347+
if cmd_name and cmd_name in BUILTIN_COMMANDS:
348+
msg = f"Extra command '{cmd_name}' conflicts with built-in command."
349+
raise ValueError(msg)
350+
cli.add_command(cmd)
351+
303352
return cli
304353

305354

0 commit comments

Comments
 (0)