apcore-cli 0.8.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.
Files changed (100) hide show
  1. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/CHANGELOG.md +46 -0
  2. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/CLAUDE.md +13 -5
  3. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/PKG-INFO +10 -11
  4. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/README.md +8 -8
  5. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/pyproject.toml +6 -5
  6. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/__init__.py +19 -3
  7. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/approval.py +52 -2
  8. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/cli.py +23 -11
  9. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/discovery.py +9 -3
  10. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/exit_codes.py +1 -3
  11. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/factory.py +10 -10
  12. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/init_cmd.py +1 -1
  13. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/output.py +30 -31
  14. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/ref_resolver.py +25 -11
  15. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/schema_parser.py +1 -1
  16. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/audit.py +8 -2
  17. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/config_encryptor.py +18 -5
  18. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/shell.py +7 -7
  19. apcore_cli-0.9.0/tests/conformance/test_snake_case_kwargs.py +82 -0
  20. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_apcli_integration.py +3 -3
  21. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_cli.py +11 -11
  22. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_output_format_exec.py +31 -3
  23. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_ref_resolver.py +72 -0
  24. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_config_encryptor.py +85 -0
  25. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_shell.py +1 -1
  26. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.github/CODEOWNERS +0 -0
  27. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.github/copilot-ignore +0 -0
  28. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.github/workflows/ci.yml +0 -0
  29. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.gitignore +0 -0
  30. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.gitmessage +0 -0
  31. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/.pre-commit-config.yaml +0 -0
  32. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/LICENSE +0 -0
  33. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/Makefile +0 -0
  34. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/README.md +0 -0
  35. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/math/add.py +0 -0
  36. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/math/multiply.py +0 -0
  37. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/disk.py +0 -0
  38. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/env.py +0 -0
  39. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/sysutil/info.py +0 -0
  40. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/text/reverse.py +0 -0
  41. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/text/upper.py +0 -0
  42. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/extensions/text/wordcount.py +0 -0
  43. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/examples/run_examples.sh +0 -0
  44. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/approval-gate.md +0 -0
  45. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/config-resolver.md +0 -0
  46. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/core-dispatcher.md +0 -0
  47. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/discovery.md +0 -0
  48. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/exposure-filtering.md +0 -0
  49. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/grouped-commands.md +0 -0
  50. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/output-formatter.md +0 -0
  51. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/overview.md +0 -0
  52. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/schema-parser.md +0 -0
  53. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/security-manager.md +0 -0
  54. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/shell-integration.md +0 -0
  55. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/planning/state.json +0 -0
  56. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/__main__.py +0 -0
  57. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/_sandbox_runner.py +0 -0
  58. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/builtin_group.py +0 -0
  59. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/config.py +0 -0
  60. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/display_helpers.py +0 -0
  61. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/exposure.py +0 -0
  62. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/__init__.py +0 -0
  63. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/auth.py +0 -0
  64. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/security/sandbox.py +0 -0
  65. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/strategy.py +0 -0
  66. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/system_cmd.py +0 -0
  67. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/system_usage.py +0 -0
  68. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/src/apcore_cli/validate.py +0 -0
  69. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/__init__.py +0 -0
  70. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/conformance/__init__.py +0 -0
  71. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/conformance/test_apcli_visibility.py +0 -0
  72. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/conftest.py +0 -0
  73. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_approval.py +0 -0
  74. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_bugfixes.py +0 -0
  75. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_builtin_group.py +0 -0
  76. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_config.py +0 -0
  77. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_discovery.py +0 -0
  78. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_discovery_fe13.py +0 -0
  79. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_display_helpers.py +0 -0
  80. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_e2e.py +0 -0
  81. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_exit_codes.py +0 -0
  82. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_exposure.py +0 -0
  83. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_factory_fe13.py +0 -0
  84. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_init_cmd.py +0 -0
  85. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_integration.py +0 -0
  86. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_list_command_filters.py +0 -0
  87. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_output.py +0 -0
  88. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_output_format_markdown_skill.py +0 -0
  89. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_public_api.py +0 -0
  90. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_sandbox_runner.py +0 -0
  91. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_schema_parser.py +0 -0
  92. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/__init__.py +0 -0
  93. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_audit.py +0 -0
  94. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_auth.py +0 -0
  95. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_security/test_sandbox.py +0 -0
  96. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_strategy.py +0 -0
  97. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_system_cmd.py +0 -0
  98. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_system_usage.py +0 -0
  99. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_toolkit_integration.py +0 -0
  100. {apcore_cli-0.8.0 → apcore_cli-0.9.0}/tests/test_validate.py +0 -0
@@ -5,6 +5,52 @@ All notable changes to apcore-cli (Python SDK) will be documented in this file.
5
5
  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
+ ## [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
+
8
54
 
9
55
  ## [0.8.0] - 2026-05-08
10
56
 
@@ -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.19.0 (v0.7.0 bump, was 0.17.1)
32
- - Optional: apcore-toolkit >= 0.4 (install via `pip install apcore-cli[toolkit]`)
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.7.0 Conventions
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). Deprecation shims
50
- remain at root level for back-compat until v0.8.
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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apcore-cli
3
- Version: 0.8.0
3
+ Version: 0.9.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,6 +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-toolkit>=0.7.0
24
25
  Requires-Dist: apcore>=0.21.0
25
26
  Requires-Dist: click>=8.1
26
27
  Requires-Dist: cryptography>=41.0
@@ -32,12 +33,10 @@ Provides-Extra: dev
32
33
  Requires-Dist: apdev[dev]>=0.2.3; extra == 'dev'
33
34
  Requires-Dist: mypy>=1.0; extra == 'dev'
34
35
  Requires-Dist: pre-commit>=3.5; extra == 'dev'
35
- Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
36
36
  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.6; extra == 'toolkit'
41
40
  Description-Content-Type: text/markdown
42
41
 
43
42
  <div align="center">
@@ -90,10 +89,10 @@ Terminal adapter for apcore. Execute AI-Perceivable modules from the command lin
90
89
  pip install apcore-cli
91
90
  ```
92
91
 
93
- Requires Python 3.11+ and `apcore >= 0.21.0`. The optional `toolkit` extra requires `apcore-toolkit >= 0.6`:
92
+ Requires Python 3.11+, `apcore >= 0.21.0`, and `apcore-toolkit >= 0.7.0` (now a **required** runtime dependency as of v0.9.0 — previously optional; see the [tech-design ADR-09](https://github.com/aiperceivable/apcore-cli/blob/main/docs/tech-design.md) for the byte-equivalent toolkit-delegated tier rationale). The `[toolkit]` extras group is retained as a no-op for backward compat:
94
93
 
95
94
  ```bash
96
- pip install "apcore-cli[toolkit]"
95
+ pip install "apcore-cli[toolkit]" # equivalent to plain `pip install apcore-cli`
97
96
  ```
98
97
 
99
98
  ## Quick Start
@@ -168,7 +167,7 @@ The `apcore_cli` package re-exports the following public surface (see [`src/apco
168
167
  | Export | Description |
169
168
  |--------|-------------|
170
169
  | `__version__` | Package version string |
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. |
170
+ | `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 14-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`, **`builtin_group_name`** (default `"apcli"`, v0.8.0+). |
172
171
  | `ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES` | FE-13 built-in `apcli` group surface (P0 break in v0.8.0). |
173
172
  | `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
174
173
  | `CliApprovalHandler`, `check_approval` | TTY-aware HITL approval handler / helper (FE-11). |
@@ -314,12 +313,12 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
314
313
  | `--log-level` | `WARNING` | Logging: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
315
314
  | `--version` | | Show version and exit |
316
315
  | `--help` | | Show help and exit |
317
- | `--verbose` | | Show hidden built-in options in `--help` output |
316
+ | `--all-options` | | Show hidden built-in options in `--help` output |
318
317
  | `--man` | | Print man page to stdout (use with `--help`) |
319
318
 
320
319
  ### Built-in Commands
321
320
 
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.
321
+ apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims were removed in v0.9.0 (they emitted deprecation warnings in v0.8.x). Use `apcore-cli apcli <subcommand>`.
323
322
 
324
323
  **Module invocation**
325
324
 
@@ -358,14 +357,14 @@ The full-program man page is reachable via the root flag combination `apcore-cli
358
357
 
359
358
  ### Module Execution Options
360
359
 
361
- When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --verbose` to display them):
360
+ When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --all-options` to display them):
362
361
 
363
362
  | Option | Description |
364
363
  |--------|-------------|
365
364
  | `--input -` | Read JSON input from STDIN |
366
365
  | `--yes` / `-y` | Bypass approval prompts |
367
366
  | `--large-input` | Allow STDIN input larger than 10MB |
368
- | `--format` | Output format: `{json, table, csv, yaml, jsonl}` |
367
+ | `--format` | Output format: `{json, table, csv, yaml, jsonl, markdown, skill}`. **v0.9.0:** `csv` and `jsonl` are byte-identical across SDKs (delegated to `apcore-toolkit.format_csv` / `format_jsonl`); fixes the prior `str(v)` Python-repr bug for nested values. |
369
368
  | `--sandbox` | Run module in subprocess sandbox *(not yet implemented)* |
370
369
  | `--dry-run` | Run preflight checks without executing (FE-11, v0.6.0) |
371
370
  | `--trace` | Emit execution pipeline trace (v0.6.0) |
@@ -505,7 +504,7 @@ apcore-cli (the adapter)
505
504
  +-- system_cmd Runtime system-management (health/usage/enable/disable/reload/config)
506
505
  +-- strategy Execution strategy dispatch (--strategy flag and describe-pipeline)
507
506
  +-- init_cmd Module scaffolding (init subcommand)
508
- +-- set_verbose_help Toggle built-in option visibility
507
+ +-- set_verbose_help Toggle built-in option visibility (internal name; controls --all-options behaviour)
509
508
  +-- set_docs_url Set base URL for online docs
510
509
  +-- build_program_man_page Full-program roff man page
511
510
  +-- configure_man_help Add --help --man support to any CLI
@@ -48,10 +48,10 @@ 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.21.0`. The optional `toolkit` extra requires `apcore-toolkit >= 0.6`:
51
+ Requires Python 3.11+, `apcore >= 0.21.0`, and `apcore-toolkit >= 0.7.0` (now a **required** runtime dependency as of v0.9.0 — previously optional; see the [tech-design ADR-09](https://github.com/aiperceivable/apcore-cli/blob/main/docs/tech-design.md) for the byte-equivalent toolkit-delegated tier rationale). The `[toolkit]` extras group is retained as a no-op for backward compat:
52
52
 
53
53
  ```bash
54
- pip install "apcore-cli[toolkit]"
54
+ pip install "apcore-cli[toolkit]" # equivalent to plain `pip install apcore-cli`
55
55
  ```
56
56
 
57
57
  ## Quick Start
@@ -126,7 +126,7 @@ The `apcore_cli` package re-exports the following public surface (see [`src/apco
126
126
  | Export | Description |
127
127
  |--------|-------------|
128
128
  | `__version__` | Package version string |
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. |
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 14-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`, **`builtin_group_name`** (default `"apcli"`, v0.8.0+). |
130
130
  | `ApcliGroup`, `ApcliMode`, `RESERVED_GROUP_NAMES` | FE-13 built-in `apcli` group surface (P0 break in v0.8.0). |
131
131
  | `ExposureFilter` | Declarative filter controlling which modules are exposed by the CLI (FE-12). |
132
132
  | `CliApprovalHandler`, `check_approval` | TTY-aware HITL approval handler / helper (FE-11). |
@@ -272,12 +272,12 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
272
272
  | `--log-level` | `WARNING` | Logging: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
273
273
  | `--version` | | Show version and exit |
274
274
  | `--help` | | Show help and exit |
275
- | `--verbose` | | Show hidden built-in options in `--help` output |
275
+ | `--all-options` | | Show hidden built-in options in `--help` output |
276
276
  | `--man` | | Print man page to stdout (use with `--help`) |
277
277
 
278
278
  ### Built-in Commands
279
279
 
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.
280
+ apcore-cli ships with 13 built-in commands, all accessible under the `apcli` subgroup (e.g. `apcore-cli apcli list`). Root-level shims were removed in v0.9.0 (they emitted deprecation warnings in v0.8.x). Use `apcore-cli apcli <subcommand>`.
281
281
 
282
282
  **Module invocation**
283
283
 
@@ -316,14 +316,14 @@ The full-program man page is reachable via the root flag combination `apcore-cli
316
316
 
317
317
  ### Module Execution Options
318
318
 
319
- When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --verbose` to display them):
319
+ When executing a module (e.g. `apcore-cli math add`), these built-in options are available (hidden by default; pass `--help --all-options` to display them):
320
320
 
321
321
  | Option | Description |
322
322
  |--------|-------------|
323
323
  | `--input -` | Read JSON input from STDIN |
324
324
  | `--yes` / `-y` | Bypass approval prompts |
325
325
  | `--large-input` | Allow STDIN input larger than 10MB |
326
- | `--format` | Output format: `{json, table, csv, yaml, jsonl}` |
326
+ | `--format` | Output format: `{json, table, csv, yaml, jsonl, markdown, skill}`. **v0.9.0:** `csv` and `jsonl` are byte-identical across SDKs (delegated to `apcore-toolkit.format_csv` / `format_jsonl`); fixes the prior `str(v)` Python-repr bug for nested values. |
327
327
  | `--sandbox` | Run module in subprocess sandbox *(not yet implemented)* |
328
328
  | `--dry-run` | Run preflight checks without executing (FE-11, v0.6.0) |
329
329
  | `--trace` | Emit execution pipeline trace (v0.6.0) |
@@ -463,7 +463,7 @@ apcore-cli (the adapter)
463
463
  +-- system_cmd Runtime system-management (health/usage/enable/disable/reload/config)
464
464
  +-- strategy Execution strategy dispatch (--strategy flag and describe-pipeline)
465
465
  +-- init_cmd Module scaffolding (init subcommand)
466
- +-- set_verbose_help Toggle built-in option visibility
466
+ +-- set_verbose_help Toggle built-in option visibility (internal name; controls --all-options behaviour)
467
467
  +-- set_docs_url Set base URL for online docs
468
468
  +-- build_program_man_page Full-program roff man page
469
469
  +-- configure_man_help Add --help --man support to any CLI
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "apcore-cli"
7
- version = "0.8.0"
7
+ version = "0.9.0"
8
8
  description = "Terminal adapter for apcore — execute AI-Perceivable modules from the command line"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -27,6 +27,7 @@ classifiers = [
27
27
  ]
28
28
  dependencies = [
29
29
  "apcore>=0.21.0",
30
+ "apcore-toolkit>=0.7.0",
30
31
  "click>=8.1",
31
32
  "jsonschema>=4.20",
32
33
  "rich>=13.0",
@@ -39,15 +40,15 @@ dependencies = [
39
40
  dev = [
40
41
  "apdev[dev]>=0.2.3",
41
42
  "pytest>=7.0",
42
- "pytest-asyncio>=0.23",
43
43
  "pytest-cov>=4.0",
44
44
  "mypy>=1.0",
45
45
  "ruff>=0.1",
46
46
  "pre-commit>=3.5",
47
47
  ]
48
- toolkit = [
49
- "apcore-toolkit>=0.6",
50
- ]
48
+ # `toolkit` is kept as a no-op extras group for backward compat with downstream
49
+ # `apcore-cli[toolkit]` install commands; apcore-toolkit is now a required
50
+ # runtime dependency (see ADR-09).
51
+ toolkit = []
51
52
 
52
53
  [project.scripts]
53
54
  apcore-cli = "apcore_cli.__main__:main"
@@ -17,6 +17,7 @@ from apcore_cli.approval import (
17
17
  )
18
18
  from apcore_cli.builtin_group import (
19
19
  APCLI_SUBCOMMAND_NAMES,
20
+ DEFAULT_BUILTIN_GROUP_NAME,
20
21
  RESERVED_GROUP_NAMES,
21
22
  ApcliConfig,
22
23
  ApcliGroup,
@@ -26,6 +27,7 @@ from apcore_cli.builtin_group import (
26
27
  from apcore_cli.cli import (
27
28
  build_module_command,
28
29
  collect_input,
30
+ set_all_options_help,
29
31
  set_audit_logger,
30
32
  set_docs_url,
31
33
  set_verbose_help,
@@ -77,8 +79,14 @@ from apcore_cli.output import (
77
79
  format_module_list,
78
80
  resolve_format,
79
81
  )
80
- from apcore_cli.ref_resolver import resolve_refs
81
- from apcore_cli.schema_parser import schema_to_click_options
82
+ from apcore_cli.ref_resolver import (
83
+ CircularRefError,
84
+ MaxDepthExceededError,
85
+ RefResolverError,
86
+ UnresolvableRefError,
87
+ resolve_refs,
88
+ )
89
+ from apcore_cli.schema_parser import reconvert_enum_values, schema_to_click_options
82
90
  from apcore_cli.security.audit import AuditLogger
83
91
  from apcore_cli.security.auth import AuthenticationError, AuthProvider
84
92
  from apcore_cli.security.config_encryptor import ConfigDecryptionError, ConfigEncryptor
@@ -114,6 +122,7 @@ __all__ = [
114
122
  "ApcliGroupError",
115
123
  "ApcliMode",
116
124
  "APCLI_SUBCOMMAND_NAMES",
125
+ "DEFAULT_BUILTIN_GROUP_NAME",
117
126
  "RESERVED_GROUP_NAMES",
118
127
  # FE-11 approval
119
128
  "CliApprovalHandler",
@@ -123,6 +132,7 @@ __all__ = [
123
132
  # Schema / output / ref resolution
124
133
  "resolve_refs",
125
134
  "schema_to_click_options",
135
+ "reconvert_enum_values", # parity with TS reconvertEnumValues / Rust reconvert_enum_values
126
136
  "format_exec_result",
127
137
  "format_module_list",
128
138
  "format_module_detail",
@@ -139,8 +149,9 @@ __all__ = [
139
149
  "build_module_command",
140
150
  "collect_input",
141
151
  "validate_module_id",
152
+ "set_all_options_help",
142
153
  "set_audit_logger",
143
- "set_verbose_help",
154
+ "set_verbose_help", # deprecated alias for set_all_options_help
144
155
  "set_docs_url",
145
156
  # Per-subcommand registrar factories (parity with TS/Rust embedder API).
146
157
  "register_list_command",
@@ -164,6 +175,11 @@ __all__ = [
164
175
  "ConfigDecryptionError",
165
176
  "ModuleExecutionError",
166
177
  "ModuleNotFoundError",
178
+ # Ref-resolver error hierarchy (parity with TS index.ts:82-84).
179
+ "RefResolverError",
180
+ "CircularRefError",
181
+ "MaxDepthExceededError",
182
+ "UnresolvableRefError",
167
183
  # Deprecated alias kept for backward compat — prefer ModuleNotFoundError.
168
184
  # Will be removed in v0.10.0.
169
185
  "CliModuleNotFoundError",
@@ -44,6 +44,33 @@ def _get_annotation(annotations: Any, key: str, default: Any = None) -> Any:
44
44
  return getattr(annotations, key, default)
45
45
 
46
46
 
47
+ def _read_timeout_from_env() -> int | None:
48
+ """Parse APCORE_CLI_APPROVAL_TIMEOUT, warn on malformed, return None on absent/invalid.
49
+
50
+ D11-012 cross-SDK parity: TS's `readTimeoutFromEnv()` reads the same
51
+ variable; Rust will be aligned in a follow-up. Documented precedence is
52
+ `CLI flag > env var > 60s default`.
53
+ """
54
+ raw = os.environ.get("APCORE_CLI_APPROVAL_TIMEOUT")
55
+ if raw is None or raw == "":
56
+ return None
57
+ try:
58
+ parsed = int(raw)
59
+ except ValueError:
60
+ print(
61
+ f"Warning: APCORE_CLI_APPROVAL_TIMEOUT='{raw}' is not an integer. Ignoring.",
62
+ file=sys.stderr,
63
+ )
64
+ return None
65
+ if parsed <= 0:
66
+ print(
67
+ f"Warning: APCORE_CLI_APPROVAL_TIMEOUT='{raw}' must be > 0. Ignoring.",
68
+ file=sys.stderr,
69
+ )
70
+ return None
71
+ return parsed
72
+
73
+
47
74
  # ---------------------------------------------------------------------------
48
75
  # CliApprovalHandler — implements apcore ApprovalHandler protocol (FE-11 §3.5)
49
76
  # ---------------------------------------------------------------------------
@@ -59,8 +86,13 @@ class CliApprovalHandler:
59
86
  Pass to Executor via ``executor.set_approval_handler(handler)``.
60
87
  """
61
88
 
62
- def __init__(self, auto_approve: bool = False, timeout: int = 60) -> None:
89
+ def __init__(self, auto_approve: bool = False, timeout: int | None = None) -> None:
63
90
  self.auto_approve = auto_approve
91
+ # D11-012: default-timeout resolution honors APCORE_CLI_APPROVAL_TIMEOUT
92
+ # env var when caller did not pass an explicit timeout. Precedence:
93
+ # constructor arg > env var > 60s default. Matches TS readTimeoutFromEnv.
94
+ if timeout is None:
95
+ timeout = _read_timeout_from_env() or 60
64
96
  self.timeout = max(1, min(timeout, 3600))
65
97
 
66
98
  async def request_approval(self, request: Any) -> Any:
@@ -72,6 +104,20 @@ class CliApprovalHandler:
72
104
  """
73
105
  module_id = getattr(request, "module_id", "unknown")
74
106
 
107
+ # D11-014: defense-in-depth — short-circuit when the request explicitly
108
+ # declares it does not require approval. Matches Rust approval.rs:421
109
+ # (`if !get_requires_approval(module_def) { return Approved::not_required }`).
110
+ requires_attr = getattr(request, "requires_approval", None)
111
+ if requires_attr is False:
112
+ return {"status": "approved", "approved_by": "not_required"}
113
+ module_def = getattr(request, "module_def", None)
114
+ if module_def is not None:
115
+ annotations = getattr(module_def, "annotations", None)
116
+ if annotations is not None:
117
+ requires = _get_annotation(annotations, "requires_approval", None)
118
+ if requires is False:
119
+ return {"status": "approved", "approved_by": "not_required"}
120
+
75
121
  # Bypass: auto_approve flag
76
122
  if self.auto_approve:
77
123
  logger.info("Approval bypassed via --yes flag for module '%s'.", module_id)
@@ -130,7 +176,7 @@ class CliApprovalHandler:
130
176
  # ---------------------------------------------------------------------------
131
177
 
132
178
 
133
- def check_approval(module_def: Any, auto_approve: bool, timeout: int = 60) -> None:
179
+ def check_approval(module_def: Any, auto_approve: bool, timeout: int | None = None) -> None:
134
180
  """Check if module requires approval and handle accordingly.
135
181
 
136
182
  Returns None if approved (or approval not required).
@@ -155,6 +201,10 @@ def check_approval(module_def: Any, auto_approve: bool, timeout: int = 60) -> No
155
201
  if requires is not True:
156
202
  return
157
203
 
204
+ # D11-012: resolve timeout precedence — explicit arg > APCORE_CLI_APPROVAL_TIMEOUT env > 60s.
205
+ if timeout is None:
206
+ timeout = _read_timeout_from_env() or 60
207
+
158
208
  module_id = getattr(module_def, "module_id", getattr(module_def, "canonical_id", "unknown"))
159
209
 
160
210
  # Bypass: --yes flag (highest priority)
@@ -46,14 +46,26 @@ logger = logging.getLogger("apcore_cli.cli")
46
46
  # Module-level audit logger, set during CLI init
47
47
  _audit_logger: AuditLogger | None = None
48
48
 
49
- # Module-level verbose help flag, set during CLI init
50
- _verbose_help: bool = False
49
+ # Module-level all-options help flag, set during CLI init
50
+ _all_options_help: bool = False
51
+
52
+
53
+ def set_all_options_help(all_options: bool) -> None:
54
+ """Set the all-options help flag. When False, built-in options are hidden.
55
+
56
+ Controls the ``--all-options`` flag introduced in FR-DISP-007 (v0.9.0).
57
+ """
58
+ global _all_options_help
59
+ _all_options_help = all_options
51
60
 
52
61
 
53
62
  def set_verbose_help(verbose: bool) -> None:
54
- """Set the verbose help flag. When False, built-in options are hidden."""
55
- global _verbose_help
56
- _verbose_help = verbose
63
+ """Deprecated alias for :func:`set_all_options_help`.
64
+
65
+ The name ``set_verbose_help`` is kept for backward compatibility.
66
+ Use ``set_all_options_help`` in new code.
67
+ """
68
+ set_all_options_help(verbose)
57
69
 
58
70
 
59
71
  # Module-level docs URL, set by downstream projects
@@ -411,7 +423,7 @@ class GroupedModuleGroup(LazyModuleGroup):
411
423
  # Footer hints for discoverability
412
424
  formatter.write_paragraph()
413
425
  formatter.write(
414
- "Use --help --verbose to show all options (including built-in options).\n"
426
+ "Use --help --all-options to show all options (including built-in options).\n"
415
427
  "Use --help --man to display a formatted man page."
416
428
  )
417
429
 
@@ -807,8 +819,8 @@ def build_module_command(
807
819
 
808
820
  # Build the command with schema-generated options + built-in options
809
821
  _epilog_parts: list[str] = []
810
- if not _verbose_help:
811
- _epilog_parts.append("Use --verbose to show all options (including built-in options).")
822
+ if not _all_options_help:
823
+ _epilog_parts.append("Use --all-options to show all options (including built-in options).")
812
824
  if _docs_url:
813
825
  _epilog_parts.append(f"Docs: {_docs_url}/commands/{effective_cmd_name}")
814
826
  _epilog = "\n".join(_epilog_parts) if _epilog_parts else None
@@ -819,8 +831,8 @@ def build_module_command(
819
831
  epilog=_epilog,
820
832
  )
821
833
 
822
- # Add built-in options (hidden unless --verbose is passed with --help)
823
- _hide = not _verbose_help
834
+ # Add built-in options (hidden unless --all-options is passed with --help)
835
+ _hide = not _all_options_help
824
836
  cmd.params.append(
825
837
  click.Option(
826
838
  ["--input"],
@@ -936,7 +948,7 @@ def build_module_command(
936
948
  "format",
937
949
  "fields",
938
950
  "sandbox",
939
- "verbose",
951
+ "all_options",
940
952
  "dry_run",
941
953
  "trace",
942
954
  "stream",
@@ -371,11 +371,17 @@ def register_exec_command(
371
371
 
372
372
  audit_start = time.monotonic()
373
373
  try:
374
- timeout = approval_timeout if approval_timeout is not None else 60
375
- check_approval(module_def, auto_approve=auto_approve, timeout=timeout)
374
+ # D11-012: pass approval_timeout through unchanged; check_approval
375
+ # internally resolves explicit arg > APCORE_CLI_APPROVAL_TIMEOUT env > 60s.
376
+ check_approval(module_def, auto_approve=auto_approve, timeout=approval_timeout)
376
377
 
377
378
  if dry_run:
378
- preflight = executor.validate(module_id, merged)
379
+ # D11-013: defensive guard mirroring TS discovery.ts:334. Some
380
+ # embedder-provided Executor subclasses lack `validate`; emit a
381
+ # synthetic `{valid: True}` preflight instead of crashing with
382
+ # AttributeError. Rust uses build_preflight_result for the same
383
+ # synthetic fallback.
384
+ preflight = executor.validate(module_id, merged) if hasattr(executor, "validate") else {"valid": True}
379
385
  format_preflight_result(preflight, output_format)
380
386
  return
381
387
 
@@ -40,11 +40,9 @@ EXIT_CONFIG_BIND_ERROR = 65
40
40
  EXIT_CONFIG_MOUNT_ERROR = 66
41
41
  EXIT_ERROR_FORMATTER_DUPLICATE = 70
42
42
  EXIT_ACL_DENIED = 77
43
- # All four namespace/env errors share exit code 78 per protocol spec.
43
+ # Both namespace errors share exit code 78 per protocol spec.
44
44
  EXIT_CONFIG_NAMESPACE_RESERVED = 78
45
45
  EXIT_CONFIG_NAMESPACE_DUPLICATE = 78
46
- EXIT_CONFIG_ENV_PREFIX_CONFLICT = 78
47
- EXIT_CONFIG_ENV_MAP_CONFLICT = 78
48
46
  EXIT_SIGINT = 130
49
47
 
50
48
  # ---------------------------------------------------------------------------
@@ -55,10 +55,10 @@ logger = logging.getLogger("apcore_cli")
55
55
  EXIT_CONFIG_NOT_FOUND = 47
56
56
 
57
57
 
58
- def _has_verbose_flag(argv: list[str] | None = None) -> bool:
59
- """Check if --verbose is present in argv (pre-parse, before Click)."""
58
+ def _has_all_options_flag(argv: list[str] | None = None) -> bool:
59
+ """Check if --all-options is present in argv (pre-parse, before Click)."""
60
60
  args = argv if argv is not None else sys.argv[1:]
61
- return "--verbose" in args
61
+ return "--all-options" in args
62
62
 
63
63
 
64
64
  def create_cli(
@@ -166,9 +166,9 @@ def create_cli(
166
166
  if prog_name is None:
167
167
  prog_name = os.path.basename(sys.argv[0]) or "apcore-cli"
168
168
 
169
- # Pre-parse --verbose before Click runs so build_module_command knows
169
+ # Pre-parse --all-options before Click runs so build_module_command knows
170
170
  # whether to hide built-in options.
171
- verbose = _has_verbose_flag()
171
+ verbose = _has_all_options_flag()
172
172
  set_verbose_help(verbose)
173
173
 
174
174
  # Resolve CLI log level (3-tier precedence, evaluated before Click runs):
@@ -423,8 +423,8 @@ def create_cli(
423
423
  help="Log verbosity. Overrides APCORE_CLI_LOGGING_LEVEL and APCORE_LOGGING_LEVEL env vars.",
424
424
  )
425
425
  @click.option(
426
- "--verbose",
427
- "verbose_help",
426
+ "--all-options",
427
+ "all_options_help",
428
428
  is_flag=True,
429
429
  default=False,
430
430
  help="Show all options in help output (including built-in options).",
@@ -433,7 +433,7 @@ def create_cli(
433
433
  def cli(
434
434
  ctx: click.Context,
435
435
  log_level: str | None = None,
436
- verbose_help: bool = False,
436
+ all_options_help: bool = False,
437
437
  **_discovery_opts: Any, # --extensions-dir/--commands-dir/--binding when standalone
438
438
  ) -> None:
439
439
  if log_level is not None:
@@ -443,7 +443,7 @@ def create_cli(
443
443
  logging.getLogger("apcore").setLevel(apcore_level)
444
444
  ctx.ensure_object(dict)
445
445
  ctx.obj["extensions_dir"] = ext_dir
446
- ctx.obj["verbose_help"] = verbose_help
446
+ ctx.obj["all_options_help"] = all_options_help
447
447
  ctx.obj["exposure_filter"] = exposure_filter
448
448
 
449
449
  # FE-13 §4.1 / FR-13-13: --extensions-dir, --commands-dir, --binding are
@@ -457,7 +457,7 @@ def create_cli(
457
457
  click.Option(
458
458
  ["--extensions-dir", "extensions_dir_opt"],
459
459
  default=None,
460
- help="Path to apcore extensions directory.",
460
+ help="Path to extensions directory.",
461
461
  ),
462
462
  click.Option(
463
463
  ["--commands-dir", "commands_dir_opt"],
@@ -42,7 +42,7 @@ def register_init_command(cli: click.Group) -> None:
42
42
 
43
43
  @cli.group("init")
44
44
  def init_group():
45
- """Scaffold new apcore modules."""
45
+ """Scaffold new modules."""
46
46
  pass
47
47
 
48
48
  @init_group.command("module")