apcore-cli 0.7.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apcore_cli-0.9.0/CHANGELOG.md +501 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/CLAUDE.md +13 -5
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/PKG-INFO +65 -25
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/README.md +62 -21
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/README.md +38 -54
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/pyproject.toml +7 -6
- apcore_cli-0.9.0/src/apcore_cli/__init__.py +212 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/__main__.py +6 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/approval.py +52 -2
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/builtin_group.py +101 -12
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/cli.py +59 -17
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/config.py +31 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/discovery.py +40 -11
- apcore_cli-0.9.0/src/apcore_cli/exit_codes.py +74 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/factory.py +87 -115
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/init_cmd.py +1 -1
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/output.py +82 -31
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/ref_resolver.py +36 -6
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/schema_parser.py +10 -4
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/security/audit.py +17 -3
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/security/config_encryptor.py +41 -14
- apcore_cli-0.9.0/src/apcore_cli/security/sandbox.py +271 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/shell.py +7 -7
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/strategy.py +6 -1
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/system_cmd.py +52 -25
- apcore_cli-0.9.0/src/apcore_cli/system_usage.py +167 -0
- apcore_cli-0.9.0/tests/conformance/test_snake_case_kwargs.py +82 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_apcli_integration.py +21 -24
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_approval.py +21 -11
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_bugfixes.py +1 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_builtin_group.py +87 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_cli.py +98 -16
- apcore_cli-0.9.0/tests/test_exit_codes.py +58 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_factory_fe13.py +43 -22
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_integration.py +5 -3
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_list_command_filters.py +14 -5
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_output_format_exec.py +31 -3
- apcore_cli-0.9.0/tests/test_output_format_markdown_skill.py +126 -0
- apcore_cli-0.9.0/tests/test_public_api.py +92 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_ref_resolver.py +130 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_schema_parser.py +8 -5
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_security/test_audit.py +63 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_security/test_config_encryptor.py +111 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_security/test_sandbox.py +132 -45
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_shell.py +1 -1
- apcore_cli-0.9.0/tests/test_system_usage.py +257 -0
- apcore_cli-0.9.0/tests/test_validate.py +227 -0
- apcore_cli-0.7.0/CHANGELOG.md +0 -253
- apcore_cli-0.7.0/commands/ops.py +0 -4
- apcore_cli-0.7.0/htmlcov/.gitignore +0 -2
- apcore_cli-0.7.0/htmlcov/class_index.html +0 -551
- apcore_cli-0.7.0/htmlcov/coverage_html_cb_bcae5fc4.js +0 -735
- apcore_cli-0.7.0/htmlcov/favicon_32_cb_58284776.png +0 -0
- apcore_cli-0.7.0/htmlcov/function_index.html +0 -1931
- apcore_cli-0.7.0/htmlcov/index.html +0 -306
- apcore_cli-0.7.0/htmlcov/keybd_closed_cb_ce680311.png +0 -0
- apcore_cli-0.7.0/htmlcov/status.json +0 -1
- apcore_cli-0.7.0/htmlcov/style_cb_a5a05ca4.css +0 -389
- apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f___init___py.html +0 -105
- apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_audit_py.html +0 -169
- apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_auth_py.html +0 -171
- apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_config_encryptor_py.html +0 -249
- apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_sandbox_py.html +0 -241
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca___init___py.html +0 -184
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca__sandbox_runner_py.html +0 -122
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_approval_py.html +0 -352
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_builtin_group_py.html +0 -475
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_cli_py.html +0 -1173
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_config_py.html +0 -238
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_discovery_py.html +0 -545
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_display_helpers_py.html +0 -127
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_exposure_py.html +0 -227
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_factory_py.html +0 -833
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_init_cmd_py.html +0 -287
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_output_py.html +0 -467
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_ref_resolver_py.html +0 -227
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_schema_parser_py.html +0 -295
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_shell_py.html +0 -739
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_strategy_py.html +0 -307
- apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_system_cmd_py.html +0 -566
- apcore_cli-0.7.0/src/apcore_cli/__init__.py +0 -87
- apcore_cli-0.7.0/src/apcore_cli/security/sandbox.py +0 -168
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/.github/CODEOWNERS +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/.github/copilot-ignore +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/.gitignore +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/.gitmessage +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/.pre-commit-config.yaml +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/LICENSE +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/Makefile +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/math/add.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/math/multiply.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/disk.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/env.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/info.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/text/reverse.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/text/upper.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/extensions/text/wordcount.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/examples/run_examples.sh +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/approval-gate.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/config-resolver.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/core-dispatcher.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/discovery.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/exposure-filtering.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/grouped-commands.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/output-formatter.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/overview.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/schema-parser.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/security-manager.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/shell-integration.md +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/planning/state.json +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/_sandbox_runner.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/display_helpers.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/exposure.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/security/__init__.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/security/auth.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/src/apcore_cli/validate.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/__init__.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/conformance/__init__.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/conformance/test_apcli_visibility.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/conftest.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_config.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_discovery.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_discovery_fe13.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_display_helpers.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_e2e.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_exposure.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_init_cmd.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_output.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_sandbox_runner.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_security/__init__.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_security/test_auth.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_strategy.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_system_cmd.py +0 -0
- {apcore_cli-0.7.0 → apcore_cli-0.9.0}/tests/test_toolkit_integration.py +0 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to apcore-cli (Python SDK) will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.9.0] - 2026-05-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`tests/conformance/test_snake_case_kwargs.py`** — runs the cross-language Algorithm C-SNAKE fixture (`apcore-cli/conformance/fixtures/snake-case-kwargs/cases.json`) against `build_module_command` via `click.testing.CliRunner`. Five cases verify that schema property names with underscores (`has_solution`, `sort_by`, `sort_order`) survive the round trip from CLI parse to the input dict received by `executor.call`. No source change required — click natively maps `--has-solution` to `has_solution`; the Python SDK is the parity reference for the parallel TypeScript fix. Surfaced as part of the cross-SDK regression coverage gap audit.
|
|
13
|
+
|
|
14
|
+
### Fixed (2026-05-13 — cross-SDK audit D10/D11/D1)
|
|
15
|
+
|
|
16
|
+
- **`ConfigEncryptor` LOGNAME key-derivation chain** (D10-001 / D11-003) — PBKDF2 username fallback was `USER → USERNAME → "unknown"` (3-tier); now `USER → LOGNAME → USERNAME → "unknown"` (4-tier) matching the spec and Rust. On hosts where `USER` is unset but `LOGNAME` is set (cron, `sudo -i`, container init), ciphertext written by the Python SDK now round-trips correctly with the Rust SDK. `src/apcore_cli/security/config_encryptor.py:96, 165`.
|
|
17
|
+
- **`ConfigEncryptor.store` keyring write-failure not wrapped** (D11-004) — raw `keyring.set_password` exceptions now caught and re-raised as `ConfigDecryptionError`, matching TypeScript and Rust. `src/apcore_cli/security/config_encryptor.py:31`.
|
|
18
|
+
- **`ref_resolver` only descended into `properties`** (D11-001) — recursive schema walk now visits every dict-valued child (items, additionalProperties, patternProperties, if/then/else, not, contains, propertyNames), matching TypeScript and Rust. `$ref` under array schemas and conditional schemas is now resolved. `src/apcore_cli/ref_resolver.py:142`.
|
|
19
|
+
- **`ref_resolver` copy-on-write visited-set** (D11-002) — now uses a single mutable set with remove-on-unwind, allowing diamond `$ref` patterns (two sibling schemas referencing the same `$def`) to resolve correctly. `src/apcore_cli/ref_resolver.py:71`.
|
|
20
|
+
- **`AuditLogger._get_user` uses real UID instead of effective UID** (D11-010) — switched from `os.getuid()` to `os.geteuid()` so audit records reflect the privileges the process actually runs with under `sudo` / setuid binaries. Matches Rust (`geteuid`) and TypeScript (`os.userInfo`). `src/apcore_cli/security/audit.py:77`.
|
|
21
|
+
- **`check_approval` ignores `APCORE_CLI_APPROVAL_TIMEOUT` env var** (D11-012) — `CliApprovalHandler` and the legacy `check_approval()` wrapper now honor the env var when no explicit timeout is passed (precedence: constructor arg > env var > 60 s default). Matches TypeScript. `src/apcore_cli/approval.py`.
|
|
22
|
+
- **`exec --dry-run` crashes with `AttributeError` when executor lacks `validate`** (D11-013) — guarded with `hasattr(executor, "validate")`; falls back to synthetic `{"valid": True}` matching TypeScript. `src/apcore_cli/discovery.py:378`.
|
|
23
|
+
- **`CliApprovalHandler.request_approval` missing `requires_approval=False` short-circuit** (D11-014) — now returns `approved/not_required` when the request explicitly carries `requires_approval=False`, matching Rust. `src/apcore_cli/approval.py:66`.
|
|
24
|
+
- **CLI brand string in auth error messages** (D11-006) — remediation strings now say `apcli config set auth.api_key` (canonical FE-13 name) instead of `apcore-cli config set auth.api_key`. `src/apcore_cli/security/auth.py`.
|
|
25
|
+
- **`reconvert_enum_values` missing from public re-export** (D1-W2) — added to `__init__.py` import block and `__all__`. Embedders can now `from apcore_cli import reconvert_enum_values` for parity with TypeScript and Rust.
|
|
26
|
+
- **Ref-resolver error hierarchy missing from public re-export** (D1-W3) — `CircularRefError`, `MaxDepthExceededError`, `UnresolvableRefError`, `RefResolverError` added to `__init__.py` import block and `__all__`. Parity with TypeScript `index.ts:82-84`.
|
|
27
|
+
- **`DEFAULT_BUILTIN_GROUP_NAME` missing from public re-export** (D1 re-audit) — added to `__init__.py`. Parity with Rust `lib.rs:190`.
|
|
28
|
+
- **Dead exit-code constants removed** (D9-W1) — `EXIT_CONFIG_ENV_PREFIX_CONFLICT` and `EXIT_CONFIG_ENV_MAP_CONFLICT` (both = 78, zero callers) deleted from `src/apcore_cli/exit_codes.py`.
|
|
29
|
+
- **Unused `pytest-asyncio` dev dependency removed** (D6) — the package was declared but never exercised; no async tests exist. Removed from `[project.optional-dependencies].dev`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **CSV `--format csv` Python-repr bug** — `csv.DictWriter` was called with `{k: str(v) for k, v in row.items()}` which emitted Python repr `{'k': 'v'}` (single quotes) for nested dict/list values. The output was not valid JSON and any downstream JSON parser would fail. Now delegates to `apcore_toolkit.format_csv(rows)` which emits canonical compact JSON. `src/apcore_cli/output.py:149, 378`.
|
|
34
|
+
- **CSV heterogeneous-keys data loss** — header is now the union of keys across all rows (was first-row only via `list(rows[0].keys())`).
|
|
35
|
+
- **CSV line terminator** — now `\r\n` per RFC 4180.
|
|
36
|
+
- **JSONL canonical form** — now compact (no spaces between separators), matching the cross-SDK contract. Tests updated.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **User-visible help/man/completion text no longer leaks the `apcore` framework name** to end users of downstream CLIs built on apcore-cli. Affected strings: `init` group description (`Scaffold new apcore modules` → `Scaffold new modules`, `init_cmd.py:45`), `--extensions-dir` option help (`Path to apcore extensions directory.` → `Path to extensions directory.`, `factory.py:460`), zsh/fish completion descriptions for `exec` (`Execute an apcore module` → `Execute a module`, `shell.py:130, 211`), and man-page `ENVIRONMENT` section text (`shell.py:299, 314, 319, 458`) — drops `apcore` from the descriptive copy (`Path to the apcore extensions directory` → `Path to the extensions directory`, `Global apcore logging verbosity` → `Global logging verbosity`, `API key for authenticating with the apcore registry` → `API key for authenticating with the registry`). Logger names, source comments, module docstrings, and environment-variable identifiers (`APCORE_*`) are unchanged — only descriptive copy that appears in `--help`, shell completion, and `man` output. Cross-SDK parity with TypeScript 0.8.2 and Rust 0.8.1.
|
|
41
|
+
|
|
42
|
+
### Changed (breaking CLI surface)
|
|
43
|
+
|
|
44
|
+
- **Global `--verbose` flag renamed to `--all-options`** — The help-display flag is now `--all-options`; use `apcore-cli module --help --all-options` to reveal hidden built-in options. `verbose` is removed from the reserved schema property names set — module schemas may now freely define `verbose: boolean` for runtime output control. Internal API: `set_verbose_help()` renamed to `set_all_options_help()`; module-level global `_verbose_help` renamed to `_all_options_help`. Tracked in [apcore-cli#21](https://github.com/aiperceivable/apcore-cli/issues/21).
|
|
45
|
+
|
|
46
|
+
### Changed (breaking dependency semantics)
|
|
47
|
+
|
|
48
|
+
- **`apcore-toolkit` promoted from optional extra to REQUIRED runtime dependency** (`>=0.7.0`). The previous `pip install 'apcore-cli[toolkit]'` extras pattern is retained as a no-op for backward compat with install scripts, but the toolkit is now always installed alongside apcore-cli. All `--format` operations route through the toolkit's reference implementation for csv/jsonl/markdown/skill.
|
|
49
|
+
|
|
50
|
+
### Why
|
|
51
|
+
|
|
52
|
+
See ADR-09 in `apcore-cli/docs/tech-design.md` for the byte-equivalent toolkit-delegated tier rationale.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## [0.8.0] - 2026-05-08
|
|
56
|
+
|
|
57
|
+
### Removed
|
|
58
|
+
|
|
59
|
+
- **D9-001 — FE-13 §11.2 deprecation shims removed**. The 13 hidden root-level
|
|
60
|
+
shims (`list`, `describe`, `exec`, `init`, `validate`, `health`, `usage`,
|
|
61
|
+
`enable`, `disable`, `reload`, `config`, `completion`, `describe-pipeline`)
|
|
62
|
+
installed by `_register_deprecation_shims` and the `__is_deprecation_shim__`
|
|
63
|
+
collision-handling path in `extra_commands` wiring have been deleted along
|
|
64
|
+
with the `_DEPRECATED_ROOT_COMMANDS` table. Use the canonical
|
|
65
|
+
`apcli <command>` paths instead. Calls like `apcore-cli list` now exit
|
|
66
|
+
non-zero with Click's "No such command" message — the warning window
|
|
67
|
+
documented as "removed in v0.8" is closed.
|
|
68
|
+
|
|
69
|
+
### Deprecated
|
|
70
|
+
|
|
71
|
+
- **`CliModuleNotFoundError` alias** — the symbol still resolves to
|
|
72
|
+
`ModuleNotFoundError` (see D1-002 in Changed) but is scheduled for
|
|
73
|
+
removal in v0.10.0. Update imports to
|
|
74
|
+
`from apcore_cli import ModuleNotFoundError`.
|
|
75
|
+
|
|
76
|
+
### Security
|
|
77
|
+
|
|
78
|
+
- **D10-001 — `Sandbox` per-stream output cap** (`sandbox.py:155`). The previous
|
|
79
|
+
implementation summed `stdout + stderr` against a single `max_output_bytes`
|
|
80
|
+
budget — a runaway child writing only to stderr could starve the stdout
|
|
81
|
+
budget and vice versa, and the diagnostic on overflow did not name the
|
|
82
|
+
offending stream. Each stream now has an independent byte budget matching
|
|
83
|
+
Rust and TypeScript; the overflow error names the stream that tripped the
|
|
84
|
+
cap.
|
|
85
|
+
- **D11-W2 — `Sandbox` switched from `subprocess.run` to `subprocess.Popen`
|
|
86
|
+
with threaded chunked reads** (`sandbox.py:155`). `capture_output=True`
|
|
87
|
+
buffered the entire child stdio into parent memory before the cap was
|
|
88
|
+
checked, so a child producing GBs of output could OOM the parent before
|
|
89
|
+
the limit was enforced. The new implementation streams stdout/stderr
|
|
90
|
+
through reader threads with bounded buffers and kills the child as soon
|
|
91
|
+
as either stream exceeds its cap. Memory consumption is now bounded by
|
|
92
|
+
`2 × max_output_bytes` regardless of child output volume.
|
|
93
|
+
- **D11-003 — `ConfigEncryptor` v1 decryption honours
|
|
94
|
+
`APCORE_CLI_CONFIG_PASSPHRASE`** (`config_encryptor.py:128`). `_aes_decrypt_v1`
|
|
95
|
+
hard-coded the host:user material, so v1 ciphertext encrypted by the Rust
|
|
96
|
+
or TypeScript SDKs under a passphrase failed to decrypt on Python.
|
|
97
|
+
Decryption now tries the passphrase-derived key first when the env var is
|
|
98
|
+
set, falling back to host:user material — matching TypeScript
|
|
99
|
+
`aesDecryptV1`. Cross-SDK config bundles are now portable.
|
|
100
|
+
- **D11-008 — `AuditLogger._get_user` fallback chain now includes `LOGNAME`**
|
|
101
|
+
(`audit.py:66`). The canonical chain per `security.md` (D11-W1) is
|
|
102
|
+
`getlogin → pwd.getpwuid → USER → LOGNAME → USERNAME → unknown`. Python
|
|
103
|
+
previously skipped `LOGNAME`, so audit-log `user` fields diverged from
|
|
104
|
+
Rust/TS on hosts where only `LOGNAME` is set (some container runtimes,
|
|
105
|
+
cron jobs).
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
|
|
109
|
+
- **`builtin_group_name="apcli"` kwarg on `create_cli`** — downstream branded CLIs that embed apcore-cli can now expose the built-in commands under a custom namespace (e.g. `mycorp-cli admin health` instead of `mycorp-cli apcli health`). `ApcliGroup` gains a `name` parameter (with property accessor) threaded through `from_cli_config` / `from_yaml` / `_build`. Default `"apcli"` is unchanged. Validated against `/^[a-z][a-z0-9_-]*$/`; invalid values exit 2. `RESERVED_GROUP_NAMES` collision check now consults `GroupedModuleGroup._reserved_group_names` (instance attribute, defaults to the static frozenset; factory replaces with the resolved name). Env var `APCORE_CLI_APCLI` and config keys `apcli.*` deliberately do NOT rename — they are apcore-cli-internal toggles, not user-facing. Cross-SDK parity with TypeScript `createCli({ builtinGroupName })`. New `DEFAULT_BUILTIN_GROUP_NAME` constant exported from `apcore_cli.builtin_group`.
|
|
110
|
+
- **`_exit_on_system_error(e)` helper in `system_cmd.py`** — centralizes the canonical error→exit-code mapping for system-management subcommands, replacing 7 sites that previously used bare `sys.exit(1)` (audit D11-B-002, see Fixed).
|
|
111
|
+
- **5 new tests in `tests/test_builtin_group.py`** — `TestBuiltinGroupRename` class covers default name, custom name via both factories, validation of valid/invalid name shapes (5 valid + 6 invalid forms each).
|
|
112
|
+
- **D1-001 — 13 `register_*_command` factories + `configure_man_help`
|
|
113
|
+
re-exported from `apcore_cli` package root**. Embedders that compose
|
|
114
|
+
their own root command tree no longer need to reach into private
|
|
115
|
+
submodules (`apcore_cli.commands.list_cmd`, etc.). All TS/Rust
|
|
116
|
+
`register_*` counterparts now have a Python public-API equivalent.
|
|
117
|
+
- **D1-003 — `apcore_cli.exit_codes` module** with 24 `EXIT_*` integer
|
|
118
|
+
constants, an `EXIT_CODES` mapping dict, and an `exit_code_for_error()`
|
|
119
|
+
helper. Mirrors TS `errors.ts` `EXIT_CODES` + `exitCodeForError` and
|
|
120
|
+
Rust `src/lib.rs` `EXIT_*` constants. Embedders can now map exceptions
|
|
121
|
+
to documented exit codes without re-implementing the table.
|
|
122
|
+
- **D1-007 — `format_module_list`, `format_module_detail`,
|
|
123
|
+
`resolve_format` re-exported from package root**. The
|
|
124
|
+
output-formatter feature spec declares these as Contracts; previously
|
|
125
|
+
only `format_exec_result` was public.
|
|
126
|
+
- **D1-W1 — `APCLI_SUBCOMMAND_NAMES` re-exported from `apcore_cli`**.
|
|
127
|
+
Matches Rust `lib.rs` and is now in `__all__` for static-analysis
|
|
128
|
+
tooling.
|
|
129
|
+
- **D1-W2 — `ApcliConfig` TypedDict** added to the public surface,
|
|
130
|
+
mirroring the TypeScript type alias and Rust struct so embedders have
|
|
131
|
+
a static contract for the `apcli.*` config block.
|
|
132
|
+
- **D1-W3 — `register_config_namespace()` helper + module-level
|
|
133
|
+
`DEFAULTS` constant** in `config.py`. The package still registers the
|
|
134
|
+
namespace at import time, but embedders can now invoke the helper
|
|
135
|
+
explicitly (parity with `apcore-cli-typescript`).
|
|
136
|
+
- **D1-W5 — Core dispatcher embedder API re-exported from package
|
|
137
|
+
root**: `build_module_command`, `collect_input`, `validate_module_id`,
|
|
138
|
+
`set_audit_logger`, `set_verbose_help`, `set_docs_url`. Embedders no
|
|
139
|
+
longer have to import from `apcore_cli.cli` directly. Matches Rust
|
|
140
|
+
`lib.rs:186-190` and TS `index.ts:18`. New `tests/test_public_api.py`
|
|
141
|
+
pins the surface against future drift.
|
|
142
|
+
- **D1-info-1 — typed `ApcliGroupError` exception**
|
|
143
|
+
(`builtin_group.py:107`). Cross-SDK parity with Rust `ApcliGroupError`;
|
|
144
|
+
embedders previously had no stable error class to match on for
|
|
145
|
+
built-in-group config validation. `ApcliGroupError(ValueError)`
|
|
146
|
+
preserves backwards compat — existing `except ValueError` callers
|
|
147
|
+
still catch it. The invalid-name regex check in `__init__` now raises
|
|
148
|
+
`ApcliGroupError`. Re-exported from `apcore_cli`.
|
|
149
|
+
|
|
150
|
+
### Fixed
|
|
151
|
+
|
|
152
|
+
- **D11-B-006 — `discovery.py:208` sort direction inverted**. `apcli list --sort calls|errors|latency` now defaults to DESCENDING (highest call count first) per spec T-LST-04, matching Rust `discovery.rs:209` and TypeScript `discovery.ts:186`. Previously the user's raw `--reverse` flag (default False) was passed directly to `sort_modules_by_usage(..., reverse=...)`, producing ASCENDING output by default — the inverse of the spec. Fix passes `reverse=not reverse` for the data path AND adds a re-sort at the call site for the audit-log-empty fallback so id-fallback continues to default ASCENDING per spec.
|
|
153
|
+
- **D11-B-002 — `system_cmd.py` collapsed every error to exit 1**. The 7 `except Exception as e: sys.exit(1)` sites bypassed Python's own `_ERROR_CODE_MAP` (canonical 44/46/47/77) — scripted operators could not distinguish "module not found" from "ACL denied" from generic failure. All 7 sites now route through the new `_exit_on_system_error(e)` helper which calls `exit_code_for_error(e)` from `apcore_cli.exit_codes`. The 4 audit-log entries previously hardcoding `exit_code=1` now log the resolved code.
|
|
154
|
+
- **D11-NEW-005 — RESERVED_PROPERTY_NAMES no longer raises generic `ValueError`**. `schema_to_click_options` previously raised `ValueError` when a schema property collided with a built-in CLI option — opaque to scripted callers and inconsistent with the neighbour flag-collision branch (which already exited 48). Now writes a user-facing `Error:` line to stderr and calls `sys.exit(48)` per spec, matching TS `process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF)` and Rust `CliError::SchemaParserFailure → EXIT_SCHEMA_CIRCULAR_REF`. Tests tightened from `pytest.raises((ValueError, Exception))` to `pytest.raises(SystemExit)` with `code == 48` assertion.
|
|
155
|
+
- **D9-NEW-002 — `ref_resolver.py` `allOf required` not deduplicated**. `_resolve_node`'s `allOf` branch concatenated parent `required` + each branch's `required` without dedup, producing duplicate entries in the merged schema's `required` array. JSON Schema validators ignore duplicates so observable validation behaviour was unchanged, but cross-SDK byte-comparison tooling (and the `anyOf`/`oneOf` paths, which already deduped) flagged the divergence. Fix: explicit seen-set dedup preserving first-seen order, matching TS `[...new Set(...)]` and Rust `merge_allof`.
|
|
156
|
+
- **D10-003 — `build_module_command` leaked `RefResolverError`
|
|
157
|
+
tracebacks** (`cli.py:538`). The `resolve_refs` catch clause re-raised
|
|
158
|
+
unchanged, so callers saw a Python traceback instead of a clean
|
|
159
|
+
documented exit code. Now translates `CircularRefError` /
|
|
160
|
+
`MaxDepthExceededError` to `sys.exit(48)` and `UnresolvableRefError`
|
|
161
|
+
(plus generic `RefResolverError`) to `sys.exit(45)`, mirroring
|
|
162
|
+
`schema_parser.py:111` and the Rust/TS contracts.
|
|
163
|
+
- **D11-NEW-003 — `ref_resolver` `max_depth` over-counted plain nested
|
|
164
|
+
`properties`** (`ref_resolver.py`). `_resolve_node` previously
|
|
165
|
+
incremented `depth + 1` when recursing into nested `properties`
|
|
166
|
+
values, so a schema with >32 levels of nested objects (no `$ref` at
|
|
167
|
+
all) was rejected with `MaxDepthExceededError`. The spec wording is
|
|
168
|
+
"Maximum `$ref` resolution recursion depth" — `$ref` hops along a
|
|
169
|
+
single chain, not total stack depth. `depth` is now only incremented
|
|
170
|
+
on `$ref` traversal, aligning with Rust `ref_resolver.rs:297`. Also
|
|
171
|
+
adds 4 regression tests for `anyOf`/`oneOf` sibling-required
|
|
172
|
+
preservation and `anyOf` overlap dedup.
|
|
173
|
+
- **D10-info-1 — `APCORE_CLI_APCLI` env var not trimmed on read**
|
|
174
|
+
(`builtin_group.py:414`). Spec invariant 2 requires the parser to be
|
|
175
|
+
case-insensitive AND trim-on-read. Surrounding whitespace previously
|
|
176
|
+
caused a silent Tier-3/Tier-4 fall-through. Now strips before
|
|
177
|
+
lowercasing, matching Rust/TS.
|
|
178
|
+
- **D11-010 — `AuditLogger` write-failure warnings deduplicated**
|
|
179
|
+
(`audit.py:55`). Previously warned on every failed write, flooding
|
|
180
|
+
logs when an audit dir is unwritable. An instance flag now gates the
|
|
181
|
+
warning so it fires once per logger instance, matching the TS
|
|
182
|
+
`writeFailureWarned` flag.
|
|
183
|
+
|
|
184
|
+
### Changed
|
|
185
|
+
|
|
186
|
+
- **`apcli system *` and `apcli strategy describe-pipeline` `--format` choices**
|
|
187
|
+
expanded from `[table, json]` to `[table, json, csv, yaml, jsonl]`, matching
|
|
188
|
+
the existing `apcli list` / `apcli exec` choice set. `markdown` and `skill`
|
|
189
|
+
are deliberately excluded from these subcommands — their payloads are
|
|
190
|
+
health / strategy results, not `ScannedModule` data. Issue
|
|
191
|
+
[#20](https://github.com/aiperceivable/apcore-cli/issues/20).
|
|
192
|
+
- **Dependency bump**: requires `apcore >= 0.21.0` (was `>= 0.19.0`) and the
|
|
193
|
+
optional `[toolkit]` extra now requires `apcore-toolkit >= 0.6` (was `>= 0.5`).
|
|
194
|
+
Aligns with upstream `apcore 0.21.0` (Module.preview / PreflightResult.predicted_changes,
|
|
195
|
+
ephemeral.* namespace pilot) and `apcore-toolkit 0.6.0` (surface-aware formatters).
|
|
196
|
+
No CLI-visible behavioural breaks — apcore 0.20→0.21 deprecations
|
|
197
|
+
(`TaskStore.put`/`save`, `TaskStatus.RETRYING`, `CircuitOpenError`) keep
|
|
198
|
+
legacy aliases for one minor release; the cli does not call those surfaces directly.
|
|
199
|
+
- **D1-002 — `CliModuleNotFoundError` renamed to `ModuleNotFoundError`**
|
|
200
|
+
for cross-language port-ability with TS / Rust `ModuleNotFoundError`.
|
|
201
|
+
The class intentionally shadows `builtins.ModuleNotFoundError` inside
|
|
202
|
+
the `apcore_cli` namespace. A deprecation alias
|
|
203
|
+
`CliModuleNotFoundError = ModuleNotFoundError` is kept for backwards
|
|
204
|
+
compatibility and will be removed in v0.10.0. Reverses the D2-001
|
|
205
|
+
rename which predated the cross-SDK parity policy.
|
|
206
|
+
- **Issue #19 — drop "apcore" branding from embedded-mode `--help`**:
|
|
207
|
+
`create_cli()` now resolves the top-level CLI description from the new
|
|
208
|
+
`description=` parameter (defaults to `f"{prog_name} CLI"`), the `apcli`
|
|
209
|
+
subgroup advertises itself as `Built-in commands` rather than
|
|
210
|
+
`apcore-cli built-in commands`, and the `--verbose` option / footer drop
|
|
211
|
+
the trailing `apcore` from `(including built-in apcore options)`. Standalone
|
|
212
|
+
bin entry (`apcore_cli/__main__.py:main()`) passes
|
|
213
|
+
`description="<prog> — execute apcore modules from the command line"`
|
|
214
|
+
explicitly so the standalone surface is unchanged.
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
|
|
218
|
+
- **`--format markdown` and `--format skill`** for `apcli list` and `apcli describe`
|
|
219
|
+
(issue [#20](https://github.com/aiperceivable/apcore-cli/issues/20)). Both
|
|
220
|
+
delegate to `apcore_toolkit.format_module(s)` (≥0.6) so the output is
|
|
221
|
+
byte-identical to the same toolkit call in the TypeScript and Rust SDKs.
|
|
222
|
+
`--format skill` produces vendor-neutral SKILL.md content directly loadable
|
|
223
|
+
by Claude Code (`.claude/skills/<id>/SKILL.md`) and Gemini CLI
|
|
224
|
+
(`.gemini/skills/<id>/SKILL.md`):
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
apcli describe users.create --format skill > .claude/skills/users.create/SKILL.md
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
A new internal adapter `_descriptor_to_scanned()` maps `ModuleDescriptor`
|
|
231
|
+
(apcore registry) to `ScannedModule` (apcore-toolkit). A `ClickException` with
|
|
232
|
+
a clear install hint is raised if the optional `[toolkit]` extra is missing.
|
|
233
|
+
- **Issue #18 — host-app `--version` opt-in**: new `version: str | None = None`
|
|
234
|
+
parameter on `create_cli()`. When supplied, registers `-V/--version` with
|
|
235
|
+
the host's version string. **When omitted, the `--version` flag is no
|
|
236
|
+
longer registered** — embedded CLIs that do not opt in stop leaking the
|
|
237
|
+
SDK's own version through `-V/--version`. The standalone bin entry
|
|
238
|
+
passes `version=apcore_cli.__version__` explicitly so the
|
|
239
|
+
`apcore-cli` binary's behaviour is preserved.
|
|
240
|
+
- **Issue #19 — `description: str | None = None`** on `create_cli()`.
|
|
241
|
+
- **Issue #17 — `system.usage` aggregator + `list --sort calls|errors|latency`**:
|
|
242
|
+
new module `apcore_cli.system_usage` reads `~/.apcore-cli/audit.jsonl`,
|
|
243
|
+
filters by period (default 24h), and returns per-module aggregates
|
|
244
|
+
(`calls`, `errors`, `avg latency_ms`). `list --sort {calls,errors,latency}`
|
|
245
|
+
now consults the aggregator instead of falling back to id-sort with a
|
|
246
|
+
buried `logger.warning`. When the audit log has no entries in the period
|
|
247
|
+
window the discovery layer prints a user-visible note to stderr
|
|
248
|
+
(`note: no usage data available for --sort <field>; sorted by id. ...`)
|
|
249
|
+
and falls back to id-sort. Module-protocol registration of
|
|
250
|
+
`system.usage.summary` / `system.usage.module` as registry-callable
|
|
251
|
+
built-ins is tracked as a follow-up — today the readers are invoked
|
|
252
|
+
directly by the discovery layer.
|
|
253
|
+
- New file: `apcore_cli/system_usage.py`.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## [0.7.0] - 2026-04-23
|
|
258
|
+
|
|
259
|
+
### Changed
|
|
260
|
+
|
|
261
|
+
- **Dependency bump**: requires `apcore >= 0.18.0` (was `>= 0.17.1`). Aligns with upstream `apcore 0.18.0` and `apcore-toolkit 0.4.2` breaking changes.
|
|
262
|
+
- **`MAX_MODULE_ID_LENGTH` 128 → 192**: `validate_module_id()` and all references updated to the new 192-character limit introduced in `apcore 0.18.0` (`apcore.registry.registry.MAX_MODULE_ID_LENGTH`).
|
|
263
|
+
- **`describe-pipeline` renders `StrategyInfo`**: `executor.describe_pipeline(strategy)` now returns a `StrategyInfo` dataclass (`name`, `step_count`, `step_names`, `description`). `strategy.py` updated to use `StrategyInfo` fields; header line is `Pipeline: {info.name} ({info.step_count} steps)`. Falls back gracefully to the legacy `_resolve_strategy_name` path when `describe_pipeline` is unavailable.
|
|
264
|
+
- **CI — spec-repo checkout**: `.github/workflows/ci.yml` now checks out `aiperceivable/apcore-cli` into `.apcore-cli-spec/` and exposes it to `pytest` via `APCORE_CLI_SPEC_REPO`. Mirrors the pattern established in `apcore-python` / `apcore-cli-typescript`.
|
|
265
|
+
|
|
266
|
+
### Added
|
|
267
|
+
|
|
268
|
+
- **`create_cli(app=...)` parameter**: `create_cli()` accepts an optional `app: APCore` unified client (introduced in `apcore 0.18.0`). `app` is mutually exclusive with `registry`/`executor` (raises `ValueError`). When `app` is provided, `registry` and `executor` are extracted from `app.registry` and `app.executor`. Filesystem discovery is skipped if `app.registry` already contains registered modules; otherwise normal discovery proceeds into `app.registry`.
|
|
269
|
+
- **Cross-language conformance test harness** (`tests/conformance/`) consuming the shared apcli-visibility fixtures from the `aiperceivable/apcore-cli` spec repo (`conformance/fixtures/apcli-visibility/`). Behavioral assertions (apcli group visibility, registered subcommand set for `include`/`exclude` modes, always-registered `exec`) run today across all five canonical scenarios (`standalone-default`, `embedded-default`, `cli-override`, `env-override`, `yaml-include`). Byte-matching against `expected_help.txt` is marked `xfail` until Click's `HelpFormatter` is replaced with a canonical clap v4 / GNU-style emitter, tracked for parity with `apcore-cli-typescript/src/canonical-help.ts`.
|
|
270
|
+
- **`APCORE_CLI_SPEC_REPO` env var** — overrides the spec-repo lookup path for conformance fixtures. Defaults to a sibling checkout (`../apcore-cli/`). Tests are skipped (not failed) when the spec repo is absent.
|
|
271
|
+
- **FE-12: Module Exposure Filtering** — Declarative control over which discovered modules are exposed as CLI commands.
|
|
272
|
+
- `ExposureFilter` class in `exposure.py` with `is_exposed(module_id)` and `filter_modules(ids)` methods.
|
|
273
|
+
- Three modes: `all` (default), `include` (whitelist), `exclude` (blacklist) with glob-pattern matching.
|
|
274
|
+
- `ExposureFilter.from_config(dict)` classmethod for loading from `apcore.yaml` `expose` section.
|
|
275
|
+
- `create_cli(expose=...)` parameter accepting `dict` or `ExposureFilter` instance.
|
|
276
|
+
- `list --exposure {exposed,hidden,all}` filter flag in discovery commands.
|
|
277
|
+
- `GroupedModuleGroup._build_group_map()` integration: calls `ExposureFilter.is_exposed()` to filter command registration.
|
|
278
|
+
- `ConfigResolver` gains `expose.*` config keys.
|
|
279
|
+
- 4-tier config precedence: `CliConfig.expose` > `--expose-mode` CLI flag > env var > `apcore.yaml`.
|
|
280
|
+
- Hidden modules remain invocable via `exec <module_id>`.
|
|
281
|
+
- New file: `exposure.py`.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## [0.6.0] - 2026-04-06
|
|
286
|
+
|
|
287
|
+
### Changed
|
|
288
|
+
|
|
289
|
+
- **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.
|
|
290
|
+
- **Schema parser**: Required schema properties now correctly enforced at CLI option level (was silently optional).
|
|
291
|
+
- **Approval gate**: Fixed inverted logic in annotation type guard; `check_approval()` now accepts `timeout` parameter.
|
|
292
|
+
|
|
293
|
+
### Added
|
|
294
|
+
|
|
295
|
+
- **FE-11: Usability Enhancements** — 11 new capabilities:
|
|
296
|
+
- `--dry-run` preflight mode via `Executor.validate()`. Standalone `validate` command.
|
|
297
|
+
- System management commands: `health`, `usage`, `enable`, `disable`, `reload`, `config get`/`config set`. Graceful no-op when system modules unavailable.
|
|
298
|
+
- Enhanced error output: structured JSON with `ai_guidance`, `suggestion`, `retryable`, `user_fixable`, `details`. TTY hides machine-only fields.
|
|
299
|
+
- `--trace` pipeline visualization via `call_with_trace()`.
|
|
300
|
+
- `CliApprovalHandler` class implementing apcore `ApprovalHandler` protocol, wired to `Executor.set_approval_handler()`. `--approval-timeout`, `--approval-token` flags.
|
|
301
|
+
- `--stream` JSONL output via `Executor.stream()`.
|
|
302
|
+
- Enhanced `list` command: `--search`, `--status`, `--annotation`, `--sort`, `--reverse`, `--deprecated`, `--deps`.
|
|
303
|
+
- `--strategy` selection: `standard`, `internal`, `testing`, `performance`, `minimal`. `describe-pipeline` command.
|
|
304
|
+
- Output format extensions: `--format csv|yaml|jsonl`, `--fields` dot-path field selection.
|
|
305
|
+
- Multi-level grouping: `cli.group_depth` config key.
|
|
306
|
+
- Custom command extension: `create_cli(extra_commands=[...])` with collision detection.
|
|
307
|
+
- New error code: `CONFIG_ENV_MAP_CONFLICT`.
|
|
308
|
+
- New config keys: `cli.approval_timeout` (60), `cli.strategy` ("standard"), `cli.group_depth` (1).
|
|
309
|
+
- New environment variables: `APCORE_CLI_APPROVAL_TIMEOUT`, `APCORE_CLI_STRATEGY`, `APCORE_CLI_GROUP_DEPTH`.
|
|
310
|
+
- New files: `system_cmd.py`, `strategy.py`.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## [0.5.1] - 2026-04-03
|
|
315
|
+
|
|
316
|
+
### Added
|
|
317
|
+
- **Pre-populated registry support** — `create_cli()` accepts optional `registry` and `executor` parameters. When a pre-populated `Registry` is provided, filesystem discovery is skipped entirely. This enables frameworks that register modules at runtime (e.g. apflow's bridge) to generate CLI commands from their existing registry without requiring an extensions directory.
|
|
318
|
+
- Passing `registry` alone auto-builds an `Executor`; passing `executor` without `registry` raises `ValueError`.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## [0.4.1] - 2026-03-30
|
|
323
|
+
|
|
324
|
+
### Fixed
|
|
325
|
+
- prevent click parameter mismatch by setting expose_value=False for the --man option
|
|
326
|
+
|
|
327
|
+
## [0.4.0] - 2026-03-29
|
|
328
|
+
|
|
329
|
+
### Added
|
|
330
|
+
- **Verbose help mode** — Built-in apcore options (`--input`, `--yes`, `--large-input`, `--format`, `--sandbox`) are now hidden from `--help` output by default. Pass `--help --verbose` to display the full option list including built-in options.
|
|
331
|
+
- **Universal man page generation** — `build_program_man_page()` generates a complete roff man page covering all registered commands. `configure_man_help()` adds `--help --man` support to any Click CLI, enabling downstream projects to get man pages for free.
|
|
332
|
+
- **Documentation URL support** — `set_docs_url()` sets a base URL for online docs. Per-command help shows `Docs: {url}/commands/{name}`, man page SEE ALSO includes `Full documentation at {url}`. No default — disabled when not set.
|
|
333
|
+
|
|
334
|
+
### Changed
|
|
335
|
+
- `build_module_command()` respects the global verbose help flag to control built-in option visibility.
|
|
336
|
+
- `--sandbox` is now always hidden from help (not yet implemented). Only four built-in options (`--input`, `--yes`, `--large-input`, `--format`) toggle with `--verbose`.
|
|
337
|
+
- Improved built-in option descriptions for clarity.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## [0.3.1] - 2026-03-27
|
|
342
|
+
|
|
343
|
+
### Added
|
|
344
|
+
|
|
345
|
+
- **DisplayResolver integration** — `__main__.py` integrates `DisplayResolver` from `apcore-toolkit` (optional) when `--binding` option is provided; gracefully skipped when not installed.
|
|
346
|
+
- **`init` to `BUILTIN_COMMANDS`** — `init` subcommand is now registered in the builtin commands set.
|
|
347
|
+
- **`APCORE_AUTH_API_KEY` to man page** — environment variable documented in generated roff man page.
|
|
348
|
+
- **Grouped shell completion with `_APCORE_GRP`** — bash/zsh/fish completion scripts now support two-level group/command completion via the `_APCORE_GRP` environment variable (`shell.py`).
|
|
349
|
+
- **Path traversal validation for `--dir` in `init` command** — rejects paths containing `..` segments to prevent directory escape (`init_cmd.py`).
|
|
350
|
+
|
|
351
|
+
### Fixed
|
|
352
|
+
|
|
353
|
+
- **`RegistryWriter` API call** — constructor now called without parameters; fixes `TypeError` introduced by upstream API change.
|
|
354
|
+
|
|
355
|
+
### Changed
|
|
356
|
+
|
|
357
|
+
- `apcore` dependency bumped to `>=0.14.0`.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## [0.3.0] - 2026-03-23
|
|
362
|
+
|
|
363
|
+
### Added
|
|
364
|
+
|
|
365
|
+
- **Display overlay routing** (§5.13) — `LazyModuleGroup` now reads `metadata["display"]["cli"]` for alias and description when building the command list and routing `get_command()`. Commands are exposed under their CLI alias instead of raw module_id.
|
|
366
|
+
- `_alias_map`: built from `metadata["display"]["cli"]["alias"]` (with module_id fallback), enabling `apcore-cli alias-name` invocation.
|
|
367
|
+
- `_descriptor_cache`: populated during alias map build to avoid double `registry.get_definition()` calls in `get_command()`.
|
|
368
|
+
- `_alias_map_built` flag only set on successful build, allowing retry after transient registry errors.
|
|
369
|
+
- **Display overlay in JSON output** — `format_module_list(..., "json")` now reads `metadata["display"]["cli"]` for `id`, `description`, and `tags`, consistent with the table output branch.
|
|
370
|
+
|
|
371
|
+
### Changed
|
|
372
|
+
|
|
373
|
+
- `_ERROR_CODE_MAP.get(error_code, 1)`: guarded with `isinstance(error_code, str)` to prevent `None`-key lookup.
|
|
374
|
+
- Runtime companion: `apcore-toolkit >= 0.4.0` enables `DisplayResolver` and `ConventionScanner` (graceful fallback when not installed).
|
|
375
|
+
|
|
376
|
+
### Tests
|
|
377
|
+
|
|
378
|
+
- `TestDisplayOverlayAliasRouting` (6 tests): `list_commands` uses CLI alias, `get_command` by alias, cache hit path, module_id fallback, `build_module_command` alias and description.
|
|
379
|
+
- `test_format_list_json_uses_display_overlay`: JSON output uses display overlay alias/description/tags.
|
|
380
|
+
- `test_format_list_json_falls_back_to_scanner_when_no_overlay`: JSON output falls back to scanner values.
|
|
381
|
+
|
|
382
|
+
### Added (Grouped Commands — FE-09)
|
|
383
|
+
|
|
384
|
+
- **`GroupedModuleGroup(LazyModuleGroup)`** — organizes modules into nested `click.Group` subcommands based on namespace prefixes. Auto-groups by first `.` segment, with `display.cli.group` override from binding.yaml.
|
|
385
|
+
- `_resolve_group()` — 3-tier group resolution: explicit `display.cli.group` > first `.` segment of CLI alias > top-level.
|
|
386
|
+
- `_build_group_map()` — lazy, idempotent group map builder with builtin collision detection and shell-safe group name validation.
|
|
387
|
+
- `format_help()` — collapsed root help with Commands, Modules, and Groups sections (with command counts).
|
|
388
|
+
- **`_LazyGroup(click.Group)`** — nested group that lazily builds subcommands from module descriptors.
|
|
389
|
+
- **`list --flat` flag** — opt-in flat display mode for `list` command; default is now grouped display.
|
|
390
|
+
- **`format_grouped_module_list()`** — Rich table output grouped by namespace.
|
|
391
|
+
- **Updated shell completions** — bash/zsh/fish completion scripts handle two-level group/command structure.
|
|
392
|
+
|
|
393
|
+
### Changed (Grouped Commands)
|
|
394
|
+
|
|
395
|
+
- `create_cli()` now uses `GroupedModuleGroup` instead of `LazyModuleGroup`.
|
|
396
|
+
|
|
397
|
+
### Tests (Grouped Commands)
|
|
398
|
+
|
|
399
|
+
- 48 new tests: `TestResolveGroup` (8+), `TestBuildGroupMap` (5+), `TestGroupedModuleGroupRouting` (7), `TestLazyGroupInner` (4), `TestGroupedHelpDisplay` (5), `TestCreateCliGrouped` (1), `TestGroupedE2E` (5), `TestGroupedDiscovery` (7+), `TestGroupedCompletion` (6).
|
|
400
|
+
|
|
401
|
+
### Added (Convention Module Discovery — §5.14)
|
|
402
|
+
|
|
403
|
+
- **`apcore-cli init module <id>`** — scaffolding command with `--style` (decorator, convention, binding) and `--description` options. Generates module templates in the appropriate directory.
|
|
404
|
+
- **`--commands-dir` CLI option** — path to a convention commands directory. When set, `ConventionScanner` from `apcore-toolkit` scans for plain functions and registers them as modules.
|
|
405
|
+
|
|
406
|
+
### Tests (Convention Module Discovery)
|
|
407
|
+
|
|
408
|
+
- 6 new tests in `tests/test_init_cmd.py` covering all three styles and options.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## [0.2.2] - 2026-03-22
|
|
413
|
+
|
|
414
|
+
### Changed
|
|
415
|
+
- Rebrand: aipartnerup → aiperceivable
|
|
416
|
+
|
|
417
|
+
## [0.2.1] - 2026-03-19
|
|
418
|
+
|
|
419
|
+
### Changed
|
|
420
|
+
- Help text truncation limit increased from 200 to 1000 characters (configurable via `cli.help_text_max_length` config key)
|
|
421
|
+
- `_extract_help`: added `max_length: int = 1000` parameter (`schema_parser.py`)
|
|
422
|
+
- `schema_to_click_options`: added `max_help_length: int = 1000` parameter (`schema_parser.py`)
|
|
423
|
+
- `build_module_command`: added `help_text_max_length: int = 1000` parameter, threaded through to schema parser (`cli.py`)
|
|
424
|
+
- `LazyModuleGroup`: constructor accepts `help_text_max_length: int = 1000`, passes to `build_module_command` (`cli.py`)
|
|
425
|
+
- `create_cli`: resolves `cli.help_text_max_length` from `ConfigResolver` and passes to `LazyModuleGroup` (`__main__.py`)
|
|
426
|
+
- `format_exec_result`: nested dict/list values in table mode now rendered with `json.dumps` instead of `str()` (`output.py`)
|
|
427
|
+
|
|
428
|
+
### Added
|
|
429
|
+
- `cli.help_text_max_length` config key (default: 1000) in `ConfigResolver.DEFAULTS` (`config.py`)
|
|
430
|
+
- `APCORE_CLI_HELP_TEXT_MAX_LENGTH` environment variable support for configuring help text max length
|
|
431
|
+
- `test_help_truncation_default`: tests default 1000-char truncation
|
|
432
|
+
- `test_help_no_truncation_within_limit`: tests no truncation at 999 chars
|
|
433
|
+
- `test_help_truncation_custom_max`: tests custom max_length parameter
|
|
434
|
+
- 263 tests (up from 261)
|
|
435
|
+
|
|
436
|
+
## [0.2.0] - 2026-03-16
|
|
437
|
+
|
|
438
|
+
### Added
|
|
439
|
+
- `APCORE_CLI_LOGGING_LEVEL` env var — CLI-specific log level that takes priority over `APCORE_LOGGING_LEVEL`; 3-tier precedence: `--log-level` flag > `APCORE_CLI_LOGGING_LEVEL` > `APCORE_LOGGING_LEVEL` > `WARNING` (`__main__.py`)
|
|
440
|
+
- `test_cli_logging_level_takes_priority_over_global` — verifies `APCORE_CLI_LOGGING_LEVEL=DEBUG` wins over `APCORE_LOGGING_LEVEL=ERROR`
|
|
441
|
+
- `test_cli_logging_level_fallback_to_global` — verifies fallback when CLI-specific var is unset
|
|
442
|
+
- `test_builtin_name_collision_exits_2` — schema property named `format` (or other reserved names) causes `build_module_command` to exit 2
|
|
443
|
+
- `test_exec_result_table_format` — `--format table` renders Rich Key/Value table to stdout
|
|
444
|
+
- `test_bash_completion_quotes_prog_name_in_directive` — verifies `shlex.quote()` applied to `complete -F` directive, not just embedded subshell
|
|
445
|
+
- `test_zsh_completion_quotes_prog_name_in_directives` — verifies `compdef` line uses quoted prog_name
|
|
446
|
+
- `test_fish_completion_quotes_prog_name_in_directives` — verifies `complete -c` lines use quoted prog_name
|
|
447
|
+
- 17 new tests (244 → 261 total)
|
|
448
|
+
|
|
449
|
+
### Changed
|
|
450
|
+
- `--log-level` accepted choices: `WARN` → `WARNING` (`__main__.py`)
|
|
451
|
+
- `schema_to_click_options`: schema-derived options now always have `required=False`; required fields marked `[required]` in help text instead of Click enforcement — allows `--input -` STDIN to supply required values without Click rejecting first (`schema_parser.py`)
|
|
452
|
+
- `format_exec_result`: now routes through `resolve_format()` and renders Rich table when `--format table` is specified; previously ignored its `format` parameter (`output.py`)
|
|
453
|
+
- `_generate_bash_completion`, `_generate_zsh_completion`, `_generate_fish_completion`: `shlex.quote()` applied to ALL prog_name positions in generated scripts (complete directives, compdef, complete -c), not only embedded subshell commands (`shell.py`)
|
|
454
|
+
- `check_approval`: removed unused `ctx: click.Context` parameter (`approval.py`)
|
|
455
|
+
- `set_audit_logger`: broadened type annotation from `AuditLogger` to `AuditLogger | None` (`cli.py`)
|
|
456
|
+
- `collect_input`: simplified redundant condition `if not raw or raw_size == 0:` → `if not raw:` (`cli.py`)
|
|
457
|
+
- Example `Input` models: all 7 modules updated with `Field(description=...)` on every field so CLI `--help` shows descriptive text for each flag
|
|
458
|
+
|
|
459
|
+
### Fixed
|
|
460
|
+
- **`--input -` STDIN blocked by Click required enforcement**: `schema_to_click_options` was generating `required=True` Click options; Click validated before the callback ran, rejecting STDIN-only invocations. Resolved by always using `required=False` and delegating required validation to `jsonschema.validate()` after input collection. Fixes all 6 `TestRealStdinPiping` failures.
|
|
461
|
+
- **`--log-level` had no effect**: `logging.basicConfig()` is a no-op after the first call; subsequent `create_cli()` calls in tests retained the prior handler's level. Fixed by calling `logging.getLogger().setLevel()` explicitly after `basicConfig()`.
|
|
462
|
+
- **`test_log_level_flag_takes_effect` false pass**: `--help` is an eager flag that exits before the group callback, so `--log-level DEBUG --help` never applied the log level. Test updated to use `completion bash` subcommand instead.
|
|
463
|
+
- **Shell completion directives not shell-safe**: prog names with spaces or special characters were unquoted in `complete -F`, `compdef`, and `complete -c` lines. Fixed by assigning `quoted = shlex.quote(prog_name)` and using it in all directive positions.
|
|
464
|
+
- **Audit `set_audit_logger(None)` type error**: type annotation rejected `None`; broadened to `AuditLogger | None`.
|
|
465
|
+
- **Test logger level leakage**: tests modifying root logger level affected subsequent tests; fixed with `try/finally` that restores the original level.
|
|
466
|
+
|
|
467
|
+
### Security
|
|
468
|
+
- `AuditLogger._hash_input`: now uses `secrets.token_bytes(16)` per-invocation salt before hashing, preventing cross-invocation input correlation via SHA-256 rainbow tables
|
|
469
|
+
- `build_module_command`: added reserved-name collision guard — exits 2 if a schema property (`input`, `yes`, `large_input`, `format`, `sandbox`) conflicts with a built-in CLI option name
|
|
470
|
+
- `_prompt_with_timeout` (SIGALRM path): wrapped in `try/finally` to guarantee signal handler restoration regardless of exit path
|
|
471
|
+
|
|
472
|
+
## [0.1.0] - 2026-03-15
|
|
473
|
+
|
|
474
|
+
### Added
|
|
475
|
+
- `--sandbox` flag for subprocess-isolated module execution (FE-05)
|
|
476
|
+
- `ModuleExecutionError` exception class for sandbox failures
|
|
477
|
+
- Windows approval timeout support via `threading.Timer` + `ctypes` (FE-03)
|
|
478
|
+
- Approval timeout clamping to 1..3600 seconds range (FE-03)
|
|
479
|
+
- Tag format validation (`^[a-z][a-z0-9_-]*$`) in `list --tag` (FE-04)
|
|
480
|
+
- `cli.auto_approve` config key with `False` default (FE-07)
|
|
481
|
+
- Extensions directory readability check with exit code 47 (FE-01)
|
|
482
|
+
- Missing required property warning in schema parser (FE-02)
|
|
483
|
+
- DEBUG log `"Loading extensions from {path}"` before registry discovery (FE-01)
|
|
484
|
+
- `TYPE_CHECKING` imports for proper type annotations (`Registry`, `Executor`, `ModuleDescriptor`, `ConfigResolver`, `AuditLogger`)
|
|
485
|
+
- `_get_module_id()` helper for `canonical_id`/`module_id` resolution
|
|
486
|
+
- `APCORE_AUTH_API_KEY` and `APCORE_CLI_SANDBOX` to README environment variables table
|
|
487
|
+
- `--sandbox` to README module execution options table
|
|
488
|
+
- CHANGELOG.md
|
|
489
|
+
- Core Dispatcher (FE-01): `LazyModuleGroup`, `build_module_command`, `collect_input`, `validate_module_id`
|
|
490
|
+
- Schema Parser (FE-02): `schema_to_click_options`, `_map_type`, `_extract_help`, `reconvert_enum_values`
|
|
491
|
+
- Ref Resolver (FE-02): `resolve_refs`, `_resolve_node` with `$ref`, `allOf`, `anyOf`, `oneOf` support
|
|
492
|
+
- Config Resolver (FE-07): `ConfigResolver` with 4-tier precedence (CLI > Env > File > Default)
|
|
493
|
+
- Approval Gate (FE-03): `check_approval`, `_prompt_with_timeout` with TTY detection and Unix SIGALRM
|
|
494
|
+
- Discovery (FE-04): `list` and `describe` commands with tag filtering and TTY-adaptive output
|
|
495
|
+
- Output Formatter (FE-08): `format_module_list`, `format_module_detail`, `format_exec_result` with Rich rendering
|
|
496
|
+
- Security Manager (FE-05): `AuthProvider`, `ConfigEncryptor` (keyring + AES-256-GCM), `AuditLogger` (JSON Lines), `Sandbox` (subprocess isolation)
|
|
497
|
+
- Shell Integration (FE-06): bash/zsh/fish completion generators, roff man page generator
|
|
498
|
+
- 8 example modules: `math.add`, `math.multiply`, `text.upper`, `text.reverse`, `text.wordcount`, `sysutil.info`, `sysutil.env`, `sysutil.disk`
|
|
499
|
+
- 244 tests (unit, integration, end-to-end)
|
|
500
|
+
- CI workflow with pytest and coverage
|
|
501
|
+
- Pre-commit hooks configuration
|
|
@@ -28,15 +28,16 @@
|
|
|
28
28
|
|
|
29
29
|
- Python >= 3.11
|
|
30
30
|
- Key dependencies: click >= 8.1, rich >= 13.0, jsonschema >= 4.20, pyyaml >= 6.0, keyring >= 24, cryptography >= 41
|
|
31
|
-
- Runtime: apcore >= 0.
|
|
32
|
-
-
|
|
31
|
+
- Runtime: apcore >= 0.21.0 (v0.9.0 bump, was 0.19.0 at v0.7.0)
|
|
32
|
+
- Runtime (required, v0.9.0+): apcore-toolkit >= 0.7.0. The `[toolkit]` extras group is retained as a no-op for downstream install scripts.
|
|
33
33
|
- Dev: pytest, pytest-asyncio, pytest-cov, mypy, ruff
|
|
34
34
|
|
|
35
|
-
## v0.
|
|
35
|
+
## v0.9.0 Conventions
|
|
36
36
|
|
|
37
37
|
- Public surface (`__init__.py`): `__version__`, `create_cli`, `ExposureFilter`,
|
|
38
38
|
`ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES`, `CliApprovalHandler`,
|
|
39
39
|
`resolve_refs`, `schema_to_click_options`, `format_exec_result`,
|
|
40
|
+
`set_verbose_help`, `set_docs_url`, `set_audit_logger`,
|
|
40
41
|
`ConfigResolver`, `AuditLogger`, `AuthProvider`, `ConfigEncryptor`, `Sandbox`,
|
|
41
42
|
plus error classes (AuthenticationError, ConfigDecryptionError,
|
|
42
43
|
ModuleExecutionError, ApprovalTimeoutError, ApprovalDeniedError).
|
|
@@ -45,9 +46,16 @@
|
|
|
45
46
|
- ExposureFilter + `expose=` kwarg on create_cli (FE-12).
|
|
46
47
|
- `extra_commands=[...]` kwarg on create_cli as the FE-11 extension point (with
|
|
47
48
|
collision detection against RESERVED_GROUP_NAMES — `BUILTIN_COMMANDS` retired v0.7.0).
|
|
49
|
+
- `builtin_group_name="apcli"` kwarg on create_cli (v0.8.0+) for embed-API renaming (FE-13).
|
|
48
50
|
- Default click Group class is `GroupedModuleGroup` (multi-level grouping since v0.3.0).
|
|
49
|
-
- All apcore-cli commands live under the `apcli` group (FE-13).
|
|
50
|
-
|
|
51
|
+
- All apcore-cli commands live exclusively under the `apcli` group (FE-13). Root-level
|
|
52
|
+
deprecation shims were removed in v0.9.0 (deprecated in v0.8.x).
|
|
53
|
+
- `--verbose` global flag renamed to `--all-options` in v0.9.0; `set_verbose_help(bool)` is
|
|
54
|
+
the programmatic equivalent (internal name retained for back-compat; Rust uses `set_all_options_help`).
|
|
55
|
+
- `verbose` removed from reserved schema property names set in v0.9.0; modules may now
|
|
56
|
+
define `verbose: boolean` freely.
|
|
57
|
+
- apcore-toolkit promoted from optional to REQUIRED runtime dep in v0.9.0; csv/jsonl/markdown/skill
|
|
58
|
+
output formats now toolkit-delegated (ADR-09).
|
|
51
59
|
- `system_cmd` module registers runtime system commands (health/usage/enable/disable/
|
|
52
60
|
reload/config) — FE-11.
|
|
53
61
|
- `strategy` module registers describe-pipeline + --strategy flag — FE-11.
|