apcore-cli 0.7.0__tar.gz → 0.8.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.
Files changed (133) hide show
  1. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/CHANGELOG.md +202 -0
  2. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/PKG-INFO +61 -20
  3. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/README.md +58 -17
  4. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/README.md +38 -54
  5. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/pyproject.toml +3 -3
  6. apcore_cli-0.8.0/src/apcore_cli/__init__.py +196 -0
  7. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/__main__.py +6 -0
  8. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/builtin_group.py +101 -12
  9. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/cli.py +38 -8
  10. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/config.py +31 -0
  11. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/discovery.py +31 -8
  12. apcore_cli-0.8.0/src/apcore_cli/exit_codes.py +76 -0
  13. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/factory.py +77 -105
  14. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/output.py +52 -0
  15. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/ref_resolver.py +18 -2
  16. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/schema_parser.py +9 -3
  17. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/security/audit.py +10 -2
  18. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/security/config_encryptor.py +23 -9
  19. apcore_cli-0.8.0/src/apcore_cli/security/sandbox.py +271 -0
  20. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/strategy.py +6 -1
  21. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/system_cmd.py +52 -25
  22. apcore_cli-0.8.0/src/apcore_cli/system_usage.py +167 -0
  23. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_apcli_integration.py +18 -21
  24. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_approval.py +21 -11
  25. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_bugfixes.py +1 -0
  26. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_builtin_group.py +87 -0
  27. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_cli.py +87 -5
  28. apcore_cli-0.8.0/tests/test_exit_codes.py +58 -0
  29. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_factory_fe13.py +43 -22
  30. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_integration.py +5 -3
  31. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_list_command_filters.py +14 -5
  32. apcore_cli-0.8.0/tests/test_output_format_markdown_skill.py +126 -0
  33. apcore_cli-0.8.0/tests/test_public_api.py +92 -0
  34. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_ref_resolver.py +58 -0
  35. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_schema_parser.py +8 -5
  36. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_security/test_audit.py +63 -0
  37. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_security/test_config_encryptor.py +26 -0
  38. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_security/test_sandbox.py +132 -45
  39. apcore_cli-0.8.0/tests/test_system_usage.py +257 -0
  40. apcore_cli-0.8.0/tests/test_validate.py +227 -0
  41. apcore_cli-0.7.0/commands/ops.py +0 -4
  42. apcore_cli-0.7.0/htmlcov/.gitignore +0 -2
  43. apcore_cli-0.7.0/htmlcov/class_index.html +0 -551
  44. apcore_cli-0.7.0/htmlcov/coverage_html_cb_bcae5fc4.js +0 -735
  45. apcore_cli-0.7.0/htmlcov/favicon_32_cb_58284776.png +0 -0
  46. apcore_cli-0.7.0/htmlcov/function_index.html +0 -1931
  47. apcore_cli-0.7.0/htmlcov/index.html +0 -306
  48. apcore_cli-0.7.0/htmlcov/keybd_closed_cb_ce680311.png +0 -0
  49. apcore_cli-0.7.0/htmlcov/status.json +0 -1
  50. apcore_cli-0.7.0/htmlcov/style_cb_a5a05ca4.css +0 -389
  51. apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f___init___py.html +0 -105
  52. apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_audit_py.html +0 -169
  53. apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_auth_py.html +0 -171
  54. apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_config_encryptor_py.html +0 -249
  55. apcore_cli-0.7.0/htmlcov/z_2a36de3398a45e2f_sandbox_py.html +0 -241
  56. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca___init___py.html +0 -184
  57. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca__sandbox_runner_py.html +0 -122
  58. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_approval_py.html +0 -352
  59. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_builtin_group_py.html +0 -475
  60. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_cli_py.html +0 -1173
  61. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_config_py.html +0 -238
  62. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_discovery_py.html +0 -545
  63. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_display_helpers_py.html +0 -127
  64. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_exposure_py.html +0 -227
  65. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_factory_py.html +0 -833
  66. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_init_cmd_py.html +0 -287
  67. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_output_py.html +0 -467
  68. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_ref_resolver_py.html +0 -227
  69. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_schema_parser_py.html +0 -295
  70. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_shell_py.html +0 -739
  71. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_strategy_py.html +0 -307
  72. apcore_cli-0.7.0/htmlcov/z_4d276f71fa2bf6ca_system_cmd_py.html +0 -566
  73. apcore_cli-0.7.0/src/apcore_cli/__init__.py +0 -87
  74. apcore_cli-0.7.0/src/apcore_cli/security/sandbox.py +0 -168
  75. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/.github/CODEOWNERS +0 -0
  76. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/.github/copilot-ignore +0 -0
  77. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/.github/workflows/ci.yml +0 -0
  78. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/.gitignore +0 -0
  79. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/.gitmessage +0 -0
  80. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/.pre-commit-config.yaml +0 -0
  81. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/CLAUDE.md +0 -0
  82. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/LICENSE +0 -0
  83. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/Makefile +0 -0
  84. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/math/add.py +0 -0
  85. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/math/multiply.py +0 -0
  86. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/sysutil/disk.py +0 -0
  87. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/sysutil/env.py +0 -0
  88. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/sysutil/info.py +0 -0
  89. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/text/reverse.py +0 -0
  90. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/text/upper.py +0 -0
  91. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/extensions/text/wordcount.py +0 -0
  92. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/examples/run_examples.sh +0 -0
  93. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/approval-gate.md +0 -0
  94. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/config-resolver.md +0 -0
  95. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/core-dispatcher.md +0 -0
  96. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/discovery.md +0 -0
  97. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/exposure-filtering.md +0 -0
  98. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/grouped-commands.md +0 -0
  99. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/output-formatter.md +0 -0
  100. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/overview.md +0 -0
  101. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/schema-parser.md +0 -0
  102. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/security-manager.md +0 -0
  103. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/shell-integration.md +0 -0
  104. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/planning/state.json +0 -0
  105. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/_sandbox_runner.py +0 -0
  106. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/approval.py +0 -0
  107. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/display_helpers.py +0 -0
  108. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/exposure.py +0 -0
  109. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/init_cmd.py +0 -0
  110. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/security/__init__.py +0 -0
  111. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/security/auth.py +0 -0
  112. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/shell.py +0 -0
  113. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/src/apcore_cli/validate.py +0 -0
  114. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/__init__.py +0 -0
  115. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/conformance/__init__.py +0 -0
  116. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/conformance/test_apcli_visibility.py +0 -0
  117. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/conftest.py +0 -0
  118. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_config.py +0 -0
  119. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_discovery.py +0 -0
  120. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_discovery_fe13.py +0 -0
  121. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_display_helpers.py +0 -0
  122. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_e2e.py +0 -0
  123. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_exposure.py +0 -0
  124. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_init_cmd.py +0 -0
  125. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_output.py +0 -0
  126. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_output_format_exec.py +0 -0
  127. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_sandbox_runner.py +0 -0
  128. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_security/__init__.py +0 -0
  129. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_security/test_auth.py +0 -0
  130. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_shell.py +0 -0
  131. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_strategy.py +0 -0
  132. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_system_cmd.py +0 -0
  133. {apcore_cli-0.7.0 → apcore_cli-0.8.0}/tests/test_toolkit_integration.py +0 -0
@@ -6,6 +6,208 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [0.8.0] - 2026-05-08
10
+
11
+ ### Removed
12
+
13
+ - **D9-001 — FE-13 §11.2 deprecation shims removed**. The 13 hidden root-level
14
+ shims (`list`, `describe`, `exec`, `init`, `validate`, `health`, `usage`,
15
+ `enable`, `disable`, `reload`, `config`, `completion`, `describe-pipeline`)
16
+ installed by `_register_deprecation_shims` and the `__is_deprecation_shim__`
17
+ collision-handling path in `extra_commands` wiring have been deleted along
18
+ with the `_DEPRECATED_ROOT_COMMANDS` table. Use the canonical
19
+ `apcli <command>` paths instead. Calls like `apcore-cli list` now exit
20
+ non-zero with Click's "No such command" message — the warning window
21
+ documented as "removed in v0.8" is closed.
22
+
23
+ ### Deprecated
24
+
25
+ - **`CliModuleNotFoundError` alias** — the symbol still resolves to
26
+ `ModuleNotFoundError` (see D1-002 in Changed) but is scheduled for
27
+ removal in v0.10.0. Update imports to
28
+ `from apcore_cli import ModuleNotFoundError`.
29
+
30
+ ### Security
31
+
32
+ - **D10-001 — `Sandbox` per-stream output cap** (`sandbox.py:155`). The previous
33
+ implementation summed `stdout + stderr` against a single `max_output_bytes`
34
+ budget — a runaway child writing only to stderr could starve the stdout
35
+ budget and vice versa, and the diagnostic on overflow did not name the
36
+ offending stream. Each stream now has an independent byte budget matching
37
+ Rust and TypeScript; the overflow error names the stream that tripped the
38
+ cap.
39
+ - **D11-W2 — `Sandbox` switched from `subprocess.run` to `subprocess.Popen`
40
+ with threaded chunked reads** (`sandbox.py:155`). `capture_output=True`
41
+ buffered the entire child stdio into parent memory before the cap was
42
+ checked, so a child producing GBs of output could OOM the parent before
43
+ the limit was enforced. The new implementation streams stdout/stderr
44
+ through reader threads with bounded buffers and kills the child as soon
45
+ as either stream exceeds its cap. Memory consumption is now bounded by
46
+ `2 × max_output_bytes` regardless of child output volume.
47
+ - **D11-003 — `ConfigEncryptor` v1 decryption honours
48
+ `APCORE_CLI_CONFIG_PASSPHRASE`** (`config_encryptor.py:128`). `_aes_decrypt_v1`
49
+ hard-coded the host:user material, so v1 ciphertext encrypted by the Rust
50
+ or TypeScript SDKs under a passphrase failed to decrypt on Python.
51
+ Decryption now tries the passphrase-derived key first when the env var is
52
+ set, falling back to host:user material — matching TypeScript
53
+ `aesDecryptV1`. Cross-SDK config bundles are now portable.
54
+ - **D11-008 — `AuditLogger._get_user` fallback chain now includes `LOGNAME`**
55
+ (`audit.py:66`). The canonical chain per `security.md` (D11-W1) is
56
+ `getlogin → pwd.getpwuid → USER → LOGNAME → USERNAME → unknown`. Python
57
+ previously skipped `LOGNAME`, so audit-log `user` fields diverged from
58
+ Rust/TS on hosts where only `LOGNAME` is set (some container runtimes,
59
+ cron jobs).
60
+
61
+ ### Added
62
+
63
+ - **`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`.
64
+ - **`_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).
65
+ - **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).
66
+ - **D1-001 — 13 `register_*_command` factories + `configure_man_help`
67
+ re-exported from `apcore_cli` package root**. Embedders that compose
68
+ their own root command tree no longer need to reach into private
69
+ submodules (`apcore_cli.commands.list_cmd`, etc.). All TS/Rust
70
+ `register_*` counterparts now have a Python public-API equivalent.
71
+ - **D1-003 — `apcore_cli.exit_codes` module** with 24 `EXIT_*` integer
72
+ constants, an `EXIT_CODES` mapping dict, and an `exit_code_for_error()`
73
+ helper. Mirrors TS `errors.ts` `EXIT_CODES` + `exitCodeForError` and
74
+ Rust `src/lib.rs` `EXIT_*` constants. Embedders can now map exceptions
75
+ to documented exit codes without re-implementing the table.
76
+ - **D1-007 — `format_module_list`, `format_module_detail`,
77
+ `resolve_format` re-exported from package root**. The
78
+ output-formatter feature spec declares these as Contracts; previously
79
+ only `format_exec_result` was public.
80
+ - **D1-W1 — `APCLI_SUBCOMMAND_NAMES` re-exported from `apcore_cli`**.
81
+ Matches Rust `lib.rs` and is now in `__all__` for static-analysis
82
+ tooling.
83
+ - **D1-W2 — `ApcliConfig` TypedDict** added to the public surface,
84
+ mirroring the TypeScript type alias and Rust struct so embedders have
85
+ a static contract for the `apcli.*` config block.
86
+ - **D1-W3 — `register_config_namespace()` helper + module-level
87
+ `DEFAULTS` constant** in `config.py`. The package still registers the
88
+ namespace at import time, but embedders can now invoke the helper
89
+ explicitly (parity with `apcore-cli-typescript`).
90
+ - **D1-W5 — Core dispatcher embedder API re-exported from package
91
+ root**: `build_module_command`, `collect_input`, `validate_module_id`,
92
+ `set_audit_logger`, `set_verbose_help`, `set_docs_url`. Embedders no
93
+ longer have to import from `apcore_cli.cli` directly. Matches Rust
94
+ `lib.rs:186-190` and TS `index.ts:18`. New `tests/test_public_api.py`
95
+ pins the surface against future drift.
96
+ - **D1-info-1 — typed `ApcliGroupError` exception**
97
+ (`builtin_group.py:107`). Cross-SDK parity with Rust `ApcliGroupError`;
98
+ embedders previously had no stable error class to match on for
99
+ built-in-group config validation. `ApcliGroupError(ValueError)`
100
+ preserves backwards compat — existing `except ValueError` callers
101
+ still catch it. The invalid-name regex check in `__init__` now raises
102
+ `ApcliGroupError`. Re-exported from `apcore_cli`.
103
+
104
+ ### Fixed
105
+
106
+ - **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.
107
+ - **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.
108
+ - **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.
109
+ - **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`.
110
+ - **D10-003 — `build_module_command` leaked `RefResolverError`
111
+ tracebacks** (`cli.py:538`). The `resolve_refs` catch clause re-raised
112
+ unchanged, so callers saw a Python traceback instead of a clean
113
+ documented exit code. Now translates `CircularRefError` /
114
+ `MaxDepthExceededError` to `sys.exit(48)` and `UnresolvableRefError`
115
+ (plus generic `RefResolverError`) to `sys.exit(45)`, mirroring
116
+ `schema_parser.py:111` and the Rust/TS contracts.
117
+ - **D11-NEW-003 — `ref_resolver` `max_depth` over-counted plain nested
118
+ `properties`** (`ref_resolver.py`). `_resolve_node` previously
119
+ incremented `depth + 1` when recursing into nested `properties`
120
+ values, so a schema with >32 levels of nested objects (no `$ref` at
121
+ all) was rejected with `MaxDepthExceededError`. The spec wording is
122
+ "Maximum `$ref` resolution recursion depth" — `$ref` hops along a
123
+ single chain, not total stack depth. `depth` is now only incremented
124
+ on `$ref` traversal, aligning with Rust `ref_resolver.rs:297`. Also
125
+ adds 4 regression tests for `anyOf`/`oneOf` sibling-required
126
+ preservation and `anyOf` overlap dedup.
127
+ - **D10-info-1 — `APCORE_CLI_APCLI` env var not trimmed on read**
128
+ (`builtin_group.py:414`). Spec invariant 2 requires the parser to be
129
+ case-insensitive AND trim-on-read. Surrounding whitespace previously
130
+ caused a silent Tier-3/Tier-4 fall-through. Now strips before
131
+ lowercasing, matching Rust/TS.
132
+ - **D11-010 — `AuditLogger` write-failure warnings deduplicated**
133
+ (`audit.py:55`). Previously warned on every failed write, flooding
134
+ logs when an audit dir is unwritable. An instance flag now gates the
135
+ warning so it fires once per logger instance, matching the TS
136
+ `writeFailureWarned` flag.
137
+
138
+ ### Changed
139
+
140
+ - **`apcli system *` and `apcli strategy describe-pipeline` `--format` choices**
141
+ expanded from `[table, json]` to `[table, json, csv, yaml, jsonl]`, matching
142
+ the existing `apcli list` / `apcli exec` choice set. `markdown` and `skill`
143
+ are deliberately excluded from these subcommands — their payloads are
144
+ health / strategy results, not `ScannedModule` data. Issue
145
+ [#20](https://github.com/aiperceivable/apcore-cli/issues/20).
146
+ - **Dependency bump**: requires `apcore >= 0.21.0` (was `>= 0.19.0`) and the
147
+ optional `[toolkit]` extra now requires `apcore-toolkit >= 0.6` (was `>= 0.5`).
148
+ Aligns with upstream `apcore 0.21.0` (Module.preview / PreflightResult.predicted_changes,
149
+ ephemeral.* namespace pilot) and `apcore-toolkit 0.6.0` (surface-aware formatters).
150
+ No CLI-visible behavioural breaks — apcore 0.20→0.21 deprecations
151
+ (`TaskStore.put`/`save`, `TaskStatus.RETRYING`, `CircuitOpenError`) keep
152
+ legacy aliases for one minor release; the cli does not call those surfaces directly.
153
+ - **D1-002 — `CliModuleNotFoundError` renamed to `ModuleNotFoundError`**
154
+ for cross-language port-ability with TS / Rust `ModuleNotFoundError`.
155
+ The class intentionally shadows `builtins.ModuleNotFoundError` inside
156
+ the `apcore_cli` namespace. A deprecation alias
157
+ `CliModuleNotFoundError = ModuleNotFoundError` is kept for backwards
158
+ compatibility and will be removed in v0.10.0. Reverses the D2-001
159
+ rename which predated the cross-SDK parity policy.
160
+ - **Issue #19 — drop "apcore" branding from embedded-mode `--help`**:
161
+ `create_cli()` now resolves the top-level CLI description from the new
162
+ `description=` parameter (defaults to `f"{prog_name} CLI"`), the `apcli`
163
+ subgroup advertises itself as `Built-in commands` rather than
164
+ `apcore-cli built-in commands`, and the `--verbose` option / footer drop
165
+ the trailing `apcore` from `(including built-in apcore options)`. Standalone
166
+ bin entry (`apcore_cli/__main__.py:main()`) passes
167
+ `description="<prog> — execute apcore modules from the command line"`
168
+ explicitly so the standalone surface is unchanged.
169
+
170
+ ### Added
171
+
172
+ - **`--format markdown` and `--format skill`** for `apcli list` and `apcli describe`
173
+ (issue [#20](https://github.com/aiperceivable/apcore-cli/issues/20)). Both
174
+ delegate to `apcore_toolkit.format_module(s)` (≥0.6) so the output is
175
+ byte-identical to the same toolkit call in the TypeScript and Rust SDKs.
176
+ `--format skill` produces vendor-neutral SKILL.md content directly loadable
177
+ by Claude Code (`.claude/skills/<id>/SKILL.md`) and Gemini CLI
178
+ (`.gemini/skills/<id>/SKILL.md`):
179
+
180
+ ```bash
181
+ apcli describe users.create --format skill > .claude/skills/users.create/SKILL.md
182
+ ```
183
+
184
+ A new internal adapter `_descriptor_to_scanned()` maps `ModuleDescriptor`
185
+ (apcore registry) to `ScannedModule` (apcore-toolkit). A `ClickException` with
186
+ a clear install hint is raised if the optional `[toolkit]` extra is missing.
187
+ - **Issue #18 — host-app `--version` opt-in**: new `version: str | None = None`
188
+ parameter on `create_cli()`. When supplied, registers `-V/--version` with
189
+ the host's version string. **When omitted, the `--version` flag is no
190
+ longer registered** — embedded CLIs that do not opt in stop leaking the
191
+ SDK's own version through `-V/--version`. The standalone bin entry
192
+ passes `version=apcore_cli.__version__` explicitly so the
193
+ `apcore-cli` binary's behaviour is preserved.
194
+ - **Issue #19 — `description: str | None = None`** on `create_cli()`.
195
+ - **Issue #17 — `system.usage` aggregator + `list --sort calls|errors|latency`**:
196
+ new module `apcore_cli.system_usage` reads `~/.apcore-cli/audit.jsonl`,
197
+ filters by period (default 24h), and returns per-module aggregates
198
+ (`calls`, `errors`, `avg latency_ms`). `list --sort {calls,errors,latency}`
199
+ now consults the aggregator instead of falling back to id-sort with a
200
+ buried `logger.warning`. When the audit log has no entries in the period
201
+ window the discovery layer prints a user-visible note to stderr
202
+ (`note: no usage data available for --sort <field>; sorted by id. ...`)
203
+ and falls back to id-sort. Module-protocol registration of
204
+ `system.usage.summary` / `system.usage.module` as registry-callable
205
+ built-ins is tracked as a follow-up — today the readers are invoked
206
+ directly by the discovery layer.
207
+ - New file: `apcore_cli/system_usage.py`.
208
+
209
+ ---
210
+
9
211
  ## [0.7.0] - 2026-04-23
10
212
 
11
213
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apcore-cli
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: Terminal adapter for apcore — execute AI-Perceivable modules from the command line
5
5
  Project-URL: Homepage, https://aiperceivable.com
6
6
  Project-URL: Repository, https://github.com/aiperceivable/apcore-cli-python
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Python: >=3.11
24
- Requires-Dist: apcore>=0.19.0
24
+ Requires-Dist: apcore>=0.21.0
25
25
  Requires-Dist: click>=8.1
26
26
  Requires-Dist: cryptography>=41.0
27
27
  Requires-Dist: jsonschema>=4.20
@@ -37,7 +37,7 @@ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
37
37
  Requires-Dist: pytest>=7.0; extra == 'dev'
38
38
  Requires-Dist: ruff>=0.1; extra == 'dev'
39
39
  Provides-Extra: toolkit
40
- Requires-Dist: apcore-toolkit>=0.5; extra == 'toolkit'
40
+ Requires-Dist: apcore-toolkit>=0.6; extra == 'toolkit'
41
41
  Description-Content-Type: text/markdown
42
42
 
43
43
  <div align="center">
@@ -50,7 +50,7 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
50
50
 
51
51
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
52
52
  [![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://python.org)
53
- [![Tests](https://img.shields.io/badge/tests-394%2B%20passed-brightgreen.svg)]()
53
+ [![Tests](https://github.com/aiperceivable/apcore-cli-python/actions/workflows/ci.yml/badge.svg)](https://github.com/aiperceivable/apcore-cli-python/actions/workflows/ci.yml)
54
54
 
55
55
  | | |
56
56
  |---|---|
@@ -90,7 +90,11 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
90
90
  pip install apcore-cli
91
91
  ```
92
92
 
93
- Requires Python 3.11+ and `apcore >= 0.17.1`.
93
+ Requires Python 3.11+ and `apcore >= 0.21.0`. The optional `toolkit` extra requires `apcore-toolkit >= 0.6`:
94
+
95
+ ```bash
96
+ pip install "apcore-cli[toolkit]"
97
+ ```
94
98
 
95
99
  ## Quick Start
96
100
 
@@ -159,13 +163,22 @@ cli = create_cli(registry=registry, executor=executor, prog_name="myapp")
159
163
 
160
164
  #### Python API
161
165
 
162
- The `apcore_cli` package exposes three public symbols:
166
+ The `apcore_cli` package re-exports the following public surface (see [`src/apcore_cli/__init__.py`](src/apcore_cli/__init__.py) for the canonical `__all__`):
163
167
 
164
168
  | Export | Description |
165
169
  |--------|-------------|
166
170
  | `__version__` | Package version string |
167
- | `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full 8-parameter signature (`extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `expose`). |
171
+ | `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full 13-parameter signature: `extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `app`, `expose`, **`apcli`** (FE-13 P0 break), `allowed_prefixes`, `version`, `description`. The `app`, `allowed_prefixes`, `version`, and `description` parameters were added in v0.8.0. |
172
+ | `ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES` | FE-13 built-in `apcli` group surface (P0 break in v0.8.0). |
168
173
  | `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
174
+ | `CliApprovalHandler`, `check_approval` | TTY-aware HITL approval handler / helper (FE-11). |
175
+ | `ConfigResolver` | 4-tier config precedence resolver (CLI flag > env var > config file > default). |
176
+ | `AuditLogger` | Append-only JSON Lines audit logger (`~/.apcore-cli/audit.jsonl`). |
177
+ | `AuthProvider` | API-key authentication provider (keyring-backed). |
178
+ | `ConfigEncryptor` | AES-256-GCM helper for encrypted config blobs. |
179
+ | `Sandbox` | Subprocess sandbox for isolated module execution. |
180
+ | `resolve_refs`, `schema_to_click_options`, `format_exec_result` | Schema/output helper functions. |
181
+ | `ApprovalDeniedError`, `ApprovalTimeoutError`, `AuthenticationError`, `ConfigDecryptionError`, `ModuleExecutionError`, `CliModuleNotFoundError`, `SchemaValidationError` | Typed error classes raised by the SDK (mapped to documented exit codes). |
169
182
 
170
183
  **Exposure filtering** — restrict which modules are visible at the CLI layer without touching the underlying registry:
171
184
 
@@ -195,24 +208,26 @@ cli = create_cli(
195
208
  cli = create_cli(registry=registry, executor=executor, extra_commands=[my_custom_click_cmd])
196
209
  ```
197
210
 
198
- Or use the `LazyModuleGroup` directly with Click:
211
+ Or build the CLI with a pre-populated registry and executor and full control over the `apcli` built-in group:
199
212
 
200
213
  ```python
201
- import click
202
214
  from apcore import Registry, Executor
203
- from apcore_cli.cli import LazyModuleGroup
215
+ from apcore_cli import create_cli, ApcliMode
204
216
 
205
217
  registry = Registry(extensions_dir="./extensions")
206
218
  registry.discover()
207
219
  executor = Executor(registry)
208
220
 
209
- @click.group(cls=LazyModuleGroup, registry=registry, executor=executor)
210
- def cli():
211
- pass
212
-
213
- cli()
221
+ cli = create_cli(
222
+ registry=registry,
223
+ executor=executor,
224
+ apcli=ApcliMode.ALL, # or "all" / "none" / {"mode": "include", "include": [...]}
225
+ )
226
+ cli(standalone_mode=True)
214
227
  ```
215
228
 
229
+ > **Note:** `apcore_cli.cli.LazyModuleGroup` and `GroupedModuleGroup` are advanced internal classes used by `create_cli`. They are subject to change between releases — prefer `create_cli(...)` for stable embedding.
230
+
216
231
  ## Adding Custom Commands
217
232
 
218
233
  ### Fastest way (30 seconds)
@@ -304,7 +319,7 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
304
319
 
305
320
  ### Built-in Commands
306
321
 
307
- apcore-cli ships with 14 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims are kept for back-compat and will be removed in v0.8.
322
+ apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims emit a deprecation warning in v0.8.x and are scheduled for removal in v0.9. Use `apcore-cli apcli <subcommand>` to avoid the warning.
308
323
 
309
324
  **Module invocation**
310
325
 
@@ -338,7 +353,8 @@ apcore-cli ships with 14 built-in commands, all accessible under the `apcli` sub
338
353
  | Command | Description |
339
354
  |---------|-------------|
340
355
  | `apcli completion <shell>` | Generate shell completion script (bash/zsh/fish) |
341
- | `apcli man <command>` | Generate roff man page for a command |
356
+
357
+ The full-program man page is reachable via the root flag combination `apcore-cli --help --man` (powered by `configure_man_help()`); there is no standalone `apcli man` subcommand.
342
358
 
343
359
  ### Module Execution Options
344
360
 
@@ -401,6 +417,7 @@ apcore-cli uses a 4-tier configuration precedence:
401
417
  | `APCORE_CLI_APPROVAL_TIMEOUT` | Approval-gate timeout in seconds (v0.6.0) | `60` |
402
418
  | `APCORE_CLI_STRATEGY` | Default execution strategy (v0.6.0) | `standard` |
403
419
  | `APCORE_CLI_GROUP_DEPTH` | Depth of automatic command grouping by `.` segments (v0.6.0) | `1` |
420
+ | `APCORE_CLI_APCLI` | FE-13 visibility for the `apcli` built-in group: `all`, `none`, `include`, `exclude`, or `auto` (v0.8.0). Overrides the `apcli:` block in `apcore.yaml`. | `auto` |
404
421
 
405
422
  ### Config File (`apcore.yaml`)
406
423
 
@@ -416,8 +433,32 @@ cli:
416
433
  approval_timeout: 60 # v0.6.0
417
434
  strategy: standard # v0.6.0
418
435
  group_depth: 1 # v0.6.0
436
+ apcli: # v0.8.0 (FE-13)
437
+ mode: all # all | none | include | exclude
438
+ include: [] # used when mode == include
439
+ exclude: [] # used when mode == exclude
419
440
  ```
420
441
 
442
+ ### `apcli` built-in group visibility (FE-13)
443
+
444
+ > **P0 breaking change in v0.8.0** — all built-ins were moved under the `apcli` group; the `BUILTIN_COMMANDS` constant has been retired.
445
+
446
+ The visibility of the `apcli` group is resolved through a 4-tier decision chain:
447
+
448
+ 1. **`create_cli(apcli=...)` kwarg** (highest) — accepts an `ApcliMode` enum, a string (`"all"`, `"none"`, `"include"`, `"exclude"`), or a dict (`{"mode": "include", "include": ["list", "describe"]}`).
449
+ 2. **`APCORE_CLI_APCLI` env var** — `all`, `none`, `include`, `exclude`, or `auto`.
450
+ 3. **`apcli:` block in `apcore.yaml`** — see the example above.
451
+ 4. **Embedded vs. standalone default** (lowest) — standalone `apcore-cli` defaults to `all`; CLIs embedded via `create_cli(app=...)` default to `none` so that host applications opt in explicitly. The `auto` sentinel selects this default and is intended for env/config use only — it is not a user-facing `ApcliMode` value.
452
+
453
+ The four user-visible modes are:
454
+
455
+ | Mode | Behavior |
456
+ |------|----------|
457
+ | `all` | All 13 `apcli` subcommands are exposed. |
458
+ | `none` | The `apcli` group is hidden entirely. |
459
+ | `include` | Only the subcommands listed in `include` are exposed. |
460
+ | `exclude` | All subcommands except those listed in `exclude` are exposed. |
461
+
421
462
  ## Features
422
463
 
423
464
  - **Auto-discovery** -- all modules in the extensions directory are found and exposed as CLI commands
@@ -432,7 +473,7 @@ cli:
432
473
  - **Schema validation** -- inputs validated against JSON Schema before execution, with `$ref`/`allOf`/`anyOf`/`oneOf` resolution
433
474
  - **Security** -- API key auth (keyring + AES-256-GCM), append-only audit logging, subprocess sandboxing
434
475
  - **Shell completions** -- `apcore-cli apcli completion bash|zsh|fish` generates completion scripts with dynamic module ID completion
435
- - **Man pages** -- `apcore-cli apcli man <command>` generates per-command man pages; `--help --man` prints a full-program man page via `configure_man_help()`
476
+ - **Man pages** -- `apcore-cli --help --man` prints a full-program roff man page to stdout, powered by `configure_man_help()` (also exposed for embedded CLIs to opt in)
436
477
  - **Documentation URL** -- `set_docs_url()` sets a base URL; per-command help shows `Docs: {url}/commands/{name}`, man page SEE ALSO links to the full docs site
437
478
  - **Audit logging** -- all executions logged to `~/.apcore-cli/audit.jsonl` with SHA-256 input hashing
438
479
 
@@ -519,8 +560,8 @@ apcore-cli apcli completion bash >> ~/.bashrc
519
560
  apcore-cli apcli completion zsh >> ~/.zshrc
520
561
  apcore-cli apcli completion fish > ~/.config/fish/completions/apcore-cli.fish
521
562
 
522
- # Man pages
523
- apcore-cli apcli man list | man -l -
563
+ # Man pages (full-program roff output)
564
+ apcore-cli --help --man | man -l -
524
565
 
525
566
  # Run all examples at once
526
567
  bash examples/run_examples.sh
@@ -8,7 +8,7 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
8
8
 
9
9
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
10
10
  [![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://python.org)
11
- [![Tests](https://img.shields.io/badge/tests-394%2B%20passed-brightgreen.svg)]()
11
+ [![Tests](https://github.com/aiperceivable/apcore-cli-python/actions/workflows/ci.yml/badge.svg)](https://github.com/aiperceivable/apcore-cli-python/actions/workflows/ci.yml)
12
12
 
13
13
  | | |
14
14
  |---|---|
@@ -48,7 +48,11 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
48
48
  pip install apcore-cli
49
49
  ```
50
50
 
51
- Requires Python 3.11+ and `apcore >= 0.17.1`.
51
+ Requires Python 3.11+ and `apcore >= 0.21.0`. The optional `toolkit` extra requires `apcore-toolkit >= 0.6`:
52
+
53
+ ```bash
54
+ pip install "apcore-cli[toolkit]"
55
+ ```
52
56
 
53
57
  ## Quick Start
54
58
 
@@ -117,13 +121,22 @@ cli = create_cli(registry=registry, executor=executor, prog_name="myapp")
117
121
 
118
122
  #### Python API
119
123
 
120
- The `apcore_cli` package exposes three public symbols:
124
+ The `apcore_cli` package re-exports the following public surface (see [`src/apcore_cli/__init__.py`](src/apcore_cli/__init__.py) for the canonical `__all__`):
121
125
 
122
126
  | Export | Description |
123
127
  |--------|-------------|
124
128
  | `__version__` | Package version string |
125
- | `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full 8-parameter signature (`extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `expose`). |
129
+ | `create_cli(...)` | Factory that builds a ready-to-invoke `click.Group`. See the [`create_cli` reference](https://github.com/aiperceivable/apcore-cli) in the docs site for the full 13-parameter signature: `extensions_dir`, `prog_name`, `commands_dir`, `binding_path`, `registry`, `executor`, `extra_commands`, `app`, `expose`, **`apcli`** (FE-13 P0 break), `allowed_prefixes`, `version`, `description`. The `app`, `allowed_prefixes`, `version`, and `description` parameters were added in v0.8.0. |
130
+ | `ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES` | FE-13 built-in `apcli` group surface (P0 break in v0.8.0). |
126
131
  | `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
132
+ | `CliApprovalHandler`, `check_approval` | TTY-aware HITL approval handler / helper (FE-11). |
133
+ | `ConfigResolver` | 4-tier config precedence resolver (CLI flag > env var > config file > default). |
134
+ | `AuditLogger` | Append-only JSON Lines audit logger (`~/.apcore-cli/audit.jsonl`). |
135
+ | `AuthProvider` | API-key authentication provider (keyring-backed). |
136
+ | `ConfigEncryptor` | AES-256-GCM helper for encrypted config blobs. |
137
+ | `Sandbox` | Subprocess sandbox for isolated module execution. |
138
+ | `resolve_refs`, `schema_to_click_options`, `format_exec_result` | Schema/output helper functions. |
139
+ | `ApprovalDeniedError`, `ApprovalTimeoutError`, `AuthenticationError`, `ConfigDecryptionError`, `ModuleExecutionError`, `CliModuleNotFoundError`, `SchemaValidationError` | Typed error classes raised by the SDK (mapped to documented exit codes). |
127
140
 
128
141
  **Exposure filtering** — restrict which modules are visible at the CLI layer without touching the underlying registry:
129
142
 
@@ -153,24 +166,26 @@ cli = create_cli(
153
166
  cli = create_cli(registry=registry, executor=executor, extra_commands=[my_custom_click_cmd])
154
167
  ```
155
168
 
156
- Or use the `LazyModuleGroup` directly with Click:
169
+ Or build the CLI with a pre-populated registry and executor and full control over the `apcli` built-in group:
157
170
 
158
171
  ```python
159
- import click
160
172
  from apcore import Registry, Executor
161
- from apcore_cli.cli import LazyModuleGroup
173
+ from apcore_cli import create_cli, ApcliMode
162
174
 
163
175
  registry = Registry(extensions_dir="./extensions")
164
176
  registry.discover()
165
177
  executor = Executor(registry)
166
178
 
167
- @click.group(cls=LazyModuleGroup, registry=registry, executor=executor)
168
- def cli():
169
- pass
170
-
171
- cli()
179
+ cli = create_cli(
180
+ registry=registry,
181
+ executor=executor,
182
+ apcli=ApcliMode.ALL, # or "all" / "none" / {"mode": "include", "include": [...]}
183
+ )
184
+ cli(standalone_mode=True)
172
185
  ```
173
186
 
187
+ > **Note:** `apcore_cli.cli.LazyModuleGroup` and `GroupedModuleGroup` are advanced internal classes used by `create_cli`. They are subject to change between releases — prefer `create_cli(...)` for stable embedding.
188
+
174
189
  ## Adding Custom Commands
175
190
 
176
191
  ### Fastest way (30 seconds)
@@ -262,7 +277,7 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
262
277
 
263
278
  ### Built-in Commands
264
279
 
265
- apcore-cli ships with 14 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims are kept for back-compat and will be removed in v0.8.
280
+ apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims emit a deprecation warning in v0.8.x and are scheduled for removal in v0.9. Use `apcore-cli apcli <subcommand>` to avoid the warning.
266
281
 
267
282
  **Module invocation**
268
283
 
@@ -296,7 +311,8 @@ apcore-cli ships with 14 built-in commands, all accessible under the `apcli` sub
296
311
  | Command | Description |
297
312
  |---------|-------------|
298
313
  | `apcli completion <shell>` | Generate shell completion script (bash/zsh/fish) |
299
- | `apcli man <command>` | Generate roff man page for a command |
314
+
315
+ The full-program man page is reachable via the root flag combination `apcore-cli --help --man` (powered by `configure_man_help()`); there is no standalone `apcli man` subcommand.
300
316
 
301
317
  ### Module Execution Options
302
318
 
@@ -359,6 +375,7 @@ apcore-cli uses a 4-tier configuration precedence:
359
375
  | `APCORE_CLI_APPROVAL_TIMEOUT` | Approval-gate timeout in seconds (v0.6.0) | `60` |
360
376
  | `APCORE_CLI_STRATEGY` | Default execution strategy (v0.6.0) | `standard` |
361
377
  | `APCORE_CLI_GROUP_DEPTH` | Depth of automatic command grouping by `.` segments (v0.6.0) | `1` |
378
+ | `APCORE_CLI_APCLI` | FE-13 visibility for the `apcli` built-in group: `all`, `none`, `include`, `exclude`, or `auto` (v0.8.0). Overrides the `apcli:` block in `apcore.yaml`. | `auto` |
362
379
 
363
380
  ### Config File (`apcore.yaml`)
364
381
 
@@ -374,8 +391,32 @@ cli:
374
391
  approval_timeout: 60 # v0.6.0
375
392
  strategy: standard # v0.6.0
376
393
  group_depth: 1 # v0.6.0
394
+ apcli: # v0.8.0 (FE-13)
395
+ mode: all # all | none | include | exclude
396
+ include: [] # used when mode == include
397
+ exclude: [] # used when mode == exclude
377
398
  ```
378
399
 
400
+ ### `apcli` built-in group visibility (FE-13)
401
+
402
+ > **P0 breaking change in v0.8.0** — all built-ins were moved under the `apcli` group; the `BUILTIN_COMMANDS` constant has been retired.
403
+
404
+ The visibility of the `apcli` group is resolved through a 4-tier decision chain:
405
+
406
+ 1. **`create_cli(apcli=...)` kwarg** (highest) — accepts an `ApcliMode` enum, a string (`"all"`, `"none"`, `"include"`, `"exclude"`), or a dict (`{"mode": "include", "include": ["list", "describe"]}`).
407
+ 2. **`APCORE_CLI_APCLI` env var** — `all`, `none`, `include`, `exclude`, or `auto`.
408
+ 3. **`apcli:` block in `apcore.yaml`** — see the example above.
409
+ 4. **Embedded vs. standalone default** (lowest) — standalone `apcore-cli` defaults to `all`; CLIs embedded via `create_cli(app=...)` default to `none` so that host applications opt in explicitly. The `auto` sentinel selects this default and is intended for env/config use only — it is not a user-facing `ApcliMode` value.
410
+
411
+ The four user-visible modes are:
412
+
413
+ | Mode | Behavior |
414
+ |------|----------|
415
+ | `all` | All 13 `apcli` subcommands are exposed. |
416
+ | `none` | The `apcli` group is hidden entirely. |
417
+ | `include` | Only the subcommands listed in `include` are exposed. |
418
+ | `exclude` | All subcommands except those listed in `exclude` are exposed. |
419
+
379
420
  ## Features
380
421
 
381
422
  - **Auto-discovery** -- all modules in the extensions directory are found and exposed as CLI commands
@@ -390,7 +431,7 @@ cli:
390
431
  - **Schema validation** -- inputs validated against JSON Schema before execution, with `$ref`/`allOf`/`anyOf`/`oneOf` resolution
391
432
  - **Security** -- API key auth (keyring + AES-256-GCM), append-only audit logging, subprocess sandboxing
392
433
  - **Shell completions** -- `apcore-cli apcli completion bash|zsh|fish` generates completion scripts with dynamic module ID completion
393
- - **Man pages** -- `apcore-cli apcli man <command>` generates per-command man pages; `--help --man` prints a full-program man page via `configure_man_help()`
434
+ - **Man pages** -- `apcore-cli --help --man` prints a full-program roff man page to stdout, powered by `configure_man_help()` (also exposed for embedded CLIs to opt in)
394
435
  - **Documentation URL** -- `set_docs_url()` sets a base URL; per-command help shows `Docs: {url}/commands/{name}`, man page SEE ALSO links to the full docs site
395
436
  - **Audit logging** -- all executions logged to `~/.apcore-cli/audit.jsonl` with SHA-256 input hashing
396
437
 
@@ -477,8 +518,8 @@ apcore-cli apcli completion bash >> ~/.bashrc
477
518
  apcore-cli apcli completion zsh >> ~/.zshrc
478
519
  apcore-cli apcli completion fish > ~/.config/fish/completions/apcore-cli.fish
479
520
 
480
- # Man pages
481
- apcore-cli apcli man list | man -l -
521
+ # Man pages (full-program roff output)
522
+ apcore-cli --help --man | man -l -
482
523
 
483
524
  # Run all examples at once
484
525
  bash examples/run_examples.sh